<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://rsewiki.electro.dtu.dk/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=S192320</id>
	<title>Rsewiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://rsewiki.electro.dtu.dk/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=S192320"/>
	<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Special:Contributions/S192320"/>
	<updated>2026-04-25T17:06:02Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.41.1</generator>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5122</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5122"/>
		<updated>2020-12-09T10:52:17Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface. The framework provides the developer with the most common user-interface objects, however, it might be lacking some of the objects that other frameworks support, like basic shapes.&lt;br /&gt;
&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039;*To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
When your user-interface is finished and is saved as .ui file from The Qt designer, the file is ready to be translated to C++ or Python interface code.&lt;br /&gt;
The .ui file will be converted to .py file with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039; *This file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By editing the object by the name, the actual user interface now can also be tweaked by editing the Python code, but this usually requires a more in-depth experience and knowledge of how Python user-interface code works.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program. Its objects don&#039;t have any functionality, because the actions have not been programmed yet for them.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined. We sort of define/initialize the functions that are going to be used in the program.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration just by reading and sending byte-type network messages from/to the IP adress port.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 $ pip install sockets&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client function connected with the button action when &amp;quot;clicked&amp;quot;.&lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type, and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP address using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented as well, for example, to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039; &#039;&#039; *To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section right above this section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream. When the subscription has been made, the Pi will provide a continuous stream of replies for the subscribed parameter.&lt;br /&gt;
&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039; &#039;&#039;*If the manual speed is not specified, the remote control is not possible.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip for designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039; How to set up mjpeg streamer on Raspberry-Pi &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
The following functions are crucial to actually display the image onto the label of the Qlabel object. The covert_cv_qt(cv_img) takes the output image of the mjpeg streamer and converts it to the qt format so it can be displayed in our Qt Gui application.&lt;br /&gt;
&lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
The update_image() actually updates the image and displays it to the Qlabel. As you can notice, this function uses the convert_cv_qt function to transform the received image in order to display it properly.&lt;br /&gt;
&lt;br /&gt;
    def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) # Putting image stream on the label&lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) # Putting snapped image on the frame&lt;br /&gt;
            self.take_snapshot = False&lt;br /&gt;
&lt;br /&gt;
Current setup for the Camera includes also the functionality to take a picture and record a video with the camera. It is a little more simple with taking a picture from the stream because we are just taking last snapped image from the update_image() function and save the image under the entered filename using snapshot_save() function. As it can be seen below, the picture by default saves the &amp;quot;.jpg&amp;quot; file with the current date and time. Otherwise, an user can enter the filename that he would like to use and save it under that. And using the opencv function cv2.imwrite() we save the picture.&lt;br /&gt;
&lt;br /&gt;
 def snapshot_save(self):    &lt;br /&gt;
        t = time.gmtime()&lt;br /&gt;
        if self.ui.Camera_SnapFilename.text() == &amp;quot;&amp;quot;:&lt;br /&gt;
            filename = &#039;snapshot_&#039; + str(t.tm_year) + str(t.tm_mon) + str(t.tm_mday) + &amp;quot;_&amp;quot; + str(t.tm_hour) + str(t.tm_min) + str(t.tm_sec) +&#039;.jpg&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            filename = str(self.ui.Camera_SnapFilename.text() + &#039;.jpg&#039;)&lt;br /&gt;
        cv2.imwrite(filename, self.snapshot_image) # Saving the image under the filename&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To record the video from the video stream, the other class was implemented which took care of the video recording functionality. &lt;br /&gt;
The key two functions to ensure the video starting/stopping and saving the video are the following. video_StartRecording() functions connects the update_image() function with the video recording thread and starts it. The video_recording flag is True. &lt;br /&gt;
&lt;br /&gt;
    def video_StartRecording(self):&lt;br /&gt;
        self.video_recording = True&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread = VideoRecordingThread()&lt;br /&gt;
            self.VideoRecordingThread.change_pixmap_signal.connect(self.update_image)&lt;br /&gt;
            self.VideoRecordingThread.start()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
 &lt;br /&gt;
Meanwhile, the recording is happening, the toogleRecLabel(), makes the recording status label visible, which indicates that the recording is happening. And the    &lt;br /&gt;
&lt;br /&gt;
    def toogleRecLabel(self):&lt;br /&gt;
        self.toogleSignalON = not self.toogleSignalON&lt;br /&gt;
        if self.toogleSignalON:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        else:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(False)&lt;br /&gt;
&lt;br /&gt;
Similarly, the video_StopRecording() function stops the video recording by stopping the video thread and setting the video_recording flag to False.&lt;br /&gt;
            &lt;br /&gt;
    def video_StopRecording(self):&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread.stop()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording Stopping...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True            &lt;br /&gt;
        self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        self.video_recording = False     &lt;br /&gt;
&lt;br /&gt;
The actual Video recording class that controls the video recording can be seen below. The function &#039;&#039;out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&#039;&#039; defines the output object and the &#039;&#039;cv_img = cap.read()&#039;&#039; and &#039;&#039;out.write(cv_img) &#039;&#039; gets the actual image from the cv2.VideoCapture() and then writes the image to the actual output video file.&lt;br /&gt;
&lt;br /&gt;
 class VideoRecordingThread(QThread): &lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;)&lt;br /&gt;
        frame_width = int(cap.get(3))&lt;br /&gt;
        frame_height = int(cap.get(4))&lt;br /&gt;
        out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                out.write(cv_img)  &lt;br /&gt;
        cap.release()&lt;br /&gt;
        out.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== Source Code ==&lt;br /&gt;
The Full Source code can be seen in the following link -&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Source Code]]&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5121</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5121"/>
		<updated>2020-12-09T10:41:44Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface. The framework provides the developer with the most common user-interface objects, however, it might be lacking some of the objects that other frameworks support, like basic shapes.&lt;br /&gt;
&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039;*To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
When your user-interface is finished and is saved as .ui file from The Qt designer, the file is ready to be translated to C++ or Python interface code.&lt;br /&gt;
The .ui file will be converted to .py file with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039; *This file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By editing the object by the name, the actual user interface now can also be tweaked by editing the Python code, but this usually requires a more in-depth experience and knowledge of how Python user-interface code works.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program. Its objects don&#039;t have any functionality, because the actions have not been programmed yet for them.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined. We sort of define/initialize the functions that are going to be used in the program.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration just by reading and sending byte-type network messages from/to the IP adress port.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 $ pip install sockets&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client function connected with the button action when &amp;quot;clicked&amp;quot;.&lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type, and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP address using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented as well, for example, to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039; &#039;&#039; *To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section right above this section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream. When the subscription has been made, the Pi will provide a continuous stream of replies for the subscribed parameter.&lt;br /&gt;
&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039; &#039;&#039;*If the manual speed is not specified, the remote control is not possible.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip for designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039; How to set up mjpeg streamer on Raspberry-Pi &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
The following functions are crucial to actually display the image onto the label of the Qlabel object. The covert_cv_qt(cv_img) takes the output image of the mjpeg streamer and converts it to the qt format so it can be displayed in our Qt Gui application.&lt;br /&gt;
&lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
The update_image() actually updates the image and displays it to the Qlabel. As you can notice, this function uses the convert_cv_qt function to transform the received image in order to display it properly.&lt;br /&gt;
&lt;br /&gt;
    def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) &lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) &lt;br /&gt;
            self.take_snapshot = False&lt;br /&gt;
&lt;br /&gt;
Current setup for the Camera includes also the functionality to take a picture and record a video with the camera. It is a little more simple with taking a picture from the stream because we are just taking last snapped image from the update_image() function and save the image under the entered filename using snapshot_save() function. As it can be seen below, the picture by default saves the &amp;quot;.jpg&amp;quot; file with the current date and time. Otherwise, an user can enter the filename that he would like to use and save it under that. And using the opencv function cv2.imwrite() we save the picture.&lt;br /&gt;
&lt;br /&gt;
 def snapshot_save(self):    &lt;br /&gt;
        t = time.gmtime()&lt;br /&gt;
        if self.ui.Camera_SnapFilename.text() == &amp;quot;&amp;quot;:&lt;br /&gt;
            filename = &#039;snapshot_&#039; + str(t.tm_year) + str(t.tm_mon) + str(t.tm_mday) + &amp;quot;_&amp;quot; + str(t.tm_hour) + str(t.tm_min) + str(t.tm_sec) +&#039;.jpg&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            filename = str(self.ui.Camera_SnapFilename.text() + &#039;.jpg&#039;)&lt;br /&gt;
        cv2.imwrite(filename, self.snapshot_image)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To record the video from the video stream, the other class was implemented which took care of the video recording functionality. &lt;br /&gt;
The key two functions to ensure the video starting/stopping and saving the video are the following. video_StartRecording() functions connects the update_image() function with the video reocrding thread and starts it. The video_recording flag is True. &lt;br /&gt;
&lt;br /&gt;
    def video_StartRecording(self):&lt;br /&gt;
        self.video_recording = True&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread = VideoRecordingThread()&lt;br /&gt;
            self.VideoRecordingThread.change_pixmap_signal.connect(self.update_image)&lt;br /&gt;
            self.VideoRecordingThread.start()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
 &lt;br /&gt;
Meanwhile, the recording is happening, the toogleRecLabel(), makes the recirding status label visible, which indicates that the recoding is happening.    &lt;br /&gt;
&lt;br /&gt;
    def toogleRecLabel(self):&lt;br /&gt;
        self.toogleSignalON = not self.toogleSignalON&lt;br /&gt;
        if self.toogleSignalON:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        else:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(False)&lt;br /&gt;
&lt;br /&gt;
Similarly, the video_StopRecording() function stops the video recording by stopping the video thread and setting video_recording flag to False.&lt;br /&gt;
            &lt;br /&gt;
    def video_StopRecording(self):&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread.stop()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording Stopping...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True            &lt;br /&gt;
        self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        self.video_recording = False     &lt;br /&gt;
&lt;br /&gt;
The actual Video recording class that controls the video recording can be seen below. The function &#039;&#039;out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&#039;&#039; defines the output object and the &#039;&#039;cv_img = cap.read()&#039;&#039; and &#039;&#039;out.write(cv_img) &#039;&#039; gets the actual image from the cv2.VideoCapture() and then writes the image to the actual output video file.&lt;br /&gt;
&lt;br /&gt;
 class VideoRecordingThread(QThread): &lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;)&lt;br /&gt;
        frame_width = int(cap.get(3))&lt;br /&gt;
        frame_height = int(cap.get(4))&lt;br /&gt;
        out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                out.write(cv_img)  &lt;br /&gt;
        cap.release()&lt;br /&gt;
        out.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== Source Code ==&lt;br /&gt;
The Full Source code can be seen in the following link -&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Source Code]]&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5120</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5120"/>
		<updated>2020-12-09T10:41:32Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface. The framework provides the developer with the most common user-interface objects, however, it might be lacking some of the objects that other frameworks support, like basic shapes.&lt;br /&gt;
&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039;*To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
When your user-interface is finished and is saved as .ui file from The Qt designer, the file is ready to be translated to C++ or Python interface code.&lt;br /&gt;
The .ui file will be converted to .py file with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039; *This file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By editing the object by the name, the actual user interface now can also be tweaked by editing the Python code, but this usually requires a more in-depth experience and knowledge of how Python user-interface code works.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program. Its objects don&#039;t have any functionality, because the actions have not been programmed yet for them.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined. We sort of define/initialize the functions that are going to be used in the program.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration just by reading and sending byte-type network messages from/to the IP adress port.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 $ pip install sockets&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client function connected with the button action when &amp;quot;clicked&amp;quot;.&lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type, and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP address using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented as well, for example, to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039; &#039;&#039; *To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section right above this section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream. When the subscription has been made, the Pi will provide a continuous stream of replies for the subscribed parameter.&lt;br /&gt;
&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039; &#039;&#039;*If the manual speed is not specified, the remote control is not possible.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip for designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039; How to set up mjpeg streamer on Raspberry-Pi &#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
The following functions are crucial to actually display the image onto the label of the Qlabel object. The covert_cv_qt(cv_img) takes the output image of the mjpeg streamer and converts it to the qt format so it can be displayed in our Qt Gui application.&lt;br /&gt;
&lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
The update_image() actually updates the image and displays it to the Qlabel. As you can notice, this function uses the convert_cv_qt function to transform the received image in order to display it properly.&lt;br /&gt;
&lt;br /&gt;
    def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) &lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) &lt;br /&gt;
            self.take_snapshot = False&lt;br /&gt;
&lt;br /&gt;
Current setup for the Camera includes also the functionality to take a picture and record a video with the camera. It is a little more simple with taking a picture from the stream because we are just taking last snapped image from the update_image() function and save the image under the entered filename using snapshot_save() function. As it can be seen below, the picture by default saves the &amp;quot;.jpg&amp;quot; file with the current date and time. Otherwise, an user can enter the filename that he would like to use and save it under that. And using the opencv function cv2.imwrite() we save the picture.&lt;br /&gt;
&lt;br /&gt;
 def snapshot_save(self):    &lt;br /&gt;
        t = time.gmtime()&lt;br /&gt;
        if self.ui.Camera_SnapFilename.text() == &amp;quot;&amp;quot;:&lt;br /&gt;
            filename = &#039;snapshot_&#039; + str(t.tm_year) + str(t.tm_mon) + str(t.tm_mday) + &amp;quot;_&amp;quot; + str(t.tm_hour) + str(t.tm_min) + str(t.tm_sec) +&#039;.jpg&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            filename = str(self.ui.Camera_SnapFilename.text() + &#039;.jpg&#039;)&lt;br /&gt;
        cv2.imwrite(filename, self.snapshot_image)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To record the video from the video stream, the other class was implemented which took care of the video recording functionality. &lt;br /&gt;
The key two functions to ensure the video starting/stopping and saving the video are the following. video_StartRecording() functions connects the update_image() function with the video reocrding thread and starts it. The video_recording flag is True. &lt;br /&gt;
&lt;br /&gt;
    def video_StartRecording(self):&lt;br /&gt;
        self.video_recording = True&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread = VideoRecordingThread()&lt;br /&gt;
            self.VideoRecordingThread.change_pixmap_signal.connect(self.update_image)&lt;br /&gt;
            self.VideoRecordingThread.start()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
 &lt;br /&gt;
Meanwhile, the recording is happening, the toogleRecLabel(), makes the recirding status label visible, which indicates that the recoding is happening.    &lt;br /&gt;
&lt;br /&gt;
    def toogleRecLabel(self):&lt;br /&gt;
        self.toogleSignalON = not self.toogleSignalON&lt;br /&gt;
        if self.toogleSignalON:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        else:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(False)&lt;br /&gt;
&lt;br /&gt;
Similarly, the video_StopRecording() function stops the video recording by stopping the video thread and setting video_recording flag to False.&lt;br /&gt;
            &lt;br /&gt;
    def video_StopRecording(self):&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread.stop()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording Stopping...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True            &lt;br /&gt;
        self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        self.video_recording = False     &lt;br /&gt;
&lt;br /&gt;
The actual Video recording class that controls the video recording can be seen below. The function &#039;&#039;out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&#039;&#039; defines the output object and the &#039;&#039;cv_img = cap.read()&#039;&#039; and &#039;&#039;out.write(cv_img) &#039;&#039; gets the actual image from the cv2.VideoCapture() and then writes the image to the actual output video file.&lt;br /&gt;
&lt;br /&gt;
 class VideoRecordingThread(QThread): &lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;)&lt;br /&gt;
        frame_width = int(cap.get(3))&lt;br /&gt;
        frame_height = int(cap.get(4))&lt;br /&gt;
        out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                out.write(cv_img)  &lt;br /&gt;
        cap.release()&lt;br /&gt;
        out.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== Source Code ==&lt;br /&gt;
The Full Source code can be seen in the following link -&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Source Code]]&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5119</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5119"/>
		<updated>2020-12-09T10:38:04Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Manual Robot Control from the GUI */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface. The framework provides the developer with the most common user-interface objects, however, it might be lacking some of the objects that other frameworks support, like basic shapes.&lt;br /&gt;
&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039;*To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
When your user-interface is finished and is saved as .ui file from The Qt designer, the file is ready to be translated to C++ or Python interface code.&lt;br /&gt;
The .ui file will be converted to .py file with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039; *This file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By editing the object by the name, the actual user interface now can also be tweaked by editing the Python code, but this usually requires a more in-depth experience and knowledge of how Python user-interface code works.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program. Its objects don&#039;t have any functionality, because the actions have not been programmed yet for them.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined. We sort of define/initialize the functions that are going to be used in the program.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration just by reading and sending byte-type network messages from/to the IP adress port.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 $ pip install sockets&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client function connected with the button action when &amp;quot;clicked&amp;quot;.&lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type, and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP address using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented as well, for example, to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039; &#039;&#039; *To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section right above this section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream. When the subscription has been made, the Pi will provide a continuous stream of replies for the subscribed parameter.&lt;br /&gt;
&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039; &#039;&#039;*If the manual speed is not specified, the remote control is not possible.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
The following functions are crucial to actually display the image onto the label of the Qlabel object. The covert_cv_qt(cv_img) takes the output image of the mjpeg streamer and converts it to the qt format so it can be displayed in our Qt Gui application.&lt;br /&gt;
&lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
The update_image() actually updates the image and displays it to the Qlabel. As you can notice, this function uses the convert_cv_qt function to transform the received image in order to display it properly.&lt;br /&gt;
&lt;br /&gt;
    def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) &lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) &lt;br /&gt;
            self.take_snapshot = False&lt;br /&gt;
&lt;br /&gt;
Current setup for the Camera includes also the functionality to take a picture and record a video with the camera. It is a little more simple with taking a picture from the stream because we are just taking last snapped image from the update_image() function and save the image under the entered filename using snapshot_save() function. As it can be seen below, the picture by default saves the &amp;quot;.jpg&amp;quot; file with the current date and time. Otherwise, an user can enter the filename that he would like to use and save it under that. And using the opencv function cv2.imwrite() we save the picture.&lt;br /&gt;
&lt;br /&gt;
 def snapshot_save(self):    &lt;br /&gt;
        t = time.gmtime()&lt;br /&gt;
        if self.ui.Camera_SnapFilename.text() == &amp;quot;&amp;quot;:&lt;br /&gt;
            filename = &#039;snapshot_&#039; + str(t.tm_year) + str(t.tm_mon) + str(t.tm_mday) + &amp;quot;_&amp;quot; + str(t.tm_hour) + str(t.tm_min) + str(t.tm_sec) +&#039;.jpg&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            filename = str(self.ui.Camera_SnapFilename.text() + &#039;.jpg&#039;)&lt;br /&gt;
        cv2.imwrite(filename, self.snapshot_image)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To record the video from the video stream, the other class was implemented which took care of the video recording functionality. &lt;br /&gt;
The key two functions to ensure the video starting/stopping and saving the video are the following. video_StartRecording() functions connects the update_image() function with the video reocrding thread and starts it. The video_recording flag is True. &lt;br /&gt;
&lt;br /&gt;
    def video_StartRecording(self):&lt;br /&gt;
        self.video_recording = True&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread = VideoRecordingThread()&lt;br /&gt;
            self.VideoRecordingThread.change_pixmap_signal.connect(self.update_image)&lt;br /&gt;
            self.VideoRecordingThread.start()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
 &lt;br /&gt;
Meanwhile, the recording is happening, the toogleRecLabel(), makes the recirding status label visible, which indicates that the recoding is happening.    &lt;br /&gt;
&lt;br /&gt;
    def toogleRecLabel(self):&lt;br /&gt;
        self.toogleSignalON = not self.toogleSignalON&lt;br /&gt;
        if self.toogleSignalON:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        else:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(False)&lt;br /&gt;
&lt;br /&gt;
Similarly, the video_StopRecording() function stops the video recording by stopping the video thread and setting video_recording flag to False.&lt;br /&gt;
            &lt;br /&gt;
    def video_StopRecording(self):&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread.stop()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording Stopping...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True            &lt;br /&gt;
        self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        self.video_recording = False     &lt;br /&gt;
&lt;br /&gt;
The actual Video recording class that controls the video recording can be seen below. The function &#039;&#039;out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&#039;&#039; defines the output object and the &#039;&#039;cv_img = cap.read()&#039;&#039; and &#039;&#039;out.write(cv_img) &#039;&#039; gets the actual image from the cv2.VideoCapture() and then writes the image to the actual output video file.&lt;br /&gt;
&lt;br /&gt;
 class VideoRecordingThread(QThread): &lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;)&lt;br /&gt;
        frame_width = int(cap.get(3))&lt;br /&gt;
        frame_height = int(cap.get(4))&lt;br /&gt;
        out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                out.write(cv_img)  &lt;br /&gt;
        cap.release()&lt;br /&gt;
        out.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== Source Code ==&lt;br /&gt;
The Full Source code can be seen in the following link -&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Source Code]]&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5118</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5118"/>
		<updated>2020-12-09T10:28:48Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Receiving, Decoding and Translating received messages */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface. The framework provides the developer with the most common user-interface objects, however, it might be lacking some of the objects that other frameworks support, like basic shapes.&lt;br /&gt;
&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039;*To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
When your user-interface is finished and is saved as .ui file from The Qt designer, the file is ready to be translated to C++ or Python interface code.&lt;br /&gt;
The .ui file will be converted to .py file with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039; *This file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By editing the object by the name, the actual user interface now can also be tweaked by editing the Python code, but this usually requires a more in-depth experience and knowledge of how Python user-interface code works.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program. Its objects don&#039;t have any functionality, because the actions have not been programmed yet for them.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined. We sort of define/initialize the functions that are going to be used in the program.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration just by reading and sending byte-type network messages from/to the IP adress port.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 $ pip install sockets&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client function connected with the button action when &amp;quot;clicked&amp;quot;.&lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type, and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP address using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented as well, for example, to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039; &#039;&#039; *To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section right above this section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream. When the subscription has been made, the Pi will provide a continuous stream of replies for the subscribed parameter.&lt;br /&gt;
&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
The following functions are crucial to actually display the image onto the label of the Qlabel object. The covert_cv_qt(cv_img) takes the output image of the mjpeg streamer and converts it to the qt format so it can be displayed in our Qt Gui application.&lt;br /&gt;
&lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
The update_image() actually updates the image and displays it to the Qlabel. As you can notice, this function uses the convert_cv_qt function to transform the received image in order to display it properly.&lt;br /&gt;
&lt;br /&gt;
    def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) &lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) &lt;br /&gt;
            self.take_snapshot = False&lt;br /&gt;
&lt;br /&gt;
Current setup for the Camera includes also the functionality to take a picture and record a video with the camera. It is a little more simple with taking a picture from the stream because we are just taking last snapped image from the update_image() function and save the image under the entered filename using snapshot_save() function. As it can be seen below, the picture by default saves the &amp;quot;.jpg&amp;quot; file with the current date and time. Otherwise, an user can enter the filename that he would like to use and save it under that. And using the opencv function cv2.imwrite() we save the picture.&lt;br /&gt;
&lt;br /&gt;
 def snapshot_save(self):    &lt;br /&gt;
        t = time.gmtime()&lt;br /&gt;
        if self.ui.Camera_SnapFilename.text() == &amp;quot;&amp;quot;:&lt;br /&gt;
            filename = &#039;snapshot_&#039; + str(t.tm_year) + str(t.tm_mon) + str(t.tm_mday) + &amp;quot;_&amp;quot; + str(t.tm_hour) + str(t.tm_min) + str(t.tm_sec) +&#039;.jpg&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            filename = str(self.ui.Camera_SnapFilename.text() + &#039;.jpg&#039;)&lt;br /&gt;
        cv2.imwrite(filename, self.snapshot_image)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To record the video from the video stream, the other class was implemented which took care of the video recording functionality. &lt;br /&gt;
The key two functions to ensure the video starting/stopping and saving the video are the following. video_StartRecording() functions connects the update_image() function with the video reocrding thread and starts it. The video_recording flag is True. &lt;br /&gt;
&lt;br /&gt;
    def video_StartRecording(self):&lt;br /&gt;
        self.video_recording = True&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread = VideoRecordingThread()&lt;br /&gt;
            self.VideoRecordingThread.change_pixmap_signal.connect(self.update_image)&lt;br /&gt;
            self.VideoRecordingThread.start()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
 &lt;br /&gt;
Meanwhile, the recording is happening, the toogleRecLabel(), makes the recirding status label visible, which indicates that the recoding is happening.    &lt;br /&gt;
&lt;br /&gt;
    def toogleRecLabel(self):&lt;br /&gt;
        self.toogleSignalON = not self.toogleSignalON&lt;br /&gt;
        if self.toogleSignalON:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        else:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(False)&lt;br /&gt;
&lt;br /&gt;
Similarly, the video_StopRecording() function stops the video recording by stopping the video thread and setting video_recording flag to False.&lt;br /&gt;
            &lt;br /&gt;
    def video_StopRecording(self):&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread.stop()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording Stopping...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True            &lt;br /&gt;
        self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        self.video_recording = False     &lt;br /&gt;
&lt;br /&gt;
The actual Video recording class that controls the video recording can be seen below. The function &#039;&#039;out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&#039;&#039; defines the output object and the &#039;&#039;cv_img = cap.read()&#039;&#039; and &#039;&#039;out.write(cv_img) &#039;&#039; gets the actual image from the cv2.VideoCapture() and then writes the image to the actual output video file.&lt;br /&gt;
&lt;br /&gt;
 class VideoRecordingThread(QThread): &lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;)&lt;br /&gt;
        frame_width = int(cap.get(3))&lt;br /&gt;
        frame_height = int(cap.get(4))&lt;br /&gt;
        out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                out.write(cv_img)  &lt;br /&gt;
        cap.release()&lt;br /&gt;
        out.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== Source Code ==&lt;br /&gt;
The Full Source code can be seen in the following link -&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Source Code]]&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5117</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5117"/>
		<updated>2020-12-09T10:19:27Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Programming GUI */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface. The framework provides the developer with the most common user-interface objects, however, it might be lacking some of the objects that other frameworks support, like basic shapes.&lt;br /&gt;
&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039;*To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
When your user-interface is finished and is saved as .ui file from The Qt designer, the file is ready to be translated to C++ or Python interface code.&lt;br /&gt;
The .ui file will be converted to .py file with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039; *This file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By editing the object by the name, the actual user interface now can also be tweaked by editing the Python code, but this usually requires a more in-depth experience and knowledge of how Python user-interface code works.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program. Its objects don&#039;t have any functionality, because the actions have not been programmed yet for them.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined. We sort of define/initialize the functions that are going to be used in the program.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration just by reading and sending byte-type network messages from/to the IP adress port.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 $ pip install sockets&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client function connected with the button action when &amp;quot;clicked&amp;quot;.&lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type, and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP address using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented as well, for example, to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039; &#039;&#039; *To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
The following functions are crucial to actually display the image onto the label of the Qlabel object. The covert_cv_qt(cv_img) takes the output image of the mjpeg streamer and converts it to the qt format so it can be displayed in our Qt Gui application.&lt;br /&gt;
&lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
The update_image() actually updates the image and displays it to the Qlabel. As you can notice, this function uses the convert_cv_qt function to transform the received image in order to display it properly.&lt;br /&gt;
&lt;br /&gt;
    def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) &lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) &lt;br /&gt;
            self.take_snapshot = False&lt;br /&gt;
&lt;br /&gt;
Current setup for the Camera includes also the functionality to take a picture and record a video with the camera. It is a little more simple with taking a picture from the stream because we are just taking last snapped image from the update_image() function and save the image under the entered filename using snapshot_save() function. As it can be seen below, the picture by default saves the &amp;quot;.jpg&amp;quot; file with the current date and time. Otherwise, an user can enter the filename that he would like to use and save it under that. And using the opencv function cv2.imwrite() we save the picture.&lt;br /&gt;
&lt;br /&gt;
 def snapshot_save(self):    &lt;br /&gt;
        t = time.gmtime()&lt;br /&gt;
        if self.ui.Camera_SnapFilename.text() == &amp;quot;&amp;quot;:&lt;br /&gt;
            filename = &#039;snapshot_&#039; + str(t.tm_year) + str(t.tm_mon) + str(t.tm_mday) + &amp;quot;_&amp;quot; + str(t.tm_hour) + str(t.tm_min) + str(t.tm_sec) +&#039;.jpg&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            filename = str(self.ui.Camera_SnapFilename.text() + &#039;.jpg&#039;)&lt;br /&gt;
        cv2.imwrite(filename, self.snapshot_image)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To record the video from the video stream, the other class was implemented which took care of the video recording functionality. &lt;br /&gt;
The key two functions to ensure the video starting/stopping and saving the video are the following. video_StartRecording() functions connects the update_image() function with the video reocrding thread and starts it. The video_recording flag is True. &lt;br /&gt;
&lt;br /&gt;
    def video_StartRecording(self):&lt;br /&gt;
        self.video_recording = True&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread = VideoRecordingThread()&lt;br /&gt;
            self.VideoRecordingThread.change_pixmap_signal.connect(self.update_image)&lt;br /&gt;
            self.VideoRecordingThread.start()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
 &lt;br /&gt;
Meanwhile, the recording is happening, the toogleRecLabel(), makes the recirding status label visible, which indicates that the recoding is happening.    &lt;br /&gt;
&lt;br /&gt;
    def toogleRecLabel(self):&lt;br /&gt;
        self.toogleSignalON = not self.toogleSignalON&lt;br /&gt;
        if self.toogleSignalON:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        else:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(False)&lt;br /&gt;
&lt;br /&gt;
Similarly, the video_StopRecording() function stops the video recording by stopping the video thread and setting video_recording flag to False.&lt;br /&gt;
            &lt;br /&gt;
    def video_StopRecording(self):&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread.stop()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording Stopping...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True            &lt;br /&gt;
        self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        self.video_recording = False     &lt;br /&gt;
&lt;br /&gt;
The actual Video recording class that controls the video recording can be seen below. The function &#039;&#039;out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&#039;&#039; defines the output object and the &#039;&#039;cv_img = cap.read()&#039;&#039; and &#039;&#039;out.write(cv_img) &#039;&#039; gets the actual image from the cv2.VideoCapture() and then writes the image to the actual output video file.&lt;br /&gt;
&lt;br /&gt;
 class VideoRecordingThread(QThread): &lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;)&lt;br /&gt;
        frame_width = int(cap.get(3))&lt;br /&gt;
        frame_height = int(cap.get(4))&lt;br /&gt;
        out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                out.write(cv_img)  &lt;br /&gt;
        cap.release()&lt;br /&gt;
        out.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== Source Code ==&lt;br /&gt;
The Full Source code can be seen in the following link -&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Source Code]]&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5116</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5116"/>
		<updated>2020-12-09T10:18:40Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Programming GUI */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface. The framework provides the developer with the most common user-interface objects, however, it might be lacking some of the objects that other frameworks support, like basic shapes.&lt;br /&gt;
&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039;*To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
When your user-interface is finished and is saved as .ui file from The Qt designer, the file is ready to be translated to C++ or Python interface code.&lt;br /&gt;
The .ui file will be converted to .py file with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039; *This file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By editing the object by the name, the actual user interface now can also be tweaked by editing the Python code, but this usually requires a more in-depth experience and knowledge of how Python user-interface code works.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program. Its objects don&#039;t have any functionality, because the actions have not been programmed yet for them.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined. We sort of define/initialize the functions that are going to be used in the program.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration just by reading and sending byte-type network messages from/to the IP adress port.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 $ pip install sockets&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client function connected with the button action when &amp;quot;clicked&amp;quot;.&lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type, and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP address using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented as well, for example, to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039;&#039; *To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
The following functions are crucial to actually display the image onto the label of the Qlabel object. The covert_cv_qt(cv_img) takes the output image of the mjpeg streamer and converts it to the qt format so it can be displayed in our Qt Gui application.&lt;br /&gt;
&lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
The update_image() actually updates the image and displays it to the Qlabel. As you can notice, this function uses the convert_cv_qt function to transform the received image in order to display it properly.&lt;br /&gt;
&lt;br /&gt;
    def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) &lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) &lt;br /&gt;
            self.take_snapshot = False&lt;br /&gt;
&lt;br /&gt;
Current setup for the Camera includes also the functionality to take a picture and record a video with the camera. It is a little more simple with taking a picture from the stream because we are just taking last snapped image from the update_image() function and save the image under the entered filename using snapshot_save() function. As it can be seen below, the picture by default saves the &amp;quot;.jpg&amp;quot; file with the current date and time. Otherwise, an user can enter the filename that he would like to use and save it under that. And using the opencv function cv2.imwrite() we save the picture.&lt;br /&gt;
&lt;br /&gt;
 def snapshot_save(self):    &lt;br /&gt;
        t = time.gmtime()&lt;br /&gt;
        if self.ui.Camera_SnapFilename.text() == &amp;quot;&amp;quot;:&lt;br /&gt;
            filename = &#039;snapshot_&#039; + str(t.tm_year) + str(t.tm_mon) + str(t.tm_mday) + &amp;quot;_&amp;quot; + str(t.tm_hour) + str(t.tm_min) + str(t.tm_sec) +&#039;.jpg&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            filename = str(self.ui.Camera_SnapFilename.text() + &#039;.jpg&#039;)&lt;br /&gt;
        cv2.imwrite(filename, self.snapshot_image)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To record the video from the video stream, the other class was implemented which took care of the video recording functionality. &lt;br /&gt;
The key two functions to ensure the video starting/stopping and saving the video are the following. video_StartRecording() functions connects the update_image() function with the video reocrding thread and starts it. The video_recording flag is True. &lt;br /&gt;
&lt;br /&gt;
    def video_StartRecording(self):&lt;br /&gt;
        self.video_recording = True&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread = VideoRecordingThread()&lt;br /&gt;
            self.VideoRecordingThread.change_pixmap_signal.connect(self.update_image)&lt;br /&gt;
            self.VideoRecordingThread.start()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
 &lt;br /&gt;
Meanwhile, the recording is happening, the toogleRecLabel(), makes the recirding status label visible, which indicates that the recoding is happening.    &lt;br /&gt;
&lt;br /&gt;
    def toogleRecLabel(self):&lt;br /&gt;
        self.toogleSignalON = not self.toogleSignalON&lt;br /&gt;
        if self.toogleSignalON:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        else:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(False)&lt;br /&gt;
&lt;br /&gt;
Similarly, the video_StopRecording() function stops the video recording by stopping the video thread and setting video_recording flag to False.&lt;br /&gt;
            &lt;br /&gt;
    def video_StopRecording(self):&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread.stop()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording Stopping...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True            &lt;br /&gt;
        self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        self.video_recording = False     &lt;br /&gt;
&lt;br /&gt;
The actual Video recording class that controls the video recording can be seen below. The function &#039;&#039;out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&#039;&#039; defines the output object and the &#039;&#039;cv_img = cap.read()&#039;&#039; and &#039;&#039;out.write(cv_img) &#039;&#039; gets the actual image from the cv2.VideoCapture() and then writes the image to the actual output video file.&lt;br /&gt;
&lt;br /&gt;
 class VideoRecordingThread(QThread): &lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;)&lt;br /&gt;
        frame_width = int(cap.get(3))&lt;br /&gt;
        frame_height = int(cap.get(4))&lt;br /&gt;
        out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                out.write(cv_img)  &lt;br /&gt;
        cap.release()&lt;br /&gt;
        out.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== Source Code ==&lt;br /&gt;
The Full Source code can be seen in the following link -&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Source Code]]&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5115</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5115"/>
		<updated>2020-12-09T10:04:38Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Qt GUI interface design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface. The framework provides the developer with the most common user-interface objects, however, it might be lacking some of the objects that other frameworks support, like basic shapes.&lt;br /&gt;
&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039;*To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
When your user-interface is finished and is saved as .ui file from The Qt designer, the file is ready to be translated to C++ or Python interface code.&lt;br /&gt;
The .ui file will be converted to .py file with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039; *This file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By editing the object by the name, the actual user interface now can also be tweaked by editing the Python code, but this usually requires a more in-depth experience and knowledge of how Python user-interface code works.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program. Its objects don&#039;t have any functionality, because the actions have not been programmed yet for them.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined. We sort of define/initialize the functions that are going to be used in the program.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
The following functions are crucial to actually display the image onto the label of the Qlabel object. The covert_cv_qt(cv_img) takes the output image of the mjpeg streamer and converts it to the qt format so it can be displayed in our Qt Gui application.&lt;br /&gt;
&lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
The update_image() actually updates the image and displays it to the Qlabel. As you can notice, this function uses the convert_cv_qt function to transform the received image in order to display it properly.&lt;br /&gt;
&lt;br /&gt;
    def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) &lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) &lt;br /&gt;
            self.take_snapshot = False&lt;br /&gt;
&lt;br /&gt;
Current setup for the Camera includes also the functionality to take a picture and record a video with the camera. It is a little more simple with taking a picture from the stream because we are just taking last snapped image from the update_image() function and save the image under the entered filename using snapshot_save() function. As it can be seen below, the picture by default saves the &amp;quot;.jpg&amp;quot; file with the current date and time. Otherwise, an user can enter the filename that he would like to use and save it under that. And using the opencv function cv2.imwrite() we save the picture.&lt;br /&gt;
&lt;br /&gt;
 def snapshot_save(self):    &lt;br /&gt;
        t = time.gmtime()&lt;br /&gt;
        if self.ui.Camera_SnapFilename.text() == &amp;quot;&amp;quot;:&lt;br /&gt;
            filename = &#039;snapshot_&#039; + str(t.tm_year) + str(t.tm_mon) + str(t.tm_mday) + &amp;quot;_&amp;quot; + str(t.tm_hour) + str(t.tm_min) + str(t.tm_sec) +&#039;.jpg&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            filename = str(self.ui.Camera_SnapFilename.text() + &#039;.jpg&#039;)&lt;br /&gt;
        cv2.imwrite(filename, self.snapshot_image)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To record the video from the video stream, the other class was implemented which took care of the video recording functionality. &lt;br /&gt;
The key two functions to ensure the video starting/stopping and saving the video are the following. video_StartRecording() functions connects the update_image() function with the video reocrding thread and starts it. The video_recording flag is True. &lt;br /&gt;
&lt;br /&gt;
    def video_StartRecording(self):&lt;br /&gt;
        self.video_recording = True&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread = VideoRecordingThread()&lt;br /&gt;
            self.VideoRecordingThread.change_pixmap_signal.connect(self.update_image)&lt;br /&gt;
            self.VideoRecordingThread.start()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
 &lt;br /&gt;
Meanwhile, the recording is happening, the toogleRecLabel(), makes the recirding status label visible, which indicates that the recoding is happening.    &lt;br /&gt;
&lt;br /&gt;
    def toogleRecLabel(self):&lt;br /&gt;
        self.toogleSignalON = not self.toogleSignalON&lt;br /&gt;
        if self.toogleSignalON:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        else:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(False)&lt;br /&gt;
&lt;br /&gt;
Similarly, the video_StopRecording() function stops the video recording by stopping the video thread and setting video_recording flag to False.&lt;br /&gt;
            &lt;br /&gt;
    def video_StopRecording(self):&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread.stop()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording Stopping...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True            &lt;br /&gt;
        self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        self.video_recording = False     &lt;br /&gt;
&lt;br /&gt;
The actual Video recording class that controls the video recording can be seen below. The function &#039;&#039;out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&#039;&#039; defines the output object and the &#039;&#039;cv_img = cap.read()&#039;&#039; and &#039;&#039;out.write(cv_img) &#039;&#039; gets the actual image from the cv2.VideoCapture() and then writes the image to the actual output video file.&lt;br /&gt;
&lt;br /&gt;
 class VideoRecordingThread(QThread): &lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;)&lt;br /&gt;
        frame_width = int(cap.get(3))&lt;br /&gt;
        frame_height = int(cap.get(4))&lt;br /&gt;
        out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                out.write(cv_img)  &lt;br /&gt;
        cap.release()&lt;br /&gt;
        out.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== Source Code ==&lt;br /&gt;
The Full Source code can be seen in the following link -&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Source Code]]&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5114</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5114"/>
		<updated>2020-12-09T09:59:30Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Qt GUI interface design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface. The framework provides the developer with the most common user-interface objects, however, it might be lacking some of the objects that other frameworks support, like basic shapes.&lt;br /&gt;
&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039;*To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
When your user-interface is finished and is saved as .ui file from The Qt designer, the file is ready to be translated to C++ or Python interface code.&lt;br /&gt;
The .ui file will be converted to .py file with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039; *This file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
The following functions are crucial to actually display the image onto the label of the Qlabel object. The covert_cv_qt(cv_img) takes the output image of the mjpeg streamer and converts it to the qt format so it can be displayed in our Qt Gui application.&lt;br /&gt;
&lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
The update_image() actually updates the image and displays it to the Qlabel. As you can notice, this function uses the convert_cv_qt function to transform the received image in order to display it properly.&lt;br /&gt;
&lt;br /&gt;
    def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) &lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) &lt;br /&gt;
            self.take_snapshot = False&lt;br /&gt;
&lt;br /&gt;
Current setup for the Camera includes also the functionality to take a picture and record a video with the camera. It is a little more simple with taking a picture from the stream because we are just taking last snapped image from the update_image() function and save the image under the entered filename using snapshot_save() function. As it can be seen below, the picture by default saves the &amp;quot;.jpg&amp;quot; file with the current date and time. Otherwise, an user can enter the filename that he would like to use and save it under that. And using the opencv function cv2.imwrite() we save the picture.&lt;br /&gt;
&lt;br /&gt;
 def snapshot_save(self):    &lt;br /&gt;
        t = time.gmtime()&lt;br /&gt;
        if self.ui.Camera_SnapFilename.text() == &amp;quot;&amp;quot;:&lt;br /&gt;
            filename = &#039;snapshot_&#039; + str(t.tm_year) + str(t.tm_mon) + str(t.tm_mday) + &amp;quot;_&amp;quot; + str(t.tm_hour) + str(t.tm_min) + str(t.tm_sec) +&#039;.jpg&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            filename = str(self.ui.Camera_SnapFilename.text() + &#039;.jpg&#039;)&lt;br /&gt;
        cv2.imwrite(filename, self.snapshot_image)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To record the video from the video stream, the other class was implemented which took care of the video recording functionality. &lt;br /&gt;
The key two functions to ensure the video starting/stopping and saving the video are the following. video_StartRecording() functions connects the update_image() function with the video reocrding thread and starts it. The video_recording flag is True. &lt;br /&gt;
&lt;br /&gt;
    def video_StartRecording(self):&lt;br /&gt;
        self.video_recording = True&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread = VideoRecordingThread()&lt;br /&gt;
            self.VideoRecordingThread.change_pixmap_signal.connect(self.update_image)&lt;br /&gt;
            self.VideoRecordingThread.start()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
 &lt;br /&gt;
Meanwhile, the recording is happening, the toogleRecLabel(), makes the recirding status label visible, which indicates that the recoding is happening.    &lt;br /&gt;
&lt;br /&gt;
    def toogleRecLabel(self):&lt;br /&gt;
        self.toogleSignalON = not self.toogleSignalON&lt;br /&gt;
        if self.toogleSignalON:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        else:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(False)&lt;br /&gt;
&lt;br /&gt;
Similarly, the video_StopRecording() function stops the video recording by stopping the video thread and setting video_recording flag to False.&lt;br /&gt;
            &lt;br /&gt;
    def video_StopRecording(self):&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread.stop()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording Stopping...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True            &lt;br /&gt;
        self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        self.video_recording = False     &lt;br /&gt;
&lt;br /&gt;
The actual Video recording class that controls the video recording can be seen below. The function &#039;&#039;out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&#039;&#039; defines the output object and the &#039;&#039;cv_img = cap.read()&#039;&#039; and &#039;&#039;out.write(cv_img) &#039;&#039; gets the actual image from the cv2.VideoCapture() and then writes the image to the actual output video file.&lt;br /&gt;
&lt;br /&gt;
 class VideoRecordingThread(QThread): &lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;)&lt;br /&gt;
        frame_width = int(cap.get(3))&lt;br /&gt;
        frame_height = int(cap.get(4))&lt;br /&gt;
        out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                out.write(cv_img)  &lt;br /&gt;
        cap.release()&lt;br /&gt;
        out.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== Source Code ==&lt;br /&gt;
The Full Source code can be seen in the following link -&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Source Code]]&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Source_Code&amp;diff=5113</id>
		<title>Source Code</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Source_Code&amp;diff=5113"/>
		<updated>2020-12-06T17:45:22Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* User Interface Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==User Interface Code==&lt;br /&gt;
&lt;br /&gt;
==Main Application Code==&lt;br /&gt;
&lt;br /&gt;
==Linesensor==&lt;br /&gt;
&lt;br /&gt;
==IR distance sensors==&lt;br /&gt;
&lt;br /&gt;
==Servo==&lt;br /&gt;
&lt;br /&gt;
==Video Threads==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Code&amp;diff=5112</id>
		<title>Code</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Code&amp;diff=5112"/>
		<updated>2020-12-06T17:45:10Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* The User Interface */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Code&amp;diff=5111</id>
		<title>Code</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Code&amp;diff=5111"/>
		<updated>2020-12-06T17:44:39Z</updated>

		<summary type="html">&lt;p&gt;S192320: Created page with &amp;quot;==The User Interface==    			# -*- coding: utf-8 -*-  			# Form implementation generated from reading ui file &amp;#039;Regbot_V1_9.ui&amp;#039; 			# 			# Created by: PyQt5 UI code generator 5....&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==The User Interface==&lt;br /&gt;
 &lt;br /&gt;
 			# -*- coding: utf-8 -*-&lt;br /&gt;
&lt;br /&gt;
			# Form implementation generated from reading ui file &#039;Regbot_V1_9.ui&#039;&lt;br /&gt;
			#&lt;br /&gt;
			# Created by: PyQt5 UI code generator 5.13.2&lt;br /&gt;
			#&lt;br /&gt;
			# WARNING! All changes made in this file will be lost!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
			from PyQt5 import QtCore, QtGui, QtWidgets&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
			class Ui_MainWindow(object):&lt;br /&gt;
				def setupUi(self, MainWindow):&lt;br /&gt;
					MainWindow.setObjectName(&amp;quot;MainWindow&amp;quot;)&lt;br /&gt;
					MainWindow.resize(1122, 555)&lt;br /&gt;
					self.centralwidget = QtWidgets.QWidget(MainWindow)&lt;br /&gt;
					self.centralwidget.setObjectName(&amp;quot;centralwidget&amp;quot;)&lt;br /&gt;
					self.SidebarMenu = QtWidgets.QToolBox(self.centralwidget)&lt;br /&gt;
					self.SidebarMenu.setGeometry(QtCore.QRect(0, 0, 331, 721))&lt;br /&gt;
					self.SidebarMenu.setAutoFillBackground(True)&lt;br /&gt;
					self.SidebarMenu.setObjectName(&amp;quot;SidebarMenu&amp;quot;)&lt;br /&gt;
					self.SidebarMenuPage1 = QtWidgets.QWidget()&lt;br /&gt;
					self.SidebarMenuPage1.setGeometry(QtCore.QRect(0, 0, 331, 640))&lt;br /&gt;
					self.SidebarMenuPage1.setObjectName(&amp;quot;SidebarMenuPage1&amp;quot;)&lt;br /&gt;
					self.line = QtWidgets.QFrame(self.SidebarMenuPage1)&lt;br /&gt;
					self.line.setGeometry(QtCore.QRect(10, 220, 311, 21))&lt;br /&gt;
					self.line.setFrameShape(QtWidgets.QFrame.HLine)&lt;br /&gt;
					self.line.setFrameShadow(QtWidgets.QFrame.Sunken)&lt;br /&gt;
					self.line.setObjectName(&amp;quot;line&amp;quot;)&lt;br /&gt;
					self.IP_Connect_frame = QtWidgets.QFrame(self.SidebarMenuPage1)&lt;br /&gt;
					self.IP_Connect_frame.setGeometry(QtCore.QRect(0, 0, 331, 111))&lt;br /&gt;
					self.IP_Connect_frame.setAutoFillBackground(True)&lt;br /&gt;
					self.IP_Connect_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
					self.IP_Connect_frame.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
					self.IP_Connect_frame.setObjectName(&amp;quot;IP_Connect_frame&amp;quot;)&lt;br /&gt;
					self.label_2 = QtWidgets.QLabel(self.IP_Connect_frame)&lt;br /&gt;
					self.label_2.setGeometry(QtCore.QRect(30, 90, 91, 16))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(True)&lt;br /&gt;
					font.setWeight(75)&lt;br /&gt;
					self.label_2.setFont(font)&lt;br /&gt;
					self.label_2.setObjectName(&amp;quot;label_2&amp;quot;)&lt;br /&gt;
					self.Disconnect_Cmd = QtWidgets.QPushButton(self.IP_Connect_frame)&lt;br /&gt;
					self.Disconnect_Cmd.setGeometry(QtCore.QRect(180, 60, 121, 23))&lt;br /&gt;
					self.Disconnect_Cmd.setObjectName(&amp;quot;Disconnect_Cmd&amp;quot;)&lt;br /&gt;
					self.connect_Cmd = QtWidgets.QPushButton(self.IP_Connect_frame)&lt;br /&gt;
					self.connect_Cmd.setGeometry(QtCore.QRect(30, 60, 131, 23))&lt;br /&gt;
					self.connect_Cmd.setObjectName(&amp;quot;connect_Cmd&amp;quot;)&lt;br /&gt;
					self.label = QtWidgets.QLabel(self.IP_Connect_frame)&lt;br /&gt;
					self.label.setGeometry(QtCore.QRect(120, 30, 61, 21))&lt;br /&gt;
					self.label.setObjectName(&amp;quot;label&amp;quot;)&lt;br /&gt;
					self.IP_Adress_field = QtWidgets.QLineEdit(self.IP_Connect_frame)&lt;br /&gt;
					self.IP_Adress_field.setGeometry(QtCore.QRect(170, 30, 131, 20))&lt;br /&gt;
					self.IP_Adress_field.setObjectName(&amp;quot;IP_Adress_field&amp;quot;)&lt;br /&gt;
					self.IP_NetworkStatus_label = QtWidgets.QLabel(self.IP_Connect_frame)&lt;br /&gt;
					self.IP_NetworkStatus_label.setGeometry(QtCore.QRect(140, 90, 141, 16))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(True)&lt;br /&gt;
					font.setWeight(75)&lt;br /&gt;
					self.IP_NetworkStatus_label.setFont(font)&lt;br /&gt;
					self.IP_NetworkStatus_label.setObjectName(&amp;quot;IP_NetworkStatus_label&amp;quot;)&lt;br /&gt;
					self.label_7 = QtWidgets.QLabel(self.IP_Connect_frame)&lt;br /&gt;
					self.label_7.setGeometry(QtCore.QRect(10, 10, 131, 16))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(True)&lt;br /&gt;
					font.setWeight(75)&lt;br /&gt;
					self.label_7.setFont(font)&lt;br /&gt;
					self.label_7.setObjectName(&amp;quot;label_7&amp;quot;)&lt;br /&gt;
					self.SidebarMenu.addItem(self.SidebarMenuPage1, &amp;quot;&amp;quot;)&lt;br /&gt;
					self.SidebarMenuPage2 = QtWidgets.QWidget()&lt;br /&gt;
					self.SidebarMenuPage2.setGeometry(QtCore.QRect(0, 0, 331, 640))&lt;br /&gt;
					self.SidebarMenuPage2.setObjectName(&amp;quot;SidebarMenuPage2&amp;quot;)&lt;br /&gt;
					self.TextCmd_field = QtWidgets.QLineEdit(self.SidebarMenuPage2)&lt;br /&gt;
					self.TextCmd_field.setGeometry(QtCore.QRect(10, 580, 311, 20))&lt;br /&gt;
					self.TextCmd_field.setObjectName(&amp;quot;TextCmd_field&amp;quot;)&lt;br /&gt;
					self.send_Cmd = QtWidgets.QPushButton(self.SidebarMenuPage2)&lt;br /&gt;
					self.send_Cmd.setGeometry(QtCore.QRect(240, 610, 81, 23))&lt;br /&gt;
					self.send_Cmd.setObjectName(&amp;quot;send_Cmd&amp;quot;)&lt;br /&gt;
					self.TX_checkBox = QtWidgets.QCheckBox(self.SidebarMenuPage2)&lt;br /&gt;
					self.TX_checkBox.setGeometry(QtCore.QRect(10, 610, 41, 17))&lt;br /&gt;
					self.TX_checkBox.setObjectName(&amp;quot;TX_checkBox&amp;quot;)&lt;br /&gt;
					self.RX_checkBox = QtWidgets.QCheckBox(self.SidebarMenuPage2)&lt;br /&gt;
					self.RX_checkBox.setGeometry(QtCore.QRect(50, 610, 41, 17))&lt;br /&gt;
					self.RX_checkBox.setObjectName(&amp;quot;RX_checkBox&amp;quot;)&lt;br /&gt;
					self.ConsoleHelp_Cmd = QtWidgets.QPushButton(self.SidebarMenuPage2)&lt;br /&gt;
					self.ConsoleHelp_Cmd.setGeometry(QtCore.QRect(150, 610, 81, 23))&lt;br /&gt;
					self.ConsoleHelp_Cmd.setObjectName(&amp;quot;ConsoleHelp_Cmd&amp;quot;)&lt;br /&gt;
					self.CommandConsole_Window = QtWidgets.QPlainTextEdit(self.SidebarMenuPage2)&lt;br /&gt;
					self.CommandConsole_Window.setGeometry(QtCore.QRect(10, 0, 311, 571))&lt;br /&gt;
					self.CommandConsole_Window.setAcceptDrops(False)&lt;br /&gt;
					self.CommandConsole_Window.setToolTip(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.CommandConsole_Window.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)&lt;br /&gt;
					self.CommandConsole_Window.setLineWrapMode(QtWidgets.QPlainTextEdit.NoWrap)&lt;br /&gt;
					self.CommandConsole_Window.setObjectName(&amp;quot;CommandConsole_Window&amp;quot;)&lt;br /&gt;
					self.ConsoleClear_Cmd = QtWidgets.QPushButton(self.SidebarMenuPage2)&lt;br /&gt;
					self.ConsoleClear_Cmd.setGeometry(QtCore.QRect(90, 610, 51, 23))&lt;br /&gt;
					self.ConsoleClear_Cmd.setObjectName(&amp;quot;ConsoleClear_Cmd&amp;quot;)&lt;br /&gt;
					self.SidebarMenu.addItem(self.SidebarMenuPage2, &amp;quot;&amp;quot;)&lt;br /&gt;
					self.SidebarMenuPage3 = QtWidgets.QWidget()&lt;br /&gt;
					self.SidebarMenuPage3.setGeometry(QtCore.QRect(0, 0, 331, 640))&lt;br /&gt;
					self.SidebarMenuPage3.setObjectName(&amp;quot;SidebarMenuPage3&amp;quot;)&lt;br /&gt;
					self.main_start = QtWidgets.QPushButton(self.SidebarMenuPage3)&lt;br /&gt;
					self.main_start.setGeometry(QtCore.QRect(170, 60, 151, 23))&lt;br /&gt;
					self.main_start.setObjectName(&amp;quot;main_start&amp;quot;)&lt;br /&gt;
					self.main_remote_ctrl = QtWidgets.QCheckBox(self.SidebarMenuPage3)&lt;br /&gt;
					self.main_remote_ctrl.setGeometry(QtCore.QRect(10, 30, 141, 17))&lt;br /&gt;
					self.main_remote_ctrl.setTristate(True)&lt;br /&gt;
					self.main_remote_ctrl.setObjectName(&amp;quot;main_remote_ctrl&amp;quot;)&lt;br /&gt;
					self.main_stop = QtWidgets.QPushButton(self.SidebarMenuPage3)&lt;br /&gt;
					self.main_stop.setGeometry(QtCore.QRect(10, 60, 151, 23))&lt;br /&gt;
					self.main_stop.setObjectName(&amp;quot;main_stop&amp;quot;)&lt;br /&gt;
					self.main_mission_state = QtWidgets.QCheckBox(self.SidebarMenuPage3)&lt;br /&gt;
					self.main_mission_state.setGeometry(QtCore.QRect(10, 10, 101, 17))&lt;br /&gt;
					self.main_mission_state.setTristate(True)&lt;br /&gt;
					self.main_mission_state.setObjectName(&amp;quot;main_mission_state&amp;quot;)&lt;br /&gt;
					self.SidebarMenu.addItem(self.SidebarMenuPage3, &amp;quot;&amp;quot;)&lt;br /&gt;
					self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)&lt;br /&gt;
					self.tabWidget.setGeometry(QtCore.QRect(330, 0, 961, 721))&lt;br /&gt;
					self.tabWidget.setAutoFillBackground(True)&lt;br /&gt;
					self.tabWidget.setObjectName(&amp;quot;tabWidget&amp;quot;)&lt;br /&gt;
					self.tab = QtWidgets.QWidget()&lt;br /&gt;
					self.tab.setObjectName(&amp;quot;tab&amp;quot;)&lt;br /&gt;
					self.MotorGUI_ctrl_frame = QtWidgets.QFrame(self.tab)&lt;br /&gt;
					self.MotorGUI_ctrl_frame.setGeometry(QtCore.QRect(0, 0, 321, 171))&lt;br /&gt;
					self.MotorGUI_ctrl_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
					self.MotorGUI_ctrl_frame.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
					self.MotorGUI_ctrl_frame.setObjectName(&amp;quot;MotorGUI_ctrl_frame&amp;quot;)&lt;br /&gt;
					self.label_5 = QtWidgets.QLabel(self.MotorGUI_ctrl_frame)&lt;br /&gt;
					self.label_5.setGeometry(QtCore.QRect(140, 100, 71, 21))&lt;br /&gt;
					self.label_5.setObjectName(&amp;quot;label_5&amp;quot;)&lt;br /&gt;
					self.enableKeyboardRC_checkBox = QtWidgets.QCheckBox(self.MotorGUI_ctrl_frame)&lt;br /&gt;
					self.enableKeyboardRC_checkBox.setGeometry(QtCore.QRect(150, 40, 141, 17))&lt;br /&gt;
					self.enableKeyboardRC_checkBox.setObjectName(&amp;quot;enableKeyboardRC_checkBox&amp;quot;)&lt;br /&gt;
					self.guiRC_Reverse_Cmd = QtWidgets.QPushButton(self.MotorGUI_ctrl_frame)&lt;br /&gt;
					self.guiRC_Reverse_Cmd.setGeometry(QtCore.QRect(50, 110, 31, 31))&lt;br /&gt;
					self.guiRC_Reverse_Cmd.setObjectName(&amp;quot;guiRC_Reverse_Cmd&amp;quot;)&lt;br /&gt;
					self.guiRC_Forward_Cmd = QtWidgets.QPushButton(self.MotorGUI_ctrl_frame)&lt;br /&gt;
					self.guiRC_Forward_Cmd.setGeometry(QtCore.QRect(50, 50, 31, 31))&lt;br /&gt;
					self.guiRC_Forward_Cmd.setObjectName(&amp;quot;guiRC_Forward_Cmd&amp;quot;)&lt;br /&gt;
					self.manualGuiSpeed_Cmd = QtWidgets.QLineEdit(self.MotorGUI_ctrl_frame)&lt;br /&gt;
					self.manualGuiSpeed_Cmd.setGeometry(QtCore.QRect(220, 100, 51, 20))&lt;br /&gt;
					self.manualGuiSpeed_Cmd.setObjectName(&amp;quot;manualGuiSpeed_Cmd&amp;quot;)&lt;br /&gt;
					self.enableGuiRC_checkBox = QtWidgets.QCheckBox(self.MotorGUI_ctrl_frame)&lt;br /&gt;
					self.enableGuiRC_checkBox.setGeometry(QtCore.QRect(150, 60, 111, 17))&lt;br /&gt;
					self.enableGuiRC_checkBox.setObjectName(&amp;quot;enableGuiRC_checkBox&amp;quot;)&lt;br /&gt;
					self.guiRC_Right_Cmd = QtWidgets.QPushButton(self.MotorGUI_ctrl_frame)&lt;br /&gt;
					self.guiRC_Right_Cmd.setGeometry(QtCore.QRect(80, 80, 31, 31))&lt;br /&gt;
					self.guiRC_Right_Cmd.setObjectName(&amp;quot;guiRC_Right_Cmd&amp;quot;)&lt;br /&gt;
					self.guiRC_Left_Cmd = QtWidgets.QPushButton(self.MotorGUI_ctrl_frame)&lt;br /&gt;
					self.guiRC_Left_Cmd.setGeometry(QtCore.QRect(20, 80, 31, 31))&lt;br /&gt;
					self.guiRC_Left_Cmd.setObjectName(&amp;quot;guiRC_Left_Cmd&amp;quot;)&lt;br /&gt;
					self.label_8 = QtWidgets.QLabel(self.MotorGUI_ctrl_frame)&lt;br /&gt;
					self.label_8.setGeometry(QtCore.QRect(10, 10, 151, 16))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(True)&lt;br /&gt;
					font.setWeight(75)&lt;br /&gt;
					self.label_8.setFont(font)&lt;br /&gt;
					self.label_8.setObjectName(&amp;quot;label_8&amp;quot;)&lt;br /&gt;
					self.frame = QtWidgets.QFrame(self.tab)&lt;br /&gt;
					self.frame.setGeometry(QtCore.QRect(0, 170, 321, 321))&lt;br /&gt;
					self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
					self.frame.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
					self.frame.setObjectName(&amp;quot;frame&amp;quot;)&lt;br /&gt;
					self.label_9 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
					self.label_9.setGeometry(QtCore.QRect(10, 10, 201, 16))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(True)&lt;br /&gt;
					font.setWeight(75)&lt;br /&gt;
					self.label_9.setFont(font)&lt;br /&gt;
					self.label_9.setObjectName(&amp;quot;label_9&amp;quot;)&lt;br /&gt;
					self.label_21 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
					self.label_21.setGeometry(QtCore.QRect(10, 149, 91, 21))&lt;br /&gt;
					self.label_21.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_21.setObjectName(&amp;quot;label_21&amp;quot;)&lt;br /&gt;
					self.Robot_WheelRadius_Right = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
					self.Robot_WheelRadius_Right.setGeometry(QtCore.QRect(109, 230, 71, 21))&lt;br /&gt;
					sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
					sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
					sizePolicy.setVerticalStretch(0)&lt;br /&gt;
					sizePolicy.setHeightForWidth(self.Robot_WheelRadius_Right.sizePolicy().hasHeightForWidth())&lt;br /&gt;
					self.Robot_WheelRadius_Right.setSizePolicy(sizePolicy)&lt;br /&gt;
					self.Robot_WheelRadius_Right.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
					self.Robot_WheelRadius_Right.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Wheel radius&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
					self.Robot_WheelRadius_Right.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.Robot_WheelRadius_Right.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_WheelRadius_Right.setReadOnly(False)&lt;br /&gt;
					self.Robot_WheelRadius_Right.setDecimals(4)&lt;br /&gt;
					self.Robot_WheelRadius_Right.setMinimum(0.001)&lt;br /&gt;
					self.Robot_WheelRadius_Right.setMaximum(0.99)&lt;br /&gt;
					self.Robot_WheelRadius_Right.setSingleStep(0.001)&lt;br /&gt;
					self.Robot_WheelRadius_Right.setProperty(&amp;quot;value&amp;quot;, 0.03)&lt;br /&gt;
					self.Robot_WheelRadius_Right.setObjectName(&amp;quot;Robot_WheelRadius_Right&amp;quot;)&lt;br /&gt;
					self.Robot_WheelRadius_Left = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
					self.Robot_WheelRadius_Left.setGeometry(QtCore.QRect(109, 190, 71, 21))&lt;br /&gt;
					sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
					sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
					sizePolicy.setVerticalStretch(0)&lt;br /&gt;
					sizePolicy.setHeightForWidth(self.Robot_WheelRadius_Left.sizePolicy().hasHeightForWidth())&lt;br /&gt;
					self.Robot_WheelRadius_Left.setSizePolicy(sizePolicy)&lt;br /&gt;
					self.Robot_WheelRadius_Left.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
					self.Robot_WheelRadius_Left.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Wheel radius&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
					self.Robot_WheelRadius_Left.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.Robot_WheelRadius_Left.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_WheelRadius_Left.setReadOnly(False)&lt;br /&gt;
					self.Robot_WheelRadius_Left.setDecimals(4)&lt;br /&gt;
					self.Robot_WheelRadius_Left.setMinimum(0.001)&lt;br /&gt;
					self.Robot_WheelRadius_Left.setMaximum(0.99)&lt;br /&gt;
					self.Robot_WheelRadius_Left.setSingleStep(0.001)&lt;br /&gt;
					self.Robot_WheelRadius_Left.setProperty(&amp;quot;value&amp;quot;, 0.03)&lt;br /&gt;
					self.Robot_WheelRadius_Left.setObjectName(&amp;quot;Robot_WheelRadius_Left&amp;quot;)&lt;br /&gt;
					self.label_22 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
					self.label_22.setGeometry(QtCore.QRect(10, 118, 91, 23))&lt;br /&gt;
					self.label_22.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_22.setObjectName(&amp;quot;label_22&amp;quot;)&lt;br /&gt;
					self.Robot_GearRatio = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
					self.Robot_GearRatio.setGeometry(QtCore.QRect(110, 120, 71, 21))&lt;br /&gt;
					sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
					sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
					sizePolicy.setVerticalStretch(0)&lt;br /&gt;
					sizePolicy.setHeightForWidth(self.Robot_GearRatio.sizePolicy().hasHeightForWidth())&lt;br /&gt;
					self.Robot_GearRatio.setSizePolicy(sizePolicy)&lt;br /&gt;
					self.Robot_GearRatio.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.Robot_GearRatio.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_GearRatio.setReadOnly(False)&lt;br /&gt;
					self.Robot_GearRatio.setDecimals(3)&lt;br /&gt;
					self.Robot_GearRatio.setMaximum(999.99)&lt;br /&gt;
					self.Robot_GearRatio.setSingleStep(0.001)&lt;br /&gt;
					self.Robot_GearRatio.setProperty(&amp;quot;value&amp;quot;, 9.68)&lt;br /&gt;
					self.Robot_GearRatio.setObjectName(&amp;quot;Robot_GearRatio&amp;quot;)&lt;br /&gt;
					self.label_24 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
					self.label_24.setGeometry(QtCore.QRect(10, 169, 121, 20))&lt;br /&gt;
					self.label_24.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
					self.label_24.setObjectName(&amp;quot;label_24&amp;quot;)&lt;br /&gt;
					self.label_127 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
					self.label_127.setGeometry(QtCore.QRect(50, 30, 51, 20))&lt;br /&gt;
					self.label_127.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_127.setObjectName(&amp;quot;label_127&amp;quot;)&lt;br /&gt;
					self.Robot_WheelBase = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
					self.Robot_WheelBase.setGeometry(QtCore.QRect(110, 90, 71, 21))&lt;br /&gt;
					sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
					sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
					sizePolicy.setVerticalStretch(0)&lt;br /&gt;
					sizePolicy.setHeightForWidth(self.Robot_WheelBase.sizePolicy().hasHeightForWidth())&lt;br /&gt;
					self.Robot_WheelBase.setSizePolicy(sizePolicy)&lt;br /&gt;
					self.Robot_WheelBase.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Distance between drivng wheels (0.155m)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
					self.Robot_WheelBase.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.Robot_WheelBase.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_WheelBase.setReadOnly(False)&lt;br /&gt;
					self.Robot_WheelBase.setDecimals(4)&lt;br /&gt;
					self.Robot_WheelBase.setMaximum(5.0)&lt;br /&gt;
					self.Robot_WheelBase.setSingleStep(0.01)&lt;br /&gt;
					self.Robot_WheelBase.setProperty(&amp;quot;value&amp;quot;, 0.155)&lt;br /&gt;
					self.Robot_WheelBase.setObjectName(&amp;quot;Robot_WheelBase&amp;quot;)&lt;br /&gt;
					self.label_128 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
					self.label_128.setGeometry(QtCore.QRect(50, 60, 51, 20))&lt;br /&gt;
					self.label_128.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_128.setObjectName(&amp;quot;label_128&amp;quot;)&lt;br /&gt;
					self.label_25 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
					self.label_25.setGeometry(QtCore.QRect(20, 90, 81, 21))&lt;br /&gt;
					self.label_25.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_25.setObjectName(&amp;quot;label_25&amp;quot;)&lt;br /&gt;
					self.RobotID = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
					self.RobotID.setEnabled(True)&lt;br /&gt;
					self.RobotID.setGeometry(QtCore.QRect(110, 31, 71, 21))&lt;br /&gt;
					sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
					sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
					sizePolicy.setVerticalStretch(0)&lt;br /&gt;
					sizePolicy.setHeightForWidth(self.RobotID.sizePolicy().hasHeightForWidth())&lt;br /&gt;
					self.RobotID.setSizePolicy(sizePolicy)&lt;br /&gt;
					self.RobotID.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;This is the ID of the robot [1..9999].&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;To change the ID of robot:&amp;lt;br/&amp;gt;change ID,&amp;lt;br/&amp;gt;click &amp;amp;quot;save&amp;amp;quot;,&amp;lt;br/&amp;gt;click &amp;amp;quot;save on robot&amp;amp;quot;.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Change ID to -1 to revert to factory settings.&amp;lt;br/&amp;gt;And then change ID back to right value&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
					self.RobotID.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.RobotID.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.RobotID.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.RobotID.setReadOnly(False)&lt;br /&gt;
					self.RobotID.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.RobotID.setSuffix(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.RobotID.setDecimals(0)&lt;br /&gt;
					self.RobotID.setMinimum(-2.0)&lt;br /&gt;
					self.RobotID.setMaximum(10000.0)&lt;br /&gt;
					self.RobotID.setObjectName(&amp;quot;RobotID&amp;quot;)&lt;br /&gt;
					self.RobotHW_type = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
					self.RobotHW_type.setEnabled(True)&lt;br /&gt;
					self.RobotHW_type.setGeometry(QtCore.QRect(110, 60, 71, 21))&lt;br /&gt;
					sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
					sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
					sizePolicy.setVerticalStretch(0)&lt;br /&gt;
					sizePolicy.setHeightForWidth(self.RobotHW_type.sizePolicy().hasHeightForWidth())&lt;br /&gt;
					self.RobotHW_type.setSizePolicy(sizePolicy)&lt;br /&gt;
					self.RobotHW_type.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;The hardware version&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;1 = old version with no wifi nor line sensor plugs on main board and round power supply plug on main board.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;2 = more plugs and satellite PCB for power management and wifi.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;3 = power management and on-off switch on-board (big motor controller board)&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;4 = same as 3, but with small motor controller board.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;5 = same as 2, but updated with new sattelite boards (power and wifi) etc.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;6 = hardware version 4 (motor controller integrated on board)&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;To change:&amp;lt;br/&amp;gt;click edit&amp;lt;br/&amp;gt;change HW setting,&amp;lt;br/&amp;gt;click &amp;amp;quot;save&amp;amp;quot;,&amp;lt;br/&amp;gt;click &amp;amp;quot;save to flash&amp;amp;quot;.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;(ID must be &amp;amp;gt; 0)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
					self.RobotHW_type.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.RobotHW_type.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.RobotHW_type.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.RobotHW_type.setReadOnly(False)&lt;br /&gt;
					self.RobotHW_type.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.RobotHW_type.setSuffix(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.RobotHW_type.setDecimals(0)&lt;br /&gt;
					self.RobotHW_type.setMinimum(-2.0)&lt;br /&gt;
					self.RobotHW_type.setMaximum(10.0)&lt;br /&gt;
					self.RobotHW_type.setObjectName(&amp;quot;RobotHW_type&amp;quot;)&lt;br /&gt;
					self.Robot_PulsePerRev = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
					self.Robot_PulsePerRev.setGeometry(QtCore.QRect(110, 150, 71, 21))&lt;br /&gt;
					sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
					sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
					sizePolicy.setVerticalStretch(0)&lt;br /&gt;
					sizePolicy.setHeightForWidth(self.Robot_PulsePerRev.sizePolicy().hasHeightForWidth())&lt;br /&gt;
					self.Robot_PulsePerRev.setSizePolicy(sizePolicy)&lt;br /&gt;
					self.Robot_PulsePerRev.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.Robot_PulsePerRev.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_PulsePerRev.setReadOnly(False)&lt;br /&gt;
					self.Robot_PulsePerRev.setDecimals(0)&lt;br /&gt;
					self.Robot_PulsePerRev.setMinimum(1.0)&lt;br /&gt;
					self.Robot_PulsePerRev.setMaximum(1000.0)&lt;br /&gt;
					self.Robot_PulsePerRev.setProperty(&amp;quot;value&amp;quot;, 48.0)&lt;br /&gt;
					self.Robot_PulsePerRev.setObjectName(&amp;quot;Robot_PulsePerRev&amp;quot;)&lt;br /&gt;
					self.label_26 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
					self.label_26.setGeometry(QtCore.QRect(10, 210, 121, 20))&lt;br /&gt;
					self.label_26.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
					self.label_26.setObjectName(&amp;quot;label_26&amp;quot;)&lt;br /&gt;
					self.Robot_RevEncoder_Right = QtWidgets.QCheckBox(self.frame)&lt;br /&gt;
					self.Robot_RevEncoder_Right.setGeometry(QtCore.QRect(190, 90, 131, 21))&lt;br /&gt;
					self.Robot_RevEncoder_Right.setObjectName(&amp;quot;Robot_RevEncoder_Right&amp;quot;)&lt;br /&gt;
					self.Robot_RevMotor = QtWidgets.QCheckBox(self.frame)&lt;br /&gt;
					self.Robot_RevMotor.setGeometry(QtCore.QRect(190, 33, 92, 16))&lt;br /&gt;
					self.Robot_RevMotor.setObjectName(&amp;quot;Robot_RevMotor&amp;quot;)&lt;br /&gt;
					self.Robot_RevEncoder_Left = QtWidgets.QCheckBox(self.frame)&lt;br /&gt;
					self.Robot_RevEncoder_Left.setGeometry(QtCore.QRect(190, 60, 131, 21))&lt;br /&gt;
					self.Robot_RevEncoder_Left.setObjectName(&amp;quot;Robot_RevEncoder_Left&amp;quot;)&lt;br /&gt;
					self.Robot_BatteryIdleVolts = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
					self.Robot_BatteryIdleVolts.setGeometry(QtCore.QRect(110, 290, 71, 21))&lt;br /&gt;
					sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
					sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
					sizePolicy.setVerticalStretch(0)&lt;br /&gt;
					sizePolicy.setHeightForWidth(self.Robot_BatteryIdleVolts.sizePolicy().hasHeightForWidth())&lt;br /&gt;
					self.Robot_BatteryIdleVolts.setSizePolicy(sizePolicy)&lt;br /&gt;
					self.Robot_BatteryIdleVolts.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
					self.Robot_BatteryIdleVolts.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Go idle when battery voltage is below this level.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;For a 3 cell LiPo battery it should go in idle mode at 3 x 3.2V = 9.6V&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;At 10V about 10% capacity is left before battery destruction, and in idle mode the robot uses about 30mA, so battey will selfdestruct after another about 3 hours.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;So remember to switch off and to recharge.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
					self.Robot_BatteryIdleVolts.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.Robot_BatteryIdleVolts.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_BatteryIdleVolts.setReadOnly(False)&lt;br /&gt;
					self.Robot_BatteryIdleVolts.setDecimals(1)&lt;br /&gt;
					self.Robot_BatteryIdleVolts.setMinimum(9.0)&lt;br /&gt;
					self.Robot_BatteryIdleVolts.setMaximum(17.0)&lt;br /&gt;
					self.Robot_BatteryIdleVolts.setSingleStep(0.1)&lt;br /&gt;
					self.Robot_BatteryIdleVolts.setProperty(&amp;quot;value&amp;quot;, 9.9)&lt;br /&gt;
					self.Robot_BatteryIdleVolts.setObjectName(&amp;quot;Robot_BatteryIdleVolts&amp;quot;)&lt;br /&gt;
					self.label_222 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
					self.label_222.setGeometry(QtCore.QRect(20, 290, 81, 20))&lt;br /&gt;
					self.label_222.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_222.setObjectName(&amp;quot;label_222&amp;quot;)&lt;br /&gt;
					self.save_id_on_robot = QtWidgets.QPushButton(self.frame)&lt;br /&gt;
					self.save_id_on_robot.setEnabled(False)&lt;br /&gt;
					self.save_id_on_robot.setGeometry(QtCore.QRect(210, 230, 92, 23))&lt;br /&gt;
					self.save_id_on_robot.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Save ID and robot specific settings (this frame) on robot.&amp;lt;br/&amp;gt;NB! you need to press &amp;amp;quot;save on robot&amp;amp;quot; in configuration block to save into EE-prom (flash)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
					self.save_id_on_robot.setCheckable(False)&lt;br /&gt;
					self.save_id_on_robot.setChecked(False)&lt;br /&gt;
					self.save_id_on_robot.setObjectName(&amp;quot;save_id_on_robot&amp;quot;)&lt;br /&gt;
					self.label_129 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
					self.label_129.setGeometry(QtCore.QRect(10, 265, 91, 21))&lt;br /&gt;
					self.label_129.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_129.setObjectName(&amp;quot;label_129&amp;quot;)&lt;br /&gt;
					self.Robot_BalanceOffset = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
					self.Robot_BalanceOffset.setGeometry(QtCore.QRect(110, 263, 71, 21))&lt;br /&gt;
					sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
					sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
					sizePolicy.setVerticalStretch(0)&lt;br /&gt;
					sizePolicy.setHeightForWidth(self.Robot_BalanceOffset.sizePolicy().hasHeightForWidth())&lt;br /&gt;
					self.Robot_BalanceOffset.setSizePolicy(sizePolicy)&lt;br /&gt;
					self.Robot_BalanceOffset.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Balance offset - where center of gravity (COG) is just above support point (wheels).&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Angle is in radians.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Positive is tilt forward.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
					self.Robot_BalanceOffset.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.Robot_BalanceOffset.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_BalanceOffset.setReadOnly(False)&lt;br /&gt;
					self.Robot_BalanceOffset.setDecimals(4)&lt;br /&gt;
					self.Robot_BalanceOffset.setMinimum(-1.0)&lt;br /&gt;
					self.Robot_BalanceOffset.setMaximum(1.0)&lt;br /&gt;
					self.Robot_BalanceOffset.setSingleStep(0.001)&lt;br /&gt;
					self.Robot_BalanceOffset.setObjectName(&amp;quot;Robot_BalanceOffset&amp;quot;)&lt;br /&gt;
					self.Robot_edit = QtWidgets.QPushButton(self.frame)&lt;br /&gt;
					self.Robot_edit.setEnabled(True)&lt;br /&gt;
					self.Robot_edit.setGeometry(QtCore.QRect(210, 259, 92, 23))&lt;br /&gt;
					self.Robot_edit.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Switch to edit mode - rather than listen to robot messages - for these fields.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
					self.Robot_edit.setCheckable(False)&lt;br /&gt;
					self.Robot_edit.setChecked(False)&lt;br /&gt;
					self.Robot_edit.setObjectName(&amp;quot;Robot_edit&amp;quot;)&lt;br /&gt;
					self.Robot_OnBattery = QtWidgets.QCheckBox(self.frame)&lt;br /&gt;
					self.Robot_OnBattery.setGeometry(QtCore.QRect(190, 120, 111, 21))&lt;br /&gt;
					self.Robot_OnBattery.setObjectName(&amp;quot;Robot_OnBattery&amp;quot;)&lt;br /&gt;
					self.robot_cancel = QtWidgets.QPushButton(self.frame)&lt;br /&gt;
					self.robot_cancel.setEnabled(False)&lt;br /&gt;
					self.robot_cancel.setGeometry(QtCore.QRect(210, 288, 92, 23))&lt;br /&gt;
					self.robot_cancel.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Revert to values on robot&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
					self.robot_cancel.setCheckable(False)&lt;br /&gt;
					self.robot_cancel.setChecked(False)&lt;br /&gt;
					self.robot_cancel.setObjectName(&amp;quot;robot_cancel&amp;quot;)&lt;br /&gt;
					self.tabWidget_2 = QtWidgets.QTabWidget(self.tab)&lt;br /&gt;
					self.tabWidget_2.setGeometry(QtCore.QRect(0, 490, 321, 211))&lt;br /&gt;
					self.tabWidget_2.setObjectName(&amp;quot;tabWidget_2&amp;quot;)&lt;br /&gt;
					self.tab_6 = QtWidgets.QWidget()&lt;br /&gt;
					self.tab_6.setObjectName(&amp;quot;tab_6&amp;quot;)&lt;br /&gt;
					self.groupBox = QtWidgets.QGroupBox(self.tab_6)&lt;br /&gt;
					self.groupBox.setGeometry(QtCore.QRect(0, 0, 311, 181))&lt;br /&gt;
					self.groupBox.setTitle(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.groupBox.setObjectName(&amp;quot;groupBox&amp;quot;)&lt;br /&gt;
					self.Robot_CurrentLeft_Value = QtWidgets.QDoubleSpinBox(self.groupBox)&lt;br /&gt;
					self.Robot_CurrentLeft_Value.setGeometry(QtCore.QRect(110, 100, 71, 21))&lt;br /&gt;
					self.Robot_CurrentLeft_Value.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
					self.Robot_CurrentLeft_Value.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.Robot_CurrentLeft_Value.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.Robot_CurrentLeft_Value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_CurrentLeft_Value.setReadOnly(True)&lt;br /&gt;
					self.Robot_CurrentLeft_Value.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
					self.Robot_CurrentLeft_Value.setDecimals(2)&lt;br /&gt;
					self.Robot_CurrentLeft_Value.setMinimum(-100000.0)&lt;br /&gt;
					self.Robot_CurrentLeft_Value.setMaximum(100000.0)&lt;br /&gt;
					self.Robot_CurrentLeft_Value.setObjectName(&amp;quot;Robot_CurrentLeft_Value&amp;quot;)&lt;br /&gt;
					self.Robot_Distance = QtWidgets.QDoubleSpinBox(self.groupBox)&lt;br /&gt;
					self.Robot_Distance.setGeometry(QtCore.QRect(110, 130, 71, 21))&lt;br /&gt;
					self.Robot_Distance.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.Robot_Distance.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.Robot_Distance.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_Distance.setReadOnly(True)&lt;br /&gt;
					self.Robot_Distance.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
					self.Robot_Distance.setDecimals(3)&lt;br /&gt;
					self.Robot_Distance.setMinimum(-999999.0)&lt;br /&gt;
					self.Robot_Distance.setMaximum(999999.99)&lt;br /&gt;
					self.Robot_Distance.setObjectName(&amp;quot;Robot_Distance&amp;quot;)&lt;br /&gt;
					self.label_36 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
					self.label_36.setGeometry(QtCore.QRect(186, 24, 71, 16))&lt;br /&gt;
					self.label_36.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
					self.label_36.setObjectName(&amp;quot;label_36&amp;quot;)&lt;br /&gt;
					self.label_37 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
					self.label_37.setGeometry(QtCore.QRect(106, 24, 71, 16))&lt;br /&gt;
					self.label_37.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
					self.label_37.setObjectName(&amp;quot;label_37&amp;quot;)&lt;br /&gt;
					self.label_42 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
					self.label_42.setGeometry(QtCore.QRect(11, 100, 91, 16))&lt;br /&gt;
					self.label_42.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
					self.label_42.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_42.setObjectName(&amp;quot;label_42&amp;quot;)&lt;br /&gt;
					self.Robot_EncLeft_Value = QtWidgets.QSpinBox(self.groupBox)&lt;br /&gt;
					self.Robot_EncLeft_Value.setGeometry(QtCore.QRect(110, 40, 71, 21))&lt;br /&gt;
					self.Robot_EncLeft_Value.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
					self.Robot_EncLeft_Value.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.Robot_EncLeft_Value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_EncLeft_Value.setReadOnly(True)&lt;br /&gt;
					self.Robot_EncLeft_Value.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
					self.Robot_EncLeft_Value.setMinimum(-2147483647)&lt;br /&gt;
					self.Robot_EncLeft_Value.setMaximum(2147483647)&lt;br /&gt;
					self.Robot_EncLeft_Value.setObjectName(&amp;quot;Robot_EncLeft_Value&amp;quot;)&lt;br /&gt;
					self.Robot_CurrentRight_Value = QtWidgets.QDoubleSpinBox(self.groupBox)&lt;br /&gt;
					self.Robot_CurrentRight_Value.setGeometry(QtCore.QRect(190, 100, 71, 21))&lt;br /&gt;
					self.Robot_CurrentRight_Value.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
					self.Robot_CurrentRight_Value.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.Robot_CurrentRight_Value.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.Robot_CurrentRight_Value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_CurrentRight_Value.setReadOnly(True)&lt;br /&gt;
					self.Robot_CurrentRight_Value.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
					self.Robot_CurrentRight_Value.setDecimals(2)&lt;br /&gt;
					self.Robot_CurrentRight_Value.setMinimum(-100000.0)&lt;br /&gt;
					self.Robot_CurrentRight_Value.setMaximum(100000.0)&lt;br /&gt;
					self.Robot_CurrentRight_Value.setObjectName(&amp;quot;Robot_CurrentRight_Value&amp;quot;)&lt;br /&gt;
					self.label_28 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
					self.label_28.setGeometry(QtCore.QRect(11, 40, 91, 16))&lt;br /&gt;
					self.label_28.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_28.setObjectName(&amp;quot;label_28&amp;quot;)&lt;br /&gt;
					self.label_38 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
					self.label_38.setGeometry(QtCore.QRect(9, 5, 121, 21))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setPointSize(10)&lt;br /&gt;
					font.setBold(True)&lt;br /&gt;
					font.setWeight(75)&lt;br /&gt;
					self.label_38.setFont(font)&lt;br /&gt;
					self.label_38.setObjectName(&amp;quot;label_38&amp;quot;)&lt;br /&gt;
					self.label_32 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
					self.label_32.setGeometry(QtCore.QRect(10, 130, 91, 16))&lt;br /&gt;
					self.label_32.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
					self.label_32.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_32.setObjectName(&amp;quot;label_32&amp;quot;)&lt;br /&gt;
					self.label_29 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
					self.label_29.setGeometry(QtCore.QRect(1, 70, 101, 16))&lt;br /&gt;
					self.label_29.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
					self.label_29.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_29.setObjectName(&amp;quot;label_29&amp;quot;)&lt;br /&gt;
					self.Robot_EncRight_Value = QtWidgets.QSpinBox(self.groupBox)&lt;br /&gt;
					self.Robot_EncRight_Value.setGeometry(QtCore.QRect(190, 40, 71, 21))&lt;br /&gt;
					self.Robot_EncRight_Value.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
					self.Robot_EncRight_Value.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.Robot_EncRight_Value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_EncRight_Value.setReadOnly(True)&lt;br /&gt;
					self.Robot_EncRight_Value.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
					self.Robot_EncRight_Value.setMinimum(-2147483647)&lt;br /&gt;
					self.Robot_EncRight_Value.setMaximum(2147483647)&lt;br /&gt;
					self.Robot_EncRight_Value.setObjectName(&amp;quot;Robot_EncRight_Value&amp;quot;)&lt;br /&gt;
					self.Robot_WheelRightSpeed_Value = QtWidgets.QDoubleSpinBox(self.groupBox)&lt;br /&gt;
					self.Robot_WheelRightSpeed_Value.setGeometry(QtCore.QRect(190, 70, 71, 21))&lt;br /&gt;
					self.Robot_WheelRightSpeed_Value.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
					self.Robot_WheelRightSpeed_Value.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.Robot_WheelRightSpeed_Value.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.Robot_WheelRightSpeed_Value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_WheelRightSpeed_Value.setReadOnly(True)&lt;br /&gt;
					self.Robot_WheelRightSpeed_Value.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
					self.Robot_WheelRightSpeed_Value.setDecimals(3)&lt;br /&gt;
					self.Robot_WheelRightSpeed_Value.setMinimum(-100000.0)&lt;br /&gt;
					self.Robot_WheelRightSpeed_Value.setMaximum(100000.0)&lt;br /&gt;
					self.Robot_WheelRightSpeed_Value.setObjectName(&amp;quot;Robot_WheelRightSpeed_Value&amp;quot;)&lt;br /&gt;
					self.Robot_WheelLeftSpeed_Value = QtWidgets.QDoubleSpinBox(self.groupBox)&lt;br /&gt;
					self.Robot_WheelLeftSpeed_Value.setGeometry(QtCore.QRect(110, 70, 71, 21))&lt;br /&gt;
					self.Robot_WheelLeftSpeed_Value.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
					self.Robot_WheelLeftSpeed_Value.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.Robot_WheelLeftSpeed_Value.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.Robot_WheelLeftSpeed_Value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_WheelLeftSpeed_Value.setReadOnly(True)&lt;br /&gt;
					self.Robot_WheelLeftSpeed_Value.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
					self.Robot_WheelLeftSpeed_Value.setDecimals(3)&lt;br /&gt;
					self.Robot_WheelLeftSpeed_Value.setMinimum(-100000.0)&lt;br /&gt;
					self.Robot_WheelLeftSpeed_Value.setMaximum(100000.0)&lt;br /&gt;
					self.Robot_WheelLeftSpeed_Value.setObjectName(&amp;quot;Robot_WheelLeftSpeed_Value&amp;quot;)&lt;br /&gt;
					self.tabWidget_2.addTab(self.tab_6, &amp;quot;&amp;quot;)&lt;br /&gt;
					self.tab_7 = QtWidgets.QWidget()&lt;br /&gt;
					self.tab_7.setObjectName(&amp;quot;tab_7&amp;quot;)&lt;br /&gt;
					self.groupBox_3 = QtWidgets.QGroupBox(self.tab_7)&lt;br /&gt;
					self.groupBox_3.setGeometry(QtCore.QRect(0, 0, 311, 181))&lt;br /&gt;
					self.groupBox_3.setTitle(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.groupBox_3.setObjectName(&amp;quot;groupBox_3&amp;quot;)&lt;br /&gt;
					self.Robot_TiltDegrees = QtWidgets.QDoubleSpinBox(self.groupBox_3)&lt;br /&gt;
					self.Robot_TiltDegrees.setGeometry(QtCore.QRect(190, 131, 71, 21))&lt;br /&gt;
					self.Robot_TiltDegrees.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.Robot_TiltDegrees.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.Robot_TiltDegrees.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_TiltDegrees.setReadOnly(True)&lt;br /&gt;
					self.Robot_TiltDegrees.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
					self.Robot_TiltDegrees.setDecimals(3)&lt;br /&gt;
					self.Robot_TiltDegrees.setMinimum(-999999.0)&lt;br /&gt;
					self.Robot_TiltDegrees.setMaximum(999999.99)&lt;br /&gt;
					self.Robot_TiltDegrees.setObjectName(&amp;quot;Robot_TiltDegrees&amp;quot;)&lt;br /&gt;
					self.label_76 = QtWidgets.QLabel(self.groupBox_3)&lt;br /&gt;
					self.label_76.setGeometry(QtCore.QRect(190, 80, 71, 21))&lt;br /&gt;
					self.label_76.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
					self.label_76.setObjectName(&amp;quot;label_76&amp;quot;)&lt;br /&gt;
					self.label_45 = QtWidgets.QLabel(self.groupBox_3)&lt;br /&gt;
					self.label_45.setGeometry(QtCore.QRect(10, 0, 271, 31))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setPointSize(10)&lt;br /&gt;
					font.setBold(True)&lt;br /&gt;
					font.setWeight(75)&lt;br /&gt;
					self.label_45.setFont(font)&lt;br /&gt;
					self.label_45.setObjectName(&amp;quot;label_45&amp;quot;)&lt;br /&gt;
					self.Robot_PoseX = QtWidgets.QDoubleSpinBox(self.groupBox_3)&lt;br /&gt;
					self.Robot_PoseX.setGeometry(QtCore.QRect(100, 40, 81, 21))&lt;br /&gt;
					self.Robot_PoseX.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
					self.Robot_PoseX.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.Robot_PoseX.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.Robot_PoseX.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_PoseX.setReadOnly(True)&lt;br /&gt;
					self.Robot_PoseX.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
					self.Robot_PoseX.setDecimals(3)&lt;br /&gt;
					self.Robot_PoseX.setMinimum(-999999.0)&lt;br /&gt;
					self.Robot_PoseX.setMaximum(999999.99)&lt;br /&gt;
					self.Robot_PoseX.setObjectName(&amp;quot;Robot_PoseX&amp;quot;)&lt;br /&gt;
					self.Robot_PoseY = QtWidgets.QDoubleSpinBox(self.groupBox_3)&lt;br /&gt;
					self.Robot_PoseY.setGeometry(QtCore.QRect(100, 71, 81, 21))&lt;br /&gt;
					self.Robot_PoseY.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.Robot_PoseY.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.Robot_PoseY.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_PoseY.setReadOnly(True)&lt;br /&gt;
					self.Robot_PoseY.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
					self.Robot_PoseY.setDecimals(3)&lt;br /&gt;
					self.Robot_PoseY.setMinimum(-999999.0)&lt;br /&gt;
					self.Robot_PoseY.setMaximum(999999.99)&lt;br /&gt;
					self.Robot_PoseY.setObjectName(&amp;quot;Robot_PoseY&amp;quot;)&lt;br /&gt;
					self.Robot_PoseReset = QtWidgets.QPushButton(self.groupBox_3)&lt;br /&gt;
					self.Robot_PoseReset.setGeometry(QtCore.QRect(190, 40, 71, 21))&lt;br /&gt;
					self.Robot_PoseReset.setToolTip(&amp;quot;Reset robot pose and distance to 0.0&amp;quot;)&lt;br /&gt;
					self.Robot_PoseReset.setObjectName(&amp;quot;Robot_PoseReset&amp;quot;)&lt;br /&gt;
					self.label_44 = QtWidgets.QLabel(self.groupBox_3)&lt;br /&gt;
					self.label_44.setGeometry(QtCore.QRect(20, 100, 71, 21))&lt;br /&gt;
					self.label_44.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_44.setObjectName(&amp;quot;label_44&amp;quot;)&lt;br /&gt;
					self.label_34 = QtWidgets.QLabel(self.groupBox_3)&lt;br /&gt;
					self.label_34.setGeometry(QtCore.QRect(10, 40, 81, 21))&lt;br /&gt;
					self.label_34.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_34.setObjectName(&amp;quot;label_34&amp;quot;)&lt;br /&gt;
					self.label_35 = QtWidgets.QLabel(self.groupBox_3)&lt;br /&gt;
					self.label_35.setGeometry(QtCore.QRect(20, 71, 71, 20))&lt;br /&gt;
					self.label_35.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_35.setObjectName(&amp;quot;label_35&amp;quot;)&lt;br /&gt;
					self.Robot_Tilt = QtWidgets.QDoubleSpinBox(self.groupBox_3)&lt;br /&gt;
					self.Robot_Tilt.setGeometry(QtCore.QRect(100, 131, 81, 21))&lt;br /&gt;
					self.Robot_Tilt.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.Robot_Tilt.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.Robot_Tilt.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_Tilt.setReadOnly(True)&lt;br /&gt;
					self.Robot_Tilt.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
					self.Robot_Tilt.setDecimals(3)&lt;br /&gt;
					self.Robot_Tilt.setMinimum(-999999.0)&lt;br /&gt;
					self.Robot_Tilt.setMaximum(999999.99)&lt;br /&gt;
					self.Robot_Tilt.setObjectName(&amp;quot;Robot_Tilt&amp;quot;)&lt;br /&gt;
					self.Robot_PoseTheta = QtWidgets.QDoubleSpinBox(self.groupBox_3)&lt;br /&gt;
					self.Robot_PoseTheta.setGeometry(QtCore.QRect(100, 100, 81, 21))&lt;br /&gt;
					self.Robot_PoseTheta.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.Robot_PoseTheta.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.Robot_PoseTheta.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_PoseTheta.setReadOnly(True)&lt;br /&gt;
					self.Robot_PoseTheta.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
					self.Robot_PoseTheta.setDecimals(3)&lt;br /&gt;
					self.Robot_PoseTheta.setMinimum(-999999.0)&lt;br /&gt;
					self.Robot_PoseTheta.setMaximum(999999.99)&lt;br /&gt;
					self.Robot_PoseTheta.setObjectName(&amp;quot;Robot_PoseTheta&amp;quot;)&lt;br /&gt;
					self.Robot_PoseDegrees = QtWidgets.QDoubleSpinBox(self.groupBox_3)&lt;br /&gt;
					self.Robot_PoseDegrees.setGeometry(QtCore.QRect(190, 100, 71, 21))&lt;br /&gt;
					self.Robot_PoseDegrees.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.Robot_PoseDegrees.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.Robot_PoseDegrees.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.Robot_PoseDegrees.setReadOnly(True)&lt;br /&gt;
					self.Robot_PoseDegrees.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
					self.Robot_PoseDegrees.setDecimals(3)&lt;br /&gt;
					self.Robot_PoseDegrees.setMinimum(-999999.0)&lt;br /&gt;
					self.Robot_PoseDegrees.setMaximum(999999.99)&lt;br /&gt;
					self.Robot_PoseDegrees.setObjectName(&amp;quot;Robot_PoseDegrees&amp;quot;)&lt;br /&gt;
					self.label_125 = QtWidgets.QLabel(self.groupBox_3)&lt;br /&gt;
					self.label_125.setGeometry(QtCore.QRect(10, 130, 81, 21))&lt;br /&gt;
					self.label_125.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
					self.label_125.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_125.setObjectName(&amp;quot;label_125&amp;quot;)&lt;br /&gt;
					self.tabWidget_2.addTab(self.tab_7, &amp;quot;&amp;quot;)&lt;br /&gt;
					self.tabWidget.addTab(self.tab, &amp;quot;&amp;quot;)&lt;br /&gt;
					self.tab_2 = QtWidgets.QWidget()&lt;br /&gt;
					self.tab_2.setObjectName(&amp;quot;tab_2&amp;quot;)&lt;br /&gt;
					self.frame_2 = QtWidgets.QFrame(self.tab_2)&lt;br /&gt;
					self.frame_2.setGeometry(QtCore.QRect(0, 0, 711, 421))&lt;br /&gt;
					self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
					self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
					self.frame_2.setObjectName(&amp;quot;frame_2&amp;quot;)&lt;br /&gt;
					self.label_10 = QtWidgets.QLabel(self.frame_2)&lt;br /&gt;
					self.label_10.setGeometry(QtCore.QRect(10, 10, 101, 16))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(True)&lt;br /&gt;
					font.setWeight(75)&lt;br /&gt;
					self.label_10.setFont(font)&lt;br /&gt;
					self.label_10.setObjectName(&amp;quot;label_10&amp;quot;)&lt;br /&gt;
					self.enableLineSensor_checkBox = QtWidgets.QCheckBox(self.frame_2)&lt;br /&gt;
					self.enableLineSensor_checkBox.setGeometry(QtCore.QRect(10, 30, 111, 17))&lt;br /&gt;
					self.enableLineSensor_checkBox.setObjectName(&amp;quot;enableLineSensor_checkBox&amp;quot;)&lt;br /&gt;
					self.frame_3 = QtWidgets.QFrame(self.frame_2)&lt;br /&gt;
					self.frame_3.setGeometry(QtCore.QRect(10, 50, 421, 231))&lt;br /&gt;
					self.frame_3.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
					self.frame_3.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
					self.frame_3.setObjectName(&amp;quot;frame_3&amp;quot;)&lt;br /&gt;
					self.line_bar_1 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
					self.line_bar_1.setGeometry(QtCore.QRect(10, 20, 16, 181))&lt;br /&gt;
					self.line_bar_1.setMaximum(1000)&lt;br /&gt;
					self.line_bar_1.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
					self.line_bar_1.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
					self.line_bar_1.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
					self.line_bar_1.setObjectName(&amp;quot;line_bar_1&amp;quot;)&lt;br /&gt;
					self.line_bar_4 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
					self.line_bar_4.setGeometry(QtCore.QRect(70, 20, 16, 181))&lt;br /&gt;
					self.line_bar_4.setMaximum(1000)&lt;br /&gt;
					self.line_bar_4.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
					self.line_bar_4.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
					self.line_bar_4.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
					self.line_bar_4.setObjectName(&amp;quot;line_bar_4&amp;quot;)&lt;br /&gt;
					self.line_bar_6 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
					self.line_bar_6.setGeometry(QtCore.QRect(110, 20, 16, 181))&lt;br /&gt;
					self.line_bar_6.setMaximum(1000)&lt;br /&gt;
					self.line_bar_6.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
					self.line_bar_6.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
					self.line_bar_6.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
					self.line_bar_6.setObjectName(&amp;quot;line_bar_6&amp;quot;)&lt;br /&gt;
					self.line_bar_5 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
					self.line_bar_5.setGeometry(QtCore.QRect(90, 20, 16, 181))&lt;br /&gt;
					self.line_bar_5.setMaximum(1000)&lt;br /&gt;
					self.line_bar_5.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
					self.line_bar_5.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
					self.line_bar_5.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
					self.line_bar_5.setObjectName(&amp;quot;line_bar_5&amp;quot;)&lt;br /&gt;
					self.line_bar_3 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
					self.line_bar_3.setGeometry(QtCore.QRect(50, 20, 16, 181))&lt;br /&gt;
					self.line_bar_3.setMaximum(1000)&lt;br /&gt;
					self.line_bar_3.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
					self.line_bar_3.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
					self.line_bar_3.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
					self.line_bar_3.setObjectName(&amp;quot;line_bar_3&amp;quot;)&lt;br /&gt;
					self.line_bar_7 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
					self.line_bar_7.setGeometry(QtCore.QRect(130, 20, 16, 181))&lt;br /&gt;
					self.line_bar_7.setMaximum(1000)&lt;br /&gt;
					self.line_bar_7.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
					self.line_bar_7.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
					self.line_bar_7.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
					self.line_bar_7.setObjectName(&amp;quot;line_bar_7&amp;quot;)&lt;br /&gt;
					self.line_bar_2 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
					self.line_bar_2.setGeometry(QtCore.QRect(30, 20, 16, 181))&lt;br /&gt;
					self.line_bar_2.setMaximum(1000)&lt;br /&gt;
					self.line_bar_2.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
					self.line_bar_2.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
					self.line_bar_2.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
					self.line_bar_2.setObjectName(&amp;quot;line_bar_2&amp;quot;)&lt;br /&gt;
					self.line_bar_8 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
					self.line_bar_8.setGeometry(QtCore.QRect(150, 20, 16, 181))&lt;br /&gt;
					self.line_bar_8.setMaximum(1000)&lt;br /&gt;
					self.line_bar_8.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
					self.line_bar_8.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
					self.line_bar_8.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
					self.line_bar_8.setObjectName(&amp;quot;line_bar_8&amp;quot;)&lt;br /&gt;
					self.label_11 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
					self.label_11.setGeometry(QtCore.QRect(10, 0, 151, 21))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(True)&lt;br /&gt;
					font.setWeight(75)&lt;br /&gt;
					self.label_11.setFont(font)&lt;br /&gt;
					self.label_11.setObjectName(&amp;quot;label_11&amp;quot;)&lt;br /&gt;
					self.label_12 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
					self.label_12.setGeometry(QtCore.QRect(10, 200, 16, 21))&lt;br /&gt;
					self.label_12.setLayoutDirection(QtCore.Qt.RightToLeft)&lt;br /&gt;
					self.label_12.setObjectName(&amp;quot;label_12&amp;quot;)&lt;br /&gt;
					self.label_13 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
					self.label_13.setGeometry(QtCore.QRect(30, 200, 16, 21))&lt;br /&gt;
					self.label_13.setObjectName(&amp;quot;label_13&amp;quot;)&lt;br /&gt;
					self.label_14 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
					self.label_14.setGeometry(QtCore.QRect(110, 200, 16, 21))&lt;br /&gt;
					self.label_14.setObjectName(&amp;quot;label_14&amp;quot;)&lt;br /&gt;
					self.label_15 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
					self.label_15.setGeometry(QtCore.QRect(50, 200, 16, 21))&lt;br /&gt;
					self.label_15.setObjectName(&amp;quot;label_15&amp;quot;)&lt;br /&gt;
					self.label_16 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
					self.label_16.setGeometry(QtCore.QRect(70, 200, 16, 21))&lt;br /&gt;
					self.label_16.setObjectName(&amp;quot;label_16&amp;quot;)&lt;br /&gt;
					self.label_17 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
					self.label_17.setGeometry(QtCore.QRect(90, 200, 20, 21))&lt;br /&gt;
					self.label_17.setObjectName(&amp;quot;label_17&amp;quot;)&lt;br /&gt;
					self.label_18 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
					self.label_18.setGeometry(QtCore.QRect(130, 200, 16, 21))&lt;br /&gt;
					self.label_18.setObjectName(&amp;quot;label_18&amp;quot;)&lt;br /&gt;
					self.label_19 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
					self.label_19.setGeometry(QtCore.QRect(150, 200, 16, 21))&lt;br /&gt;
					self.label_19.setObjectName(&amp;quot;label_19&amp;quot;)&lt;br /&gt;
					self.ls_right_side = QtWidgets.QDoubleSpinBox(self.frame_3)&lt;br /&gt;
					self.ls_right_side.setGeometry(QtCore.QRect(300, 170, 61, 21))&lt;br /&gt;
					self.ls_right_side.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Position of right edge (cm)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
					self.ls_right_side.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.ls_right_side.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.ls_right_side.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ls_right_side.setReadOnly(True)&lt;br /&gt;
					self.ls_right_side.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
					self.ls_right_side.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.ls_right_side.setDecimals(3)&lt;br /&gt;
					self.ls_right_side.setMinimum(-100.0)&lt;br /&gt;
					self.ls_right_side.setMaximum(100.0)&lt;br /&gt;
					self.ls_right_side.setObjectName(&amp;quot;ls_right_side&amp;quot;)&lt;br /&gt;
					self.ls_left_side = QtWidgets.QDoubleSpinBox(self.frame_3)&lt;br /&gt;
					self.ls_left_side.setGeometry(QtCore.QRect(230, 170, 61, 21))&lt;br /&gt;
					self.ls_left_side.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Position of left edge (cm)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
					self.ls_left_side.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.ls_left_side.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.ls_left_side.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ls_left_side.setReadOnly(True)&lt;br /&gt;
					self.ls_left_side.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
					self.ls_left_side.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.ls_left_side.setDecimals(3)&lt;br /&gt;
					self.ls_left_side.setMinimum(-100.0)&lt;br /&gt;
					self.ls_left_side.setMaximum(100.0)&lt;br /&gt;
					self.ls_left_side.setObjectName(&amp;quot;ls_left_side&amp;quot;)&lt;br /&gt;
					self.label_118 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
					self.label_118.setGeometry(QtCore.QRect(230, 40, 61, 18))&lt;br /&gt;
					self.label_118.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
					self.label_118.setObjectName(&amp;quot;label_118&amp;quot;)&lt;br /&gt;
					self.ls_left_edge = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
					self.ls_left_edge.setEnabled(True)&lt;br /&gt;
					self.ls_left_edge.setGeometry(QtCore.QRect(240, 60, 41, 101))&lt;br /&gt;
					self.ls_left_edge.setMinimum(-3)&lt;br /&gt;
					self.ls_left_edge.setMaximum(103)&lt;br /&gt;
					self.ls_left_edge.setProperty(&amp;quot;value&amp;quot;, 24)&lt;br /&gt;
					self.ls_left_edge.setTextVisible(False)&lt;br /&gt;
					self.ls_left_edge.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
					self.ls_left_edge.setInvertedAppearance(False)&lt;br /&gt;
					self.ls_left_edge.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
					self.ls_left_edge.setObjectName(&amp;quot;ls_left_edge&amp;quot;)&lt;br /&gt;
					self.ls_right_edge = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
					self.ls_right_edge.setGeometry(QtCore.QRect(310, 60, 41, 101))&lt;br /&gt;
					self.ls_right_edge.setMinimum(-3)&lt;br /&gt;
					self.ls_right_edge.setMaximum(103)&lt;br /&gt;
					self.ls_right_edge.setProperty(&amp;quot;value&amp;quot;, 24)&lt;br /&gt;
					self.ls_right_edge.setTextVisible(False)&lt;br /&gt;
					self.ls_right_edge.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
					self.ls_right_edge.setInvertedAppearance(False)&lt;br /&gt;
					self.ls_right_edge.setObjectName(&amp;quot;ls_right_edge&amp;quot;)&lt;br /&gt;
					self.label_121 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
					self.label_121.setGeometry(QtCore.QRect(300, 40, 61, 18))&lt;br /&gt;
					self.label_121.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
					self.label_121.setObjectName(&amp;quot;label_121&amp;quot;)&lt;br /&gt;
					self.label_20 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
					self.label_20.setGeometry(QtCore.QRect(180, 0, 101, 21))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(True)&lt;br /&gt;
					font.setWeight(75)&lt;br /&gt;
					self.label_20.setFont(font)&lt;br /&gt;
					self.label_20.setObjectName(&amp;quot;label_20&amp;quot;)&lt;br /&gt;
					self.label_27 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
					self.label_27.setGeometry(QtCore.QRect(290, 20, 48, 20))&lt;br /&gt;
					self.label_27.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_27.setObjectName(&amp;quot;label_27&amp;quot;)&lt;br /&gt;
					self.label_171 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
					self.label_171.setGeometry(QtCore.QRect(176, 20, 48, 20))&lt;br /&gt;
					self.label_171.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_171.setObjectName(&amp;quot;label_171&amp;quot;)&lt;br /&gt;
					self.ls_crossing_cnt = QtWidgets.QLineEdit(self.frame_3)&lt;br /&gt;
					self.ls_crossing_cnt.setGeometry(QtCore.QRect(344, 20, 54, 20))&lt;br /&gt;
					self.ls_crossing_cnt.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.ls_crossing_cnt.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.ls_crossing_cnt.setObjectName(&amp;quot;ls_crossing_cnt&amp;quot;)&lt;br /&gt;
					self.ls_line_valid_cnt = QtWidgets.QLineEdit(self.frame_3)&lt;br /&gt;
					self.ls_line_valid_cnt.setGeometry(QtCore.QRect(230, 20, 54, 20))&lt;br /&gt;
					self.ls_line_valid_cnt.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.ls_line_valid_cnt.setObjectName(&amp;quot;ls_line_valid_cnt&amp;quot;)&lt;br /&gt;
					self.groupBox_5 = QtWidgets.QGroupBox(self.frame_2)&lt;br /&gt;
					self.groupBox_5.setGeometry(QtCore.QRect(430, 10, 251, 361))&lt;br /&gt;
					self.groupBox_5.setObjectName(&amp;quot;groupBox_5&amp;quot;)&lt;br /&gt;
					self.CalibrateWhite_Cmd = QtWidgets.QPushButton(self.groupBox_5)&lt;br /&gt;
					self.CalibrateWhite_Cmd.setGeometry(QtCore.QRect(20, 310, 101, 31))&lt;br /&gt;
					self.CalibrateWhite_Cmd.setObjectName(&amp;quot;CalibrateWhite_Cmd&amp;quot;)&lt;br /&gt;
					self.label_217 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.label_217.setGeometry(QtCore.QRect(20, 75, 21, 16))&lt;br /&gt;
					self.label_217.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_217.setObjectName(&amp;quot;label_217&amp;quot;)&lt;br /&gt;
					self.label_218 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.label_218.setGeometry(QtCore.QRect(20, 117, 21, 16))&lt;br /&gt;
					self.label_218.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_218.setObjectName(&amp;quot;label_218&amp;quot;)&lt;br /&gt;
					self.label_219 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.label_219.setGeometry(QtCore.QRect(20, 159, 21, 16))&lt;br /&gt;
					self.label_219.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_219.setObjectName(&amp;quot;label_219&amp;quot;)&lt;br /&gt;
					self.label_221 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.label_221.setGeometry(QtCore.QRect(20, 96, 21, 16))&lt;br /&gt;
					self.label_221.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_221.setObjectName(&amp;quot;label_221&amp;quot;)&lt;br /&gt;
					self.label_223 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.label_223.setGeometry(QtCore.QRect(20, 138, 21, 16))&lt;br /&gt;
					self.label_223.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_223.setObjectName(&amp;quot;label_223&amp;quot;)&lt;br /&gt;
					self.label_224 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.label_224.setGeometry(QtCore.QRect(20, 33, 21, 16))&lt;br /&gt;
					self.label_224.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_224.setObjectName(&amp;quot;label_224&amp;quot;)&lt;br /&gt;
					self.label_225 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.label_225.setGeometry(QtCore.QRect(20, 180, 21, 16))&lt;br /&gt;
					self.label_225.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_225.setObjectName(&amp;quot;label_225&amp;quot;)&lt;br /&gt;
					self.label_226 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.label_226.setGeometry(QtCore.QRect(20, 54, 21, 16))&lt;br /&gt;
					self.label_226.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_226.setObjectName(&amp;quot;label_226&amp;quot;)&lt;br /&gt;
					self.ls_max_white_7 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.ls_max_white_7.setGeometry(QtCore.QRect(50, 159, 50, 15))&lt;br /&gt;
					self.ls_max_white_7.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
					self.ls_max_white_7.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
					self.ls_max_white_7.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ls_max_white_7.setObjectName(&amp;quot;ls_max_white_7&amp;quot;)&lt;br /&gt;
					self.ls_max_white_8 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.ls_max_white_8.setGeometry(QtCore.QRect(50, 180, 50, 15))&lt;br /&gt;
					self.ls_max_white_8.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
					self.ls_max_white_8.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
					self.ls_max_white_8.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ls_max_white_8.setObjectName(&amp;quot;ls_max_white_8&amp;quot;)&lt;br /&gt;
					self.ls_max_white_1 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.ls_max_white_1.setGeometry(QtCore.QRect(50, 33, 50, 15))&lt;br /&gt;
					self.ls_max_white_1.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
					self.ls_max_white_1.setAutoFillBackground(False)&lt;br /&gt;
					self.ls_max_white_1.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
					self.ls_max_white_1.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
					self.ls_max_white_1.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ls_max_white_1.setObjectName(&amp;quot;ls_max_white_1&amp;quot;)&lt;br /&gt;
					self.ls_max_white_3 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.ls_max_white_3.setGeometry(QtCore.QRect(50, 75, 50, 15))&lt;br /&gt;
					self.ls_max_white_3.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
					self.ls_max_white_3.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
					self.ls_max_white_3.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ls_max_white_3.setObjectName(&amp;quot;ls_max_white_3&amp;quot;)&lt;br /&gt;
					self.ls_max_white_5 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.ls_max_white_5.setGeometry(QtCore.QRect(50, 117, 50, 15))&lt;br /&gt;
					self.ls_max_white_5.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
					self.ls_max_white_5.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
					self.ls_max_white_5.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ls_max_white_5.setObjectName(&amp;quot;ls_max_white_5&amp;quot;)&lt;br /&gt;
					self.ls_max_white_2 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.ls_max_white_2.setGeometry(QtCore.QRect(50, 54, 50, 15))&lt;br /&gt;
					self.ls_max_white_2.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
					self.ls_max_white_2.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
					self.ls_max_white_2.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ls_max_white_2.setObjectName(&amp;quot;ls_max_white_2&amp;quot;)&lt;br /&gt;
					self.ls_max_white_4 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.ls_max_white_4.setGeometry(QtCore.QRect(50, 96, 50, 15))&lt;br /&gt;
					self.ls_max_white_4.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
					self.ls_max_white_4.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
					self.ls_max_white_4.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ls_max_white_4.setObjectName(&amp;quot;ls_max_white_4&amp;quot;)&lt;br /&gt;
					self.ls_max_white_6 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.ls_max_white_6.setGeometry(QtCore.QRect(50, 138, 50, 15))&lt;br /&gt;
					self.ls_max_white_6.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
					self.ls_max_white_6.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
					self.ls_max_white_6.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ls_max_white_6.setObjectName(&amp;quot;ls_max_white_6&amp;quot;)&lt;br /&gt;
					self.ls_max_black_5 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.ls_max_black_5.setGeometry(QtCore.QRect(150, 117, 50, 15))&lt;br /&gt;
					self.ls_max_black_5.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
					self.ls_max_black_5.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
					self.ls_max_black_5.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ls_max_black_5.setObjectName(&amp;quot;ls_max_black_5&amp;quot;)&lt;br /&gt;
					self.ls_max_black_4 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.ls_max_black_4.setGeometry(QtCore.QRect(150, 96, 50, 15))&lt;br /&gt;
					self.ls_max_black_4.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
					self.ls_max_black_4.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
					self.ls_max_black_4.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ls_max_black_4.setObjectName(&amp;quot;ls_max_black_4&amp;quot;)&lt;br /&gt;
					self.ls_max_black_8 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.ls_max_black_8.setGeometry(QtCore.QRect(150, 180, 50, 15))&lt;br /&gt;
					self.ls_max_black_8.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
					self.ls_max_black_8.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
					self.ls_max_black_8.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ls_max_black_8.setObjectName(&amp;quot;ls_max_black_8&amp;quot;)&lt;br /&gt;
					self.ls_max_black_1 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.ls_max_black_1.setGeometry(QtCore.QRect(150, 33, 50, 15))&lt;br /&gt;
					self.ls_max_black_1.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
					self.ls_max_black_1.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
					self.ls_max_black_1.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
					self.ls_max_black_1.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ls_max_black_1.setObjectName(&amp;quot;ls_max_black_1&amp;quot;)&lt;br /&gt;
					self.ls_max_black_2 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.ls_max_black_2.setGeometry(QtCore.QRect(150, 54, 50, 15))&lt;br /&gt;
					self.ls_max_black_2.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
					self.ls_max_black_2.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
					self.ls_max_black_2.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ls_max_black_2.setObjectName(&amp;quot;ls_max_black_2&amp;quot;)&lt;br /&gt;
					self.ls_max_black_3 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.ls_max_black_3.setGeometry(QtCore.QRect(150, 75, 50, 15))&lt;br /&gt;
					self.ls_max_black_3.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
					self.ls_max_black_3.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
					self.ls_max_black_3.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ls_max_black_3.setObjectName(&amp;quot;ls_max_black_3&amp;quot;)&lt;br /&gt;
					self.ls_max_black_6 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.ls_max_black_6.setGeometry(QtCore.QRect(150, 138, 50, 15))&lt;br /&gt;
					self.ls_max_black_6.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
					self.ls_max_black_6.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
					self.ls_max_black_6.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ls_max_black_6.setObjectName(&amp;quot;ls_max_black_6&amp;quot;)&lt;br /&gt;
					self.ls_max_black_7 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.ls_max_black_7.setGeometry(QtCore.QRect(150, 159, 50, 15))&lt;br /&gt;
					self.ls_max_black_7.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
					self.ls_max_black_7.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
					self.ls_max_black_7.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ls_max_black_7.setObjectName(&amp;quot;ls_max_black_7&amp;quot;)&lt;br /&gt;
					self.label_227 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.label_227.setGeometry(QtCore.QRect(50, 14, 51, 16))&lt;br /&gt;
					self.label_227.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
					self.label_227.setObjectName(&amp;quot;label_227&amp;quot;)&lt;br /&gt;
					self.CalibrateBlack_Cmd = QtWidgets.QPushButton(self.groupBox_5)&lt;br /&gt;
					self.CalibrateBlack_Cmd.setGeometry(QtCore.QRect(130, 310, 101, 31))&lt;br /&gt;
					self.CalibrateBlack_Cmd.setObjectName(&amp;quot;CalibrateBlack_Cmd&amp;quot;)&lt;br /&gt;
					self.label_228 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.label_228.setGeometry(QtCore.QRect(150, 15, 51, 16))&lt;br /&gt;
					self.label_228.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
					self.label_228.setObjectName(&amp;quot;label_228&amp;quot;)&lt;br /&gt;
					self.ls_swap_left_right = QtWidgets.QCheckBox(self.groupBox_5)&lt;br /&gt;
					self.ls_swap_left_right.setGeometry(QtCore.QRect(140, 220, 81, 20))&lt;br /&gt;
					self.ls_swap_left_right.setObjectName(&amp;quot;ls_swap_left_right&amp;quot;)&lt;br /&gt;
					self.ls_show_normalized = QtWidgets.QCheckBox(self.groupBox_5)&lt;br /&gt;
					self.ls_show_normalized.setGeometry(QtCore.QRect(30, 220, 101, 17))&lt;br /&gt;
					self.ls_show_normalized.setObjectName(&amp;quot;ls_show_normalized&amp;quot;)&lt;br /&gt;
					self.label_23 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.label_23.setGeometry(QtCore.QRect(130, 240, 75, 21))&lt;br /&gt;
					self.label_23.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_23.setObjectName(&amp;quot;label_23&amp;quot;)&lt;br /&gt;
					self.line_disp_max_value = QtWidgets.QSpinBox(self.groupBox_5)&lt;br /&gt;
					self.line_disp_max_value.setGeometry(QtCore.QRect(30, 260, 75, 20))&lt;br /&gt;
					self.line_disp_max_value.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Maximum value used as 100% in bar display.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
					self.line_disp_max_value.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.line_disp_max_value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.line_disp_max_value.setMaximum(4250)&lt;br /&gt;
					self.line_disp_max_value.setSingleStep(256)&lt;br /&gt;
					self.line_disp_max_value.setProperty(&amp;quot;value&amp;quot;, 4096)&lt;br /&gt;
					self.line_disp_max_value.setObjectName(&amp;quot;line_disp_max_value&amp;quot;)&lt;br /&gt;
					self.label_156 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
					self.label_156.setGeometry(QtCore.QRect(34, 240, 61, 20))&lt;br /&gt;
					self.label_156.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.label_156.setObjectName(&amp;quot;label_156&amp;quot;)&lt;br /&gt;
					self.ls_crossing_detect = QtWidgets.QDoubleSpinBox(self.groupBox_5)&lt;br /&gt;
					self.ls_crossing_detect.setGeometry(QtCore.QRect(130, 260, 75, 20))&lt;br /&gt;
					self.ls_crossing_detect.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ls_crossing_detect.setDecimals(1)&lt;br /&gt;
					self.ls_crossing_detect.setMinimum(2.0)&lt;br /&gt;
					self.ls_crossing_detect.setMaximum(8.0)&lt;br /&gt;
					self.ls_crossing_detect.setSingleStep(0.1)&lt;br /&gt;
					self.ls_crossing_detect.setProperty(&amp;quot;value&amp;quot;, 4.5)&lt;br /&gt;
					self.ls_crossing_detect.setObjectName(&amp;quot;ls_crossing_detect&amp;quot;)&lt;br /&gt;
					self.line_2 = QtWidgets.QFrame(self.groupBox_5)&lt;br /&gt;
					self.line_2.setGeometry(QtCore.QRect(20, 200, 211, 21))&lt;br /&gt;
					self.line_2.setFrameShape(QtWidgets.QFrame.HLine)&lt;br /&gt;
					self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)&lt;br /&gt;
					self.line_2.setObjectName(&amp;quot;line_2&amp;quot;)&lt;br /&gt;
					self.line_3 = QtWidgets.QFrame(self.groupBox_5)&lt;br /&gt;
					self.line_3.setGeometry(QtCore.QRect(20, 290, 211, 21))&lt;br /&gt;
					self.line_3.setFrameShape(QtWidgets.QFrame.HLine)&lt;br /&gt;
					self.line_3.setFrameShadow(QtWidgets.QFrame.Sunken)&lt;br /&gt;
					self.line_3.setObjectName(&amp;quot;line_3&amp;quot;)&lt;br /&gt;
					self.ls_power_high = QtWidgets.QCheckBox(self.frame_2)&lt;br /&gt;
					self.ls_power_high.setGeometry(QtCore.QRect(230, 30, 91, 17))&lt;br /&gt;
					self.ls_power_high.setObjectName(&amp;quot;ls_power_high&amp;quot;)&lt;br /&gt;
					self.ls_line_white = QtWidgets.QCheckBox(self.frame_2)&lt;br /&gt;
					self.ls_line_white.setGeometry(QtCore.QRect(140, 30, 81, 17))&lt;br /&gt;
					self.ls_line_white.setObjectName(&amp;quot;ls_line_white&amp;quot;)&lt;br /&gt;
					self.tabWidget.addTab(self.tab_2, &amp;quot;&amp;quot;)&lt;br /&gt;
					self.tab_8 = QtWidgets.QWidget()&lt;br /&gt;
					self.tab_8.setObjectName(&amp;quot;tab_8&amp;quot;)&lt;br /&gt;
					self.frame_37 = QtWidgets.QFrame(self.tab_8)&lt;br /&gt;
					self.frame_37.setGeometry(QtCore.QRect(0, 0, 521, 291))&lt;br /&gt;
					self.frame_37.setMinimumSize(QtCore.QSize(250, 0))&lt;br /&gt;
					self.frame_37.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
					self.frame_37.setFrameShadow(QtWidgets.QFrame.Sunken)&lt;br /&gt;
					self.frame_37.setObjectName(&amp;quot;frame_37&amp;quot;)&lt;br /&gt;
					self.label_56 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
					self.label_56.setGeometry(QtCore.QRect(10, 10, 104, 16))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setPointSize(10)&lt;br /&gt;
					font.setBold(True)&lt;br /&gt;
					font.setWeight(75)&lt;br /&gt;
					self.label_56.setFont(font)&lt;br /&gt;
					self.label_56.setObjectName(&amp;quot;label_56&amp;quot;)&lt;br /&gt;
					self.ir_bar_1 = QtWidgets.QProgressBar(self.frame_37)&lt;br /&gt;
					self.ir_bar_1.setGeometry(QtCore.QRect(320, 200, 171, 41))&lt;br /&gt;
					self.ir_bar_1.setMaximum(130000)&lt;br /&gt;
					self.ir_bar_1.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
					self.ir_bar_1.setOrientation(QtCore.Qt.Horizontal)&lt;br /&gt;
					self.ir_bar_1.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
					self.ir_bar_1.setFormat(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.ir_bar_1.setObjectName(&amp;quot;ir_bar_1&amp;quot;)&lt;br /&gt;
					self.ir_bar_2 = QtWidgets.QProgressBar(self.frame_37)&lt;br /&gt;
					self.ir_bar_2.setGeometry(QtCore.QRect(280, 60, 41, 141))&lt;br /&gt;
					self.ir_bar_2.setMaximum(130000)&lt;br /&gt;
					self.ir_bar_2.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
					self.ir_bar_2.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
					self.ir_bar_2.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
					self.ir_bar_2.setFormat(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.ir_bar_2.setObjectName(&amp;quot;ir_bar_2&amp;quot;)&lt;br /&gt;
					self.ir_d1_meters = QtWidgets.QLineEdit(self.frame_37)&lt;br /&gt;
					self.ir_d1_meters.setGeometry(QtCore.QRect(400, 170, 81, 20))&lt;br /&gt;
					self.ir_d1_meters.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.ir_d1_meters.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
					self.ir_d1_meters.setObjectName(&amp;quot;ir_d1_meters&amp;quot;)&lt;br /&gt;
					self.label_279 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
					self.label_279.setGeometry(QtCore.QRect(360, 170, 41, 20))&lt;br /&gt;
					self.label_279.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
					self.label_279.setObjectName(&amp;quot;label_279&amp;quot;)&lt;br /&gt;
					self.label_281 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
					self.label_281.setGeometry(QtCore.QRect(330, 140, 71, 18))&lt;br /&gt;
					self.label_281.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
					self.label_281.setObjectName(&amp;quot;label_281&amp;quot;)&lt;br /&gt;
					self.ir_d1_raw = QtWidgets.QDoubleSpinBox(self.frame_37)&lt;br /&gt;
					self.ir_d1_raw.setGeometry(QtCore.QRect(400, 140, 81, 20))&lt;br /&gt;
					self.ir_d1_raw.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Raw measured value 2Y0A21F sensor from A/D converter&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
					self.ir_d1_raw.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.ir_d1_raw.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.ir_d1_raw.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ir_d1_raw.setReadOnly(True)&lt;br /&gt;
					self.ir_d1_raw.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
					self.ir_d1_raw.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.ir_d1_raw.setDecimals(0)&lt;br /&gt;
					self.ir_d1_raw.setMinimum(0.0)&lt;br /&gt;
					self.ir_d1_raw.setMaximum(1000000.0)&lt;br /&gt;
					self.ir_d1_raw.setObjectName(&amp;quot;ir_d1_raw&amp;quot;)&lt;br /&gt;
					self.label_287 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
					self.label_287.setGeometry(QtCore.QRect(490, 170, 16, 16))&lt;br /&gt;
					self.label_287.setObjectName(&amp;quot;label_287&amp;quot;)&lt;br /&gt;
					self.ir_d2_meters = QtWidgets.QLineEdit(self.frame_37)&lt;br /&gt;
					self.ir_d2_meters.setGeometry(QtCore.QRect(402, 90, 81, 20))&lt;br /&gt;
					self.ir_d2_meters.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.ir_d2_meters.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
					self.ir_d2_meters.setObjectName(&amp;quot;ir_d2_meters&amp;quot;)&lt;br /&gt;
					self.label_280 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
					self.label_280.setGeometry(QtCore.QRect(360, 90, 51, 20))&lt;br /&gt;
					sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)&lt;br /&gt;
					sizePolicy.setHorizontalStretch(0)&lt;br /&gt;
					sizePolicy.setVerticalStretch(0)&lt;br /&gt;
					sizePolicy.setHeightForWidth(self.label_280.sizePolicy().hasHeightForWidth())&lt;br /&gt;
					self.label_280.setSizePolicy(sizePolicy)&lt;br /&gt;
					self.label_280.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
					self.label_280.setObjectName(&amp;quot;label_280&amp;quot;)&lt;br /&gt;
					self.ir_d2_raw = QtWidgets.QDoubleSpinBox(self.frame_37)&lt;br /&gt;
					self.ir_d2_raw.setGeometry(QtCore.QRect(400, 60, 81, 20))&lt;br /&gt;
					self.ir_d2_raw.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Raw measured value 2Y0A21F sensor from A/D converter&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
					self.ir_d2_raw.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.ir_d2_raw.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.ir_d2_raw.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ir_d2_raw.setReadOnly(True)&lt;br /&gt;
					self.ir_d2_raw.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
					self.ir_d2_raw.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.ir_d2_raw.setDecimals(0)&lt;br /&gt;
					self.ir_d2_raw.setMinimum(0.0)&lt;br /&gt;
					self.ir_d2_raw.setMaximum(1000000.0)&lt;br /&gt;
					self.ir_d2_raw.setObjectName(&amp;quot;ir_d2_raw&amp;quot;)&lt;br /&gt;
					self.label_282 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
					self.label_282.setGeometry(QtCore.QRect(330, 60, 71, 18))&lt;br /&gt;
					self.label_282.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
					self.label_282.setObjectName(&amp;quot;label_282&amp;quot;)&lt;br /&gt;
					self.label_288 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
					self.label_288.setGeometry(QtCore.QRect(490, 90, 16, 16))&lt;br /&gt;
					self.label_288.setObjectName(&amp;quot;label_288&amp;quot;)&lt;br /&gt;
					self.ir_d2_80cm = QtWidgets.QDoubleSpinBox(self.frame_37)&lt;br /&gt;
					self.ir_d2_80cm.setGeometry(QtCore.QRect(170, 200, 85, 20))&lt;br /&gt;
					self.ir_d2_80cm.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Raw value at 50 cm from base of sensor (about 24000)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
					self.ir_d2_80cm.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.ir_d2_80cm.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ir_d2_80cm.setReadOnly(False)&lt;br /&gt;
					self.ir_d2_80cm.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)&lt;br /&gt;
					self.ir_d2_80cm.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.ir_d2_80cm.setDecimals(0)&lt;br /&gt;
					self.ir_d2_80cm.setMinimum(0.0)&lt;br /&gt;
					self.ir_d2_80cm.setMaximum(128000.0)&lt;br /&gt;
					self.ir_d2_80cm.setProperty(&amp;quot;value&amp;quot;, 24100.0)&lt;br /&gt;
					self.ir_d2_80cm.setObjectName(&amp;quot;ir_d2_80cm&amp;quot;)&lt;br /&gt;
					self.ir_d1_80cm = QtWidgets.QDoubleSpinBox(self.frame_37)&lt;br /&gt;
					self.ir_d1_80cm.setGeometry(QtCore.QRect(170, 148, 85, 20))&lt;br /&gt;
					self.ir_d1_80cm.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Raw value at 50cm from base of sensor (about 24000)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
					self.ir_d1_80cm.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.ir_d1_80cm.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ir_d1_80cm.setReadOnly(False)&lt;br /&gt;
					self.ir_d1_80cm.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)&lt;br /&gt;
					self.ir_d1_80cm.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.ir_d1_80cm.setDecimals(0)&lt;br /&gt;
					self.ir_d1_80cm.setMinimum(0.0)&lt;br /&gt;
					self.ir_d1_80cm.setMaximum(128000.0)&lt;br /&gt;
					self.ir_d1_80cm.setProperty(&amp;quot;value&amp;quot;, 24100.0)&lt;br /&gt;
					self.ir_d1_80cm.setObjectName(&amp;quot;ir_d1_80cm&amp;quot;)&lt;br /&gt;
					self.ir_d2_20cm = QtWidgets.QDoubleSpinBox(self.frame_37)&lt;br /&gt;
					self.ir_d2_20cm.setGeometry(QtCore.QRect(170, 174, 85, 20))&lt;br /&gt;
					self.ir_d2_20cm.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Raw value at 13 cm from base of sensor (about 80000)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
					self.ir_d2_20cm.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.ir_d2_20cm.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ir_d2_20cm.setReadOnly(False)&lt;br /&gt;
					self.ir_d2_20cm.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)&lt;br /&gt;
					self.ir_d2_20cm.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.ir_d2_20cm.setDecimals(0)&lt;br /&gt;
					self.ir_d2_20cm.setMinimum(0.0)&lt;br /&gt;
					self.ir_d2_20cm.setMaximum(128000.0)&lt;br /&gt;
					self.ir_d2_20cm.setProperty(&amp;quot;value&amp;quot;, 72300.0)&lt;br /&gt;
					self.ir_d2_20cm.setObjectName(&amp;quot;ir_d2_20cm&amp;quot;)&lt;br /&gt;
					self.label_284 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
					self.label_284.setGeometry(QtCore.QRect(32, 148, 132, 20))&lt;br /&gt;
					self.label_284.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
					self.label_284.setObjectName(&amp;quot;label_284&amp;quot;)&lt;br /&gt;
					self.label_286 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
					self.label_286.setGeometry(QtCore.QRect(32, 174, 132, 20))&lt;br /&gt;
					self.label_286.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
					self.label_286.setObjectName(&amp;quot;label_286&amp;quot;)&lt;br /&gt;
					self.label_285 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
					self.label_285.setGeometry(QtCore.QRect(32, 200, 132, 20))&lt;br /&gt;
					self.label_285.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
					self.label_285.setObjectName(&amp;quot;label_285&amp;quot;)&lt;br /&gt;
					self.label_283 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
					self.label_283.setGeometry(QtCore.QRect(32, 122, 132, 20))&lt;br /&gt;
					self.label_283.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
					self.label_283.setObjectName(&amp;quot;label_283&amp;quot;)&lt;br /&gt;
					self.ir_d1_20cm = QtWidgets.QDoubleSpinBox(self.frame_37)&lt;br /&gt;
					self.ir_d1_20cm.setGeometry(QtCore.QRect(170, 122, 85, 20))&lt;br /&gt;
					self.ir_d1_20cm.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Raw value at 13 cm from base of sensor (about 80000)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
					self.ir_d1_20cm.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
					self.ir_d1_20cm.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.ir_d1_20cm.setReadOnly(False)&lt;br /&gt;
					self.ir_d1_20cm.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)&lt;br /&gt;
					self.ir_d1_20cm.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.ir_d1_20cm.setDecimals(0)&lt;br /&gt;
					self.ir_d1_20cm.setMinimum(0.0)&lt;br /&gt;
					self.ir_d1_20cm.setMaximum(128000.0)&lt;br /&gt;
					self.ir_d1_20cm.setProperty(&amp;quot;value&amp;quot;, 72000.0)&lt;br /&gt;
					self.ir_d1_20cm.setObjectName(&amp;quot;ir_d1_20cm&amp;quot;)&lt;br /&gt;
					self.checkBox_ir_use = QtWidgets.QCheckBox(self.frame_37)&lt;br /&gt;
					self.checkBox_ir_use.setGeometry(QtCore.QRect(30, 70, 132, 17))&lt;br /&gt;
					self.checkBox_ir_use.setObjectName(&amp;quot;checkBox_ir_use&amp;quot;)&lt;br /&gt;
					self.checkBox_ir_installed = QtWidgets.QCheckBox(self.frame_37)&lt;br /&gt;
					self.checkBox_ir_installed.setGeometry(QtCore.QRect(30, 44, 111, 17))&lt;br /&gt;
					self.checkBox_ir_installed.setObjectName(&amp;quot;checkBox_ir_installed&amp;quot;)&lt;br /&gt;
					self.label_277 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
					self.label_277.setGeometry(QtCore.QRect(30, 100, 132, 13))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(True)&lt;br /&gt;
					font.setWeight(75)&lt;br /&gt;
					self.label_277.setFont(font)&lt;br /&gt;
					self.label_277.setObjectName(&amp;quot;label_277&amp;quot;)&lt;br /&gt;
					self.ir_apply = QtWidgets.QPushButton(self.frame_37)&lt;br /&gt;
					self.ir_apply.setGeometry(QtCore.QRect(170, 230, 85, 23))&lt;br /&gt;
					self.ir_apply.setObjectName(&amp;quot;ir_apply&amp;quot;)&lt;br /&gt;
					self.label_278 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
					self.label_278.setGeometry(QtCore.QRect(310, 30, 171, 16))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(True)&lt;br /&gt;
					font.setWeight(75)&lt;br /&gt;
					self.label_278.setFont(font)&lt;br /&gt;
					self.label_278.setObjectName(&amp;quot;label_278&amp;quot;)&lt;br /&gt;
					self.ir_edit = QtWidgets.QPushButton(self.frame_37)&lt;br /&gt;
					self.ir_edit.setGeometry(QtCore.QRect(30, 230, 132, 23))&lt;br /&gt;
					self.ir_edit.setObjectName(&amp;quot;ir_edit&amp;quot;)&lt;br /&gt;
					self.ir_cancel = QtWidgets.QPushButton(self.frame_37)&lt;br /&gt;
					self.ir_cancel.setGeometry(QtCore.QRect(260, 230, 51, 23))&lt;br /&gt;
					self.ir_cancel.setObjectName(&amp;quot;ir_cancel&amp;quot;)&lt;br /&gt;
					self.tabWidget.addTab(self.tab_8, &amp;quot;&amp;quot;)&lt;br /&gt;
					self.tab_3 = QtWidgets.QWidget()&lt;br /&gt;
					self.tab_3.setObjectName(&amp;quot;tab_3&amp;quot;)&lt;br /&gt;
					self.frame_4 = QtWidgets.QFrame(self.tab_3)&lt;br /&gt;
					self.frame_4.setGeometry(QtCore.QRect(0, 0, 711, 691))&lt;br /&gt;
					self.frame_4.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
					self.frame_4.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
					self.frame_4.setObjectName(&amp;quot;frame_4&amp;quot;)&lt;br /&gt;
					self.EnableCamera_checkBox = QtWidgets.QCheckBox(self.frame_4)&lt;br /&gt;
					self.EnableCamera_checkBox.setGeometry(QtCore.QRect(20, 20, 121, 17))&lt;br /&gt;
					self.EnableCamera_checkBox.setObjectName(&amp;quot;EnableCamera_checkBox&amp;quot;)&lt;br /&gt;
					self.frame_5 = QtWidgets.QFrame(self.frame_4)&lt;br /&gt;
					self.frame_5.setGeometry(QtCore.QRect(10, 430, 461, 211))&lt;br /&gt;
					self.frame_5.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
					self.frame_5.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
					self.frame_5.setObjectName(&amp;quot;frame_5&amp;quot;)&lt;br /&gt;
					self.Camera_Record_Cmd = QtWidgets.QPushButton(self.frame_5)&lt;br /&gt;
					self.Camera_Record_Cmd.setGeometry(QtCore.QRect(240, 10, 91, 23))&lt;br /&gt;
					self.Camera_Record_Cmd.setObjectName(&amp;quot;Camera_Record_Cmd&amp;quot;)&lt;br /&gt;
					self.Camera_StopRecord_Cmd = QtWidgets.QPushButton(self.frame_5)&lt;br /&gt;
					self.Camera_StopRecord_Cmd.setGeometry(QtCore.QRect(340, 10, 91, 23))&lt;br /&gt;
					self.Camera_StopRecord_Cmd.setObjectName(&amp;quot;Camera_StopRecord_Cmd&amp;quot;)&lt;br /&gt;
					self.Camera_Snapshot_Cmd = QtWidgets.QPushButton(self.frame_5)&lt;br /&gt;
					self.Camera_Snapshot_Cmd.setGeometry(QtCore.QRect(10, 10, 111, 23))&lt;br /&gt;
					self.Camera_Snapshot_Cmd.setObjectName(&amp;quot;Camera_Snapshot_Cmd&amp;quot;)&lt;br /&gt;
					self.Camera_SnapFilename = QtWidgets.QLineEdit(self.frame_5)&lt;br /&gt;
					self.Camera_SnapFilename.setGeometry(QtCore.QRect(240, 70, 191, 20))&lt;br /&gt;
					self.Camera_SnapFilename.setObjectName(&amp;quot;Camera_SnapFilename&amp;quot;)&lt;br /&gt;
					self.label_55 = QtWidgets.QLabel(self.frame_5)&lt;br /&gt;
					self.label_55.setGeometry(QtCore.QRect(240, 50, 121, 20))&lt;br /&gt;
					self.label_55.setObjectName(&amp;quot;label_55&amp;quot;)&lt;br /&gt;
					self.Camera_SnapSave_Cmd = QtWidgets.QPushButton(self.frame_5)&lt;br /&gt;
					self.Camera_SnapSave_Cmd.setGeometry(QtCore.QRect(340, 100, 91, 23))&lt;br /&gt;
					self.Camera_SnapSave_Cmd.setObjectName(&amp;quot;Camera_SnapSave_Cmd&amp;quot;)&lt;br /&gt;
					self.Snapshot_Label = QtWidgets.QLabel(self.frame_5)&lt;br /&gt;
					self.Snapshot_Label.setGeometry(QtCore.QRect(10, 40, 221, 161))&lt;br /&gt;
					self.Snapshot_Label.setLayoutDirection(QtCore.Qt.LeftToRight)&lt;br /&gt;
					self.Snapshot_Label.setAutoFillBackground(True)&lt;br /&gt;
					self.Snapshot_Label.setFrameShape(QtWidgets.QFrame.Box)&lt;br /&gt;
					self.Snapshot_Label.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
					self.Snapshot_Label.setLineWidth(-2)&lt;br /&gt;
					self.Snapshot_Label.setText(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.Snapshot_Label.setScaledContents(True)&lt;br /&gt;
					self.Snapshot_Label.setIndent(0)&lt;br /&gt;
					self.Snapshot_Label.setObjectName(&amp;quot;Snapshot_Label&amp;quot;)&lt;br /&gt;
					self.VideoRecording_Label = QtWidgets.QLabel(self.frame_5)&lt;br /&gt;
					self.VideoRecording_Label.setGeometry(QtCore.QRect(240, 30, 71, 16))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(True)&lt;br /&gt;
					font.setWeight(75)&lt;br /&gt;
					self.VideoRecording_Label.setFont(font)&lt;br /&gt;
					self.VideoRecording_Label.setObjectName(&amp;quot;VideoRecording_Label&amp;quot;)&lt;br /&gt;
					self.Camera_ClearSnap_Cmd = QtWidgets.QPushButton(self.frame_5)&lt;br /&gt;
					self.Camera_ClearSnap_Cmd.setGeometry(QtCore.QRect(130, 10, 91, 23))&lt;br /&gt;
					self.Camera_ClearSnap_Cmd.setObjectName(&amp;quot;Camera_ClearSnap_Cmd&amp;quot;)&lt;br /&gt;
					self.frame_6 = QtWidgets.QFrame(self.frame_4)&lt;br /&gt;
					self.frame_6.setGeometry(QtCore.QRect(10, 40, 561, 381))&lt;br /&gt;
					self.frame_6.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
					self.frame_6.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
					self.frame_6.setObjectName(&amp;quot;frame_6&amp;quot;)&lt;br /&gt;
					self.Stream_Label = QtWidgets.QLabel(self.frame_6)&lt;br /&gt;
					self.Stream_Label.setGeometry(QtCore.QRect(10, 0, 541, 371))&lt;br /&gt;
					self.Stream_Label.setText(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.Stream_Label.setScaledContents(True)&lt;br /&gt;
					self.Stream_Label.setObjectName(&amp;quot;Stream_Label&amp;quot;)&lt;br /&gt;
					self.tabWidget.addTab(self.tab_3, &amp;quot;&amp;quot;)&lt;br /&gt;
					self.tab_4 = QtWidgets.QWidget()&lt;br /&gt;
					self.tab_4.setObjectName(&amp;quot;tab_4&amp;quot;)&lt;br /&gt;
					self.frame_7 = QtWidgets.QFrame(self.tab_4)&lt;br /&gt;
					self.frame_7.setGeometry(QtCore.QRect(0, 0, 541, 631))&lt;br /&gt;
					self.frame_7.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
					self.frame_7.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
					self.frame_7.setObjectName(&amp;quot;frame_7&amp;quot;)&lt;br /&gt;
					self.label_30 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
					self.label_30.setGeometry(QtCore.QRect(10, 0, 151, 21))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setPointSize(10)&lt;br /&gt;
					font.setBold(True)&lt;br /&gt;
					font.setWeight(75)&lt;br /&gt;
					self.label_30.setFont(font)&lt;br /&gt;
					self.label_30.setObjectName(&amp;quot;label_30&amp;quot;)&lt;br /&gt;
					self.enableServo1_checkBox = QtWidgets.QCheckBox(self.frame_7)&lt;br /&gt;
					self.enableServo1_checkBox.setGeometry(QtCore.QRect(10, 30, 111, 17))&lt;br /&gt;
					self.enableServo1_checkBox.setObjectName(&amp;quot;enableServo1_checkBox&amp;quot;)&lt;br /&gt;
					self.enableServo2_checkBox = QtWidgets.QCheckBox(self.frame_7)&lt;br /&gt;
					self.enableServo2_checkBox.setGeometry(QtCore.QRect(10, 150, 111, 17))&lt;br /&gt;
					self.enableServo2_checkBox.setObjectName(&amp;quot;enableServo2_checkBox&amp;quot;)&lt;br /&gt;
					self.enableServo3_checkBox = QtWidgets.QCheckBox(self.frame_7)&lt;br /&gt;
					self.enableServo3_checkBox.setGeometry(QtCore.QRect(10, 270, 111, 17))&lt;br /&gt;
					self.enableServo3_checkBox.setObjectName(&amp;quot;enableServo3_checkBox&amp;quot;)&lt;br /&gt;
					self.Servo1_Pos_dial = QtWidgets.QDial(self.frame_7)&lt;br /&gt;
					self.Servo1_Pos_dial.setGeometry(QtCore.QRect(210, 50, 81, 81))&lt;br /&gt;
					self.Servo1_Pos_dial.setMinimum(-920)&lt;br /&gt;
					self.Servo1_Pos_dial.setMaximum(920)&lt;br /&gt;
					self.Servo1_Pos_dial.setTracking(False)&lt;br /&gt;
					self.Servo1_Pos_dial.setObjectName(&amp;quot;Servo1_Pos_dial&amp;quot;)&lt;br /&gt;
					self.label_31 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
					self.label_31.setGeometry(QtCore.QRect(10, 50, 81, 21))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(False)&lt;br /&gt;
					font.setWeight(50)&lt;br /&gt;
					self.label_31.setFont(font)&lt;br /&gt;
					self.label_31.setObjectName(&amp;quot;label_31&amp;quot;)&lt;br /&gt;
					self.servo1_current_pos = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
					self.servo1_current_pos.setGeometry(QtCore.QRect(100, 50, 91, 20))&lt;br /&gt;
					self.servo1_current_pos.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.servo1_current_pos.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.servo1_current_pos.setObjectName(&amp;quot;servo1_current_pos&amp;quot;)&lt;br /&gt;
					self.label_33 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
					self.label_33.setGeometry(QtCore.QRect(10, 80, 91, 21))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(False)&lt;br /&gt;
					font.setWeight(50)&lt;br /&gt;
					self.label_33.setFont(font)&lt;br /&gt;
					self.label_33.setObjectName(&amp;quot;label_33&amp;quot;)&lt;br /&gt;
					self.manualServoPos1_Val = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
					self.manualServoPos1_Val.setGeometry(QtCore.QRect(100, 80, 91, 20))&lt;br /&gt;
					self.manualServoPos1_Val.setObjectName(&amp;quot;manualServoPos1_Val&amp;quot;)&lt;br /&gt;
					self.manualServoPos1_Cmd = QtWidgets.QPushButton(self.frame_7)&lt;br /&gt;
					self.manualServoPos1_Cmd.setGeometry(QtCore.QRect(100, 110, 91, 23))&lt;br /&gt;
					self.manualServoPos1_Cmd.setObjectName(&amp;quot;manualServoPos1_Cmd&amp;quot;)&lt;br /&gt;
					self.label_39 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
					self.label_39.setGeometry(QtCore.QRect(220, 30, 61, 21))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(False)&lt;br /&gt;
					font.setWeight(50)&lt;br /&gt;
					self.label_39.setFont(font)&lt;br /&gt;
					self.label_39.setObjectName(&amp;quot;label_39&amp;quot;)&lt;br /&gt;
					self.Servo2_Pos_dial = QtWidgets.QDial(self.frame_7)&lt;br /&gt;
					self.Servo2_Pos_dial.setGeometry(QtCore.QRect(210, 170, 81, 81))&lt;br /&gt;
					self.Servo2_Pos_dial.setMinimum(-920)&lt;br /&gt;
					self.Servo2_Pos_dial.setMaximum(920)&lt;br /&gt;
					self.Servo2_Pos_dial.setTracking(False)&lt;br /&gt;
					self.Servo2_Pos_dial.setObjectName(&amp;quot;Servo2_Pos_dial&amp;quot;)&lt;br /&gt;
					self.label_40 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
					self.label_40.setGeometry(QtCore.QRect(220, 150, 61, 21))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(False)&lt;br /&gt;
					font.setWeight(50)&lt;br /&gt;
					self.label_40.setFont(font)&lt;br /&gt;
					self.label_40.setObjectName(&amp;quot;label_40&amp;quot;)&lt;br /&gt;
					self.manualServoPos2_Val = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
					self.manualServoPos2_Val.setGeometry(QtCore.QRect(100, 200, 91, 20))&lt;br /&gt;
					self.manualServoPos2_Val.setObjectName(&amp;quot;manualServoPos2_Val&amp;quot;)&lt;br /&gt;
					self.servo2_current_pos = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
					self.servo2_current_pos.setGeometry(QtCore.QRect(100, 170, 91, 20))&lt;br /&gt;
					self.servo2_current_pos.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.servo2_current_pos.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.servo2_current_pos.setObjectName(&amp;quot;servo2_current_pos&amp;quot;)&lt;br /&gt;
					self.label_41 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
					self.label_41.setGeometry(QtCore.QRect(10, 200, 91, 21))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(False)&lt;br /&gt;
					font.setWeight(50)&lt;br /&gt;
					self.label_41.setFont(font)&lt;br /&gt;
					self.label_41.setObjectName(&amp;quot;label_41&amp;quot;)&lt;br /&gt;
					self.manualServoPos2_Cmd = QtWidgets.QPushButton(self.frame_7)&lt;br /&gt;
					self.manualServoPos2_Cmd.setGeometry(QtCore.QRect(100, 230, 91, 23))&lt;br /&gt;
					self.manualServoPos2_Cmd.setObjectName(&amp;quot;manualServoPos2_Cmd&amp;quot;)&lt;br /&gt;
					self.label_43 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
					self.label_43.setGeometry(QtCore.QRect(10, 170, 81, 21))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(False)&lt;br /&gt;
					font.setWeight(50)&lt;br /&gt;
					self.label_43.setFont(font)&lt;br /&gt;
					self.label_43.setObjectName(&amp;quot;label_43&amp;quot;)&lt;br /&gt;
					self.Servo3_Pos_dial = QtWidgets.QDial(self.frame_7)&lt;br /&gt;
					self.Servo3_Pos_dial.setGeometry(QtCore.QRect(210, 290, 81, 81))&lt;br /&gt;
					self.Servo3_Pos_dial.setMinimum(-920)&lt;br /&gt;
					self.Servo3_Pos_dial.setMaximum(920)&lt;br /&gt;
					self.Servo3_Pos_dial.setTracking(False)&lt;br /&gt;
					self.Servo3_Pos_dial.setObjectName(&amp;quot;Servo3_Pos_dial&amp;quot;)&lt;br /&gt;
					self.label_46 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
					self.label_46.setGeometry(QtCore.QRect(220, 270, 61, 21))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(False)&lt;br /&gt;
					font.setWeight(50)&lt;br /&gt;
					self.label_46.setFont(font)&lt;br /&gt;
					self.label_46.setObjectName(&amp;quot;label_46&amp;quot;)&lt;br /&gt;
					self.manualServoPos3_Val = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
					self.manualServoPos3_Val.setGeometry(QtCore.QRect(100, 320, 91, 20))&lt;br /&gt;
					self.manualServoPos3_Val.setObjectName(&amp;quot;manualServoPos3_Val&amp;quot;)&lt;br /&gt;
					self.servo3_current_pos = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
					self.servo3_current_pos.setGeometry(QtCore.QRect(100, 290, 91, 20))&lt;br /&gt;
					self.servo3_current_pos.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.servo3_current_pos.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.servo3_current_pos.setObjectName(&amp;quot;servo3_current_pos&amp;quot;)&lt;br /&gt;
					self.label_47 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
					self.label_47.setGeometry(QtCore.QRect(10, 320, 91, 21))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(False)&lt;br /&gt;
					font.setWeight(50)&lt;br /&gt;
					self.label_47.setFont(font)&lt;br /&gt;
					self.label_47.setObjectName(&amp;quot;label_47&amp;quot;)&lt;br /&gt;
					self.manualServoPos3_Cmd = QtWidgets.QPushButton(self.frame_7)&lt;br /&gt;
					self.manualServoPos3_Cmd.setGeometry(QtCore.QRect(100, 350, 91, 23))&lt;br /&gt;
					self.manualServoPos3_Cmd.setObjectName(&amp;quot;manualServoPos3_Cmd&amp;quot;)&lt;br /&gt;
					self.label_48 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
					self.label_48.setGeometry(QtCore.QRect(10, 290, 81, 21))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(False)&lt;br /&gt;
					font.setWeight(50)&lt;br /&gt;
					self.label_48.setFont(font)&lt;br /&gt;
					self.label_48.setObjectName(&amp;quot;label_48&amp;quot;)&lt;br /&gt;
					self.enableServo4_checkBox = QtWidgets.QCheckBox(self.frame_7)&lt;br /&gt;
					self.enableServo4_checkBox.setGeometry(QtCore.QRect(10, 390, 111, 17))&lt;br /&gt;
					self.enableServo4_checkBox.setObjectName(&amp;quot;enableServo4_checkBox&amp;quot;)&lt;br /&gt;
					self.Servo4_Pos_dial = QtWidgets.QDial(self.frame_7)&lt;br /&gt;
					self.Servo4_Pos_dial.setGeometry(QtCore.QRect(210, 410, 81, 81))&lt;br /&gt;
					self.Servo4_Pos_dial.setMinimum(-920)&lt;br /&gt;
					self.Servo4_Pos_dial.setMaximum(920)&lt;br /&gt;
					self.Servo4_Pos_dial.setTracking(False)&lt;br /&gt;
					self.Servo4_Pos_dial.setObjectName(&amp;quot;Servo4_Pos_dial&amp;quot;)&lt;br /&gt;
					self.label_49 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
					self.label_49.setGeometry(QtCore.QRect(220, 390, 61, 21))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(False)&lt;br /&gt;
					font.setWeight(50)&lt;br /&gt;
					self.label_49.setFont(font)&lt;br /&gt;
					self.label_49.setObjectName(&amp;quot;label_49&amp;quot;)&lt;br /&gt;
					self.manualServoPos4_Val = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
					self.manualServoPos4_Val.setGeometry(QtCore.QRect(100, 440, 91, 20))&lt;br /&gt;
					self.manualServoPos4_Val.setObjectName(&amp;quot;manualServoPos4_Val&amp;quot;)&lt;br /&gt;
					self.servo4_current_pos = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
					self.servo4_current_pos.setGeometry(QtCore.QRect(100, 410, 91, 20))&lt;br /&gt;
					self.servo4_current_pos.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.servo4_current_pos.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.servo4_current_pos.setObjectName(&amp;quot;servo4_current_pos&amp;quot;)&lt;br /&gt;
					self.label_50 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
					self.label_50.setGeometry(QtCore.QRect(10, 440, 91, 21))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(False)&lt;br /&gt;
					font.setWeight(50)&lt;br /&gt;
					self.label_50.setFont(font)&lt;br /&gt;
					self.label_50.setObjectName(&amp;quot;label_50&amp;quot;)&lt;br /&gt;
					self.manualServoPos4_Cmd = QtWidgets.QPushButton(self.frame_7)&lt;br /&gt;
					self.manualServoPos4_Cmd.setGeometry(QtCore.QRect(100, 470, 91, 23))&lt;br /&gt;
					self.manualServoPos4_Cmd.setObjectName(&amp;quot;manualServoPos4_Cmd&amp;quot;)&lt;br /&gt;
					self.label_51 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
					self.label_51.setGeometry(QtCore.QRect(10, 410, 81, 21))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(False)&lt;br /&gt;
					font.setWeight(50)&lt;br /&gt;
					self.label_51.setFont(font)&lt;br /&gt;
					self.label_51.setObjectName(&amp;quot;label_51&amp;quot;)&lt;br /&gt;
					self.enableServo5_checkBox = QtWidgets.QCheckBox(self.frame_7)&lt;br /&gt;
					self.enableServo5_checkBox.setGeometry(QtCore.QRect(10, 510, 111, 17))&lt;br /&gt;
					self.enableServo5_checkBox.setObjectName(&amp;quot;enableServo5_checkBox&amp;quot;)&lt;br /&gt;
					self.Servo5_Pos_dial = QtWidgets.QDial(self.frame_7)&lt;br /&gt;
					self.Servo5_Pos_dial.setGeometry(QtCore.QRect(210, 530, 81, 81))&lt;br /&gt;
					self.Servo5_Pos_dial.setMinimum(-920)&lt;br /&gt;
					self.Servo5_Pos_dial.setMaximum(920)&lt;br /&gt;
					self.Servo5_Pos_dial.setTracking(False)&lt;br /&gt;
					self.Servo5_Pos_dial.setObjectName(&amp;quot;Servo5_Pos_dial&amp;quot;)&lt;br /&gt;
					self.label_52 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
					self.label_52.setGeometry(QtCore.QRect(220, 510, 61, 21))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(False)&lt;br /&gt;
					font.setWeight(50)&lt;br /&gt;
					self.label_52.setFont(font)&lt;br /&gt;
					self.label_52.setObjectName(&amp;quot;label_52&amp;quot;)&lt;br /&gt;
					self.manualServoPos5_Val = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
					self.manualServoPos5_Val.setGeometry(QtCore.QRect(100, 560, 91, 20))&lt;br /&gt;
					self.manualServoPos5_Val.setObjectName(&amp;quot;manualServoPos5_Val&amp;quot;)&lt;br /&gt;
					self.servo5_current_pos = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
					self.servo5_current_pos.setGeometry(QtCore.QRect(100, 530, 91, 20))&lt;br /&gt;
					self.servo5_current_pos.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.servo5_current_pos.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
					self.servo5_current_pos.setObjectName(&amp;quot;servo5_current_pos&amp;quot;)&lt;br /&gt;
					self.label_53 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
					self.label_53.setGeometry(QtCore.QRect(10, 560, 91, 21))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(False)&lt;br /&gt;
					font.setWeight(50)&lt;br /&gt;
					self.label_53.setFont(font)&lt;br /&gt;
					self.label_53.setObjectName(&amp;quot;label_53&amp;quot;)&lt;br /&gt;
					self.manualServoPos5_Cmd = QtWidgets.QPushButton(self.frame_7)&lt;br /&gt;
					self.manualServoPos5_Cmd.setGeometry(QtCore.QRect(100, 590, 91, 23))&lt;br /&gt;
					self.manualServoPos5_Cmd.setObjectName(&amp;quot;manualServoPos5_Cmd&amp;quot;)&lt;br /&gt;
					self.label_54 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
					self.label_54.setGeometry(QtCore.QRect(10, 530, 81, 21))&lt;br /&gt;
					font = QtGui.QFont()&lt;br /&gt;
					font.setBold(False)&lt;br /&gt;
					font.setWeight(50)&lt;br /&gt;
					self.label_54.setFont(font)&lt;br /&gt;
					self.label_54.setObjectName(&amp;quot;label_54&amp;quot;)&lt;br /&gt;
					self.Servo1Steering_checkBox = QtWidgets.QCheckBox(self.frame_7)&lt;br /&gt;
					self.Servo1Steering_checkBox.setGeometry(QtCore.QRect(110, 30, 111, 17))&lt;br /&gt;
					self.Servo1Steering_checkBox.setObjectName(&amp;quot;Servo1Steering_checkBox&amp;quot;)&lt;br /&gt;
					self.val_servo1_offset = QtWidgets.QDoubleSpinBox(self.frame_7)&lt;br /&gt;
					self.val_servo1_offset.setGeometry(QtCore.QRect(300, 50, 81, 18))&lt;br /&gt;
					self.val_servo1_offset.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.val_servo1_offset.setReadOnly(False)&lt;br /&gt;
					self.val_servo1_offset.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)&lt;br /&gt;
					self.val_servo1_offset.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.val_servo1_offset.setDecimals(0)&lt;br /&gt;
					self.val_servo1_offset.setMinimum(-200.0)&lt;br /&gt;
					self.val_servo1_offset.setMaximum(200.0)&lt;br /&gt;
					self.val_servo1_offset.setSingleStep(0.01)&lt;br /&gt;
					self.val_servo1_offset.setProperty(&amp;quot;value&amp;quot;, 10.0)&lt;br /&gt;
					self.val_servo1_offset.setObjectName(&amp;quot;val_servo1_offset&amp;quot;)&lt;br /&gt;
					self.label_servo_6 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
					self.label_servo_6.setGeometry(QtCore.QRect(310, 70, 75, 13))&lt;br /&gt;
					self.label_servo_6.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
					self.label_servo_6.setObjectName(&amp;quot;label_servo_6&amp;quot;)&lt;br /&gt;
					self.val_servo1_scale = QtWidgets.QDoubleSpinBox(self.frame_7)&lt;br /&gt;
					self.val_servo1_scale.setGeometry(QtCore.QRect(300, 130, 81, 18))&lt;br /&gt;
					self.val_servo1_scale.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.val_servo1_scale.setReadOnly(False)&lt;br /&gt;
					self.val_servo1_scale.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)&lt;br /&gt;
					self.val_servo1_scale.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.val_servo1_scale.setDecimals(1)&lt;br /&gt;
					self.val_servo1_scale.setMinimum(-180.0)&lt;br /&gt;
					self.val_servo1_scale.setMaximum(180.0)&lt;br /&gt;
					self.val_servo1_scale.setSingleStep(1.0)&lt;br /&gt;
					self.val_servo1_scale.setProperty(&amp;quot;value&amp;quot;, 160.0)&lt;br /&gt;
					self.val_servo1_scale.setObjectName(&amp;quot;val_servo1_scale&amp;quot;)&lt;br /&gt;
					self.label_servo_9 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
					self.label_servo_9.setGeometry(QtCore.QRect(300, 110, 87, 13))&lt;br /&gt;
					self.label_servo_9.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
					self.label_servo_9.setObjectName(&amp;quot;label_servo_9&amp;quot;)&lt;br /&gt;
					self.label_servo_8 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
					self.label_servo_8.setGeometry(QtCore.QRect(303, 31, 81, 13))&lt;br /&gt;
					self.label_servo_8.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
					self.label_servo_8.setObjectName(&amp;quot;label_servo_8&amp;quot;)&lt;br /&gt;
					self.val_steer_distance = QtWidgets.QDoubleSpinBox(self.frame_7)&lt;br /&gt;
					self.val_steer_distance.setGeometry(QtCore.QRect(300, 90, 81, 18))&lt;br /&gt;
					self.val_steer_distance.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
					self.val_steer_distance.setReadOnly(False)&lt;br /&gt;
					self.val_steer_distance.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
					self.val_steer_distance.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
					self.val_steer_distance.setDecimals(3)&lt;br /&gt;
					self.val_steer_distance.setMinimum(-1.0)&lt;br /&gt;
					self.val_steer_distance.setMaximum(1.0)&lt;br /&gt;
					self.val_steer_distance.setSingleStep(0.01)&lt;br /&gt;
					self.val_steer_distance.setProperty(&amp;quot;value&amp;quot;, 0.16)&lt;br /&gt;
					self.val_steer_distance.setObjectName(&amp;quot;val_steer_distance&amp;quot;)&lt;br /&gt;
					self.tabWidget.addTab(self.tab_4, &amp;quot;&amp;quot;)&lt;br /&gt;
					self.tab_5 = QtWidgets.QWidget()&lt;br /&gt;
					self.tab_5.setObjectName(&amp;quot;tab_5&amp;quot;)&lt;br /&gt;
					self.tabWidget.addTab(self.tab_5, &amp;quot;&amp;quot;)&lt;br /&gt;
					MainWindow.setCentralWidget(self.centralwidget)&lt;br /&gt;
					self.menubar = QtWidgets.QMenuBar(MainWindow)&lt;br /&gt;
					self.menubar.setGeometry(QtCore.QRect(0, 0, 1122, 21))&lt;br /&gt;
					self.menubar.setObjectName(&amp;quot;menubar&amp;quot;)&lt;br /&gt;
					self.menuFile = QtWidgets.QMenu(self.menubar)&lt;br /&gt;
					self.menuFile.setObjectName(&amp;quot;menuFile&amp;quot;)&lt;br /&gt;
					self.menuHelp = QtWidgets.QMenu(self.menubar)&lt;br /&gt;
					self.menuHelp.setObjectName(&amp;quot;menuHelp&amp;quot;)&lt;br /&gt;
					self.menuTools = QtWidgets.QMenu(self.menubar)&lt;br /&gt;
					self.menuTools.setObjectName(&amp;quot;menuTools&amp;quot;)&lt;br /&gt;
					MainWindow.setMenuBar(self.menubar)&lt;br /&gt;
					self.statusbar = QtWidgets.QStatusBar(MainWindow)&lt;br /&gt;
					self.statusbar.setObjectName(&amp;quot;statusbar&amp;quot;)&lt;br /&gt;
					MainWindow.setStatusBar(self.statusbar)&lt;br /&gt;
					self.menubar.addAction(self.menuFile.menuAction())&lt;br /&gt;
					self.menubar.addAction(self.menuTools.menuAction())&lt;br /&gt;
					self.menubar.addAction(self.menuHelp.menuAction())&lt;br /&gt;
&lt;br /&gt;
					self.retranslateUi(MainWindow)&lt;br /&gt;
					self.tabWidget.setCurrentIndex(2)&lt;br /&gt;
					self.tabWidget_2.setCurrentIndex(0)&lt;br /&gt;
					QtCore.QMetaObject.connectSlotsByName(MainWindow)&lt;br /&gt;
&lt;br /&gt;
				def retranslateUi(self, MainWindow):&lt;br /&gt;
					_translate = QtCore.QCoreApplication.translate&lt;br /&gt;
					MainWindow.setWindowTitle(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;MainWindow&amp;quot;))&lt;br /&gt;
					self.label_2.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Network Status: &amp;quot;))&lt;br /&gt;
					self.Disconnect_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Disconnect&amp;quot;))&lt;br /&gt;
					self.connect_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Connect&amp;quot;))&lt;br /&gt;
					self.label.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;IP adress:&amp;quot;))&lt;br /&gt;
					self.IP_NetworkStatus_label.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Status&amp;quot;))&lt;br /&gt;
					self.label_7.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;INTERNET CONNECTION&amp;quot;))&lt;br /&gt;
					self.SidebarMenu.setItemText(self.SidebarMenu.indexOf(self.SidebarMenuPage1), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Robot Connection&amp;quot;))&lt;br /&gt;
					self.send_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Send&amp;quot;))&lt;br /&gt;
					self.TX_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;TX&amp;quot;))&lt;br /&gt;
					self.RX_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;RX&amp;quot;))&lt;br /&gt;
					self.ConsoleHelp_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Help&amp;quot;))&lt;br /&gt;
					self.ConsoleClear_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Clear&amp;quot;))&lt;br /&gt;
					self.SidebarMenu.setItemText(self.SidebarMenu.indexOf(self.SidebarMenuPage2), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Debugger&amp;quot;))&lt;br /&gt;
					self.main_start.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Start&amp;quot;))&lt;br /&gt;
					self.main_remote_ctrl.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Remote control is active,&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;and mission paused.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.main_remote_ctrl.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Remote-Control&amp;quot;))&lt;br /&gt;
					self.main_stop.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Stop&amp;quot;))&lt;br /&gt;
					self.main_mission_state.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Shows that a mission is active.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Number is&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0: not active&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;1: waiting 1 second to start&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;2: running mission line&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;6,7: listening to button (more button presses)&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;8: stop mission&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.main_mission_state.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Mission&amp;quot;))&lt;br /&gt;
					self.SidebarMenu.setItemText(self.SidebarMenu.indexOf(self.SidebarMenuPage3), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Mission&amp;quot;))&lt;br /&gt;
					self.label_5.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Manual Speed:&amp;quot;))&lt;br /&gt;
					self.enableKeyboardRC_checkBox.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Enable Remote Keyboard control&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.enableKeyboardRC_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable keyboard control&amp;quot;))&lt;br /&gt;
					self.guiRC_Reverse_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;V&amp;quot;))&lt;br /&gt;
					self.guiRC_Forward_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;^&amp;quot;))&lt;br /&gt;
					self.enableGuiRC_checkBox.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Enable Remote - GUI control&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.enableGuiRC_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable GUI control&amp;quot;))&lt;br /&gt;
					self.guiRC_Right_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;gt;&amp;quot;))&lt;br /&gt;
					self.guiRC_Left_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;&amp;quot;))&lt;br /&gt;
					self.label_8.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Motor Remote GUI control&amp;quot;))&lt;br /&gt;
					self.label_9.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Robot Motor hardware parameters&amp;quot;))&lt;br /&gt;
					self.label_21.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Number of encoder pulses on each motor revolution (normally 48)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.label_21.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enc. pulse per rev:&amp;quot;))&lt;br /&gt;
					self.label_22.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Gear ratio [N]:&amp;quot;))&lt;br /&gt;
					self.Robot_GearRatio.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Gear ratio from motor to wheel axle (9.68).&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.label_24.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Wheel radius (Left) [m]:&amp;quot;))&lt;br /&gt;
					self.label_127.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Robot ID:&amp;quot;))&lt;br /&gt;
					self.label_128.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Hardware:&amp;quot;))&lt;br /&gt;
					self.label_25.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Wheel base is distance between wheels (mid-wheel to mid-wheel) for turn calculations.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.label_25.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Wheel-base [m]:&amp;quot;))&lt;br /&gt;
					self.Robot_PulsePerRev.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Number of encoder ticks per motor revolution (48)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.label_26.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Wheel radius (Right) [m]:&amp;quot;))&lt;br /&gt;
					self.Robot_RevEncoder_Right.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Checking this makes odometry go the other way, i.e. encoder pins swapped (right motor).&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;This should NOT be checked on normal REGBOT.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.Robot_RevEncoder_Right.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Reverse Right Encoder&amp;quot;))&lt;br /&gt;
					self.Robot_RevMotor.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Checking this makes wheels go the other way when orderd forward.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;This should NOT be checked on normal REGBOT.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.Robot_RevMotor.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Reverse Motor&amp;quot;))&lt;br /&gt;
					self.Robot_RevEncoder_Left.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Checking this makes odometry go the other way, i.e. encoder pins swapped.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;This should NOT be checked on normal REGBOT.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.Robot_RevEncoder_Left.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Reverse Left Encoder&amp;quot;))&lt;br /&gt;
					self.label_222.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Battery is assumed to be of type LiPo 3cell.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Any battery with a normal voltage between 9 and 15V should do.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.label_222.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Go idle at [V]:&amp;quot;))&lt;br /&gt;
					self.save_id_on_robot.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Save&amp;quot;))&lt;br /&gt;
					self.label_129.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Tilt offset [Rad]:&amp;quot;))&lt;br /&gt;
					self.Robot_edit.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Edit&amp;quot;))&lt;br /&gt;
					self.Robot_OnBattery.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Robot On Battery&amp;quot;))&lt;br /&gt;
					self.robot_cancel.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Cancel&amp;quot;))&lt;br /&gt;
					self.label_36.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Right&amp;quot;))&lt;br /&gt;
					self.label_37.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Left&amp;quot;))&lt;br /&gt;
					self.label_42.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Current [A]:&amp;quot;))&lt;br /&gt;
					self.label_28.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Encoder:&amp;quot;))&lt;br /&gt;
					self.label_38.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Drive information&amp;quot;))&lt;br /&gt;
					self.label_32.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Distance [m]:&amp;quot;))&lt;br /&gt;
					self.label_29.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Wheel Speed [m/s]:&amp;quot;))&lt;br /&gt;
					self.tabWidget_2.setTabText(self.tabWidget_2.indexOf(self.tab_6), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Drive Info&amp;quot;))&lt;br /&gt;
					self.label_76.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;degrees&amp;quot;))&lt;br /&gt;
					self.label_45.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Pose (relative to starting/reset point)&amp;quot;))&lt;br /&gt;
					self.Robot_PoseReset.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;reset&amp;quot;))&lt;br /&gt;
					self.label_44.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Heading [rad]:&amp;quot;))&lt;br /&gt;
					self.label_34.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;X (forward) [m]:&amp;quot;))&lt;br /&gt;
					self.label_35.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Y (left) [m]:&amp;quot;))&lt;br /&gt;
					self.label_125.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Tilt [rad]:&amp;quot;))&lt;br /&gt;
					self.tabWidget_2.setTabText(self.tabWidget_2.indexOf(self.tab_7), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Pose Info&amp;quot;))&lt;br /&gt;
					self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Motor&amp;quot;))&lt;br /&gt;
					self.label_10.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Line Sensor&amp;quot;))&lt;br /&gt;
					self.enableLineSensor_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Line Sensor&amp;quot;))&lt;br /&gt;
					self.line_bar_1.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
					self.line_bar_4.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
					self.line_bar_6.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
					self.line_bar_5.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
					self.line_bar_3.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
					self.line_bar_7.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
					self.line_bar_2.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
					self.line_bar_8.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
					self.label_11.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Sensor readings:&amp;quot;))&lt;br /&gt;
					self.label_12.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
					self.label_13.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L2&amp;quot;))&lt;br /&gt;
					self.label_14.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L6&amp;quot;))&lt;br /&gt;
					self.label_15.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L3&amp;quot;))&lt;br /&gt;
					self.label_16.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L4&amp;quot;))&lt;br /&gt;
					self.label_17.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L5&amp;quot;))&lt;br /&gt;
					self.label_18.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L7&amp;quot;))&lt;br /&gt;
					self.label_19.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L8&amp;quot;))&lt;br /&gt;
					self.label_118.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Left edge&amp;quot;))&lt;br /&gt;
					self.ls_left_edge.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Left edge position&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Gray if an edge is not detected&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.ls_right_edge.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Right edge position&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Either no edge is valid or both are valid.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.label_121.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Right edge&amp;quot;))&lt;br /&gt;
					self.label_20.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Edge Analysis:&amp;quot;))&lt;br /&gt;
					self.label_27.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Crossing&amp;quot;))&lt;br /&gt;
					self.label_171.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Line valid&amp;quot;))&lt;br /&gt;
					self.ls_crossing_cnt.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 20.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no crossing, 20 = crossing detected in 20ms. &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Counts +1 when detection is above &amp;amp;quot;Crossing det.&amp;amp;quot; value.&amp;lt;br/&amp;gt;else -1.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.ls_line_valid_cnt.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 5.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no line, 5=line seen in 5ms.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.groupBox_5.setTitle(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Sensor Calibration&amp;quot;))&lt;br /&gt;
					self.CalibrateWhite_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Calibrate White&amp;quot;))&lt;br /&gt;
					self.label_217.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L3&amp;quot;))&lt;br /&gt;
					self.label_218.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L5&amp;quot;))&lt;br /&gt;
					self.label_219.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L7&amp;quot;))&lt;br /&gt;
					self.label_221.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L4&amp;quot;))&lt;br /&gt;
					self.label_223.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L6&amp;quot;))&lt;br /&gt;
					self.label_224.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
					self.label_225.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L8&amp;quot;))&lt;br /&gt;
					self.label_226.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L2&amp;quot;))&lt;br /&gt;
					self.ls_max_white_7.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
					self.ls_max_white_8.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
					self.ls_max_white_1.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
					self.ls_max_white_3.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
					self.ls_max_white_5.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
					self.ls_max_white_2.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
					self.ls_max_white_4.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
					self.ls_max_white_6.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
					self.ls_max_black_5.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
					self.ls_max_black_4.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
					self.ls_max_black_8.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
					self.ls_max_black_1.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
					self.ls_max_black_2.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
					self.ls_max_black_3.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
					self.ls_max_black_6.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
					self.ls_max_black_7.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
					self.label_227.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;White&amp;quot;))&lt;br /&gt;
					self.CalibrateBlack_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Calibrate Black &amp;quot;))&lt;br /&gt;
					self.label_228.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Black&amp;quot;))&lt;br /&gt;
					self.ls_swap_left_right.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Swap the detected left and right edge.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Check this if LED\&#039;s are (mostly) in the forward (X) direction.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;(Normally the LED end of the line sensor PCB is closer to driving wheel axis).&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.ls_swap_left_right.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Swap Sides&amp;quot;))&lt;br /&gt;
					self.ls_show_normalized.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;If line is not white (bright), then assumed black (dark)&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;The normalized value should be shown as 75%.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.ls_show_normalized.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Show normalized&amp;quot;))&lt;br /&gt;
					self.label_23.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Crossing det.&amp;quot;))&lt;br /&gt;
					self.label_156.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Max RAW&amp;quot;))&lt;br /&gt;
					self.ls_crossing_detect.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Distance between left and right edge in a 0 to 8 scale.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Value depends distance between edges with an extra value if width increse fast. Therefore the width can temorarily be higher than 6 (edge values are from -3 to 3)&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;this should make it easier to detect a crossing.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;For Regbot in balance (and normal tape (38mm)), then about 4.8 cm to trigger crossing line.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;For Robobot this could be as low as 3-4, as the linesensor is wider.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.ls_power_high.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;If not in auto mode, should the line sensor LED power be high (ca. 30mA), else low (ca 10mA)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.ls_power_high.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;High power&amp;quot;))&lt;br /&gt;
					self.ls_line_white.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;If line is not white (bright), then assumed black (dark)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.ls_line_white.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;White line&amp;quot;))&lt;br /&gt;
					self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Edge Detection&amp;quot;))&lt;br /&gt;
					self.label_56.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Distance sensor&amp;quot;))&lt;br /&gt;
					self.label_279.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D1 right&amp;quot;))&lt;br /&gt;
					self.label_281.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D1 right RAW&amp;quot;))&lt;br /&gt;
					self.label_287.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;m&amp;quot;))&lt;br /&gt;
					self.label_280.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D2 front&amp;quot;))&lt;br /&gt;
					self.label_282.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D2 front RAW&amp;quot;))&lt;br /&gt;
					self.label_288.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;m&amp;quot;))&lt;br /&gt;
					self.label_284.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D1 right (50cm)&amp;quot;))&lt;br /&gt;
					self.label_286.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D2 front (13cm)&amp;quot;))&lt;br /&gt;
					self.label_285.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D2 front (50cm)&amp;quot;))&lt;br /&gt;
					self.label_283.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D1 right (13cm)&amp;quot;))&lt;br /&gt;
					self.checkBox_ir_use.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Should be turned on at power on (will be turned on automatically in missions that use the sensor - if installed). If not installed, then distance is  10m&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.checkBox_ir_use.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Sensor Enable&amp;quot;))&lt;br /&gt;
					self.checkBox_ir_installed.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Is the sensor installed (can not be turned on if not installed)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.checkBox_ir_installed.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Sensor Installed&amp;quot;))&lt;br /&gt;
					self.label_277.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Calibrate values (RAW)&amp;quot;))&lt;br /&gt;
					self.ir_apply.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Apply&amp;quot;))&lt;br /&gt;
					self.label_278.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Distance values (measured)&amp;quot;))&lt;br /&gt;
					self.ir_edit.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Edit&amp;quot;))&lt;br /&gt;
					self.ir_cancel.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Cancel&amp;quot;))&lt;br /&gt;
					self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_8), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;IR Distance&amp;quot;))&lt;br /&gt;
					self.EnableCamera_checkBox.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;To enable Camera stream, install mjpeg-streamer and run follwing commands:&amp;lt;br/&amp;gt;cd mjpg-streamer/mjpg-streamer-experimental/ &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;./mjpg_streamer -o &amp;amp;quot;output_http.so -w ./www&amp;amp;quot; -i &amp;amp;quot;input_raspicam.so&amp;amp;quot;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.EnableCamera_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Camera&amp;quot;))&lt;br /&gt;
					self.Camera_Record_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Record&amp;quot;))&lt;br /&gt;
					self.Camera_StopRecord_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Stop Recording&amp;quot;))&lt;br /&gt;
					self.Camera_Snapshot_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Take Snapshot&amp;quot;))&lt;br /&gt;
					self.label_55.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Snapshot Save Filename&amp;quot;))&lt;br /&gt;
					self.Camera_SnapSave_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Save&amp;quot;))&lt;br /&gt;
					self.VideoRecording_Label.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;&amp;lt;span style=\&amp;quot; color:#ff0000;\&amp;quot;&amp;gt;RECORDING...&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.Camera_ClearSnap_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Clear Snap&amp;quot;))&lt;br /&gt;
					self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Camera&amp;quot;))&lt;br /&gt;
					self.label_30.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Servo Controls&amp;quot;))&lt;br /&gt;
					self.enableServo1_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Servo 1&amp;quot;))&lt;br /&gt;
					self.enableServo2_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Servo 2&amp;quot;))&lt;br /&gt;
					self.enableServo3_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Servo 3&amp;quot;))&lt;br /&gt;
					self.label_31.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Current Position&amp;quot;))&lt;br /&gt;
					self.servo1_current_pos.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 20.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no crossing, 20 = crossing detected in 20ms. &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Counts +1 when detection is above &amp;amp;quot;Crossing det.&amp;amp;quot; value.&amp;lt;br/&amp;gt;else -1.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.label_33.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Command Position&amp;quot;))&lt;br /&gt;
					self.manualServoPos1_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Apply&amp;quot;))&lt;br /&gt;
					self.label_39.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Position Dial&amp;quot;))&lt;br /&gt;
					self.label_40.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Position Dial&amp;quot;))&lt;br /&gt;
					self.servo2_current_pos.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 20.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no crossing, 20 = crossing detected in 20ms. &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Counts +1 when detection is above &amp;amp;quot;Crossing det.&amp;amp;quot; value.&amp;lt;br/&amp;gt;else -1.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.label_41.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Command Position&amp;quot;))&lt;br /&gt;
					self.manualServoPos2_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Apply&amp;quot;))&lt;br /&gt;
					self.label_43.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Current Position&amp;quot;))&lt;br /&gt;
					self.label_46.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Position Dial&amp;quot;))&lt;br /&gt;
					self.servo3_current_pos.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 20.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no crossing, 20 = crossing detected in 20ms. &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Counts +1 when detection is above &amp;amp;quot;Crossing det.&amp;amp;quot; value.&amp;lt;br/&amp;gt;else -1.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.label_47.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Command Position&amp;quot;))&lt;br /&gt;
					self.manualServoPos3_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Apply&amp;quot;))&lt;br /&gt;
					self.label_48.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Current Position&amp;quot;))&lt;br /&gt;
					self.enableServo4_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Servo 4&amp;quot;))&lt;br /&gt;
					self.label_49.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Position Dial&amp;quot;))&lt;br /&gt;
					self.servo4_current_pos.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 20.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no crossing, 20 = crossing detected in 20ms. &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Counts +1 when detection is above &amp;amp;quot;Crossing det.&amp;amp;quot; value.&amp;lt;br/&amp;gt;else -1.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.label_50.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Command Position&amp;quot;))&lt;br /&gt;
					self.manualServoPos4_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Apply&amp;quot;))&lt;br /&gt;
					self.label_51.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Current Position&amp;quot;))&lt;br /&gt;
					self.enableServo5_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Servo 5&amp;quot;))&lt;br /&gt;
					self.label_52.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Position Dial&amp;quot;))&lt;br /&gt;
					self.servo5_current_pos.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 20.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no crossing, 20 = crossing detected in 20ms. &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Counts +1 when detection is above &amp;amp;quot;Crossing det.&amp;amp;quot; value.&amp;lt;br/&amp;gt;else -1.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.label_53.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Command Position&amp;quot;))&lt;br /&gt;
					self.manualServoPos5_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Apply&amp;quot;))&lt;br /&gt;
					self.label_54.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Current Position&amp;quot;))&lt;br /&gt;
					self.Servo1Steering_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Steering&amp;quot;))&lt;br /&gt;
					self.val_servo1_offset.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Steering offset to align steering wheel with straight forward.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Units is control units, i.e. about 50 is 5 degrees.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Zero offset means forward is 1.5ms pulse (center for servo)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.label_servo_6.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;distance&amp;quot;))&lt;br /&gt;
					self.val_servo1_scale.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Servo angle change from 1ms to 2ms PWM pulse to servo.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;(typically 160 deg for hitec7235 and 90 deg for &amp;amp;quot;old&amp;amp;quot; servos)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.label_servo_9.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;scale&amp;quot;))&lt;br /&gt;
					self.label_servo_8.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Offset&amp;quot;))&lt;br /&gt;
					self.val_steer_distance.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Distance from drive axle to steering wheel.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Positive is forward (front wheel). In meters.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Typically 0.16 m for ROBOBOT.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
					self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_4), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Servo&amp;quot;))&lt;br /&gt;
					self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_5), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Mission&amp;quot;))&lt;br /&gt;
					self.menuFile.setTitle(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;File&amp;quot;))&lt;br /&gt;
					self.menuHelp.setTitle(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Help&amp;quot;))&lt;br /&gt;
					self.menuTools.setTitle(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Tools&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
			if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
				import sys&lt;br /&gt;
				app = QtWidgets.QApplication(sys.argv)&lt;br /&gt;
				MainWindow = QtWidgets.QMainWindow()&lt;br /&gt;
				ui = Ui_MainWindow()&lt;br /&gt;
				ui.setupUi(MainWindow)&lt;br /&gt;
				MainWindow.show()&lt;br /&gt;
				sys.exit(app.exec_())&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Source_Code&amp;diff=5110</id>
		<title>Source Code</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Source_Code&amp;diff=5110"/>
		<updated>2020-12-06T17:43:40Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* User Interface Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==User Interface Code==&lt;br /&gt;
&lt;br /&gt;
[[Code]]&lt;br /&gt;
&lt;br /&gt;
==Main Application Code==&lt;br /&gt;
&lt;br /&gt;
==Linesensor==&lt;br /&gt;
&lt;br /&gt;
==IR distance sensors==&lt;br /&gt;
&lt;br /&gt;
==Servo==&lt;br /&gt;
&lt;br /&gt;
==Video Threads==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Source_Code&amp;diff=5109</id>
		<title>Source Code</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Source_Code&amp;diff=5109"/>
		<updated>2020-12-06T17:42:20Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* User Interface Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==User Interface Code==&lt;br /&gt;
&lt;br /&gt;
==Main Application Code==&lt;br /&gt;
&lt;br /&gt;
==Linesensor==&lt;br /&gt;
&lt;br /&gt;
==IR distance sensors==&lt;br /&gt;
&lt;br /&gt;
==Servo==&lt;br /&gt;
&lt;br /&gt;
==Video Threads==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=User_Interface_Code&amp;diff=5108</id>
		<title>User Interface Code</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=User_Interface_Code&amp;diff=5108"/>
		<updated>2020-12-06T17:41:09Z</updated>

		<summary type="html">&lt;p&gt;S192320: Blanked the page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=User_Interface_Code&amp;diff=5107</id>
		<title>User Interface Code</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=User_Interface_Code&amp;diff=5107"/>
		<updated>2020-12-06T17:40:31Z</updated>

		<summary type="html">&lt;p&gt;S192320: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;	# -*- coding: utf-8 -*-&lt;br /&gt;
&lt;br /&gt;
	# Form implementation generated from reading ui file &#039;Regbot_V1_9.ui&#039;&lt;br /&gt;
	#&lt;br /&gt;
	# Created by: PyQt5 UI code generator 5.13.2&lt;br /&gt;
	#&lt;br /&gt;
	# WARNING! All changes made in this file will be lost!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	from PyQt5 import QtCore, QtGui, QtWidgets&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	class Ui_MainWindow(object):&lt;br /&gt;
		def setupUi(self, MainWindow):&lt;br /&gt;
			MainWindow.setObjectName(&amp;quot;MainWindow&amp;quot;)&lt;br /&gt;
			MainWindow.resize(1122, 555)&lt;br /&gt;
			self.centralwidget = QtWidgets.QWidget(MainWindow)&lt;br /&gt;
			self.centralwidget.setObjectName(&amp;quot;centralwidget&amp;quot;)&lt;br /&gt;
			self.SidebarMenu = QtWidgets.QToolBox(self.centralwidget)&lt;br /&gt;
			self.SidebarMenu.setGeometry(QtCore.QRect(0, 0, 331, 721))&lt;br /&gt;
			self.SidebarMenu.setAutoFillBackground(True)&lt;br /&gt;
			self.SidebarMenu.setObjectName(&amp;quot;SidebarMenu&amp;quot;)&lt;br /&gt;
			self.SidebarMenuPage1 = QtWidgets.QWidget()&lt;br /&gt;
			self.SidebarMenuPage1.setGeometry(QtCore.QRect(0, 0, 331, 640))&lt;br /&gt;
			self.SidebarMenuPage1.setObjectName(&amp;quot;SidebarMenuPage1&amp;quot;)&lt;br /&gt;
			self.line = QtWidgets.QFrame(self.SidebarMenuPage1)&lt;br /&gt;
			self.line.setGeometry(QtCore.QRect(10, 220, 311, 21))&lt;br /&gt;
			self.line.setFrameShape(QtWidgets.QFrame.HLine)&lt;br /&gt;
			self.line.setFrameShadow(QtWidgets.QFrame.Sunken)&lt;br /&gt;
			self.line.setObjectName(&amp;quot;line&amp;quot;)&lt;br /&gt;
			self.IP_Connect_frame = QtWidgets.QFrame(self.SidebarMenuPage1)&lt;br /&gt;
			self.IP_Connect_frame.setGeometry(QtCore.QRect(0, 0, 331, 111))&lt;br /&gt;
			self.IP_Connect_frame.setAutoFillBackground(True)&lt;br /&gt;
			self.IP_Connect_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.IP_Connect_frame.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.IP_Connect_frame.setObjectName(&amp;quot;IP_Connect_frame&amp;quot;)&lt;br /&gt;
			self.label_2 = QtWidgets.QLabel(self.IP_Connect_frame)&lt;br /&gt;
			self.label_2.setGeometry(QtCore.QRect(30, 90, 91, 16))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_2.setFont(font)&lt;br /&gt;
			self.label_2.setObjectName(&amp;quot;label_2&amp;quot;)&lt;br /&gt;
			self.Disconnect_Cmd = QtWidgets.QPushButton(self.IP_Connect_frame)&lt;br /&gt;
			self.Disconnect_Cmd.setGeometry(QtCore.QRect(180, 60, 121, 23))&lt;br /&gt;
			self.Disconnect_Cmd.setObjectName(&amp;quot;Disconnect_Cmd&amp;quot;)&lt;br /&gt;
			self.connect_Cmd = QtWidgets.QPushButton(self.IP_Connect_frame)&lt;br /&gt;
			self.connect_Cmd.setGeometry(QtCore.QRect(30, 60, 131, 23))&lt;br /&gt;
			self.connect_Cmd.setObjectName(&amp;quot;connect_Cmd&amp;quot;)&lt;br /&gt;
			self.label = QtWidgets.QLabel(self.IP_Connect_frame)&lt;br /&gt;
			self.label.setGeometry(QtCore.QRect(120, 30, 61, 21))&lt;br /&gt;
			self.label.setObjectName(&amp;quot;label&amp;quot;)&lt;br /&gt;
			self.IP_Adress_field = QtWidgets.QLineEdit(self.IP_Connect_frame)&lt;br /&gt;
			self.IP_Adress_field.setGeometry(QtCore.QRect(170, 30, 131, 20))&lt;br /&gt;
			self.IP_Adress_field.setObjectName(&amp;quot;IP_Adress_field&amp;quot;)&lt;br /&gt;
			self.IP_NetworkStatus_label = QtWidgets.QLabel(self.IP_Connect_frame)&lt;br /&gt;
			self.IP_NetworkStatus_label.setGeometry(QtCore.QRect(140, 90, 141, 16))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.IP_NetworkStatus_label.setFont(font)&lt;br /&gt;
			self.IP_NetworkStatus_label.setObjectName(&amp;quot;IP_NetworkStatus_label&amp;quot;)&lt;br /&gt;
			self.label_7 = QtWidgets.QLabel(self.IP_Connect_frame)&lt;br /&gt;
			self.label_7.setGeometry(QtCore.QRect(10, 10, 131, 16))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_7.setFont(font)&lt;br /&gt;
			self.label_7.setObjectName(&amp;quot;label_7&amp;quot;)&lt;br /&gt;
			self.SidebarMenu.addItem(self.SidebarMenuPage1, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.SidebarMenuPage2 = QtWidgets.QWidget()&lt;br /&gt;
			self.SidebarMenuPage2.setGeometry(QtCore.QRect(0, 0, 331, 640))&lt;br /&gt;
			self.SidebarMenuPage2.setObjectName(&amp;quot;SidebarMenuPage2&amp;quot;)&lt;br /&gt;
			self.TextCmd_field = QtWidgets.QLineEdit(self.SidebarMenuPage2)&lt;br /&gt;
			self.TextCmd_field.setGeometry(QtCore.QRect(10, 580, 311, 20))&lt;br /&gt;
			self.TextCmd_field.setObjectName(&amp;quot;TextCmd_field&amp;quot;)&lt;br /&gt;
			self.send_Cmd = QtWidgets.QPushButton(self.SidebarMenuPage2)&lt;br /&gt;
			self.send_Cmd.setGeometry(QtCore.QRect(240, 610, 81, 23))&lt;br /&gt;
			self.send_Cmd.setObjectName(&amp;quot;send_Cmd&amp;quot;)&lt;br /&gt;
			self.TX_checkBox = QtWidgets.QCheckBox(self.SidebarMenuPage2)&lt;br /&gt;
			self.TX_checkBox.setGeometry(QtCore.QRect(10, 610, 41, 17))&lt;br /&gt;
			self.TX_checkBox.setObjectName(&amp;quot;TX_checkBox&amp;quot;)&lt;br /&gt;
			self.RX_checkBox = QtWidgets.QCheckBox(self.SidebarMenuPage2)&lt;br /&gt;
			self.RX_checkBox.setGeometry(QtCore.QRect(50, 610, 41, 17))&lt;br /&gt;
			self.RX_checkBox.setObjectName(&amp;quot;RX_checkBox&amp;quot;)&lt;br /&gt;
			self.ConsoleHelp_Cmd = QtWidgets.QPushButton(self.SidebarMenuPage2)&lt;br /&gt;
			self.ConsoleHelp_Cmd.setGeometry(QtCore.QRect(150, 610, 81, 23))&lt;br /&gt;
			self.ConsoleHelp_Cmd.setObjectName(&amp;quot;ConsoleHelp_Cmd&amp;quot;)&lt;br /&gt;
			self.CommandConsole_Window = QtWidgets.QPlainTextEdit(self.SidebarMenuPage2)&lt;br /&gt;
			self.CommandConsole_Window.setGeometry(QtCore.QRect(10, 0, 311, 571))&lt;br /&gt;
			self.CommandConsole_Window.setAcceptDrops(False)&lt;br /&gt;
			self.CommandConsole_Window.setToolTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.CommandConsole_Window.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)&lt;br /&gt;
			self.CommandConsole_Window.setLineWrapMode(QtWidgets.QPlainTextEdit.NoWrap)&lt;br /&gt;
			self.CommandConsole_Window.setObjectName(&amp;quot;CommandConsole_Window&amp;quot;)&lt;br /&gt;
			self.ConsoleClear_Cmd = QtWidgets.QPushButton(self.SidebarMenuPage2)&lt;br /&gt;
			self.ConsoleClear_Cmd.setGeometry(QtCore.QRect(90, 610, 51, 23))&lt;br /&gt;
			self.ConsoleClear_Cmd.setObjectName(&amp;quot;ConsoleClear_Cmd&amp;quot;)&lt;br /&gt;
			self.SidebarMenu.addItem(self.SidebarMenuPage2, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.SidebarMenuPage3 = QtWidgets.QWidget()&lt;br /&gt;
			self.SidebarMenuPage3.setGeometry(QtCore.QRect(0, 0, 331, 640))&lt;br /&gt;
			self.SidebarMenuPage3.setObjectName(&amp;quot;SidebarMenuPage3&amp;quot;)&lt;br /&gt;
			self.main_start = QtWidgets.QPushButton(self.SidebarMenuPage3)&lt;br /&gt;
			self.main_start.setGeometry(QtCore.QRect(170, 60, 151, 23))&lt;br /&gt;
			self.main_start.setObjectName(&amp;quot;main_start&amp;quot;)&lt;br /&gt;
			self.main_remote_ctrl = QtWidgets.QCheckBox(self.SidebarMenuPage3)&lt;br /&gt;
			self.main_remote_ctrl.setGeometry(QtCore.QRect(10, 30, 141, 17))&lt;br /&gt;
			self.main_remote_ctrl.setTristate(True)&lt;br /&gt;
			self.main_remote_ctrl.setObjectName(&amp;quot;main_remote_ctrl&amp;quot;)&lt;br /&gt;
			self.main_stop = QtWidgets.QPushButton(self.SidebarMenuPage3)&lt;br /&gt;
			self.main_stop.setGeometry(QtCore.QRect(10, 60, 151, 23))&lt;br /&gt;
			self.main_stop.setObjectName(&amp;quot;main_stop&amp;quot;)&lt;br /&gt;
			self.main_mission_state = QtWidgets.QCheckBox(self.SidebarMenuPage3)&lt;br /&gt;
			self.main_mission_state.setGeometry(QtCore.QRect(10, 10, 101, 17))&lt;br /&gt;
			self.main_mission_state.setTristate(True)&lt;br /&gt;
			self.main_mission_state.setObjectName(&amp;quot;main_mission_state&amp;quot;)&lt;br /&gt;
			self.SidebarMenu.addItem(self.SidebarMenuPage3, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)&lt;br /&gt;
			self.tabWidget.setGeometry(QtCore.QRect(330, 0, 961, 721))&lt;br /&gt;
			self.tabWidget.setAutoFillBackground(True)&lt;br /&gt;
			self.tabWidget.setObjectName(&amp;quot;tabWidget&amp;quot;)&lt;br /&gt;
			self.tab = QtWidgets.QWidget()&lt;br /&gt;
			self.tab.setObjectName(&amp;quot;tab&amp;quot;)&lt;br /&gt;
			self.MotorGUI_ctrl_frame = QtWidgets.QFrame(self.tab)&lt;br /&gt;
			self.MotorGUI_ctrl_frame.setGeometry(QtCore.QRect(0, 0, 321, 171))&lt;br /&gt;
			self.MotorGUI_ctrl_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.MotorGUI_ctrl_frame.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.MotorGUI_ctrl_frame.setObjectName(&amp;quot;MotorGUI_ctrl_frame&amp;quot;)&lt;br /&gt;
			self.label_5 = QtWidgets.QLabel(self.MotorGUI_ctrl_frame)&lt;br /&gt;
			self.label_5.setGeometry(QtCore.QRect(140, 100, 71, 21))&lt;br /&gt;
			self.label_5.setObjectName(&amp;quot;label_5&amp;quot;)&lt;br /&gt;
			self.enableKeyboardRC_checkBox = QtWidgets.QCheckBox(self.MotorGUI_ctrl_frame)&lt;br /&gt;
			self.enableKeyboardRC_checkBox.setGeometry(QtCore.QRect(150, 40, 141, 17))&lt;br /&gt;
			self.enableKeyboardRC_checkBox.setObjectName(&amp;quot;enableKeyboardRC_checkBox&amp;quot;)&lt;br /&gt;
			self.guiRC_Reverse_Cmd = QtWidgets.QPushButton(self.MotorGUI_ctrl_frame)&lt;br /&gt;
			self.guiRC_Reverse_Cmd.setGeometry(QtCore.QRect(50, 110, 31, 31))&lt;br /&gt;
			self.guiRC_Reverse_Cmd.setObjectName(&amp;quot;guiRC_Reverse_Cmd&amp;quot;)&lt;br /&gt;
			self.guiRC_Forward_Cmd = QtWidgets.QPushButton(self.MotorGUI_ctrl_frame)&lt;br /&gt;
			self.guiRC_Forward_Cmd.setGeometry(QtCore.QRect(50, 50, 31, 31))&lt;br /&gt;
			self.guiRC_Forward_Cmd.setObjectName(&amp;quot;guiRC_Forward_Cmd&amp;quot;)&lt;br /&gt;
			self.manualGuiSpeed_Cmd = QtWidgets.QLineEdit(self.MotorGUI_ctrl_frame)&lt;br /&gt;
			self.manualGuiSpeed_Cmd.setGeometry(QtCore.QRect(220, 100, 51, 20))&lt;br /&gt;
			self.manualGuiSpeed_Cmd.setObjectName(&amp;quot;manualGuiSpeed_Cmd&amp;quot;)&lt;br /&gt;
			self.enableGuiRC_checkBox = QtWidgets.QCheckBox(self.MotorGUI_ctrl_frame)&lt;br /&gt;
			self.enableGuiRC_checkBox.setGeometry(QtCore.QRect(150, 60, 111, 17))&lt;br /&gt;
			self.enableGuiRC_checkBox.setObjectName(&amp;quot;enableGuiRC_checkBox&amp;quot;)&lt;br /&gt;
			self.guiRC_Right_Cmd = QtWidgets.QPushButton(self.MotorGUI_ctrl_frame)&lt;br /&gt;
			self.guiRC_Right_Cmd.setGeometry(QtCore.QRect(80, 80, 31, 31))&lt;br /&gt;
			self.guiRC_Right_Cmd.setObjectName(&amp;quot;guiRC_Right_Cmd&amp;quot;)&lt;br /&gt;
			self.guiRC_Left_Cmd = QtWidgets.QPushButton(self.MotorGUI_ctrl_frame)&lt;br /&gt;
			self.guiRC_Left_Cmd.setGeometry(QtCore.QRect(20, 80, 31, 31))&lt;br /&gt;
			self.guiRC_Left_Cmd.setObjectName(&amp;quot;guiRC_Left_Cmd&amp;quot;)&lt;br /&gt;
			self.label_8 = QtWidgets.QLabel(self.MotorGUI_ctrl_frame)&lt;br /&gt;
			self.label_8.setGeometry(QtCore.QRect(10, 10, 151, 16))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_8.setFont(font)&lt;br /&gt;
			self.label_8.setObjectName(&amp;quot;label_8&amp;quot;)&lt;br /&gt;
			self.frame = QtWidgets.QFrame(self.tab)&lt;br /&gt;
			self.frame.setGeometry(QtCore.QRect(0, 170, 321, 321))&lt;br /&gt;
			self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.frame.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.frame.setObjectName(&amp;quot;frame&amp;quot;)&lt;br /&gt;
			self.label_9 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_9.setGeometry(QtCore.QRect(10, 10, 201, 16))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_9.setFont(font)&lt;br /&gt;
			self.label_9.setObjectName(&amp;quot;label_9&amp;quot;)&lt;br /&gt;
			self.label_21 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_21.setGeometry(QtCore.QRect(10, 149, 91, 21))&lt;br /&gt;
			self.label_21.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_21.setObjectName(&amp;quot;label_21&amp;quot;)&lt;br /&gt;
			self.Robot_WheelRadius_Right = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setGeometry(QtCore.QRect(109, 230, 71, 21))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.Robot_WheelRadius_Right.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.Robot_WheelRadius_Right.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_WheelRadius_Right.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Wheel radius&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_WheelRadius_Right.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setReadOnly(False)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setDecimals(4)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setMinimum(0.001)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setMaximum(0.99)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setSingleStep(0.001)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setProperty(&amp;quot;value&amp;quot;, 0.03)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setObjectName(&amp;quot;Robot_WheelRadius_Right&amp;quot;)&lt;br /&gt;
			self.Robot_WheelRadius_Left = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setGeometry(QtCore.QRect(109, 190, 71, 21))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.Robot_WheelRadius_Left.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.Robot_WheelRadius_Left.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_WheelRadius_Left.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Wheel radius&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_WheelRadius_Left.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setReadOnly(False)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setDecimals(4)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setMinimum(0.001)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setMaximum(0.99)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setSingleStep(0.001)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setProperty(&amp;quot;value&amp;quot;, 0.03)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setObjectName(&amp;quot;Robot_WheelRadius_Left&amp;quot;)&lt;br /&gt;
			self.label_22 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_22.setGeometry(QtCore.QRect(10, 118, 91, 23))&lt;br /&gt;
			self.label_22.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_22.setObjectName(&amp;quot;label_22&amp;quot;)&lt;br /&gt;
			self.Robot_GearRatio = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
			self.Robot_GearRatio.setGeometry(QtCore.QRect(110, 120, 71, 21))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.Robot_GearRatio.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.Robot_GearRatio.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.Robot_GearRatio.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_GearRatio.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_GearRatio.setReadOnly(False)&lt;br /&gt;
			self.Robot_GearRatio.setDecimals(3)&lt;br /&gt;
			self.Robot_GearRatio.setMaximum(999.99)&lt;br /&gt;
			self.Robot_GearRatio.setSingleStep(0.001)&lt;br /&gt;
			self.Robot_GearRatio.setProperty(&amp;quot;value&amp;quot;, 9.68)&lt;br /&gt;
			self.Robot_GearRatio.setObjectName(&amp;quot;Robot_GearRatio&amp;quot;)&lt;br /&gt;
			self.label_24 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_24.setGeometry(QtCore.QRect(10, 169, 121, 20))&lt;br /&gt;
			self.label_24.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_24.setObjectName(&amp;quot;label_24&amp;quot;)&lt;br /&gt;
			self.label_127 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_127.setGeometry(QtCore.QRect(50, 30, 51, 20))&lt;br /&gt;
			self.label_127.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_127.setObjectName(&amp;quot;label_127&amp;quot;)&lt;br /&gt;
			self.Robot_WheelBase = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
			self.Robot_WheelBase.setGeometry(QtCore.QRect(110, 90, 71, 21))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.Robot_WheelBase.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.Robot_WheelBase.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.Robot_WheelBase.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Distance between drivng wheels (0.155m)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.Robot_WheelBase.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_WheelBase.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_WheelBase.setReadOnly(False)&lt;br /&gt;
			self.Robot_WheelBase.setDecimals(4)&lt;br /&gt;
			self.Robot_WheelBase.setMaximum(5.0)&lt;br /&gt;
			self.Robot_WheelBase.setSingleStep(0.01)&lt;br /&gt;
			self.Robot_WheelBase.setProperty(&amp;quot;value&amp;quot;, 0.155)&lt;br /&gt;
			self.Robot_WheelBase.setObjectName(&amp;quot;Robot_WheelBase&amp;quot;)&lt;br /&gt;
			self.label_128 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_128.setGeometry(QtCore.QRect(50, 60, 51, 20))&lt;br /&gt;
			self.label_128.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_128.setObjectName(&amp;quot;label_128&amp;quot;)&lt;br /&gt;
			self.label_25 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_25.setGeometry(QtCore.QRect(20, 90, 81, 21))&lt;br /&gt;
			self.label_25.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_25.setObjectName(&amp;quot;label_25&amp;quot;)&lt;br /&gt;
			self.RobotID = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
			self.RobotID.setEnabled(True)&lt;br /&gt;
			self.RobotID.setGeometry(QtCore.QRect(110, 31, 71, 21))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.RobotID.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.RobotID.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.RobotID.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;This is the ID of the robot [1..9999].&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;To change the ID of robot:&amp;lt;br/&amp;gt;change ID,&amp;lt;br/&amp;gt;click &amp;amp;quot;save&amp;amp;quot;,&amp;lt;br/&amp;gt;click &amp;amp;quot;save on robot&amp;amp;quot;.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Change ID to -1 to revert to factory settings.&amp;lt;br/&amp;gt;And then change ID back to right value&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.RobotID.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.RobotID.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.RobotID.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.RobotID.setReadOnly(False)&lt;br /&gt;
			self.RobotID.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.RobotID.setSuffix(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.RobotID.setDecimals(0)&lt;br /&gt;
			self.RobotID.setMinimum(-2.0)&lt;br /&gt;
			self.RobotID.setMaximum(10000.0)&lt;br /&gt;
			self.RobotID.setObjectName(&amp;quot;RobotID&amp;quot;)&lt;br /&gt;
			self.RobotHW_type = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
			self.RobotHW_type.setEnabled(True)&lt;br /&gt;
			self.RobotHW_type.setGeometry(QtCore.QRect(110, 60, 71, 21))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.RobotHW_type.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.RobotHW_type.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.RobotHW_type.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;The hardware version&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;1 = old version with no wifi nor line sensor plugs on main board and round power supply plug on main board.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;2 = more plugs and satellite PCB for power management and wifi.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;3 = power management and on-off switch on-board (big motor controller board)&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;4 = same as 3, but with small motor controller board.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;5 = same as 2, but updated with new sattelite boards (power and wifi) etc.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;6 = hardware version 4 (motor controller integrated on board)&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;To change:&amp;lt;br/&amp;gt;click edit&amp;lt;br/&amp;gt;change HW setting,&amp;lt;br/&amp;gt;click &amp;amp;quot;save&amp;amp;quot;,&amp;lt;br/&amp;gt;click &amp;amp;quot;save to flash&amp;amp;quot;.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;(ID must be &amp;amp;gt; 0)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.RobotHW_type.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.RobotHW_type.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.RobotHW_type.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.RobotHW_type.setReadOnly(False)&lt;br /&gt;
			self.RobotHW_type.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.RobotHW_type.setSuffix(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.RobotHW_type.setDecimals(0)&lt;br /&gt;
			self.RobotHW_type.setMinimum(-2.0)&lt;br /&gt;
			self.RobotHW_type.setMaximum(10.0)&lt;br /&gt;
			self.RobotHW_type.setObjectName(&amp;quot;RobotHW_type&amp;quot;)&lt;br /&gt;
			self.Robot_PulsePerRev = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
			self.Robot_PulsePerRev.setGeometry(QtCore.QRect(110, 150, 71, 21))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.Robot_PulsePerRev.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.Robot_PulsePerRev.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.Robot_PulsePerRev.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_PulsePerRev.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_PulsePerRev.setReadOnly(False)&lt;br /&gt;
			self.Robot_PulsePerRev.setDecimals(0)&lt;br /&gt;
			self.Robot_PulsePerRev.setMinimum(1.0)&lt;br /&gt;
			self.Robot_PulsePerRev.setMaximum(1000.0)&lt;br /&gt;
			self.Robot_PulsePerRev.setProperty(&amp;quot;value&amp;quot;, 48.0)&lt;br /&gt;
			self.Robot_PulsePerRev.setObjectName(&amp;quot;Robot_PulsePerRev&amp;quot;)&lt;br /&gt;
			self.label_26 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_26.setGeometry(QtCore.QRect(10, 210, 121, 20))&lt;br /&gt;
			self.label_26.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_26.setObjectName(&amp;quot;label_26&amp;quot;)&lt;br /&gt;
			self.Robot_RevEncoder_Right = QtWidgets.QCheckBox(self.frame)&lt;br /&gt;
			self.Robot_RevEncoder_Right.setGeometry(QtCore.QRect(190, 90, 131, 21))&lt;br /&gt;
			self.Robot_RevEncoder_Right.setObjectName(&amp;quot;Robot_RevEncoder_Right&amp;quot;)&lt;br /&gt;
			self.Robot_RevMotor = QtWidgets.QCheckBox(self.frame)&lt;br /&gt;
			self.Robot_RevMotor.setGeometry(QtCore.QRect(190, 33, 92, 16))&lt;br /&gt;
			self.Robot_RevMotor.setObjectName(&amp;quot;Robot_RevMotor&amp;quot;)&lt;br /&gt;
			self.Robot_RevEncoder_Left = QtWidgets.QCheckBox(self.frame)&lt;br /&gt;
			self.Robot_RevEncoder_Left.setGeometry(QtCore.QRect(190, 60, 131, 21))&lt;br /&gt;
			self.Robot_RevEncoder_Left.setObjectName(&amp;quot;Robot_RevEncoder_Left&amp;quot;)&lt;br /&gt;
			self.Robot_BatteryIdleVolts = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setGeometry(QtCore.QRect(110, 290, 71, 21))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.Robot_BatteryIdleVolts.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Go idle when battery voltage is below this level.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;For a 3 cell LiPo battery it should go in idle mode at 3 x 3.2V = 9.6V&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;At 10V about 10% capacity is left before battery destruction, and in idle mode the robot uses about 30mA, so battey will selfdestruct after another about 3 hours.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;So remember to switch off and to recharge.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setReadOnly(False)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setDecimals(1)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setMinimum(9.0)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setMaximum(17.0)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setSingleStep(0.1)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setProperty(&amp;quot;value&amp;quot;, 9.9)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setObjectName(&amp;quot;Robot_BatteryIdleVolts&amp;quot;)&lt;br /&gt;
			self.label_222 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_222.setGeometry(QtCore.QRect(20, 290, 81, 20))&lt;br /&gt;
			self.label_222.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_222.setObjectName(&amp;quot;label_222&amp;quot;)&lt;br /&gt;
			self.save_id_on_robot = QtWidgets.QPushButton(self.frame)&lt;br /&gt;
			self.save_id_on_robot.setEnabled(False)&lt;br /&gt;
			self.save_id_on_robot.setGeometry(QtCore.QRect(210, 230, 92, 23))&lt;br /&gt;
			self.save_id_on_robot.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Save ID and robot specific settings (this frame) on robot.&amp;lt;br/&amp;gt;NB! you need to press &amp;amp;quot;save on robot&amp;amp;quot; in configuration block to save into EE-prom (flash)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.save_id_on_robot.setCheckable(False)&lt;br /&gt;
			self.save_id_on_robot.setChecked(False)&lt;br /&gt;
			self.save_id_on_robot.setObjectName(&amp;quot;save_id_on_robot&amp;quot;)&lt;br /&gt;
			self.label_129 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_129.setGeometry(QtCore.QRect(10, 265, 91, 21))&lt;br /&gt;
			self.label_129.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_129.setObjectName(&amp;quot;label_129&amp;quot;)&lt;br /&gt;
			self.Robot_BalanceOffset = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
			self.Robot_BalanceOffset.setGeometry(QtCore.QRect(110, 263, 71, 21))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.Robot_BalanceOffset.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.Robot_BalanceOffset.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.Robot_BalanceOffset.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Balance offset - where center of gravity (COG) is just above support point (wheels).&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Angle is in radians.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Positive is tilt forward.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.Robot_BalanceOffset.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_BalanceOffset.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_BalanceOffset.setReadOnly(False)&lt;br /&gt;
			self.Robot_BalanceOffset.setDecimals(4)&lt;br /&gt;
			self.Robot_BalanceOffset.setMinimum(-1.0)&lt;br /&gt;
			self.Robot_BalanceOffset.setMaximum(1.0)&lt;br /&gt;
			self.Robot_BalanceOffset.setSingleStep(0.001)&lt;br /&gt;
			self.Robot_BalanceOffset.setObjectName(&amp;quot;Robot_BalanceOffset&amp;quot;)&lt;br /&gt;
			self.Robot_edit = QtWidgets.QPushButton(self.frame)&lt;br /&gt;
			self.Robot_edit.setEnabled(True)&lt;br /&gt;
			self.Robot_edit.setGeometry(QtCore.QRect(210, 259, 92, 23))&lt;br /&gt;
			self.Robot_edit.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Switch to edit mode - rather than listen to robot messages - for these fields.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.Robot_edit.setCheckable(False)&lt;br /&gt;
			self.Robot_edit.setChecked(False)&lt;br /&gt;
			self.Robot_edit.setObjectName(&amp;quot;Robot_edit&amp;quot;)&lt;br /&gt;
			self.Robot_OnBattery = QtWidgets.QCheckBox(self.frame)&lt;br /&gt;
			self.Robot_OnBattery.setGeometry(QtCore.QRect(190, 120, 111, 21))&lt;br /&gt;
			self.Robot_OnBattery.setObjectName(&amp;quot;Robot_OnBattery&amp;quot;)&lt;br /&gt;
			self.robot_cancel = QtWidgets.QPushButton(self.frame)&lt;br /&gt;
			self.robot_cancel.setEnabled(False)&lt;br /&gt;
			self.robot_cancel.setGeometry(QtCore.QRect(210, 288, 92, 23))&lt;br /&gt;
			self.robot_cancel.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Revert to values on robot&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.robot_cancel.setCheckable(False)&lt;br /&gt;
			self.robot_cancel.setChecked(False)&lt;br /&gt;
			self.robot_cancel.setObjectName(&amp;quot;robot_cancel&amp;quot;)&lt;br /&gt;
			self.tabWidget_2 = QtWidgets.QTabWidget(self.tab)&lt;br /&gt;
			self.tabWidget_2.setGeometry(QtCore.QRect(0, 490, 321, 211))&lt;br /&gt;
			self.tabWidget_2.setObjectName(&amp;quot;tabWidget_2&amp;quot;)&lt;br /&gt;
			self.tab_6 = QtWidgets.QWidget()&lt;br /&gt;
			self.tab_6.setObjectName(&amp;quot;tab_6&amp;quot;)&lt;br /&gt;
			self.groupBox = QtWidgets.QGroupBox(self.tab_6)&lt;br /&gt;
			self.groupBox.setGeometry(QtCore.QRect(0, 0, 311, 181))&lt;br /&gt;
			self.groupBox.setTitle(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.groupBox.setObjectName(&amp;quot;groupBox&amp;quot;)&lt;br /&gt;
			self.Robot_CurrentLeft_Value = QtWidgets.QDoubleSpinBox(self.groupBox)&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setGeometry(QtCore.QRect(110, 100, 71, 21))&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setReadOnly(True)&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setDecimals(2)&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setMinimum(-100000.0)&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setMaximum(100000.0)&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setObjectName(&amp;quot;Robot_CurrentLeft_Value&amp;quot;)&lt;br /&gt;
			self.Robot_Distance = QtWidgets.QDoubleSpinBox(self.groupBox)&lt;br /&gt;
			self.Robot_Distance.setGeometry(QtCore.QRect(110, 130, 71, 21))&lt;br /&gt;
			self.Robot_Distance.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_Distance.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_Distance.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_Distance.setReadOnly(True)&lt;br /&gt;
			self.Robot_Distance.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_Distance.setDecimals(3)&lt;br /&gt;
			self.Robot_Distance.setMinimum(-999999.0)&lt;br /&gt;
			self.Robot_Distance.setMaximum(999999.99)&lt;br /&gt;
			self.Robot_Distance.setObjectName(&amp;quot;Robot_Distance&amp;quot;)&lt;br /&gt;
			self.label_36 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
			self.label_36.setGeometry(QtCore.QRect(186, 24, 71, 16))&lt;br /&gt;
			self.label_36.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_36.setObjectName(&amp;quot;label_36&amp;quot;)&lt;br /&gt;
			self.label_37 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
			self.label_37.setGeometry(QtCore.QRect(106, 24, 71, 16))&lt;br /&gt;
			self.label_37.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_37.setObjectName(&amp;quot;label_37&amp;quot;)&lt;br /&gt;
			self.label_42 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
			self.label_42.setGeometry(QtCore.QRect(11, 100, 91, 16))&lt;br /&gt;
			self.label_42.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.label_42.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_42.setObjectName(&amp;quot;label_42&amp;quot;)&lt;br /&gt;
			self.Robot_EncLeft_Value = QtWidgets.QSpinBox(self.groupBox)&lt;br /&gt;
			self.Robot_EncLeft_Value.setGeometry(QtCore.QRect(110, 40, 71, 21))&lt;br /&gt;
			self.Robot_EncLeft_Value.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_EncLeft_Value.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_EncLeft_Value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_EncLeft_Value.setReadOnly(True)&lt;br /&gt;
			self.Robot_EncLeft_Value.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_EncLeft_Value.setMinimum(-2147483647)&lt;br /&gt;
			self.Robot_EncLeft_Value.setMaximum(2147483647)&lt;br /&gt;
			self.Robot_EncLeft_Value.setObjectName(&amp;quot;Robot_EncLeft_Value&amp;quot;)&lt;br /&gt;
			self.Robot_CurrentRight_Value = QtWidgets.QDoubleSpinBox(self.groupBox)&lt;br /&gt;
			self.Robot_CurrentRight_Value.setGeometry(QtCore.QRect(190, 100, 71, 21))&lt;br /&gt;
			self.Robot_CurrentRight_Value.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_CurrentRight_Value.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_CurrentRight_Value.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_CurrentRight_Value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_CurrentRight_Value.setReadOnly(True)&lt;br /&gt;
			self.Robot_CurrentRight_Value.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_CurrentRight_Value.setDecimals(2)&lt;br /&gt;
			self.Robot_CurrentRight_Value.setMinimum(-100000.0)&lt;br /&gt;
			self.Robot_CurrentRight_Value.setMaximum(100000.0)&lt;br /&gt;
			self.Robot_CurrentRight_Value.setObjectName(&amp;quot;Robot_CurrentRight_Value&amp;quot;)&lt;br /&gt;
			self.label_28 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
			self.label_28.setGeometry(QtCore.QRect(11, 40, 91, 16))&lt;br /&gt;
			self.label_28.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_28.setObjectName(&amp;quot;label_28&amp;quot;)&lt;br /&gt;
			self.label_38 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
			self.label_38.setGeometry(QtCore.QRect(9, 5, 121, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setPointSize(10)&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_38.setFont(font)&lt;br /&gt;
			self.label_38.setObjectName(&amp;quot;label_38&amp;quot;)&lt;br /&gt;
			self.label_32 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
			self.label_32.setGeometry(QtCore.QRect(10, 130, 91, 16))&lt;br /&gt;
			self.label_32.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.label_32.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_32.setObjectName(&amp;quot;label_32&amp;quot;)&lt;br /&gt;
			self.label_29 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
			self.label_29.setGeometry(QtCore.QRect(1, 70, 101, 16))&lt;br /&gt;
			self.label_29.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.label_29.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_29.setObjectName(&amp;quot;label_29&amp;quot;)&lt;br /&gt;
			self.Robot_EncRight_Value = QtWidgets.QSpinBox(self.groupBox)&lt;br /&gt;
			self.Robot_EncRight_Value.setGeometry(QtCore.QRect(190, 40, 71, 21))&lt;br /&gt;
			self.Robot_EncRight_Value.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_EncRight_Value.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_EncRight_Value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_EncRight_Value.setReadOnly(True)&lt;br /&gt;
			self.Robot_EncRight_Value.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_EncRight_Value.setMinimum(-2147483647)&lt;br /&gt;
			self.Robot_EncRight_Value.setMaximum(2147483647)&lt;br /&gt;
			self.Robot_EncRight_Value.setObjectName(&amp;quot;Robot_EncRight_Value&amp;quot;)&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value = QtWidgets.QDoubleSpinBox(self.groupBox)&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setGeometry(QtCore.QRect(190, 70, 71, 21))&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setReadOnly(True)&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setDecimals(3)&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setMinimum(-100000.0)&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setMaximum(100000.0)&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setObjectName(&amp;quot;Robot_WheelRightSpeed_Value&amp;quot;)&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value = QtWidgets.QDoubleSpinBox(self.groupBox)&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setGeometry(QtCore.QRect(110, 70, 71, 21))&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setReadOnly(True)&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setDecimals(3)&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setMinimum(-100000.0)&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setMaximum(100000.0)&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setObjectName(&amp;quot;Robot_WheelLeftSpeed_Value&amp;quot;)&lt;br /&gt;
			self.tabWidget_2.addTab(self.tab_6, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.tab_7 = QtWidgets.QWidget()&lt;br /&gt;
			self.tab_7.setObjectName(&amp;quot;tab_7&amp;quot;)&lt;br /&gt;
			self.groupBox_3 = QtWidgets.QGroupBox(self.tab_7)&lt;br /&gt;
			self.groupBox_3.setGeometry(QtCore.QRect(0, 0, 311, 181))&lt;br /&gt;
			self.groupBox_3.setTitle(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.groupBox_3.setObjectName(&amp;quot;groupBox_3&amp;quot;)&lt;br /&gt;
			self.Robot_TiltDegrees = QtWidgets.QDoubleSpinBox(self.groupBox_3)&lt;br /&gt;
			self.Robot_TiltDegrees.setGeometry(QtCore.QRect(190, 131, 71, 21))&lt;br /&gt;
			self.Robot_TiltDegrees.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_TiltDegrees.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_TiltDegrees.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_TiltDegrees.setReadOnly(True)&lt;br /&gt;
			self.Robot_TiltDegrees.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_TiltDegrees.setDecimals(3)&lt;br /&gt;
			self.Robot_TiltDegrees.setMinimum(-999999.0)&lt;br /&gt;
			self.Robot_TiltDegrees.setMaximum(999999.99)&lt;br /&gt;
			self.Robot_TiltDegrees.setObjectName(&amp;quot;Robot_TiltDegrees&amp;quot;)&lt;br /&gt;
			self.label_76 = QtWidgets.QLabel(self.groupBox_3)&lt;br /&gt;
			self.label_76.setGeometry(QtCore.QRect(190, 80, 71, 21))&lt;br /&gt;
			self.label_76.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_76.setObjectName(&amp;quot;label_76&amp;quot;)&lt;br /&gt;
			self.label_45 = QtWidgets.QLabel(self.groupBox_3)&lt;br /&gt;
			self.label_45.setGeometry(QtCore.QRect(10, 0, 271, 31))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setPointSize(10)&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_45.setFont(font)&lt;br /&gt;
			self.label_45.setObjectName(&amp;quot;label_45&amp;quot;)&lt;br /&gt;
			self.Robot_PoseX = QtWidgets.QDoubleSpinBox(self.groupBox_3)&lt;br /&gt;
			self.Robot_PoseX.setGeometry(QtCore.QRect(100, 40, 81, 21))&lt;br /&gt;
			self.Robot_PoseX.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_PoseX.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_PoseX.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_PoseX.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_PoseX.setReadOnly(True)&lt;br /&gt;
			self.Robot_PoseX.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_PoseX.setDecimals(3)&lt;br /&gt;
			self.Robot_PoseX.setMinimum(-999999.0)&lt;br /&gt;
			self.Robot_PoseX.setMaximum(999999.99)&lt;br /&gt;
			self.Robot_PoseX.setObjectName(&amp;quot;Robot_PoseX&amp;quot;)&lt;br /&gt;
			self.Robot_PoseY = QtWidgets.QDoubleSpinBox(self.groupBox_3)&lt;br /&gt;
			self.Robot_PoseY.setGeometry(QtCore.QRect(100, 71, 81, 21))&lt;br /&gt;
			self.Robot_PoseY.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_PoseY.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_PoseY.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_PoseY.setReadOnly(True)&lt;br /&gt;
			self.Robot_PoseY.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_PoseY.setDecimals(3)&lt;br /&gt;
			self.Robot_PoseY.setMinimum(-999999.0)&lt;br /&gt;
			self.Robot_PoseY.setMaximum(999999.99)&lt;br /&gt;
			self.Robot_PoseY.setObjectName(&amp;quot;Robot_PoseY&amp;quot;)&lt;br /&gt;
			self.Robot_PoseReset = QtWidgets.QPushButton(self.groupBox_3)&lt;br /&gt;
			self.Robot_PoseReset.setGeometry(QtCore.QRect(190, 40, 71, 21))&lt;br /&gt;
			self.Robot_PoseReset.setToolTip(&amp;quot;Reset robot pose and distance to 0.0&amp;quot;)&lt;br /&gt;
			self.Robot_PoseReset.setObjectName(&amp;quot;Robot_PoseReset&amp;quot;)&lt;br /&gt;
			self.label_44 = QtWidgets.QLabel(self.groupBox_3)&lt;br /&gt;
			self.label_44.setGeometry(QtCore.QRect(20, 100, 71, 21))&lt;br /&gt;
			self.label_44.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_44.setObjectName(&amp;quot;label_44&amp;quot;)&lt;br /&gt;
			self.label_34 = QtWidgets.QLabel(self.groupBox_3)&lt;br /&gt;
			self.label_34.setGeometry(QtCore.QRect(10, 40, 81, 21))&lt;br /&gt;
			self.label_34.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_34.setObjectName(&amp;quot;label_34&amp;quot;)&lt;br /&gt;
			self.label_35 = QtWidgets.QLabel(self.groupBox_3)&lt;br /&gt;
			self.label_35.setGeometry(QtCore.QRect(20, 71, 71, 20))&lt;br /&gt;
			self.label_35.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_35.setObjectName(&amp;quot;label_35&amp;quot;)&lt;br /&gt;
			self.Robot_Tilt = QtWidgets.QDoubleSpinBox(self.groupBox_3)&lt;br /&gt;
			self.Robot_Tilt.setGeometry(QtCore.QRect(100, 131, 81, 21))&lt;br /&gt;
			self.Robot_Tilt.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_Tilt.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_Tilt.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_Tilt.setReadOnly(True)&lt;br /&gt;
			self.Robot_Tilt.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_Tilt.setDecimals(3)&lt;br /&gt;
			self.Robot_Tilt.setMinimum(-999999.0)&lt;br /&gt;
			self.Robot_Tilt.setMaximum(999999.99)&lt;br /&gt;
			self.Robot_Tilt.setObjectName(&amp;quot;Robot_Tilt&amp;quot;)&lt;br /&gt;
			self.Robot_PoseTheta = QtWidgets.QDoubleSpinBox(self.groupBox_3)&lt;br /&gt;
			self.Robot_PoseTheta.setGeometry(QtCore.QRect(100, 100, 81, 21))&lt;br /&gt;
			self.Robot_PoseTheta.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_PoseTheta.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_PoseTheta.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_PoseTheta.setReadOnly(True)&lt;br /&gt;
			self.Robot_PoseTheta.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_PoseTheta.setDecimals(3)&lt;br /&gt;
			self.Robot_PoseTheta.setMinimum(-999999.0)&lt;br /&gt;
			self.Robot_PoseTheta.setMaximum(999999.99)&lt;br /&gt;
			self.Robot_PoseTheta.setObjectName(&amp;quot;Robot_PoseTheta&amp;quot;)&lt;br /&gt;
			self.Robot_PoseDegrees = QtWidgets.QDoubleSpinBox(self.groupBox_3)&lt;br /&gt;
			self.Robot_PoseDegrees.setGeometry(QtCore.QRect(190, 100, 71, 21))&lt;br /&gt;
			self.Robot_PoseDegrees.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_PoseDegrees.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_PoseDegrees.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_PoseDegrees.setReadOnly(True)&lt;br /&gt;
			self.Robot_PoseDegrees.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_PoseDegrees.setDecimals(3)&lt;br /&gt;
			self.Robot_PoseDegrees.setMinimum(-999999.0)&lt;br /&gt;
			self.Robot_PoseDegrees.setMaximum(999999.99)&lt;br /&gt;
			self.Robot_PoseDegrees.setObjectName(&amp;quot;Robot_PoseDegrees&amp;quot;)&lt;br /&gt;
			self.label_125 = QtWidgets.QLabel(self.groupBox_3)&lt;br /&gt;
			self.label_125.setGeometry(QtCore.QRect(10, 130, 81, 21))&lt;br /&gt;
			self.label_125.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.label_125.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_125.setObjectName(&amp;quot;label_125&amp;quot;)&lt;br /&gt;
			self.tabWidget_2.addTab(self.tab_7, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.tabWidget.addTab(self.tab, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.tab_2 = QtWidgets.QWidget()&lt;br /&gt;
			self.tab_2.setObjectName(&amp;quot;tab_2&amp;quot;)&lt;br /&gt;
			self.frame_2 = QtWidgets.QFrame(self.tab_2)&lt;br /&gt;
			self.frame_2.setGeometry(QtCore.QRect(0, 0, 711, 421))&lt;br /&gt;
			self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.frame_2.setObjectName(&amp;quot;frame_2&amp;quot;)&lt;br /&gt;
			self.label_10 = QtWidgets.QLabel(self.frame_2)&lt;br /&gt;
			self.label_10.setGeometry(QtCore.QRect(10, 10, 101, 16))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_10.setFont(font)&lt;br /&gt;
			self.label_10.setObjectName(&amp;quot;label_10&amp;quot;)&lt;br /&gt;
			self.enableLineSensor_checkBox = QtWidgets.QCheckBox(self.frame_2)&lt;br /&gt;
			self.enableLineSensor_checkBox.setGeometry(QtCore.QRect(10, 30, 111, 17))&lt;br /&gt;
			self.enableLineSensor_checkBox.setObjectName(&amp;quot;enableLineSensor_checkBox&amp;quot;)&lt;br /&gt;
			self.frame_3 = QtWidgets.QFrame(self.frame_2)&lt;br /&gt;
			self.frame_3.setGeometry(QtCore.QRect(10, 50, 421, 231))&lt;br /&gt;
			self.frame_3.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.frame_3.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.frame_3.setObjectName(&amp;quot;frame_3&amp;quot;)&lt;br /&gt;
			self.line_bar_1 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.line_bar_1.setGeometry(QtCore.QRect(10, 20, 16, 181))&lt;br /&gt;
			self.line_bar_1.setMaximum(1000)&lt;br /&gt;
			self.line_bar_1.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.line_bar_1.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.line_bar_1.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.line_bar_1.setObjectName(&amp;quot;line_bar_1&amp;quot;)&lt;br /&gt;
			self.line_bar_4 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.line_bar_4.setGeometry(QtCore.QRect(70, 20, 16, 181))&lt;br /&gt;
			self.line_bar_4.setMaximum(1000)&lt;br /&gt;
			self.line_bar_4.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.line_bar_4.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.line_bar_4.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.line_bar_4.setObjectName(&amp;quot;line_bar_4&amp;quot;)&lt;br /&gt;
			self.line_bar_6 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.line_bar_6.setGeometry(QtCore.QRect(110, 20, 16, 181))&lt;br /&gt;
			self.line_bar_6.setMaximum(1000)&lt;br /&gt;
			self.line_bar_6.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.line_bar_6.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.line_bar_6.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.line_bar_6.setObjectName(&amp;quot;line_bar_6&amp;quot;)&lt;br /&gt;
			self.line_bar_5 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.line_bar_5.setGeometry(QtCore.QRect(90, 20, 16, 181))&lt;br /&gt;
			self.line_bar_5.setMaximum(1000)&lt;br /&gt;
			self.line_bar_5.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.line_bar_5.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.line_bar_5.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.line_bar_5.setObjectName(&amp;quot;line_bar_5&amp;quot;)&lt;br /&gt;
			self.line_bar_3 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.line_bar_3.setGeometry(QtCore.QRect(50, 20, 16, 181))&lt;br /&gt;
			self.line_bar_3.setMaximum(1000)&lt;br /&gt;
			self.line_bar_3.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.line_bar_3.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.line_bar_3.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.line_bar_3.setObjectName(&amp;quot;line_bar_3&amp;quot;)&lt;br /&gt;
			self.line_bar_7 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.line_bar_7.setGeometry(QtCore.QRect(130, 20, 16, 181))&lt;br /&gt;
			self.line_bar_7.setMaximum(1000)&lt;br /&gt;
			self.line_bar_7.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.line_bar_7.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.line_bar_7.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.line_bar_7.setObjectName(&amp;quot;line_bar_7&amp;quot;)&lt;br /&gt;
			self.line_bar_2 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.line_bar_2.setGeometry(QtCore.QRect(30, 20, 16, 181))&lt;br /&gt;
			self.line_bar_2.setMaximum(1000)&lt;br /&gt;
			self.line_bar_2.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.line_bar_2.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.line_bar_2.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.line_bar_2.setObjectName(&amp;quot;line_bar_2&amp;quot;)&lt;br /&gt;
			self.line_bar_8 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.line_bar_8.setGeometry(QtCore.QRect(150, 20, 16, 181))&lt;br /&gt;
			self.line_bar_8.setMaximum(1000)&lt;br /&gt;
			self.line_bar_8.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.line_bar_8.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.line_bar_8.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.line_bar_8.setObjectName(&amp;quot;line_bar_8&amp;quot;)&lt;br /&gt;
			self.label_11 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_11.setGeometry(QtCore.QRect(10, 0, 151, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_11.setFont(font)&lt;br /&gt;
			self.label_11.setObjectName(&amp;quot;label_11&amp;quot;)&lt;br /&gt;
			self.label_12 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_12.setGeometry(QtCore.QRect(10, 200, 16, 21))&lt;br /&gt;
			self.label_12.setLayoutDirection(QtCore.Qt.RightToLeft)&lt;br /&gt;
			self.label_12.setObjectName(&amp;quot;label_12&amp;quot;)&lt;br /&gt;
			self.label_13 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_13.setGeometry(QtCore.QRect(30, 200, 16, 21))&lt;br /&gt;
			self.label_13.setObjectName(&amp;quot;label_13&amp;quot;)&lt;br /&gt;
			self.label_14 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_14.setGeometry(QtCore.QRect(110, 200, 16, 21))&lt;br /&gt;
			self.label_14.setObjectName(&amp;quot;label_14&amp;quot;)&lt;br /&gt;
			self.label_15 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_15.setGeometry(QtCore.QRect(50, 200, 16, 21))&lt;br /&gt;
			self.label_15.setObjectName(&amp;quot;label_15&amp;quot;)&lt;br /&gt;
			self.label_16 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_16.setGeometry(QtCore.QRect(70, 200, 16, 21))&lt;br /&gt;
			self.label_16.setObjectName(&amp;quot;label_16&amp;quot;)&lt;br /&gt;
			self.label_17 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_17.setGeometry(QtCore.QRect(90, 200, 20, 21))&lt;br /&gt;
			self.label_17.setObjectName(&amp;quot;label_17&amp;quot;)&lt;br /&gt;
			self.label_18 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_18.setGeometry(QtCore.QRect(130, 200, 16, 21))&lt;br /&gt;
			self.label_18.setObjectName(&amp;quot;label_18&amp;quot;)&lt;br /&gt;
			self.label_19 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_19.setGeometry(QtCore.QRect(150, 200, 16, 21))&lt;br /&gt;
			self.label_19.setObjectName(&amp;quot;label_19&amp;quot;)&lt;br /&gt;
			self.ls_right_side = QtWidgets.QDoubleSpinBox(self.frame_3)&lt;br /&gt;
			self.ls_right_side.setGeometry(QtCore.QRect(300, 170, 61, 21))&lt;br /&gt;
			self.ls_right_side.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Position of right edge (cm)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.ls_right_side.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.ls_right_side.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.ls_right_side.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_right_side.setReadOnly(True)&lt;br /&gt;
			self.ls_right_side.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.ls_right_side.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ls_right_side.setDecimals(3)&lt;br /&gt;
			self.ls_right_side.setMinimum(-100.0)&lt;br /&gt;
			self.ls_right_side.setMaximum(100.0)&lt;br /&gt;
			self.ls_right_side.setObjectName(&amp;quot;ls_right_side&amp;quot;)&lt;br /&gt;
			self.ls_left_side = QtWidgets.QDoubleSpinBox(self.frame_3)&lt;br /&gt;
			self.ls_left_side.setGeometry(QtCore.QRect(230, 170, 61, 21))&lt;br /&gt;
			self.ls_left_side.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Position of left edge (cm)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.ls_left_side.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.ls_left_side.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.ls_left_side.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_left_side.setReadOnly(True)&lt;br /&gt;
			self.ls_left_side.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.ls_left_side.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ls_left_side.setDecimals(3)&lt;br /&gt;
			self.ls_left_side.setMinimum(-100.0)&lt;br /&gt;
			self.ls_left_side.setMaximum(100.0)&lt;br /&gt;
			self.ls_left_side.setObjectName(&amp;quot;ls_left_side&amp;quot;)&lt;br /&gt;
			self.label_118 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_118.setGeometry(QtCore.QRect(230, 40, 61, 18))&lt;br /&gt;
			self.label_118.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_118.setObjectName(&amp;quot;label_118&amp;quot;)&lt;br /&gt;
			self.ls_left_edge = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.ls_left_edge.setEnabled(True)&lt;br /&gt;
			self.ls_left_edge.setGeometry(QtCore.QRect(240, 60, 41, 101))&lt;br /&gt;
			self.ls_left_edge.setMinimum(-3)&lt;br /&gt;
			self.ls_left_edge.setMaximum(103)&lt;br /&gt;
			self.ls_left_edge.setProperty(&amp;quot;value&amp;quot;, 24)&lt;br /&gt;
			self.ls_left_edge.setTextVisible(False)&lt;br /&gt;
			self.ls_left_edge.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.ls_left_edge.setInvertedAppearance(False)&lt;br /&gt;
			self.ls_left_edge.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.ls_left_edge.setObjectName(&amp;quot;ls_left_edge&amp;quot;)&lt;br /&gt;
			self.ls_right_edge = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.ls_right_edge.setGeometry(QtCore.QRect(310, 60, 41, 101))&lt;br /&gt;
			self.ls_right_edge.setMinimum(-3)&lt;br /&gt;
			self.ls_right_edge.setMaximum(103)&lt;br /&gt;
			self.ls_right_edge.setProperty(&amp;quot;value&amp;quot;, 24)&lt;br /&gt;
			self.ls_right_edge.setTextVisible(False)&lt;br /&gt;
			self.ls_right_edge.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.ls_right_edge.setInvertedAppearance(False)&lt;br /&gt;
			self.ls_right_edge.setObjectName(&amp;quot;ls_right_edge&amp;quot;)&lt;br /&gt;
			self.label_121 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_121.setGeometry(QtCore.QRect(300, 40, 61, 18))&lt;br /&gt;
			self.label_121.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_121.setObjectName(&amp;quot;label_121&amp;quot;)&lt;br /&gt;
			self.label_20 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_20.setGeometry(QtCore.QRect(180, 0, 101, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_20.setFont(font)&lt;br /&gt;
			self.label_20.setObjectName(&amp;quot;label_20&amp;quot;)&lt;br /&gt;
			self.label_27 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_27.setGeometry(QtCore.QRect(290, 20, 48, 20))&lt;br /&gt;
			self.label_27.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_27.setObjectName(&amp;quot;label_27&amp;quot;)&lt;br /&gt;
			self.label_171 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_171.setGeometry(QtCore.QRect(176, 20, 48, 20))&lt;br /&gt;
			self.label_171.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_171.setObjectName(&amp;quot;label_171&amp;quot;)&lt;br /&gt;
			self.ls_crossing_cnt = QtWidgets.QLineEdit(self.frame_3)&lt;br /&gt;
			self.ls_crossing_cnt.setGeometry(QtCore.QRect(344, 20, 54, 20))&lt;br /&gt;
			self.ls_crossing_cnt.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ls_crossing_cnt.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.ls_crossing_cnt.setObjectName(&amp;quot;ls_crossing_cnt&amp;quot;)&lt;br /&gt;
			self.ls_line_valid_cnt = QtWidgets.QLineEdit(self.frame_3)&lt;br /&gt;
			self.ls_line_valid_cnt.setGeometry(QtCore.QRect(230, 20, 54, 20))&lt;br /&gt;
			self.ls_line_valid_cnt.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.ls_line_valid_cnt.setObjectName(&amp;quot;ls_line_valid_cnt&amp;quot;)&lt;br /&gt;
			self.groupBox_5 = QtWidgets.QGroupBox(self.frame_2)&lt;br /&gt;
			self.groupBox_5.setGeometry(QtCore.QRect(430, 10, 251, 361))&lt;br /&gt;
			self.groupBox_5.setObjectName(&amp;quot;groupBox_5&amp;quot;)&lt;br /&gt;
			self.CalibrateWhite_Cmd = QtWidgets.QPushButton(self.groupBox_5)&lt;br /&gt;
			self.CalibrateWhite_Cmd.setGeometry(QtCore.QRect(20, 310, 101, 31))&lt;br /&gt;
			self.CalibrateWhite_Cmd.setObjectName(&amp;quot;CalibrateWhite_Cmd&amp;quot;)&lt;br /&gt;
			self.label_217 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_217.setGeometry(QtCore.QRect(20, 75, 21, 16))&lt;br /&gt;
			self.label_217.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_217.setObjectName(&amp;quot;label_217&amp;quot;)&lt;br /&gt;
			self.label_218 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_218.setGeometry(QtCore.QRect(20, 117, 21, 16))&lt;br /&gt;
			self.label_218.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_218.setObjectName(&amp;quot;label_218&amp;quot;)&lt;br /&gt;
			self.label_219 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_219.setGeometry(QtCore.QRect(20, 159, 21, 16))&lt;br /&gt;
			self.label_219.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_219.setObjectName(&amp;quot;label_219&amp;quot;)&lt;br /&gt;
			self.label_221 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_221.setGeometry(QtCore.QRect(20, 96, 21, 16))&lt;br /&gt;
			self.label_221.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_221.setObjectName(&amp;quot;label_221&amp;quot;)&lt;br /&gt;
			self.label_223 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_223.setGeometry(QtCore.QRect(20, 138, 21, 16))&lt;br /&gt;
			self.label_223.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_223.setObjectName(&amp;quot;label_223&amp;quot;)&lt;br /&gt;
			self.label_224 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_224.setGeometry(QtCore.QRect(20, 33, 21, 16))&lt;br /&gt;
			self.label_224.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_224.setObjectName(&amp;quot;label_224&amp;quot;)&lt;br /&gt;
			self.label_225 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_225.setGeometry(QtCore.QRect(20, 180, 21, 16))&lt;br /&gt;
			self.label_225.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_225.setObjectName(&amp;quot;label_225&amp;quot;)&lt;br /&gt;
			self.label_226 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_226.setGeometry(QtCore.QRect(20, 54, 21, 16))&lt;br /&gt;
			self.label_226.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_226.setObjectName(&amp;quot;label_226&amp;quot;)&lt;br /&gt;
			self.ls_max_white_7 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_white_7.setGeometry(QtCore.QRect(50, 159, 50, 15))&lt;br /&gt;
			self.ls_max_white_7.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_white_7.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_white_7.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_white_7.setObjectName(&amp;quot;ls_max_white_7&amp;quot;)&lt;br /&gt;
			self.ls_max_white_8 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_white_8.setGeometry(QtCore.QRect(50, 180, 50, 15))&lt;br /&gt;
			self.ls_max_white_8.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_white_8.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_white_8.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_white_8.setObjectName(&amp;quot;ls_max_white_8&amp;quot;)&lt;br /&gt;
			self.ls_max_white_1 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_white_1.setGeometry(QtCore.QRect(50, 33, 50, 15))&lt;br /&gt;
			self.ls_max_white_1.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.ls_max_white_1.setAutoFillBackground(False)&lt;br /&gt;
			self.ls_max_white_1.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_white_1.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_white_1.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_white_1.setObjectName(&amp;quot;ls_max_white_1&amp;quot;)&lt;br /&gt;
			self.ls_max_white_3 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_white_3.setGeometry(QtCore.QRect(50, 75, 50, 15))&lt;br /&gt;
			self.ls_max_white_3.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_white_3.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_white_3.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_white_3.setObjectName(&amp;quot;ls_max_white_3&amp;quot;)&lt;br /&gt;
			self.ls_max_white_5 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_white_5.setGeometry(QtCore.QRect(50, 117, 50, 15))&lt;br /&gt;
			self.ls_max_white_5.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_white_5.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_white_5.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_white_5.setObjectName(&amp;quot;ls_max_white_5&amp;quot;)&lt;br /&gt;
			self.ls_max_white_2 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_white_2.setGeometry(QtCore.QRect(50, 54, 50, 15))&lt;br /&gt;
			self.ls_max_white_2.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_white_2.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_white_2.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_white_2.setObjectName(&amp;quot;ls_max_white_2&amp;quot;)&lt;br /&gt;
			self.ls_max_white_4 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_white_4.setGeometry(QtCore.QRect(50, 96, 50, 15))&lt;br /&gt;
			self.ls_max_white_4.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_white_4.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_white_4.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_white_4.setObjectName(&amp;quot;ls_max_white_4&amp;quot;)&lt;br /&gt;
			self.ls_max_white_6 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_white_6.setGeometry(QtCore.QRect(50, 138, 50, 15))&lt;br /&gt;
			self.ls_max_white_6.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_white_6.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_white_6.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_white_6.setObjectName(&amp;quot;ls_max_white_6&amp;quot;)&lt;br /&gt;
			self.ls_max_black_5 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_black_5.setGeometry(QtCore.QRect(150, 117, 50, 15))&lt;br /&gt;
			self.ls_max_black_5.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_black_5.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_black_5.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_black_5.setObjectName(&amp;quot;ls_max_black_5&amp;quot;)&lt;br /&gt;
			self.ls_max_black_4 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_black_4.setGeometry(QtCore.QRect(150, 96, 50, 15))&lt;br /&gt;
			self.ls_max_black_4.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_black_4.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_black_4.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_black_4.setObjectName(&amp;quot;ls_max_black_4&amp;quot;)&lt;br /&gt;
			self.ls_max_black_8 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_black_8.setGeometry(QtCore.QRect(150, 180, 50, 15))&lt;br /&gt;
			self.ls_max_black_8.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_black_8.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_black_8.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_black_8.setObjectName(&amp;quot;ls_max_black_8&amp;quot;)&lt;br /&gt;
			self.ls_max_black_1 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_black_1.setGeometry(QtCore.QRect(150, 33, 50, 15))&lt;br /&gt;
			self.ls_max_black_1.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.ls_max_black_1.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_black_1.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_black_1.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_black_1.setObjectName(&amp;quot;ls_max_black_1&amp;quot;)&lt;br /&gt;
			self.ls_max_black_2 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_black_2.setGeometry(QtCore.QRect(150, 54, 50, 15))&lt;br /&gt;
			self.ls_max_black_2.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_black_2.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_black_2.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_black_2.setObjectName(&amp;quot;ls_max_black_2&amp;quot;)&lt;br /&gt;
			self.ls_max_black_3 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_black_3.setGeometry(QtCore.QRect(150, 75, 50, 15))&lt;br /&gt;
			self.ls_max_black_3.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_black_3.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_black_3.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_black_3.setObjectName(&amp;quot;ls_max_black_3&amp;quot;)&lt;br /&gt;
			self.ls_max_black_6 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_black_6.setGeometry(QtCore.QRect(150, 138, 50, 15))&lt;br /&gt;
			self.ls_max_black_6.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_black_6.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_black_6.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_black_6.setObjectName(&amp;quot;ls_max_black_6&amp;quot;)&lt;br /&gt;
			self.ls_max_black_7 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_black_7.setGeometry(QtCore.QRect(150, 159, 50, 15))&lt;br /&gt;
			self.ls_max_black_7.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_black_7.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_black_7.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_black_7.setObjectName(&amp;quot;ls_max_black_7&amp;quot;)&lt;br /&gt;
			self.label_227 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_227.setGeometry(QtCore.QRect(50, 14, 51, 16))&lt;br /&gt;
			self.label_227.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_227.setObjectName(&amp;quot;label_227&amp;quot;)&lt;br /&gt;
			self.CalibrateBlack_Cmd = QtWidgets.QPushButton(self.groupBox_5)&lt;br /&gt;
			self.CalibrateBlack_Cmd.setGeometry(QtCore.QRect(130, 310, 101, 31))&lt;br /&gt;
			self.CalibrateBlack_Cmd.setObjectName(&amp;quot;CalibrateBlack_Cmd&amp;quot;)&lt;br /&gt;
			self.label_228 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_228.setGeometry(QtCore.QRect(150, 15, 51, 16))&lt;br /&gt;
			self.label_228.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_228.setObjectName(&amp;quot;label_228&amp;quot;)&lt;br /&gt;
			self.ls_swap_left_right = QtWidgets.QCheckBox(self.groupBox_5)&lt;br /&gt;
			self.ls_swap_left_right.setGeometry(QtCore.QRect(140, 220, 81, 20))&lt;br /&gt;
			self.ls_swap_left_right.setObjectName(&amp;quot;ls_swap_left_right&amp;quot;)&lt;br /&gt;
			self.ls_show_normalized = QtWidgets.QCheckBox(self.groupBox_5)&lt;br /&gt;
			self.ls_show_normalized.setGeometry(QtCore.QRect(30, 220, 101, 17))&lt;br /&gt;
			self.ls_show_normalized.setObjectName(&amp;quot;ls_show_normalized&amp;quot;)&lt;br /&gt;
			self.label_23 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_23.setGeometry(QtCore.QRect(130, 240, 75, 21))&lt;br /&gt;
			self.label_23.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_23.setObjectName(&amp;quot;label_23&amp;quot;)&lt;br /&gt;
			self.line_disp_max_value = QtWidgets.QSpinBox(self.groupBox_5)&lt;br /&gt;
			self.line_disp_max_value.setGeometry(QtCore.QRect(30, 260, 75, 20))&lt;br /&gt;
			self.line_disp_max_value.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Maximum value used as 100% in bar display.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.line_disp_max_value.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.line_disp_max_value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.line_disp_max_value.setMaximum(4250)&lt;br /&gt;
			self.line_disp_max_value.setSingleStep(256)&lt;br /&gt;
			self.line_disp_max_value.setProperty(&amp;quot;value&amp;quot;, 4096)&lt;br /&gt;
			self.line_disp_max_value.setObjectName(&amp;quot;line_disp_max_value&amp;quot;)&lt;br /&gt;
			self.label_156 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_156.setGeometry(QtCore.QRect(34, 240, 61, 20))&lt;br /&gt;
			self.label_156.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_156.setObjectName(&amp;quot;label_156&amp;quot;)&lt;br /&gt;
			self.ls_crossing_detect = QtWidgets.QDoubleSpinBox(self.groupBox_5)&lt;br /&gt;
			self.ls_crossing_detect.setGeometry(QtCore.QRect(130, 260, 75, 20))&lt;br /&gt;
			self.ls_crossing_detect.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_crossing_detect.setDecimals(1)&lt;br /&gt;
			self.ls_crossing_detect.setMinimum(2.0)&lt;br /&gt;
			self.ls_crossing_detect.setMaximum(8.0)&lt;br /&gt;
			self.ls_crossing_detect.setSingleStep(0.1)&lt;br /&gt;
			self.ls_crossing_detect.setProperty(&amp;quot;value&amp;quot;, 4.5)&lt;br /&gt;
			self.ls_crossing_detect.setObjectName(&amp;quot;ls_crossing_detect&amp;quot;)&lt;br /&gt;
			self.line_2 = QtWidgets.QFrame(self.groupBox_5)&lt;br /&gt;
			self.line_2.setGeometry(QtCore.QRect(20, 200, 211, 21))&lt;br /&gt;
			self.line_2.setFrameShape(QtWidgets.QFrame.HLine)&lt;br /&gt;
			self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)&lt;br /&gt;
			self.line_2.setObjectName(&amp;quot;line_2&amp;quot;)&lt;br /&gt;
			self.line_3 = QtWidgets.QFrame(self.groupBox_5)&lt;br /&gt;
			self.line_3.setGeometry(QtCore.QRect(20, 290, 211, 21))&lt;br /&gt;
			self.line_3.setFrameShape(QtWidgets.QFrame.HLine)&lt;br /&gt;
			self.line_3.setFrameShadow(QtWidgets.QFrame.Sunken)&lt;br /&gt;
			self.line_3.setObjectName(&amp;quot;line_3&amp;quot;)&lt;br /&gt;
			self.ls_power_high = QtWidgets.QCheckBox(self.frame_2)&lt;br /&gt;
			self.ls_power_high.setGeometry(QtCore.QRect(230, 30, 91, 17))&lt;br /&gt;
			self.ls_power_high.setObjectName(&amp;quot;ls_power_high&amp;quot;)&lt;br /&gt;
			self.ls_line_white = QtWidgets.QCheckBox(self.frame_2)&lt;br /&gt;
			self.ls_line_white.setGeometry(QtCore.QRect(140, 30, 81, 17))&lt;br /&gt;
			self.ls_line_white.setObjectName(&amp;quot;ls_line_white&amp;quot;)&lt;br /&gt;
			self.tabWidget.addTab(self.tab_2, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.tab_8 = QtWidgets.QWidget()&lt;br /&gt;
			self.tab_8.setObjectName(&amp;quot;tab_8&amp;quot;)&lt;br /&gt;
			self.frame_37 = QtWidgets.QFrame(self.tab_8)&lt;br /&gt;
			self.frame_37.setGeometry(QtCore.QRect(0, 0, 521, 291))&lt;br /&gt;
			self.frame_37.setMinimumSize(QtCore.QSize(250, 0))&lt;br /&gt;
			self.frame_37.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.frame_37.setFrameShadow(QtWidgets.QFrame.Sunken)&lt;br /&gt;
			self.frame_37.setObjectName(&amp;quot;frame_37&amp;quot;)&lt;br /&gt;
			self.label_56 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_56.setGeometry(QtCore.QRect(10, 10, 104, 16))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setPointSize(10)&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_56.setFont(font)&lt;br /&gt;
			self.label_56.setObjectName(&amp;quot;label_56&amp;quot;)&lt;br /&gt;
			self.ir_bar_1 = QtWidgets.QProgressBar(self.frame_37)&lt;br /&gt;
			self.ir_bar_1.setGeometry(QtCore.QRect(320, 200, 171, 41))&lt;br /&gt;
			self.ir_bar_1.setMaximum(130000)&lt;br /&gt;
			self.ir_bar_1.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.ir_bar_1.setOrientation(QtCore.Qt.Horizontal)&lt;br /&gt;
			self.ir_bar_1.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.ir_bar_1.setFormat(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ir_bar_1.setObjectName(&amp;quot;ir_bar_1&amp;quot;)&lt;br /&gt;
			self.ir_bar_2 = QtWidgets.QProgressBar(self.frame_37)&lt;br /&gt;
			self.ir_bar_2.setGeometry(QtCore.QRect(280, 60, 41, 141))&lt;br /&gt;
			self.ir_bar_2.setMaximum(130000)&lt;br /&gt;
			self.ir_bar_2.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.ir_bar_2.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.ir_bar_2.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.ir_bar_2.setFormat(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ir_bar_2.setObjectName(&amp;quot;ir_bar_2&amp;quot;)&lt;br /&gt;
			self.ir_d1_meters = QtWidgets.QLineEdit(self.frame_37)&lt;br /&gt;
			self.ir_d1_meters.setGeometry(QtCore.QRect(400, 170, 81, 20))&lt;br /&gt;
			self.ir_d1_meters.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.ir_d1_meters.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.ir_d1_meters.setObjectName(&amp;quot;ir_d1_meters&amp;quot;)&lt;br /&gt;
			self.label_279 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_279.setGeometry(QtCore.QRect(360, 170, 41, 20))&lt;br /&gt;
			self.label_279.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
			self.label_279.setObjectName(&amp;quot;label_279&amp;quot;)&lt;br /&gt;
			self.label_281 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_281.setGeometry(QtCore.QRect(330, 140, 71, 18))&lt;br /&gt;
			self.label_281.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
			self.label_281.setObjectName(&amp;quot;label_281&amp;quot;)&lt;br /&gt;
			self.ir_d1_raw = QtWidgets.QDoubleSpinBox(self.frame_37)&lt;br /&gt;
			self.ir_d1_raw.setGeometry(QtCore.QRect(400, 140, 81, 20))&lt;br /&gt;
			self.ir_d1_raw.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Raw measured value 2Y0A21F sensor from A/D converter&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.ir_d1_raw.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.ir_d1_raw.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.ir_d1_raw.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ir_d1_raw.setReadOnly(True)&lt;br /&gt;
			self.ir_d1_raw.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.ir_d1_raw.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ir_d1_raw.setDecimals(0)&lt;br /&gt;
			self.ir_d1_raw.setMinimum(0.0)&lt;br /&gt;
			self.ir_d1_raw.setMaximum(1000000.0)&lt;br /&gt;
			self.ir_d1_raw.setObjectName(&amp;quot;ir_d1_raw&amp;quot;)&lt;br /&gt;
			self.label_287 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_287.setGeometry(QtCore.QRect(490, 170, 16, 16))&lt;br /&gt;
			self.label_287.setObjectName(&amp;quot;label_287&amp;quot;)&lt;br /&gt;
			self.ir_d2_meters = QtWidgets.QLineEdit(self.frame_37)&lt;br /&gt;
			self.ir_d2_meters.setGeometry(QtCore.QRect(402, 90, 81, 20))&lt;br /&gt;
			self.ir_d2_meters.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.ir_d2_meters.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.ir_d2_meters.setObjectName(&amp;quot;ir_d2_meters&amp;quot;)&lt;br /&gt;
			self.label_280 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_280.setGeometry(QtCore.QRect(360, 90, 51, 20))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(0)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.label_280.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.label_280.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.label_280.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
			self.label_280.setObjectName(&amp;quot;label_280&amp;quot;)&lt;br /&gt;
			self.ir_d2_raw = QtWidgets.QDoubleSpinBox(self.frame_37)&lt;br /&gt;
			self.ir_d2_raw.setGeometry(QtCore.QRect(400, 60, 81, 20))&lt;br /&gt;
			self.ir_d2_raw.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Raw measured value 2Y0A21F sensor from A/D converter&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.ir_d2_raw.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.ir_d2_raw.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.ir_d2_raw.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ir_d2_raw.setReadOnly(True)&lt;br /&gt;
			self.ir_d2_raw.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.ir_d2_raw.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ir_d2_raw.setDecimals(0)&lt;br /&gt;
			self.ir_d2_raw.setMinimum(0.0)&lt;br /&gt;
			self.ir_d2_raw.setMaximum(1000000.0)&lt;br /&gt;
			self.ir_d2_raw.setObjectName(&amp;quot;ir_d2_raw&amp;quot;)&lt;br /&gt;
			self.label_282 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_282.setGeometry(QtCore.QRect(330, 60, 71, 18))&lt;br /&gt;
			self.label_282.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
			self.label_282.setObjectName(&amp;quot;label_282&amp;quot;)&lt;br /&gt;
			self.label_288 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_288.setGeometry(QtCore.QRect(490, 90, 16, 16))&lt;br /&gt;
			self.label_288.setObjectName(&amp;quot;label_288&amp;quot;)&lt;br /&gt;
			self.ir_d2_80cm = QtWidgets.QDoubleSpinBox(self.frame_37)&lt;br /&gt;
			self.ir_d2_80cm.setGeometry(QtCore.QRect(170, 200, 85, 20))&lt;br /&gt;
			self.ir_d2_80cm.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Raw value at 50 cm from base of sensor (about 24000)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.ir_d2_80cm.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.ir_d2_80cm.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ir_d2_80cm.setReadOnly(False)&lt;br /&gt;
			self.ir_d2_80cm.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)&lt;br /&gt;
			self.ir_d2_80cm.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ir_d2_80cm.setDecimals(0)&lt;br /&gt;
			self.ir_d2_80cm.setMinimum(0.0)&lt;br /&gt;
			self.ir_d2_80cm.setMaximum(128000.0)&lt;br /&gt;
			self.ir_d2_80cm.setProperty(&amp;quot;value&amp;quot;, 24100.0)&lt;br /&gt;
			self.ir_d2_80cm.setObjectName(&amp;quot;ir_d2_80cm&amp;quot;)&lt;br /&gt;
			self.ir_d1_80cm = QtWidgets.QDoubleSpinBox(self.frame_37)&lt;br /&gt;
			self.ir_d1_80cm.setGeometry(QtCore.QRect(170, 148, 85, 20))&lt;br /&gt;
			self.ir_d1_80cm.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Raw value at 50cm from base of sensor (about 24000)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.ir_d1_80cm.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.ir_d1_80cm.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ir_d1_80cm.setReadOnly(False)&lt;br /&gt;
			self.ir_d1_80cm.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)&lt;br /&gt;
			self.ir_d1_80cm.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ir_d1_80cm.setDecimals(0)&lt;br /&gt;
			self.ir_d1_80cm.setMinimum(0.0)&lt;br /&gt;
			self.ir_d1_80cm.setMaximum(128000.0)&lt;br /&gt;
			self.ir_d1_80cm.setProperty(&amp;quot;value&amp;quot;, 24100.0)&lt;br /&gt;
			self.ir_d1_80cm.setObjectName(&amp;quot;ir_d1_80cm&amp;quot;)&lt;br /&gt;
			self.ir_d2_20cm = QtWidgets.QDoubleSpinBox(self.frame_37)&lt;br /&gt;
			self.ir_d2_20cm.setGeometry(QtCore.QRect(170, 174, 85, 20))&lt;br /&gt;
			self.ir_d2_20cm.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Raw value at 13 cm from base of sensor (about 80000)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.ir_d2_20cm.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.ir_d2_20cm.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ir_d2_20cm.setReadOnly(False)&lt;br /&gt;
			self.ir_d2_20cm.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)&lt;br /&gt;
			self.ir_d2_20cm.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ir_d2_20cm.setDecimals(0)&lt;br /&gt;
			self.ir_d2_20cm.setMinimum(0.0)&lt;br /&gt;
			self.ir_d2_20cm.setMaximum(128000.0)&lt;br /&gt;
			self.ir_d2_20cm.setProperty(&amp;quot;value&amp;quot;, 72300.0)&lt;br /&gt;
			self.ir_d2_20cm.setObjectName(&amp;quot;ir_d2_20cm&amp;quot;)&lt;br /&gt;
			self.label_284 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_284.setGeometry(QtCore.QRect(32, 148, 132, 20))&lt;br /&gt;
			self.label_284.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
			self.label_284.setObjectName(&amp;quot;label_284&amp;quot;)&lt;br /&gt;
			self.label_286 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_286.setGeometry(QtCore.QRect(32, 174, 132, 20))&lt;br /&gt;
			self.label_286.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
			self.label_286.setObjectName(&amp;quot;label_286&amp;quot;)&lt;br /&gt;
			self.label_285 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_285.setGeometry(QtCore.QRect(32, 200, 132, 20))&lt;br /&gt;
			self.label_285.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
			self.label_285.setObjectName(&amp;quot;label_285&amp;quot;)&lt;br /&gt;
			self.label_283 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_283.setGeometry(QtCore.QRect(32, 122, 132, 20))&lt;br /&gt;
			self.label_283.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
			self.label_283.setObjectName(&amp;quot;label_283&amp;quot;)&lt;br /&gt;
			self.ir_d1_20cm = QtWidgets.QDoubleSpinBox(self.frame_37)&lt;br /&gt;
			self.ir_d1_20cm.setGeometry(QtCore.QRect(170, 122, 85, 20))&lt;br /&gt;
			self.ir_d1_20cm.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Raw value at 13 cm from base of sensor (about 80000)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.ir_d1_20cm.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.ir_d1_20cm.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ir_d1_20cm.setReadOnly(False)&lt;br /&gt;
			self.ir_d1_20cm.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)&lt;br /&gt;
			self.ir_d1_20cm.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ir_d1_20cm.setDecimals(0)&lt;br /&gt;
			self.ir_d1_20cm.setMinimum(0.0)&lt;br /&gt;
			self.ir_d1_20cm.setMaximum(128000.0)&lt;br /&gt;
			self.ir_d1_20cm.setProperty(&amp;quot;value&amp;quot;, 72000.0)&lt;br /&gt;
			self.ir_d1_20cm.setObjectName(&amp;quot;ir_d1_20cm&amp;quot;)&lt;br /&gt;
			self.checkBox_ir_use = QtWidgets.QCheckBox(self.frame_37)&lt;br /&gt;
			self.checkBox_ir_use.setGeometry(QtCore.QRect(30, 70, 132, 17))&lt;br /&gt;
			self.checkBox_ir_use.setObjectName(&amp;quot;checkBox_ir_use&amp;quot;)&lt;br /&gt;
			self.checkBox_ir_installed = QtWidgets.QCheckBox(self.frame_37)&lt;br /&gt;
			self.checkBox_ir_installed.setGeometry(QtCore.QRect(30, 44, 111, 17))&lt;br /&gt;
			self.checkBox_ir_installed.setObjectName(&amp;quot;checkBox_ir_installed&amp;quot;)&lt;br /&gt;
			self.label_277 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_277.setGeometry(QtCore.QRect(30, 100, 132, 13))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_277.setFont(font)&lt;br /&gt;
			self.label_277.setObjectName(&amp;quot;label_277&amp;quot;)&lt;br /&gt;
			self.ir_apply = QtWidgets.QPushButton(self.frame_37)&lt;br /&gt;
			self.ir_apply.setGeometry(QtCore.QRect(170, 230, 85, 23))&lt;br /&gt;
			self.ir_apply.setObjectName(&amp;quot;ir_apply&amp;quot;)&lt;br /&gt;
			self.label_278 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_278.setGeometry(QtCore.QRect(310, 30, 171, 16))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_278.setFont(font)&lt;br /&gt;
			self.label_278.setObjectName(&amp;quot;label_278&amp;quot;)&lt;br /&gt;
			self.ir_edit = QtWidgets.QPushButton(self.frame_37)&lt;br /&gt;
			self.ir_edit.setGeometry(QtCore.QRect(30, 230, 132, 23))&lt;br /&gt;
			self.ir_edit.setObjectName(&amp;quot;ir_edit&amp;quot;)&lt;br /&gt;
			self.ir_cancel = QtWidgets.QPushButton(self.frame_37)&lt;br /&gt;
			self.ir_cancel.setGeometry(QtCore.QRect(260, 230, 51, 23))&lt;br /&gt;
			self.ir_cancel.setObjectName(&amp;quot;ir_cancel&amp;quot;)&lt;br /&gt;
			self.tabWidget.addTab(self.tab_8, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.tab_3 = QtWidgets.QWidget()&lt;br /&gt;
			self.tab_3.setObjectName(&amp;quot;tab_3&amp;quot;)&lt;br /&gt;
			self.frame_4 = QtWidgets.QFrame(self.tab_3)&lt;br /&gt;
			self.frame_4.setGeometry(QtCore.QRect(0, 0, 711, 691))&lt;br /&gt;
			self.frame_4.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.frame_4.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.frame_4.setObjectName(&amp;quot;frame_4&amp;quot;)&lt;br /&gt;
			self.EnableCamera_checkBox = QtWidgets.QCheckBox(self.frame_4)&lt;br /&gt;
			self.EnableCamera_checkBox.setGeometry(QtCore.QRect(20, 20, 121, 17))&lt;br /&gt;
			self.EnableCamera_checkBox.setObjectName(&amp;quot;EnableCamera_checkBox&amp;quot;)&lt;br /&gt;
			self.frame_5 = QtWidgets.QFrame(self.frame_4)&lt;br /&gt;
			self.frame_5.setGeometry(QtCore.QRect(10, 430, 461, 211))&lt;br /&gt;
			self.frame_5.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.frame_5.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.frame_5.setObjectName(&amp;quot;frame_5&amp;quot;)&lt;br /&gt;
			self.Camera_Record_Cmd = QtWidgets.QPushButton(self.frame_5)&lt;br /&gt;
			self.Camera_Record_Cmd.setGeometry(QtCore.QRect(240, 10, 91, 23))&lt;br /&gt;
			self.Camera_Record_Cmd.setObjectName(&amp;quot;Camera_Record_Cmd&amp;quot;)&lt;br /&gt;
			self.Camera_StopRecord_Cmd = QtWidgets.QPushButton(self.frame_5)&lt;br /&gt;
			self.Camera_StopRecord_Cmd.setGeometry(QtCore.QRect(340, 10, 91, 23))&lt;br /&gt;
			self.Camera_StopRecord_Cmd.setObjectName(&amp;quot;Camera_StopRecord_Cmd&amp;quot;)&lt;br /&gt;
			self.Camera_Snapshot_Cmd = QtWidgets.QPushButton(self.frame_5)&lt;br /&gt;
			self.Camera_Snapshot_Cmd.setGeometry(QtCore.QRect(10, 10, 111, 23))&lt;br /&gt;
			self.Camera_Snapshot_Cmd.setObjectName(&amp;quot;Camera_Snapshot_Cmd&amp;quot;)&lt;br /&gt;
			self.Camera_SnapFilename = QtWidgets.QLineEdit(self.frame_5)&lt;br /&gt;
			self.Camera_SnapFilename.setGeometry(QtCore.QRect(240, 70, 191, 20))&lt;br /&gt;
			self.Camera_SnapFilename.setObjectName(&amp;quot;Camera_SnapFilename&amp;quot;)&lt;br /&gt;
			self.label_55 = QtWidgets.QLabel(self.frame_5)&lt;br /&gt;
			self.label_55.setGeometry(QtCore.QRect(240, 50, 121, 20))&lt;br /&gt;
			self.label_55.setObjectName(&amp;quot;label_55&amp;quot;)&lt;br /&gt;
			self.Camera_SnapSave_Cmd = QtWidgets.QPushButton(self.frame_5)&lt;br /&gt;
			self.Camera_SnapSave_Cmd.setGeometry(QtCore.QRect(340, 100, 91, 23))&lt;br /&gt;
			self.Camera_SnapSave_Cmd.setObjectName(&amp;quot;Camera_SnapSave_Cmd&amp;quot;)&lt;br /&gt;
			self.Snapshot_Label = QtWidgets.QLabel(self.frame_5)&lt;br /&gt;
			self.Snapshot_Label.setGeometry(QtCore.QRect(10, 40, 221, 161))&lt;br /&gt;
			self.Snapshot_Label.setLayoutDirection(QtCore.Qt.LeftToRight)&lt;br /&gt;
			self.Snapshot_Label.setAutoFillBackground(True)&lt;br /&gt;
			self.Snapshot_Label.setFrameShape(QtWidgets.QFrame.Box)&lt;br /&gt;
			self.Snapshot_Label.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.Snapshot_Label.setLineWidth(-2)&lt;br /&gt;
			self.Snapshot_Label.setText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.Snapshot_Label.setScaledContents(True)&lt;br /&gt;
			self.Snapshot_Label.setIndent(0)&lt;br /&gt;
			self.Snapshot_Label.setObjectName(&amp;quot;Snapshot_Label&amp;quot;)&lt;br /&gt;
			self.VideoRecording_Label = QtWidgets.QLabel(self.frame_5)&lt;br /&gt;
			self.VideoRecording_Label.setGeometry(QtCore.QRect(240, 30, 71, 16))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.VideoRecording_Label.setFont(font)&lt;br /&gt;
			self.VideoRecording_Label.setObjectName(&amp;quot;VideoRecording_Label&amp;quot;)&lt;br /&gt;
			self.Camera_ClearSnap_Cmd = QtWidgets.QPushButton(self.frame_5)&lt;br /&gt;
			self.Camera_ClearSnap_Cmd.setGeometry(QtCore.QRect(130, 10, 91, 23))&lt;br /&gt;
			self.Camera_ClearSnap_Cmd.setObjectName(&amp;quot;Camera_ClearSnap_Cmd&amp;quot;)&lt;br /&gt;
			self.frame_6 = QtWidgets.QFrame(self.frame_4)&lt;br /&gt;
			self.frame_6.setGeometry(QtCore.QRect(10, 40, 561, 381))&lt;br /&gt;
			self.frame_6.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.frame_6.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.frame_6.setObjectName(&amp;quot;frame_6&amp;quot;)&lt;br /&gt;
			self.Stream_Label = QtWidgets.QLabel(self.frame_6)&lt;br /&gt;
			self.Stream_Label.setGeometry(QtCore.QRect(10, 0, 541, 371))&lt;br /&gt;
			self.Stream_Label.setText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.Stream_Label.setScaledContents(True)&lt;br /&gt;
			self.Stream_Label.setObjectName(&amp;quot;Stream_Label&amp;quot;)&lt;br /&gt;
			self.tabWidget.addTab(self.tab_3, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.tab_4 = QtWidgets.QWidget()&lt;br /&gt;
			self.tab_4.setObjectName(&amp;quot;tab_4&amp;quot;)&lt;br /&gt;
			self.frame_7 = QtWidgets.QFrame(self.tab_4)&lt;br /&gt;
			self.frame_7.setGeometry(QtCore.QRect(0, 0, 541, 631))&lt;br /&gt;
			self.frame_7.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.frame_7.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.frame_7.setObjectName(&amp;quot;frame_7&amp;quot;)&lt;br /&gt;
			self.label_30 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_30.setGeometry(QtCore.QRect(10, 0, 151, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setPointSize(10)&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_30.setFont(font)&lt;br /&gt;
			self.label_30.setObjectName(&amp;quot;label_30&amp;quot;)&lt;br /&gt;
			self.enableServo1_checkBox = QtWidgets.QCheckBox(self.frame_7)&lt;br /&gt;
			self.enableServo1_checkBox.setGeometry(QtCore.QRect(10, 30, 111, 17))&lt;br /&gt;
			self.enableServo1_checkBox.setObjectName(&amp;quot;enableServo1_checkBox&amp;quot;)&lt;br /&gt;
			self.enableServo2_checkBox = QtWidgets.QCheckBox(self.frame_7)&lt;br /&gt;
			self.enableServo2_checkBox.setGeometry(QtCore.QRect(10, 150, 111, 17))&lt;br /&gt;
			self.enableServo2_checkBox.setObjectName(&amp;quot;enableServo2_checkBox&amp;quot;)&lt;br /&gt;
			self.enableServo3_checkBox = QtWidgets.QCheckBox(self.frame_7)&lt;br /&gt;
			self.enableServo3_checkBox.setGeometry(QtCore.QRect(10, 270, 111, 17))&lt;br /&gt;
			self.enableServo3_checkBox.setObjectName(&amp;quot;enableServo3_checkBox&amp;quot;)&lt;br /&gt;
			self.Servo1_Pos_dial = QtWidgets.QDial(self.frame_7)&lt;br /&gt;
			self.Servo1_Pos_dial.setGeometry(QtCore.QRect(210, 50, 81, 81))&lt;br /&gt;
			self.Servo1_Pos_dial.setMinimum(-920)&lt;br /&gt;
			self.Servo1_Pos_dial.setMaximum(920)&lt;br /&gt;
			self.Servo1_Pos_dial.setTracking(False)&lt;br /&gt;
			self.Servo1_Pos_dial.setObjectName(&amp;quot;Servo1_Pos_dial&amp;quot;)&lt;br /&gt;
			self.label_31 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_31.setGeometry(QtCore.QRect(10, 50, 81, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_31.setFont(font)&lt;br /&gt;
			self.label_31.setObjectName(&amp;quot;label_31&amp;quot;)&lt;br /&gt;
			self.servo1_current_pos = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.servo1_current_pos.setGeometry(QtCore.QRect(100, 50, 91, 20))&lt;br /&gt;
			self.servo1_current_pos.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.servo1_current_pos.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.servo1_current_pos.setObjectName(&amp;quot;servo1_current_pos&amp;quot;)&lt;br /&gt;
			self.label_33 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_33.setGeometry(QtCore.QRect(10, 80, 91, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_33.setFont(font)&lt;br /&gt;
			self.label_33.setObjectName(&amp;quot;label_33&amp;quot;)&lt;br /&gt;
			self.manualServoPos1_Val = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.manualServoPos1_Val.setGeometry(QtCore.QRect(100, 80, 91, 20))&lt;br /&gt;
			self.manualServoPos1_Val.setObjectName(&amp;quot;manualServoPos1_Val&amp;quot;)&lt;br /&gt;
			self.manualServoPos1_Cmd = QtWidgets.QPushButton(self.frame_7)&lt;br /&gt;
			self.manualServoPos1_Cmd.setGeometry(QtCore.QRect(100, 110, 91, 23))&lt;br /&gt;
			self.manualServoPos1_Cmd.setObjectName(&amp;quot;manualServoPos1_Cmd&amp;quot;)&lt;br /&gt;
			self.label_39 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_39.setGeometry(QtCore.QRect(220, 30, 61, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_39.setFont(font)&lt;br /&gt;
			self.label_39.setObjectName(&amp;quot;label_39&amp;quot;)&lt;br /&gt;
			self.Servo2_Pos_dial = QtWidgets.QDial(self.frame_7)&lt;br /&gt;
			self.Servo2_Pos_dial.setGeometry(QtCore.QRect(210, 170, 81, 81))&lt;br /&gt;
			self.Servo2_Pos_dial.setMinimum(-920)&lt;br /&gt;
			self.Servo2_Pos_dial.setMaximum(920)&lt;br /&gt;
			self.Servo2_Pos_dial.setTracking(False)&lt;br /&gt;
			self.Servo2_Pos_dial.setObjectName(&amp;quot;Servo2_Pos_dial&amp;quot;)&lt;br /&gt;
			self.label_40 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_40.setGeometry(QtCore.QRect(220, 150, 61, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_40.setFont(font)&lt;br /&gt;
			self.label_40.setObjectName(&amp;quot;label_40&amp;quot;)&lt;br /&gt;
			self.manualServoPos2_Val = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.manualServoPos2_Val.setGeometry(QtCore.QRect(100, 200, 91, 20))&lt;br /&gt;
			self.manualServoPos2_Val.setObjectName(&amp;quot;manualServoPos2_Val&amp;quot;)&lt;br /&gt;
			self.servo2_current_pos = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.servo2_current_pos.setGeometry(QtCore.QRect(100, 170, 91, 20))&lt;br /&gt;
			self.servo2_current_pos.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.servo2_current_pos.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.servo2_current_pos.setObjectName(&amp;quot;servo2_current_pos&amp;quot;)&lt;br /&gt;
			self.label_41 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_41.setGeometry(QtCore.QRect(10, 200, 91, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_41.setFont(font)&lt;br /&gt;
			self.label_41.setObjectName(&amp;quot;label_41&amp;quot;)&lt;br /&gt;
			self.manualServoPos2_Cmd = QtWidgets.QPushButton(self.frame_7)&lt;br /&gt;
			self.manualServoPos2_Cmd.setGeometry(QtCore.QRect(100, 230, 91, 23))&lt;br /&gt;
			self.manualServoPos2_Cmd.setObjectName(&amp;quot;manualServoPos2_Cmd&amp;quot;)&lt;br /&gt;
			self.label_43 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_43.setGeometry(QtCore.QRect(10, 170, 81, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_43.setFont(font)&lt;br /&gt;
			self.label_43.setObjectName(&amp;quot;label_43&amp;quot;)&lt;br /&gt;
			self.Servo3_Pos_dial = QtWidgets.QDial(self.frame_7)&lt;br /&gt;
			self.Servo3_Pos_dial.setGeometry(QtCore.QRect(210, 290, 81, 81))&lt;br /&gt;
			self.Servo3_Pos_dial.setMinimum(-920)&lt;br /&gt;
			self.Servo3_Pos_dial.setMaximum(920)&lt;br /&gt;
			self.Servo3_Pos_dial.setTracking(False)&lt;br /&gt;
			self.Servo3_Pos_dial.setObjectName(&amp;quot;Servo3_Pos_dial&amp;quot;)&lt;br /&gt;
			self.label_46 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_46.setGeometry(QtCore.QRect(220, 270, 61, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_46.setFont(font)&lt;br /&gt;
			self.label_46.setObjectName(&amp;quot;label_46&amp;quot;)&lt;br /&gt;
			self.manualServoPos3_Val = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.manualServoPos3_Val.setGeometry(QtCore.QRect(100, 320, 91, 20))&lt;br /&gt;
			self.manualServoPos3_Val.setObjectName(&amp;quot;manualServoPos3_Val&amp;quot;)&lt;br /&gt;
			self.servo3_current_pos = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.servo3_current_pos.setGeometry(QtCore.QRect(100, 290, 91, 20))&lt;br /&gt;
			self.servo3_current_pos.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.servo3_current_pos.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.servo3_current_pos.setObjectName(&amp;quot;servo3_current_pos&amp;quot;)&lt;br /&gt;
			self.label_47 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_47.setGeometry(QtCore.QRect(10, 320, 91, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_47.setFont(font)&lt;br /&gt;
			self.label_47.setObjectName(&amp;quot;label_47&amp;quot;)&lt;br /&gt;
			self.manualServoPos3_Cmd = QtWidgets.QPushButton(self.frame_7)&lt;br /&gt;
			self.manualServoPos3_Cmd.setGeometry(QtCore.QRect(100, 350, 91, 23))&lt;br /&gt;
			self.manualServoPos3_Cmd.setObjectName(&amp;quot;manualServoPos3_Cmd&amp;quot;)&lt;br /&gt;
			self.label_48 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_48.setGeometry(QtCore.QRect(10, 290, 81, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_48.setFont(font)&lt;br /&gt;
			self.label_48.setObjectName(&amp;quot;label_48&amp;quot;)&lt;br /&gt;
			self.enableServo4_checkBox = QtWidgets.QCheckBox(self.frame_7)&lt;br /&gt;
			self.enableServo4_checkBox.setGeometry(QtCore.QRect(10, 390, 111, 17))&lt;br /&gt;
			self.enableServo4_checkBox.setObjectName(&amp;quot;enableServo4_checkBox&amp;quot;)&lt;br /&gt;
			self.Servo4_Pos_dial = QtWidgets.QDial(self.frame_7)&lt;br /&gt;
			self.Servo4_Pos_dial.setGeometry(QtCore.QRect(210, 410, 81, 81))&lt;br /&gt;
			self.Servo4_Pos_dial.setMinimum(-920)&lt;br /&gt;
			self.Servo4_Pos_dial.setMaximum(920)&lt;br /&gt;
			self.Servo4_Pos_dial.setTracking(False)&lt;br /&gt;
			self.Servo4_Pos_dial.setObjectName(&amp;quot;Servo4_Pos_dial&amp;quot;)&lt;br /&gt;
			self.label_49 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_49.setGeometry(QtCore.QRect(220, 390, 61, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_49.setFont(font)&lt;br /&gt;
			self.label_49.setObjectName(&amp;quot;label_49&amp;quot;)&lt;br /&gt;
			self.manualServoPos4_Val = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.manualServoPos4_Val.setGeometry(QtCore.QRect(100, 440, 91, 20))&lt;br /&gt;
			self.manualServoPos4_Val.setObjectName(&amp;quot;manualServoPos4_Val&amp;quot;)&lt;br /&gt;
			self.servo4_current_pos = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.servo4_current_pos.setGeometry(QtCore.QRect(100, 410, 91, 20))&lt;br /&gt;
			self.servo4_current_pos.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.servo4_current_pos.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.servo4_current_pos.setObjectName(&amp;quot;servo4_current_pos&amp;quot;)&lt;br /&gt;
			self.label_50 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_50.setGeometry(QtCore.QRect(10, 440, 91, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_50.setFont(font)&lt;br /&gt;
			self.label_50.setObjectName(&amp;quot;label_50&amp;quot;)&lt;br /&gt;
			self.manualServoPos4_Cmd = QtWidgets.QPushButton(self.frame_7)&lt;br /&gt;
			self.manualServoPos4_Cmd.setGeometry(QtCore.QRect(100, 470, 91, 23))&lt;br /&gt;
			self.manualServoPos4_Cmd.setObjectName(&amp;quot;manualServoPos4_Cmd&amp;quot;)&lt;br /&gt;
			self.label_51 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_51.setGeometry(QtCore.QRect(10, 410, 81, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_51.setFont(font)&lt;br /&gt;
			self.label_51.setObjectName(&amp;quot;label_51&amp;quot;)&lt;br /&gt;
			self.enableServo5_checkBox = QtWidgets.QCheckBox(self.frame_7)&lt;br /&gt;
			self.enableServo5_checkBox.setGeometry(QtCore.QRect(10, 510, 111, 17))&lt;br /&gt;
			self.enableServo5_checkBox.setObjectName(&amp;quot;enableServo5_checkBox&amp;quot;)&lt;br /&gt;
			self.Servo5_Pos_dial = QtWidgets.QDial(self.frame_7)&lt;br /&gt;
			self.Servo5_Pos_dial.setGeometry(QtCore.QRect(210, 530, 81, 81))&lt;br /&gt;
			self.Servo5_Pos_dial.setMinimum(-920)&lt;br /&gt;
			self.Servo5_Pos_dial.setMaximum(920)&lt;br /&gt;
			self.Servo5_Pos_dial.setTracking(False)&lt;br /&gt;
			self.Servo5_Pos_dial.setObjectName(&amp;quot;Servo5_Pos_dial&amp;quot;)&lt;br /&gt;
			self.label_52 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_52.setGeometry(QtCore.QRect(220, 510, 61, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_52.setFont(font)&lt;br /&gt;
			self.label_52.setObjectName(&amp;quot;label_52&amp;quot;)&lt;br /&gt;
			self.manualServoPos5_Val = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.manualServoPos5_Val.setGeometry(QtCore.QRect(100, 560, 91, 20))&lt;br /&gt;
			self.manualServoPos5_Val.setObjectName(&amp;quot;manualServoPos5_Val&amp;quot;)&lt;br /&gt;
			self.servo5_current_pos = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.servo5_current_pos.setGeometry(QtCore.QRect(100, 530, 91, 20))&lt;br /&gt;
			self.servo5_current_pos.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.servo5_current_pos.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.servo5_current_pos.setObjectName(&amp;quot;servo5_current_pos&amp;quot;)&lt;br /&gt;
			self.label_53 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_53.setGeometry(QtCore.QRect(10, 560, 91, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_53.setFont(font)&lt;br /&gt;
			self.label_53.setObjectName(&amp;quot;label_53&amp;quot;)&lt;br /&gt;
			self.manualServoPos5_Cmd = QtWidgets.QPushButton(self.frame_7)&lt;br /&gt;
			self.manualServoPos5_Cmd.setGeometry(QtCore.QRect(100, 590, 91, 23))&lt;br /&gt;
			self.manualServoPos5_Cmd.setObjectName(&amp;quot;manualServoPos5_Cmd&amp;quot;)&lt;br /&gt;
			self.label_54 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_54.setGeometry(QtCore.QRect(10, 530, 81, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_54.setFont(font)&lt;br /&gt;
			self.label_54.setObjectName(&amp;quot;label_54&amp;quot;)&lt;br /&gt;
			self.Servo1Steering_checkBox = QtWidgets.QCheckBox(self.frame_7)&lt;br /&gt;
			self.Servo1Steering_checkBox.setGeometry(QtCore.QRect(110, 30, 111, 17))&lt;br /&gt;
			self.Servo1Steering_checkBox.setObjectName(&amp;quot;Servo1Steering_checkBox&amp;quot;)&lt;br /&gt;
			self.val_servo1_offset = QtWidgets.QDoubleSpinBox(self.frame_7)&lt;br /&gt;
			self.val_servo1_offset.setGeometry(QtCore.QRect(300, 50, 81, 18))&lt;br /&gt;
			self.val_servo1_offset.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.val_servo1_offset.setReadOnly(False)&lt;br /&gt;
			self.val_servo1_offset.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)&lt;br /&gt;
			self.val_servo1_offset.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.val_servo1_offset.setDecimals(0)&lt;br /&gt;
			self.val_servo1_offset.setMinimum(-200.0)&lt;br /&gt;
			self.val_servo1_offset.setMaximum(200.0)&lt;br /&gt;
			self.val_servo1_offset.setSingleStep(0.01)&lt;br /&gt;
			self.val_servo1_offset.setProperty(&amp;quot;value&amp;quot;, 10.0)&lt;br /&gt;
			self.val_servo1_offset.setObjectName(&amp;quot;val_servo1_offset&amp;quot;)&lt;br /&gt;
			self.label_servo_6 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_servo_6.setGeometry(QtCore.QRect(310, 70, 75, 13))&lt;br /&gt;
			self.label_servo_6.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_servo_6.setObjectName(&amp;quot;label_servo_6&amp;quot;)&lt;br /&gt;
			self.val_servo1_scale = QtWidgets.QDoubleSpinBox(self.frame_7)&lt;br /&gt;
			self.val_servo1_scale.setGeometry(QtCore.QRect(300, 130, 81, 18))&lt;br /&gt;
			self.val_servo1_scale.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.val_servo1_scale.setReadOnly(False)&lt;br /&gt;
			self.val_servo1_scale.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)&lt;br /&gt;
			self.val_servo1_scale.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.val_servo1_scale.setDecimals(1)&lt;br /&gt;
			self.val_servo1_scale.setMinimum(-180.0)&lt;br /&gt;
			self.val_servo1_scale.setMaximum(180.0)&lt;br /&gt;
			self.val_servo1_scale.setSingleStep(1.0)&lt;br /&gt;
			self.val_servo1_scale.setProperty(&amp;quot;value&amp;quot;, 160.0)&lt;br /&gt;
			self.val_servo1_scale.setObjectName(&amp;quot;val_servo1_scale&amp;quot;)&lt;br /&gt;
			self.label_servo_9 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_servo_9.setGeometry(QtCore.QRect(300, 110, 87, 13))&lt;br /&gt;
			self.label_servo_9.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_servo_9.setObjectName(&amp;quot;label_servo_9&amp;quot;)&lt;br /&gt;
			self.label_servo_8 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_servo_8.setGeometry(QtCore.QRect(303, 31, 81, 13))&lt;br /&gt;
			self.label_servo_8.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_servo_8.setObjectName(&amp;quot;label_servo_8&amp;quot;)&lt;br /&gt;
			self.val_steer_distance = QtWidgets.QDoubleSpinBox(self.frame_7)&lt;br /&gt;
			self.val_steer_distance.setGeometry(QtCore.QRect(300, 90, 81, 18))&lt;br /&gt;
			self.val_steer_distance.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.val_steer_distance.setReadOnly(False)&lt;br /&gt;
			self.val_steer_distance.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.val_steer_distance.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.val_steer_distance.setDecimals(3)&lt;br /&gt;
			self.val_steer_distance.setMinimum(-1.0)&lt;br /&gt;
			self.val_steer_distance.setMaximum(1.0)&lt;br /&gt;
			self.val_steer_distance.setSingleStep(0.01)&lt;br /&gt;
			self.val_steer_distance.setProperty(&amp;quot;value&amp;quot;, 0.16)&lt;br /&gt;
			self.val_steer_distance.setObjectName(&amp;quot;val_steer_distance&amp;quot;)&lt;br /&gt;
			self.tabWidget.addTab(self.tab_4, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.tab_5 = QtWidgets.QWidget()&lt;br /&gt;
			self.tab_5.setObjectName(&amp;quot;tab_5&amp;quot;)&lt;br /&gt;
			self.tabWidget.addTab(self.tab_5, &amp;quot;&amp;quot;)&lt;br /&gt;
			MainWindow.setCentralWidget(self.centralwidget)&lt;br /&gt;
			self.menubar = QtWidgets.QMenuBar(MainWindow)&lt;br /&gt;
			self.menubar.setGeometry(QtCore.QRect(0, 0, 1122, 21))&lt;br /&gt;
			self.menubar.setObjectName(&amp;quot;menubar&amp;quot;)&lt;br /&gt;
			self.menuFile = QtWidgets.QMenu(self.menubar)&lt;br /&gt;
			self.menuFile.setObjectName(&amp;quot;menuFile&amp;quot;)&lt;br /&gt;
			self.menuHelp = QtWidgets.QMenu(self.menubar)&lt;br /&gt;
			self.menuHelp.setObjectName(&amp;quot;menuHelp&amp;quot;)&lt;br /&gt;
			self.menuTools = QtWidgets.QMenu(self.menubar)&lt;br /&gt;
			self.menuTools.setObjectName(&amp;quot;menuTools&amp;quot;)&lt;br /&gt;
			MainWindow.setMenuBar(self.menubar)&lt;br /&gt;
			self.statusbar = QtWidgets.QStatusBar(MainWindow)&lt;br /&gt;
			self.statusbar.setObjectName(&amp;quot;statusbar&amp;quot;)&lt;br /&gt;
			MainWindow.setStatusBar(self.statusbar)&lt;br /&gt;
			self.menubar.addAction(self.menuFile.menuAction())&lt;br /&gt;
			self.menubar.addAction(self.menuTools.menuAction())&lt;br /&gt;
			self.menubar.addAction(self.menuHelp.menuAction())&lt;br /&gt;
&lt;br /&gt;
			self.retranslateUi(MainWindow)&lt;br /&gt;
			self.tabWidget.setCurrentIndex(2)&lt;br /&gt;
			self.tabWidget_2.setCurrentIndex(0)&lt;br /&gt;
			QtCore.QMetaObject.connectSlotsByName(MainWindow)&lt;br /&gt;
&lt;br /&gt;
		def retranslateUi(self, MainWindow):&lt;br /&gt;
			_translate = QtCore.QCoreApplication.translate&lt;br /&gt;
			MainWindow.setWindowTitle(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;MainWindow&amp;quot;))&lt;br /&gt;
			self.label_2.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Network Status: &amp;quot;))&lt;br /&gt;
			self.Disconnect_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Disconnect&amp;quot;))&lt;br /&gt;
			self.connect_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Connect&amp;quot;))&lt;br /&gt;
			self.label.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;IP adress:&amp;quot;))&lt;br /&gt;
			self.IP_NetworkStatus_label.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Status&amp;quot;))&lt;br /&gt;
			self.label_7.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;INTERNET CONNECTION&amp;quot;))&lt;br /&gt;
			self.SidebarMenu.setItemText(self.SidebarMenu.indexOf(self.SidebarMenuPage1), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Robot Connection&amp;quot;))&lt;br /&gt;
			self.send_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Send&amp;quot;))&lt;br /&gt;
			self.TX_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;TX&amp;quot;))&lt;br /&gt;
			self.RX_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;RX&amp;quot;))&lt;br /&gt;
			self.ConsoleHelp_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Help&amp;quot;))&lt;br /&gt;
			self.ConsoleClear_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Clear&amp;quot;))&lt;br /&gt;
			self.SidebarMenu.setItemText(self.SidebarMenu.indexOf(self.SidebarMenuPage2), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Debugger&amp;quot;))&lt;br /&gt;
			self.main_start.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Start&amp;quot;))&lt;br /&gt;
			self.main_remote_ctrl.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Remote control is active,&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;and mission paused.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.main_remote_ctrl.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Remote-Control&amp;quot;))&lt;br /&gt;
			self.main_stop.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Stop&amp;quot;))&lt;br /&gt;
			self.main_mission_state.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Shows that a mission is active.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Number is&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0: not active&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;1: waiting 1 second to start&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;2: running mission line&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;6,7: listening to button (more button presses)&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;8: stop mission&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.main_mission_state.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Mission&amp;quot;))&lt;br /&gt;
			self.SidebarMenu.setItemText(self.SidebarMenu.indexOf(self.SidebarMenuPage3), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Mission&amp;quot;))&lt;br /&gt;
			self.label_5.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Manual Speed:&amp;quot;))&lt;br /&gt;
			self.enableKeyboardRC_checkBox.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Enable Remote Keyboard control&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.enableKeyboardRC_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable keyboard control&amp;quot;))&lt;br /&gt;
			self.guiRC_Reverse_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;V&amp;quot;))&lt;br /&gt;
			self.guiRC_Forward_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;^&amp;quot;))&lt;br /&gt;
			self.enableGuiRC_checkBox.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Enable Remote - GUI control&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.enableGuiRC_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable GUI control&amp;quot;))&lt;br /&gt;
			self.guiRC_Right_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;gt;&amp;quot;))&lt;br /&gt;
			self.guiRC_Left_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;&amp;quot;))&lt;br /&gt;
			self.label_8.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Motor Remote GUI control&amp;quot;))&lt;br /&gt;
			self.label_9.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Robot Motor hardware parameters&amp;quot;))&lt;br /&gt;
			self.label_21.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Number of encoder pulses on each motor revolution (normally 48)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_21.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enc. pulse per rev:&amp;quot;))&lt;br /&gt;
			self.label_22.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Gear ratio [N]:&amp;quot;))&lt;br /&gt;
			self.Robot_GearRatio.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Gear ratio from motor to wheel axle (9.68).&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_24.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Wheel radius (Left) [m]:&amp;quot;))&lt;br /&gt;
			self.label_127.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Robot ID:&amp;quot;))&lt;br /&gt;
			self.label_128.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Hardware:&amp;quot;))&lt;br /&gt;
			self.label_25.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Wheel base is distance between wheels (mid-wheel to mid-wheel) for turn calculations.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_25.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Wheel-base [m]:&amp;quot;))&lt;br /&gt;
			self.Robot_PulsePerRev.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Number of encoder ticks per motor revolution (48)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_26.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Wheel radius (Right) [m]:&amp;quot;))&lt;br /&gt;
			self.Robot_RevEncoder_Right.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Checking this makes odometry go the other way, i.e. encoder pins swapped (right motor).&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;This should NOT be checked on normal REGBOT.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.Robot_RevEncoder_Right.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Reverse Right Encoder&amp;quot;))&lt;br /&gt;
			self.Robot_RevMotor.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Checking this makes wheels go the other way when orderd forward.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;This should NOT be checked on normal REGBOT.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.Robot_RevMotor.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Reverse Motor&amp;quot;))&lt;br /&gt;
			self.Robot_RevEncoder_Left.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Checking this makes odometry go the other way, i.e. encoder pins swapped.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;This should NOT be checked on normal REGBOT.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.Robot_RevEncoder_Left.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Reverse Left Encoder&amp;quot;))&lt;br /&gt;
			self.label_222.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Battery is assumed to be of type LiPo 3cell.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Any battery with a normal voltage between 9 and 15V should do.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_222.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Go idle at [V]:&amp;quot;))&lt;br /&gt;
			self.save_id_on_robot.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Save&amp;quot;))&lt;br /&gt;
			self.label_129.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Tilt offset [Rad]:&amp;quot;))&lt;br /&gt;
			self.Robot_edit.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Edit&amp;quot;))&lt;br /&gt;
			self.Robot_OnBattery.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Robot On Battery&amp;quot;))&lt;br /&gt;
			self.robot_cancel.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Cancel&amp;quot;))&lt;br /&gt;
			self.label_36.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Right&amp;quot;))&lt;br /&gt;
			self.label_37.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Left&amp;quot;))&lt;br /&gt;
			self.label_42.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Current [A]:&amp;quot;))&lt;br /&gt;
			self.label_28.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Encoder:&amp;quot;))&lt;br /&gt;
			self.label_38.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Drive information&amp;quot;))&lt;br /&gt;
			self.label_32.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Distance [m]:&amp;quot;))&lt;br /&gt;
			self.label_29.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Wheel Speed [m/s]:&amp;quot;))&lt;br /&gt;
			self.tabWidget_2.setTabText(self.tabWidget_2.indexOf(self.tab_6), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Drive Info&amp;quot;))&lt;br /&gt;
			self.label_76.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;degrees&amp;quot;))&lt;br /&gt;
			self.label_45.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Pose (relative to starting/reset point)&amp;quot;))&lt;br /&gt;
			self.Robot_PoseReset.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;reset&amp;quot;))&lt;br /&gt;
			self.label_44.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Heading [rad]:&amp;quot;))&lt;br /&gt;
			self.label_34.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;X (forward) [m]:&amp;quot;))&lt;br /&gt;
			self.label_35.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Y (left) [m]:&amp;quot;))&lt;br /&gt;
			self.label_125.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Tilt [rad]:&amp;quot;))&lt;br /&gt;
			self.tabWidget_2.setTabText(self.tabWidget_2.indexOf(self.tab_7), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Pose Info&amp;quot;))&lt;br /&gt;
			self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Motor&amp;quot;))&lt;br /&gt;
			self.label_10.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Line Sensor&amp;quot;))&lt;br /&gt;
			self.enableLineSensor_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Line Sensor&amp;quot;))&lt;br /&gt;
			self.line_bar_1.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
			self.line_bar_4.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
			self.line_bar_6.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
			self.line_bar_5.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
			self.line_bar_3.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
			self.line_bar_7.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
			self.line_bar_2.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
			self.line_bar_8.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
			self.label_11.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Sensor readings:&amp;quot;))&lt;br /&gt;
			self.label_12.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.label_13.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L2&amp;quot;))&lt;br /&gt;
			self.label_14.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L6&amp;quot;))&lt;br /&gt;
			self.label_15.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L3&amp;quot;))&lt;br /&gt;
			self.label_16.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L4&amp;quot;))&lt;br /&gt;
			self.label_17.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L5&amp;quot;))&lt;br /&gt;
			self.label_18.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L7&amp;quot;))&lt;br /&gt;
			self.label_19.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L8&amp;quot;))&lt;br /&gt;
			self.label_118.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Left edge&amp;quot;))&lt;br /&gt;
			self.ls_left_edge.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Left edge position&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Gray if an edge is not detected&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.ls_right_edge.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Right edge position&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Either no edge is valid or both are valid.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_121.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Right edge&amp;quot;))&lt;br /&gt;
			self.label_20.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Edge Analysis:&amp;quot;))&lt;br /&gt;
			self.label_27.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Crossing&amp;quot;))&lt;br /&gt;
			self.label_171.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Line valid&amp;quot;))&lt;br /&gt;
			self.ls_crossing_cnt.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 20.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no crossing, 20 = crossing detected in 20ms. &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Counts +1 when detection is above &amp;amp;quot;Crossing det.&amp;amp;quot; value.&amp;lt;br/&amp;gt;else -1.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.ls_line_valid_cnt.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 5.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no line, 5=line seen in 5ms.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.groupBox_5.setTitle(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Sensor Calibration&amp;quot;))&lt;br /&gt;
			self.CalibrateWhite_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Calibrate White&amp;quot;))&lt;br /&gt;
			self.label_217.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L3&amp;quot;))&lt;br /&gt;
			self.label_218.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L5&amp;quot;))&lt;br /&gt;
			self.label_219.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L7&amp;quot;))&lt;br /&gt;
			self.label_221.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L4&amp;quot;))&lt;br /&gt;
			self.label_223.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L6&amp;quot;))&lt;br /&gt;
			self.label_224.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.label_225.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L8&amp;quot;))&lt;br /&gt;
			self.label_226.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L2&amp;quot;))&lt;br /&gt;
			self.ls_max_white_7.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_white_8.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_white_1.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_white_3.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_white_5.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_white_2.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_white_4.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_white_6.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_black_5.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_black_4.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_black_8.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_black_1.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_black_2.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_black_3.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_black_6.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_black_7.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.label_227.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;White&amp;quot;))&lt;br /&gt;
			self.CalibrateBlack_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Calibrate Black &amp;quot;))&lt;br /&gt;
			self.label_228.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Black&amp;quot;))&lt;br /&gt;
			self.ls_swap_left_right.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Swap the detected left and right edge.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Check this if LED\&#039;s are (mostly) in the forward (X) direction.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;(Normally the LED end of the line sensor PCB is closer to driving wheel axis).&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.ls_swap_left_right.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Swap Sides&amp;quot;))&lt;br /&gt;
			self.ls_show_normalized.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;If line is not white (bright), then assumed black (dark)&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;The normalized value should be shown as 75%.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.ls_show_normalized.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Show normalized&amp;quot;))&lt;br /&gt;
			self.label_23.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Crossing det.&amp;quot;))&lt;br /&gt;
			self.label_156.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Max RAW&amp;quot;))&lt;br /&gt;
			self.ls_crossing_detect.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Distance between left and right edge in a 0 to 8 scale.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Value depends distance between edges with an extra value if width increse fast. Therefore the width can temorarily be higher than 6 (edge values are from -3 to 3)&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;this should make it easier to detect a crossing.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;For Regbot in balance (and normal tape (38mm)), then about 4.8 cm to trigger crossing line.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;For Robobot this could be as low as 3-4, as the linesensor is wider.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.ls_power_high.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;If not in auto mode, should the line sensor LED power be high (ca. 30mA), else low (ca 10mA)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.ls_power_high.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;High power&amp;quot;))&lt;br /&gt;
			self.ls_line_white.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;If line is not white (bright), then assumed black (dark)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.ls_line_white.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;White line&amp;quot;))&lt;br /&gt;
			self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Edge Detection&amp;quot;))&lt;br /&gt;
			self.label_56.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Distance sensor&amp;quot;))&lt;br /&gt;
			self.label_279.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D1 right&amp;quot;))&lt;br /&gt;
			self.label_281.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D1 right RAW&amp;quot;))&lt;br /&gt;
			self.label_287.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;m&amp;quot;))&lt;br /&gt;
			self.label_280.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D2 front&amp;quot;))&lt;br /&gt;
			self.label_282.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D2 front RAW&amp;quot;))&lt;br /&gt;
			self.label_288.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;m&amp;quot;))&lt;br /&gt;
			self.label_284.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D1 right (50cm)&amp;quot;))&lt;br /&gt;
			self.label_286.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D2 front (13cm)&amp;quot;))&lt;br /&gt;
			self.label_285.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D2 front (50cm)&amp;quot;))&lt;br /&gt;
			self.label_283.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D1 right (13cm)&amp;quot;))&lt;br /&gt;
			self.checkBox_ir_use.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Should be turned on at power on (will be turned on automatically in missions that use the sensor - if installed). If not installed, then distance is  10m&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.checkBox_ir_use.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Sensor Enable&amp;quot;))&lt;br /&gt;
			self.checkBox_ir_installed.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Is the sensor installed (can not be turned on if not installed)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.checkBox_ir_installed.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Sensor Installed&amp;quot;))&lt;br /&gt;
			self.label_277.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Calibrate values (RAW)&amp;quot;))&lt;br /&gt;
			self.ir_apply.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Apply&amp;quot;))&lt;br /&gt;
			self.label_278.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Distance values (measured)&amp;quot;))&lt;br /&gt;
			self.ir_edit.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Edit&amp;quot;))&lt;br /&gt;
			self.ir_cancel.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Cancel&amp;quot;))&lt;br /&gt;
			self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_8), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;IR Distance&amp;quot;))&lt;br /&gt;
			self.EnableCamera_checkBox.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;To enable Camera stream, install mjpeg-streamer and run follwing commands:&amp;lt;br/&amp;gt;cd mjpg-streamer/mjpg-streamer-experimental/ &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;./mjpg_streamer -o &amp;amp;quot;output_http.so -w ./www&amp;amp;quot; -i &amp;amp;quot;input_raspicam.so&amp;amp;quot;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.EnableCamera_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Camera&amp;quot;))&lt;br /&gt;
			self.Camera_Record_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Record&amp;quot;))&lt;br /&gt;
			self.Camera_StopRecord_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Stop Recording&amp;quot;))&lt;br /&gt;
			self.Camera_Snapshot_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Take Snapshot&amp;quot;))&lt;br /&gt;
			self.label_55.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Snapshot Save Filename&amp;quot;))&lt;br /&gt;
			self.Camera_SnapSave_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Save&amp;quot;))&lt;br /&gt;
			self.VideoRecording_Label.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;&amp;lt;span style=\&amp;quot; color:#ff0000;\&amp;quot;&amp;gt;RECORDING...&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.Camera_ClearSnap_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Clear Snap&amp;quot;))&lt;br /&gt;
			self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Camera&amp;quot;))&lt;br /&gt;
			self.label_30.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Servo Controls&amp;quot;))&lt;br /&gt;
			self.enableServo1_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Servo 1&amp;quot;))&lt;br /&gt;
			self.enableServo2_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Servo 2&amp;quot;))&lt;br /&gt;
			self.enableServo3_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Servo 3&amp;quot;))&lt;br /&gt;
			self.label_31.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Current Position&amp;quot;))&lt;br /&gt;
			self.servo1_current_pos.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 20.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no crossing, 20 = crossing detected in 20ms. &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Counts +1 when detection is above &amp;amp;quot;Crossing det.&amp;amp;quot; value.&amp;lt;br/&amp;gt;else -1.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_33.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Command Position&amp;quot;))&lt;br /&gt;
			self.manualServoPos1_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Apply&amp;quot;))&lt;br /&gt;
			self.label_39.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Position Dial&amp;quot;))&lt;br /&gt;
			self.label_40.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Position Dial&amp;quot;))&lt;br /&gt;
			self.servo2_current_pos.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 20.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no crossing, 20 = crossing detected in 20ms. &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Counts +1 when detection is above &amp;amp;quot;Crossing det.&amp;amp;quot; value.&amp;lt;br/&amp;gt;else -1.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_41.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Command Position&amp;quot;))&lt;br /&gt;
			self.manualServoPos2_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Apply&amp;quot;))&lt;br /&gt;
			self.label_43.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Current Position&amp;quot;))&lt;br /&gt;
			self.label_46.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Position Dial&amp;quot;))&lt;br /&gt;
			self.servo3_current_pos.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 20.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no crossing, 20 = crossing detected in 20ms. &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Counts +1 when detection is above &amp;amp;quot;Crossing det.&amp;amp;quot; value.&amp;lt;br/&amp;gt;else -1.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_47.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Command Position&amp;quot;))&lt;br /&gt;
			self.manualServoPos3_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Apply&amp;quot;))&lt;br /&gt;
			self.label_48.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Current Position&amp;quot;))&lt;br /&gt;
			self.enableServo4_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Servo 4&amp;quot;))&lt;br /&gt;
			self.label_49.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Position Dial&amp;quot;))&lt;br /&gt;
			self.servo4_current_pos.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 20.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no crossing, 20 = crossing detected in 20ms. &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Counts +1 when detection is above &amp;amp;quot;Crossing det.&amp;amp;quot; value.&amp;lt;br/&amp;gt;else -1.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_50.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Command Position&amp;quot;))&lt;br /&gt;
			self.manualServoPos4_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Apply&amp;quot;))&lt;br /&gt;
			self.label_51.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Current Position&amp;quot;))&lt;br /&gt;
			self.enableServo5_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Servo 5&amp;quot;))&lt;br /&gt;
			self.label_52.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Position Dial&amp;quot;))&lt;br /&gt;
			self.servo5_current_pos.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 20.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no crossing, 20 = crossing detected in 20ms. &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Counts +1 when detection is above &amp;amp;quot;Crossing det.&amp;amp;quot; value.&amp;lt;br/&amp;gt;else -1.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_53.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Command Position&amp;quot;))&lt;br /&gt;
			self.manualServoPos5_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Apply&amp;quot;))&lt;br /&gt;
			self.label_54.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Current Position&amp;quot;))&lt;br /&gt;
			self.Servo1Steering_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Steering&amp;quot;))&lt;br /&gt;
			self.val_servo1_offset.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Steering offset to align steering wheel with straight forward.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Units is control units, i.e. about 50 is 5 degrees.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Zero offset means forward is 1.5ms pulse (center for servo)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_servo_6.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;distance&amp;quot;))&lt;br /&gt;
			self.val_servo1_scale.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Servo angle change from 1ms to 2ms PWM pulse to servo.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;(typically 160 deg for hitec7235 and 90 deg for &amp;amp;quot;old&amp;amp;quot; servos)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_servo_9.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;scale&amp;quot;))&lt;br /&gt;
			self.label_servo_8.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Offset&amp;quot;))&lt;br /&gt;
			self.val_steer_distance.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Distance from drive axle to steering wheel.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Positive is forward (front wheel). In meters.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Typically 0.16 m for ROBOBOT.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_4), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Servo&amp;quot;))&lt;br /&gt;
			self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_5), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Mission&amp;quot;))&lt;br /&gt;
			self.menuFile.setTitle(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;File&amp;quot;))&lt;br /&gt;
			self.menuHelp.setTitle(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Help&amp;quot;))&lt;br /&gt;
			self.menuTools.setTitle(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Tools&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
		import sys&lt;br /&gt;
		app = QtWidgets.QApplication(sys.argv)&lt;br /&gt;
		MainWindow = QtWidgets.QMainWindow()&lt;br /&gt;
		ui = Ui_MainWindow()&lt;br /&gt;
		ui.setupUi(MainWindow)&lt;br /&gt;
		MainWindow.show()&lt;br /&gt;
		sys.exit(app.exec_())&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=User_Interface_Code&amp;diff=5106</id>
		<title>User Interface Code</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=User_Interface_Code&amp;diff=5106"/>
		<updated>2020-12-06T17:39:55Z</updated>

		<summary type="html">&lt;p&gt;S192320: Blanked the page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=User_Interface_Code&amp;diff=5105</id>
		<title>User Interface Code</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=User_Interface_Code&amp;diff=5105"/>
		<updated>2020-12-06T17:39:27Z</updated>

		<summary type="html">&lt;p&gt;S192320: Created page with &amp;quot; 	# -*- coding: utf-8 -*-  	# Form implementation generated from reading ui file &amp;#039;Regbot_V1_9.ui&amp;#039; 	# 	# Created by: PyQt5 UI code generator 5.13.2 	# 	# WARNING! All changes m...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; 	# -*- coding: utf-8 -*-&lt;br /&gt;
&lt;br /&gt;
	# Form implementation generated from reading ui file &#039;Regbot_V1_9.ui&#039;&lt;br /&gt;
	#&lt;br /&gt;
	# Created by: PyQt5 UI code generator 5.13.2&lt;br /&gt;
	#&lt;br /&gt;
	# WARNING! All changes made in this file will be lost!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	from PyQt5 import QtCore, QtGui, QtWidgets&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	class Ui_MainWindow(object):&lt;br /&gt;
		def setupUi(self, MainWindow):&lt;br /&gt;
			MainWindow.setObjectName(&amp;quot;MainWindow&amp;quot;)&lt;br /&gt;
			MainWindow.resize(1122, 555)&lt;br /&gt;
			self.centralwidget = QtWidgets.QWidget(MainWindow)&lt;br /&gt;
			self.centralwidget.setObjectName(&amp;quot;centralwidget&amp;quot;)&lt;br /&gt;
			self.SidebarMenu = QtWidgets.QToolBox(self.centralwidget)&lt;br /&gt;
			self.SidebarMenu.setGeometry(QtCore.QRect(0, 0, 331, 721))&lt;br /&gt;
			self.SidebarMenu.setAutoFillBackground(True)&lt;br /&gt;
			self.SidebarMenu.setObjectName(&amp;quot;SidebarMenu&amp;quot;)&lt;br /&gt;
			self.SidebarMenuPage1 = QtWidgets.QWidget()&lt;br /&gt;
			self.SidebarMenuPage1.setGeometry(QtCore.QRect(0, 0, 331, 640))&lt;br /&gt;
			self.SidebarMenuPage1.setObjectName(&amp;quot;SidebarMenuPage1&amp;quot;)&lt;br /&gt;
			self.line = QtWidgets.QFrame(self.SidebarMenuPage1)&lt;br /&gt;
			self.line.setGeometry(QtCore.QRect(10, 220, 311, 21))&lt;br /&gt;
			self.line.setFrameShape(QtWidgets.QFrame.HLine)&lt;br /&gt;
			self.line.setFrameShadow(QtWidgets.QFrame.Sunken)&lt;br /&gt;
			self.line.setObjectName(&amp;quot;line&amp;quot;)&lt;br /&gt;
			self.IP_Connect_frame = QtWidgets.QFrame(self.SidebarMenuPage1)&lt;br /&gt;
			self.IP_Connect_frame.setGeometry(QtCore.QRect(0, 0, 331, 111))&lt;br /&gt;
			self.IP_Connect_frame.setAutoFillBackground(True)&lt;br /&gt;
			self.IP_Connect_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.IP_Connect_frame.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.IP_Connect_frame.setObjectName(&amp;quot;IP_Connect_frame&amp;quot;)&lt;br /&gt;
			self.label_2 = QtWidgets.QLabel(self.IP_Connect_frame)&lt;br /&gt;
			self.label_2.setGeometry(QtCore.QRect(30, 90, 91, 16))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_2.setFont(font)&lt;br /&gt;
			self.label_2.setObjectName(&amp;quot;label_2&amp;quot;)&lt;br /&gt;
			self.Disconnect_Cmd = QtWidgets.QPushButton(self.IP_Connect_frame)&lt;br /&gt;
			self.Disconnect_Cmd.setGeometry(QtCore.QRect(180, 60, 121, 23))&lt;br /&gt;
			self.Disconnect_Cmd.setObjectName(&amp;quot;Disconnect_Cmd&amp;quot;)&lt;br /&gt;
			self.connect_Cmd = QtWidgets.QPushButton(self.IP_Connect_frame)&lt;br /&gt;
			self.connect_Cmd.setGeometry(QtCore.QRect(30, 60, 131, 23))&lt;br /&gt;
			self.connect_Cmd.setObjectName(&amp;quot;connect_Cmd&amp;quot;)&lt;br /&gt;
			self.label = QtWidgets.QLabel(self.IP_Connect_frame)&lt;br /&gt;
			self.label.setGeometry(QtCore.QRect(120, 30, 61, 21))&lt;br /&gt;
			self.label.setObjectName(&amp;quot;label&amp;quot;)&lt;br /&gt;
			self.IP_Adress_field = QtWidgets.QLineEdit(self.IP_Connect_frame)&lt;br /&gt;
			self.IP_Adress_field.setGeometry(QtCore.QRect(170, 30, 131, 20))&lt;br /&gt;
			self.IP_Adress_field.setObjectName(&amp;quot;IP_Adress_field&amp;quot;)&lt;br /&gt;
			self.IP_NetworkStatus_label = QtWidgets.QLabel(self.IP_Connect_frame)&lt;br /&gt;
			self.IP_NetworkStatus_label.setGeometry(QtCore.QRect(140, 90, 141, 16))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.IP_NetworkStatus_label.setFont(font)&lt;br /&gt;
			self.IP_NetworkStatus_label.setObjectName(&amp;quot;IP_NetworkStatus_label&amp;quot;)&lt;br /&gt;
			self.label_7 = QtWidgets.QLabel(self.IP_Connect_frame)&lt;br /&gt;
			self.label_7.setGeometry(QtCore.QRect(10, 10, 131, 16))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_7.setFont(font)&lt;br /&gt;
			self.label_7.setObjectName(&amp;quot;label_7&amp;quot;)&lt;br /&gt;
			self.SidebarMenu.addItem(self.SidebarMenuPage1, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.SidebarMenuPage2 = QtWidgets.QWidget()&lt;br /&gt;
			self.SidebarMenuPage2.setGeometry(QtCore.QRect(0, 0, 331, 640))&lt;br /&gt;
			self.SidebarMenuPage2.setObjectName(&amp;quot;SidebarMenuPage2&amp;quot;)&lt;br /&gt;
			self.TextCmd_field = QtWidgets.QLineEdit(self.SidebarMenuPage2)&lt;br /&gt;
			self.TextCmd_field.setGeometry(QtCore.QRect(10, 580, 311, 20))&lt;br /&gt;
			self.TextCmd_field.setObjectName(&amp;quot;TextCmd_field&amp;quot;)&lt;br /&gt;
			self.send_Cmd = QtWidgets.QPushButton(self.SidebarMenuPage2)&lt;br /&gt;
			self.send_Cmd.setGeometry(QtCore.QRect(240, 610, 81, 23))&lt;br /&gt;
			self.send_Cmd.setObjectName(&amp;quot;send_Cmd&amp;quot;)&lt;br /&gt;
			self.TX_checkBox = QtWidgets.QCheckBox(self.SidebarMenuPage2)&lt;br /&gt;
			self.TX_checkBox.setGeometry(QtCore.QRect(10, 610, 41, 17))&lt;br /&gt;
			self.TX_checkBox.setObjectName(&amp;quot;TX_checkBox&amp;quot;)&lt;br /&gt;
			self.RX_checkBox = QtWidgets.QCheckBox(self.SidebarMenuPage2)&lt;br /&gt;
			self.RX_checkBox.setGeometry(QtCore.QRect(50, 610, 41, 17))&lt;br /&gt;
			self.RX_checkBox.setObjectName(&amp;quot;RX_checkBox&amp;quot;)&lt;br /&gt;
			self.ConsoleHelp_Cmd = QtWidgets.QPushButton(self.SidebarMenuPage2)&lt;br /&gt;
			self.ConsoleHelp_Cmd.setGeometry(QtCore.QRect(150, 610, 81, 23))&lt;br /&gt;
			self.ConsoleHelp_Cmd.setObjectName(&amp;quot;ConsoleHelp_Cmd&amp;quot;)&lt;br /&gt;
			self.CommandConsole_Window = QtWidgets.QPlainTextEdit(self.SidebarMenuPage2)&lt;br /&gt;
			self.CommandConsole_Window.setGeometry(QtCore.QRect(10, 0, 311, 571))&lt;br /&gt;
			self.CommandConsole_Window.setAcceptDrops(False)&lt;br /&gt;
			self.CommandConsole_Window.setToolTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.CommandConsole_Window.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)&lt;br /&gt;
			self.CommandConsole_Window.setLineWrapMode(QtWidgets.QPlainTextEdit.NoWrap)&lt;br /&gt;
			self.CommandConsole_Window.setObjectName(&amp;quot;CommandConsole_Window&amp;quot;)&lt;br /&gt;
			self.ConsoleClear_Cmd = QtWidgets.QPushButton(self.SidebarMenuPage2)&lt;br /&gt;
			self.ConsoleClear_Cmd.setGeometry(QtCore.QRect(90, 610, 51, 23))&lt;br /&gt;
			self.ConsoleClear_Cmd.setObjectName(&amp;quot;ConsoleClear_Cmd&amp;quot;)&lt;br /&gt;
			self.SidebarMenu.addItem(self.SidebarMenuPage2, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.SidebarMenuPage3 = QtWidgets.QWidget()&lt;br /&gt;
			self.SidebarMenuPage3.setGeometry(QtCore.QRect(0, 0, 331, 640))&lt;br /&gt;
			self.SidebarMenuPage3.setObjectName(&amp;quot;SidebarMenuPage3&amp;quot;)&lt;br /&gt;
			self.main_start = QtWidgets.QPushButton(self.SidebarMenuPage3)&lt;br /&gt;
			self.main_start.setGeometry(QtCore.QRect(170, 60, 151, 23))&lt;br /&gt;
			self.main_start.setObjectName(&amp;quot;main_start&amp;quot;)&lt;br /&gt;
			self.main_remote_ctrl = QtWidgets.QCheckBox(self.SidebarMenuPage3)&lt;br /&gt;
			self.main_remote_ctrl.setGeometry(QtCore.QRect(10, 30, 141, 17))&lt;br /&gt;
			self.main_remote_ctrl.setTristate(True)&lt;br /&gt;
			self.main_remote_ctrl.setObjectName(&amp;quot;main_remote_ctrl&amp;quot;)&lt;br /&gt;
			self.main_stop = QtWidgets.QPushButton(self.SidebarMenuPage3)&lt;br /&gt;
			self.main_stop.setGeometry(QtCore.QRect(10, 60, 151, 23))&lt;br /&gt;
			self.main_stop.setObjectName(&amp;quot;main_stop&amp;quot;)&lt;br /&gt;
			self.main_mission_state = QtWidgets.QCheckBox(self.SidebarMenuPage3)&lt;br /&gt;
			self.main_mission_state.setGeometry(QtCore.QRect(10, 10, 101, 17))&lt;br /&gt;
			self.main_mission_state.setTristate(True)&lt;br /&gt;
			self.main_mission_state.setObjectName(&amp;quot;main_mission_state&amp;quot;)&lt;br /&gt;
			self.SidebarMenu.addItem(self.SidebarMenuPage3, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)&lt;br /&gt;
			self.tabWidget.setGeometry(QtCore.QRect(330, 0, 961, 721))&lt;br /&gt;
			self.tabWidget.setAutoFillBackground(True)&lt;br /&gt;
			self.tabWidget.setObjectName(&amp;quot;tabWidget&amp;quot;)&lt;br /&gt;
			self.tab = QtWidgets.QWidget()&lt;br /&gt;
			self.tab.setObjectName(&amp;quot;tab&amp;quot;)&lt;br /&gt;
			self.MotorGUI_ctrl_frame = QtWidgets.QFrame(self.tab)&lt;br /&gt;
			self.MotorGUI_ctrl_frame.setGeometry(QtCore.QRect(0, 0, 321, 171))&lt;br /&gt;
			self.MotorGUI_ctrl_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.MotorGUI_ctrl_frame.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.MotorGUI_ctrl_frame.setObjectName(&amp;quot;MotorGUI_ctrl_frame&amp;quot;)&lt;br /&gt;
			self.label_5 = QtWidgets.QLabel(self.MotorGUI_ctrl_frame)&lt;br /&gt;
			self.label_5.setGeometry(QtCore.QRect(140, 100, 71, 21))&lt;br /&gt;
			self.label_5.setObjectName(&amp;quot;label_5&amp;quot;)&lt;br /&gt;
			self.enableKeyboardRC_checkBox = QtWidgets.QCheckBox(self.MotorGUI_ctrl_frame)&lt;br /&gt;
			self.enableKeyboardRC_checkBox.setGeometry(QtCore.QRect(150, 40, 141, 17))&lt;br /&gt;
			self.enableKeyboardRC_checkBox.setObjectName(&amp;quot;enableKeyboardRC_checkBox&amp;quot;)&lt;br /&gt;
			self.guiRC_Reverse_Cmd = QtWidgets.QPushButton(self.MotorGUI_ctrl_frame)&lt;br /&gt;
			self.guiRC_Reverse_Cmd.setGeometry(QtCore.QRect(50, 110, 31, 31))&lt;br /&gt;
			self.guiRC_Reverse_Cmd.setObjectName(&amp;quot;guiRC_Reverse_Cmd&amp;quot;)&lt;br /&gt;
			self.guiRC_Forward_Cmd = QtWidgets.QPushButton(self.MotorGUI_ctrl_frame)&lt;br /&gt;
			self.guiRC_Forward_Cmd.setGeometry(QtCore.QRect(50, 50, 31, 31))&lt;br /&gt;
			self.guiRC_Forward_Cmd.setObjectName(&amp;quot;guiRC_Forward_Cmd&amp;quot;)&lt;br /&gt;
			self.manualGuiSpeed_Cmd = QtWidgets.QLineEdit(self.MotorGUI_ctrl_frame)&lt;br /&gt;
			self.manualGuiSpeed_Cmd.setGeometry(QtCore.QRect(220, 100, 51, 20))&lt;br /&gt;
			self.manualGuiSpeed_Cmd.setObjectName(&amp;quot;manualGuiSpeed_Cmd&amp;quot;)&lt;br /&gt;
			self.enableGuiRC_checkBox = QtWidgets.QCheckBox(self.MotorGUI_ctrl_frame)&lt;br /&gt;
			self.enableGuiRC_checkBox.setGeometry(QtCore.QRect(150, 60, 111, 17))&lt;br /&gt;
			self.enableGuiRC_checkBox.setObjectName(&amp;quot;enableGuiRC_checkBox&amp;quot;)&lt;br /&gt;
			self.guiRC_Right_Cmd = QtWidgets.QPushButton(self.MotorGUI_ctrl_frame)&lt;br /&gt;
			self.guiRC_Right_Cmd.setGeometry(QtCore.QRect(80, 80, 31, 31))&lt;br /&gt;
			self.guiRC_Right_Cmd.setObjectName(&amp;quot;guiRC_Right_Cmd&amp;quot;)&lt;br /&gt;
			self.guiRC_Left_Cmd = QtWidgets.QPushButton(self.MotorGUI_ctrl_frame)&lt;br /&gt;
			self.guiRC_Left_Cmd.setGeometry(QtCore.QRect(20, 80, 31, 31))&lt;br /&gt;
			self.guiRC_Left_Cmd.setObjectName(&amp;quot;guiRC_Left_Cmd&amp;quot;)&lt;br /&gt;
			self.label_8 = QtWidgets.QLabel(self.MotorGUI_ctrl_frame)&lt;br /&gt;
			self.label_8.setGeometry(QtCore.QRect(10, 10, 151, 16))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_8.setFont(font)&lt;br /&gt;
			self.label_8.setObjectName(&amp;quot;label_8&amp;quot;)&lt;br /&gt;
			self.frame = QtWidgets.QFrame(self.tab)&lt;br /&gt;
			self.frame.setGeometry(QtCore.QRect(0, 170, 321, 321))&lt;br /&gt;
			self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.frame.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.frame.setObjectName(&amp;quot;frame&amp;quot;)&lt;br /&gt;
			self.label_9 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_9.setGeometry(QtCore.QRect(10, 10, 201, 16))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_9.setFont(font)&lt;br /&gt;
			self.label_9.setObjectName(&amp;quot;label_9&amp;quot;)&lt;br /&gt;
			self.label_21 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_21.setGeometry(QtCore.QRect(10, 149, 91, 21))&lt;br /&gt;
			self.label_21.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_21.setObjectName(&amp;quot;label_21&amp;quot;)&lt;br /&gt;
			self.Robot_WheelRadius_Right = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setGeometry(QtCore.QRect(109, 230, 71, 21))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.Robot_WheelRadius_Right.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.Robot_WheelRadius_Right.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_WheelRadius_Right.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Wheel radius&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_WheelRadius_Right.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setReadOnly(False)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setDecimals(4)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setMinimum(0.001)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setMaximum(0.99)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setSingleStep(0.001)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setProperty(&amp;quot;value&amp;quot;, 0.03)&lt;br /&gt;
			self.Robot_WheelRadius_Right.setObjectName(&amp;quot;Robot_WheelRadius_Right&amp;quot;)&lt;br /&gt;
			self.Robot_WheelRadius_Left = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setGeometry(QtCore.QRect(109, 190, 71, 21))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.Robot_WheelRadius_Left.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.Robot_WheelRadius_Left.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_WheelRadius_Left.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Wheel radius&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_WheelRadius_Left.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setReadOnly(False)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setDecimals(4)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setMinimum(0.001)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setMaximum(0.99)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setSingleStep(0.001)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setProperty(&amp;quot;value&amp;quot;, 0.03)&lt;br /&gt;
			self.Robot_WheelRadius_Left.setObjectName(&amp;quot;Robot_WheelRadius_Left&amp;quot;)&lt;br /&gt;
			self.label_22 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_22.setGeometry(QtCore.QRect(10, 118, 91, 23))&lt;br /&gt;
			self.label_22.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_22.setObjectName(&amp;quot;label_22&amp;quot;)&lt;br /&gt;
			self.Robot_GearRatio = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
			self.Robot_GearRatio.setGeometry(QtCore.QRect(110, 120, 71, 21))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.Robot_GearRatio.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.Robot_GearRatio.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.Robot_GearRatio.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_GearRatio.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_GearRatio.setReadOnly(False)&lt;br /&gt;
			self.Robot_GearRatio.setDecimals(3)&lt;br /&gt;
			self.Robot_GearRatio.setMaximum(999.99)&lt;br /&gt;
			self.Robot_GearRatio.setSingleStep(0.001)&lt;br /&gt;
			self.Robot_GearRatio.setProperty(&amp;quot;value&amp;quot;, 9.68)&lt;br /&gt;
			self.Robot_GearRatio.setObjectName(&amp;quot;Robot_GearRatio&amp;quot;)&lt;br /&gt;
			self.label_24 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_24.setGeometry(QtCore.QRect(10, 169, 121, 20))&lt;br /&gt;
			self.label_24.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_24.setObjectName(&amp;quot;label_24&amp;quot;)&lt;br /&gt;
			self.label_127 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_127.setGeometry(QtCore.QRect(50, 30, 51, 20))&lt;br /&gt;
			self.label_127.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_127.setObjectName(&amp;quot;label_127&amp;quot;)&lt;br /&gt;
			self.Robot_WheelBase = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
			self.Robot_WheelBase.setGeometry(QtCore.QRect(110, 90, 71, 21))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.Robot_WheelBase.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.Robot_WheelBase.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.Robot_WheelBase.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Distance between drivng wheels (0.155m)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.Robot_WheelBase.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_WheelBase.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_WheelBase.setReadOnly(False)&lt;br /&gt;
			self.Robot_WheelBase.setDecimals(4)&lt;br /&gt;
			self.Robot_WheelBase.setMaximum(5.0)&lt;br /&gt;
			self.Robot_WheelBase.setSingleStep(0.01)&lt;br /&gt;
			self.Robot_WheelBase.setProperty(&amp;quot;value&amp;quot;, 0.155)&lt;br /&gt;
			self.Robot_WheelBase.setObjectName(&amp;quot;Robot_WheelBase&amp;quot;)&lt;br /&gt;
			self.label_128 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_128.setGeometry(QtCore.QRect(50, 60, 51, 20))&lt;br /&gt;
			self.label_128.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_128.setObjectName(&amp;quot;label_128&amp;quot;)&lt;br /&gt;
			self.label_25 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_25.setGeometry(QtCore.QRect(20, 90, 81, 21))&lt;br /&gt;
			self.label_25.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_25.setObjectName(&amp;quot;label_25&amp;quot;)&lt;br /&gt;
			self.RobotID = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
			self.RobotID.setEnabled(True)&lt;br /&gt;
			self.RobotID.setGeometry(QtCore.QRect(110, 31, 71, 21))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.RobotID.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.RobotID.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.RobotID.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;This is the ID of the robot [1..9999].&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;To change the ID of robot:&amp;lt;br/&amp;gt;change ID,&amp;lt;br/&amp;gt;click &amp;amp;quot;save&amp;amp;quot;,&amp;lt;br/&amp;gt;click &amp;amp;quot;save on robot&amp;amp;quot;.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Change ID to -1 to revert to factory settings.&amp;lt;br/&amp;gt;And then change ID back to right value&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.RobotID.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.RobotID.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.RobotID.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.RobotID.setReadOnly(False)&lt;br /&gt;
			self.RobotID.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.RobotID.setSuffix(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.RobotID.setDecimals(0)&lt;br /&gt;
			self.RobotID.setMinimum(-2.0)&lt;br /&gt;
			self.RobotID.setMaximum(10000.0)&lt;br /&gt;
			self.RobotID.setObjectName(&amp;quot;RobotID&amp;quot;)&lt;br /&gt;
			self.RobotHW_type = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
			self.RobotHW_type.setEnabled(True)&lt;br /&gt;
			self.RobotHW_type.setGeometry(QtCore.QRect(110, 60, 71, 21))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.RobotHW_type.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.RobotHW_type.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.RobotHW_type.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;The hardware version&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;1 = old version with no wifi nor line sensor plugs on main board and round power supply plug on main board.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;2 = more plugs and satellite PCB for power management and wifi.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;3 = power management and on-off switch on-board (big motor controller board)&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;4 = same as 3, but with small motor controller board.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;5 = same as 2, but updated with new sattelite boards (power and wifi) etc.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;6 = hardware version 4 (motor controller integrated on board)&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;To change:&amp;lt;br/&amp;gt;click edit&amp;lt;br/&amp;gt;change HW setting,&amp;lt;br/&amp;gt;click &amp;amp;quot;save&amp;amp;quot;,&amp;lt;br/&amp;gt;click &amp;amp;quot;save to flash&amp;amp;quot;.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;(ID must be &amp;amp;gt; 0)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.RobotHW_type.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.RobotHW_type.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.RobotHW_type.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.RobotHW_type.setReadOnly(False)&lt;br /&gt;
			self.RobotHW_type.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.RobotHW_type.setSuffix(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.RobotHW_type.setDecimals(0)&lt;br /&gt;
			self.RobotHW_type.setMinimum(-2.0)&lt;br /&gt;
			self.RobotHW_type.setMaximum(10.0)&lt;br /&gt;
			self.RobotHW_type.setObjectName(&amp;quot;RobotHW_type&amp;quot;)&lt;br /&gt;
			self.Robot_PulsePerRev = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
			self.Robot_PulsePerRev.setGeometry(QtCore.QRect(110, 150, 71, 21))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.Robot_PulsePerRev.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.Robot_PulsePerRev.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.Robot_PulsePerRev.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_PulsePerRev.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_PulsePerRev.setReadOnly(False)&lt;br /&gt;
			self.Robot_PulsePerRev.setDecimals(0)&lt;br /&gt;
			self.Robot_PulsePerRev.setMinimum(1.0)&lt;br /&gt;
			self.Robot_PulsePerRev.setMaximum(1000.0)&lt;br /&gt;
			self.Robot_PulsePerRev.setProperty(&amp;quot;value&amp;quot;, 48.0)&lt;br /&gt;
			self.Robot_PulsePerRev.setObjectName(&amp;quot;Robot_PulsePerRev&amp;quot;)&lt;br /&gt;
			self.label_26 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_26.setGeometry(QtCore.QRect(10, 210, 121, 20))&lt;br /&gt;
			self.label_26.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_26.setObjectName(&amp;quot;label_26&amp;quot;)&lt;br /&gt;
			self.Robot_RevEncoder_Right = QtWidgets.QCheckBox(self.frame)&lt;br /&gt;
			self.Robot_RevEncoder_Right.setGeometry(QtCore.QRect(190, 90, 131, 21))&lt;br /&gt;
			self.Robot_RevEncoder_Right.setObjectName(&amp;quot;Robot_RevEncoder_Right&amp;quot;)&lt;br /&gt;
			self.Robot_RevMotor = QtWidgets.QCheckBox(self.frame)&lt;br /&gt;
			self.Robot_RevMotor.setGeometry(QtCore.QRect(190, 33, 92, 16))&lt;br /&gt;
			self.Robot_RevMotor.setObjectName(&amp;quot;Robot_RevMotor&amp;quot;)&lt;br /&gt;
			self.Robot_RevEncoder_Left = QtWidgets.QCheckBox(self.frame)&lt;br /&gt;
			self.Robot_RevEncoder_Left.setGeometry(QtCore.QRect(190, 60, 131, 21))&lt;br /&gt;
			self.Robot_RevEncoder_Left.setObjectName(&amp;quot;Robot_RevEncoder_Left&amp;quot;)&lt;br /&gt;
			self.Robot_BatteryIdleVolts = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setGeometry(QtCore.QRect(110, 290, 71, 21))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.Robot_BatteryIdleVolts.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Go idle when battery voltage is below this level.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;For a 3 cell LiPo battery it should go in idle mode at 3 x 3.2V = 9.6V&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;At 10V about 10% capacity is left before battery destruction, and in idle mode the robot uses about 30mA, so battey will selfdestruct after another about 3 hours.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;So remember to switch off and to recharge.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setReadOnly(False)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setDecimals(1)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setMinimum(9.0)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setMaximum(17.0)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setSingleStep(0.1)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setProperty(&amp;quot;value&amp;quot;, 9.9)&lt;br /&gt;
			self.Robot_BatteryIdleVolts.setObjectName(&amp;quot;Robot_BatteryIdleVolts&amp;quot;)&lt;br /&gt;
			self.label_222 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_222.setGeometry(QtCore.QRect(20, 290, 81, 20))&lt;br /&gt;
			self.label_222.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_222.setObjectName(&amp;quot;label_222&amp;quot;)&lt;br /&gt;
			self.save_id_on_robot = QtWidgets.QPushButton(self.frame)&lt;br /&gt;
			self.save_id_on_robot.setEnabled(False)&lt;br /&gt;
			self.save_id_on_robot.setGeometry(QtCore.QRect(210, 230, 92, 23))&lt;br /&gt;
			self.save_id_on_robot.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Save ID and robot specific settings (this frame) on robot.&amp;lt;br/&amp;gt;NB! you need to press &amp;amp;quot;save on robot&amp;amp;quot; in configuration block to save into EE-prom (flash)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.save_id_on_robot.setCheckable(False)&lt;br /&gt;
			self.save_id_on_robot.setChecked(False)&lt;br /&gt;
			self.save_id_on_robot.setObjectName(&amp;quot;save_id_on_robot&amp;quot;)&lt;br /&gt;
			self.label_129 = QtWidgets.QLabel(self.frame)&lt;br /&gt;
			self.label_129.setGeometry(QtCore.QRect(10, 265, 91, 21))&lt;br /&gt;
			self.label_129.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_129.setObjectName(&amp;quot;label_129&amp;quot;)&lt;br /&gt;
			self.Robot_BalanceOffset = QtWidgets.QDoubleSpinBox(self.frame)&lt;br /&gt;
			self.Robot_BalanceOffset.setGeometry(QtCore.QRect(110, 263, 71, 21))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(2)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.Robot_BalanceOffset.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.Robot_BalanceOffset.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.Robot_BalanceOffset.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Balance offset - where center of gravity (COG) is just above support point (wheels).&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Angle is in radians.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Positive is tilt forward.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.Robot_BalanceOffset.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_BalanceOffset.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_BalanceOffset.setReadOnly(False)&lt;br /&gt;
			self.Robot_BalanceOffset.setDecimals(4)&lt;br /&gt;
			self.Robot_BalanceOffset.setMinimum(-1.0)&lt;br /&gt;
			self.Robot_BalanceOffset.setMaximum(1.0)&lt;br /&gt;
			self.Robot_BalanceOffset.setSingleStep(0.001)&lt;br /&gt;
			self.Robot_BalanceOffset.setObjectName(&amp;quot;Robot_BalanceOffset&amp;quot;)&lt;br /&gt;
			self.Robot_edit = QtWidgets.QPushButton(self.frame)&lt;br /&gt;
			self.Robot_edit.setEnabled(True)&lt;br /&gt;
			self.Robot_edit.setGeometry(QtCore.QRect(210, 259, 92, 23))&lt;br /&gt;
			self.Robot_edit.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Switch to edit mode - rather than listen to robot messages - for these fields.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.Robot_edit.setCheckable(False)&lt;br /&gt;
			self.Robot_edit.setChecked(False)&lt;br /&gt;
			self.Robot_edit.setObjectName(&amp;quot;Robot_edit&amp;quot;)&lt;br /&gt;
			self.Robot_OnBattery = QtWidgets.QCheckBox(self.frame)&lt;br /&gt;
			self.Robot_OnBattery.setGeometry(QtCore.QRect(190, 120, 111, 21))&lt;br /&gt;
			self.Robot_OnBattery.setObjectName(&amp;quot;Robot_OnBattery&amp;quot;)&lt;br /&gt;
			self.robot_cancel = QtWidgets.QPushButton(self.frame)&lt;br /&gt;
			self.robot_cancel.setEnabled(False)&lt;br /&gt;
			self.robot_cancel.setGeometry(QtCore.QRect(210, 288, 92, 23))&lt;br /&gt;
			self.robot_cancel.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Revert to values on robot&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.robot_cancel.setCheckable(False)&lt;br /&gt;
			self.robot_cancel.setChecked(False)&lt;br /&gt;
			self.robot_cancel.setObjectName(&amp;quot;robot_cancel&amp;quot;)&lt;br /&gt;
			self.tabWidget_2 = QtWidgets.QTabWidget(self.tab)&lt;br /&gt;
			self.tabWidget_2.setGeometry(QtCore.QRect(0, 490, 321, 211))&lt;br /&gt;
			self.tabWidget_2.setObjectName(&amp;quot;tabWidget_2&amp;quot;)&lt;br /&gt;
			self.tab_6 = QtWidgets.QWidget()&lt;br /&gt;
			self.tab_6.setObjectName(&amp;quot;tab_6&amp;quot;)&lt;br /&gt;
			self.groupBox = QtWidgets.QGroupBox(self.tab_6)&lt;br /&gt;
			self.groupBox.setGeometry(QtCore.QRect(0, 0, 311, 181))&lt;br /&gt;
			self.groupBox.setTitle(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.groupBox.setObjectName(&amp;quot;groupBox&amp;quot;)&lt;br /&gt;
			self.Robot_CurrentLeft_Value = QtWidgets.QDoubleSpinBox(self.groupBox)&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setGeometry(QtCore.QRect(110, 100, 71, 21))&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setReadOnly(True)&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setDecimals(2)&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setMinimum(-100000.0)&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setMaximum(100000.0)&lt;br /&gt;
			self.Robot_CurrentLeft_Value.setObjectName(&amp;quot;Robot_CurrentLeft_Value&amp;quot;)&lt;br /&gt;
			self.Robot_Distance = QtWidgets.QDoubleSpinBox(self.groupBox)&lt;br /&gt;
			self.Robot_Distance.setGeometry(QtCore.QRect(110, 130, 71, 21))&lt;br /&gt;
			self.Robot_Distance.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_Distance.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_Distance.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_Distance.setReadOnly(True)&lt;br /&gt;
			self.Robot_Distance.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_Distance.setDecimals(3)&lt;br /&gt;
			self.Robot_Distance.setMinimum(-999999.0)&lt;br /&gt;
			self.Robot_Distance.setMaximum(999999.99)&lt;br /&gt;
			self.Robot_Distance.setObjectName(&amp;quot;Robot_Distance&amp;quot;)&lt;br /&gt;
			self.label_36 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
			self.label_36.setGeometry(QtCore.QRect(186, 24, 71, 16))&lt;br /&gt;
			self.label_36.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_36.setObjectName(&amp;quot;label_36&amp;quot;)&lt;br /&gt;
			self.label_37 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
			self.label_37.setGeometry(QtCore.QRect(106, 24, 71, 16))&lt;br /&gt;
			self.label_37.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_37.setObjectName(&amp;quot;label_37&amp;quot;)&lt;br /&gt;
			self.label_42 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
			self.label_42.setGeometry(QtCore.QRect(11, 100, 91, 16))&lt;br /&gt;
			self.label_42.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.label_42.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_42.setObjectName(&amp;quot;label_42&amp;quot;)&lt;br /&gt;
			self.Robot_EncLeft_Value = QtWidgets.QSpinBox(self.groupBox)&lt;br /&gt;
			self.Robot_EncLeft_Value.setGeometry(QtCore.QRect(110, 40, 71, 21))&lt;br /&gt;
			self.Robot_EncLeft_Value.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_EncLeft_Value.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_EncLeft_Value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_EncLeft_Value.setReadOnly(True)&lt;br /&gt;
			self.Robot_EncLeft_Value.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_EncLeft_Value.setMinimum(-2147483647)&lt;br /&gt;
			self.Robot_EncLeft_Value.setMaximum(2147483647)&lt;br /&gt;
			self.Robot_EncLeft_Value.setObjectName(&amp;quot;Robot_EncLeft_Value&amp;quot;)&lt;br /&gt;
			self.Robot_CurrentRight_Value = QtWidgets.QDoubleSpinBox(self.groupBox)&lt;br /&gt;
			self.Robot_CurrentRight_Value.setGeometry(QtCore.QRect(190, 100, 71, 21))&lt;br /&gt;
			self.Robot_CurrentRight_Value.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_CurrentRight_Value.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_CurrentRight_Value.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_CurrentRight_Value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_CurrentRight_Value.setReadOnly(True)&lt;br /&gt;
			self.Robot_CurrentRight_Value.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_CurrentRight_Value.setDecimals(2)&lt;br /&gt;
			self.Robot_CurrentRight_Value.setMinimum(-100000.0)&lt;br /&gt;
			self.Robot_CurrentRight_Value.setMaximum(100000.0)&lt;br /&gt;
			self.Robot_CurrentRight_Value.setObjectName(&amp;quot;Robot_CurrentRight_Value&amp;quot;)&lt;br /&gt;
			self.label_28 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
			self.label_28.setGeometry(QtCore.QRect(11, 40, 91, 16))&lt;br /&gt;
			self.label_28.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_28.setObjectName(&amp;quot;label_28&amp;quot;)&lt;br /&gt;
			self.label_38 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
			self.label_38.setGeometry(QtCore.QRect(9, 5, 121, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setPointSize(10)&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_38.setFont(font)&lt;br /&gt;
			self.label_38.setObjectName(&amp;quot;label_38&amp;quot;)&lt;br /&gt;
			self.label_32 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
			self.label_32.setGeometry(QtCore.QRect(10, 130, 91, 16))&lt;br /&gt;
			self.label_32.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.label_32.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_32.setObjectName(&amp;quot;label_32&amp;quot;)&lt;br /&gt;
			self.label_29 = QtWidgets.QLabel(self.groupBox)&lt;br /&gt;
			self.label_29.setGeometry(QtCore.QRect(1, 70, 101, 16))&lt;br /&gt;
			self.label_29.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.label_29.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_29.setObjectName(&amp;quot;label_29&amp;quot;)&lt;br /&gt;
			self.Robot_EncRight_Value = QtWidgets.QSpinBox(self.groupBox)&lt;br /&gt;
			self.Robot_EncRight_Value.setGeometry(QtCore.QRect(190, 40, 71, 21))&lt;br /&gt;
			self.Robot_EncRight_Value.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_EncRight_Value.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_EncRight_Value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_EncRight_Value.setReadOnly(True)&lt;br /&gt;
			self.Robot_EncRight_Value.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_EncRight_Value.setMinimum(-2147483647)&lt;br /&gt;
			self.Robot_EncRight_Value.setMaximum(2147483647)&lt;br /&gt;
			self.Robot_EncRight_Value.setObjectName(&amp;quot;Robot_EncRight_Value&amp;quot;)&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value = QtWidgets.QDoubleSpinBox(self.groupBox)&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setGeometry(QtCore.QRect(190, 70, 71, 21))&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setReadOnly(True)&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setDecimals(3)&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setMinimum(-100000.0)&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setMaximum(100000.0)&lt;br /&gt;
			self.Robot_WheelRightSpeed_Value.setObjectName(&amp;quot;Robot_WheelRightSpeed_Value&amp;quot;)&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value = QtWidgets.QDoubleSpinBox(self.groupBox)&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setGeometry(QtCore.QRect(110, 70, 71, 21))&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setReadOnly(True)&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setDecimals(3)&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setMinimum(-100000.0)&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setMaximum(100000.0)&lt;br /&gt;
			self.Robot_WheelLeftSpeed_Value.setObjectName(&amp;quot;Robot_WheelLeftSpeed_Value&amp;quot;)&lt;br /&gt;
			self.tabWidget_2.addTab(self.tab_6, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.tab_7 = QtWidgets.QWidget()&lt;br /&gt;
			self.tab_7.setObjectName(&amp;quot;tab_7&amp;quot;)&lt;br /&gt;
			self.groupBox_3 = QtWidgets.QGroupBox(self.tab_7)&lt;br /&gt;
			self.groupBox_3.setGeometry(QtCore.QRect(0, 0, 311, 181))&lt;br /&gt;
			self.groupBox_3.setTitle(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.groupBox_3.setObjectName(&amp;quot;groupBox_3&amp;quot;)&lt;br /&gt;
			self.Robot_TiltDegrees = QtWidgets.QDoubleSpinBox(self.groupBox_3)&lt;br /&gt;
			self.Robot_TiltDegrees.setGeometry(QtCore.QRect(190, 131, 71, 21))&lt;br /&gt;
			self.Robot_TiltDegrees.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_TiltDegrees.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_TiltDegrees.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_TiltDegrees.setReadOnly(True)&lt;br /&gt;
			self.Robot_TiltDegrees.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_TiltDegrees.setDecimals(3)&lt;br /&gt;
			self.Robot_TiltDegrees.setMinimum(-999999.0)&lt;br /&gt;
			self.Robot_TiltDegrees.setMaximum(999999.99)&lt;br /&gt;
			self.Robot_TiltDegrees.setObjectName(&amp;quot;Robot_TiltDegrees&amp;quot;)&lt;br /&gt;
			self.label_76 = QtWidgets.QLabel(self.groupBox_3)&lt;br /&gt;
			self.label_76.setGeometry(QtCore.QRect(190, 80, 71, 21))&lt;br /&gt;
			self.label_76.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_76.setObjectName(&amp;quot;label_76&amp;quot;)&lt;br /&gt;
			self.label_45 = QtWidgets.QLabel(self.groupBox_3)&lt;br /&gt;
			self.label_45.setGeometry(QtCore.QRect(10, 0, 271, 31))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setPointSize(10)&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_45.setFont(font)&lt;br /&gt;
			self.label_45.setObjectName(&amp;quot;label_45&amp;quot;)&lt;br /&gt;
			self.Robot_PoseX = QtWidgets.QDoubleSpinBox(self.groupBox_3)&lt;br /&gt;
			self.Robot_PoseX.setGeometry(QtCore.QRect(100, 40, 81, 21))&lt;br /&gt;
			self.Robot_PoseX.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.Robot_PoseX.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_PoseX.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_PoseX.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_PoseX.setReadOnly(True)&lt;br /&gt;
			self.Robot_PoseX.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_PoseX.setDecimals(3)&lt;br /&gt;
			self.Robot_PoseX.setMinimum(-999999.0)&lt;br /&gt;
			self.Robot_PoseX.setMaximum(999999.99)&lt;br /&gt;
			self.Robot_PoseX.setObjectName(&amp;quot;Robot_PoseX&amp;quot;)&lt;br /&gt;
			self.Robot_PoseY = QtWidgets.QDoubleSpinBox(self.groupBox_3)&lt;br /&gt;
			self.Robot_PoseY.setGeometry(QtCore.QRect(100, 71, 81, 21))&lt;br /&gt;
			self.Robot_PoseY.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_PoseY.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_PoseY.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_PoseY.setReadOnly(True)&lt;br /&gt;
			self.Robot_PoseY.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_PoseY.setDecimals(3)&lt;br /&gt;
			self.Robot_PoseY.setMinimum(-999999.0)&lt;br /&gt;
			self.Robot_PoseY.setMaximum(999999.99)&lt;br /&gt;
			self.Robot_PoseY.setObjectName(&amp;quot;Robot_PoseY&amp;quot;)&lt;br /&gt;
			self.Robot_PoseReset = QtWidgets.QPushButton(self.groupBox_3)&lt;br /&gt;
			self.Robot_PoseReset.setGeometry(QtCore.QRect(190, 40, 71, 21))&lt;br /&gt;
			self.Robot_PoseReset.setToolTip(&amp;quot;Reset robot pose and distance to 0.0&amp;quot;)&lt;br /&gt;
			self.Robot_PoseReset.setObjectName(&amp;quot;Robot_PoseReset&amp;quot;)&lt;br /&gt;
			self.label_44 = QtWidgets.QLabel(self.groupBox_3)&lt;br /&gt;
			self.label_44.setGeometry(QtCore.QRect(20, 100, 71, 21))&lt;br /&gt;
			self.label_44.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_44.setObjectName(&amp;quot;label_44&amp;quot;)&lt;br /&gt;
			self.label_34 = QtWidgets.QLabel(self.groupBox_3)&lt;br /&gt;
			self.label_34.setGeometry(QtCore.QRect(10, 40, 81, 21))&lt;br /&gt;
			self.label_34.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_34.setObjectName(&amp;quot;label_34&amp;quot;)&lt;br /&gt;
			self.label_35 = QtWidgets.QLabel(self.groupBox_3)&lt;br /&gt;
			self.label_35.setGeometry(QtCore.QRect(20, 71, 71, 20))&lt;br /&gt;
			self.label_35.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_35.setObjectName(&amp;quot;label_35&amp;quot;)&lt;br /&gt;
			self.Robot_Tilt = QtWidgets.QDoubleSpinBox(self.groupBox_3)&lt;br /&gt;
			self.Robot_Tilt.setGeometry(QtCore.QRect(100, 131, 81, 21))&lt;br /&gt;
			self.Robot_Tilt.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_Tilt.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_Tilt.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_Tilt.setReadOnly(True)&lt;br /&gt;
			self.Robot_Tilt.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_Tilt.setDecimals(3)&lt;br /&gt;
			self.Robot_Tilt.setMinimum(-999999.0)&lt;br /&gt;
			self.Robot_Tilt.setMaximum(999999.99)&lt;br /&gt;
			self.Robot_Tilt.setObjectName(&amp;quot;Robot_Tilt&amp;quot;)&lt;br /&gt;
			self.Robot_PoseTheta = QtWidgets.QDoubleSpinBox(self.groupBox_3)&lt;br /&gt;
			self.Robot_PoseTheta.setGeometry(QtCore.QRect(100, 100, 81, 21))&lt;br /&gt;
			self.Robot_PoseTheta.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_PoseTheta.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_PoseTheta.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_PoseTheta.setReadOnly(True)&lt;br /&gt;
			self.Robot_PoseTheta.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_PoseTheta.setDecimals(3)&lt;br /&gt;
			self.Robot_PoseTheta.setMinimum(-999999.0)&lt;br /&gt;
			self.Robot_PoseTheta.setMaximum(999999.99)&lt;br /&gt;
			self.Robot_PoseTheta.setObjectName(&amp;quot;Robot_PoseTheta&amp;quot;)&lt;br /&gt;
			self.Robot_PoseDegrees = QtWidgets.QDoubleSpinBox(self.groupBox_3)&lt;br /&gt;
			self.Robot_PoseDegrees.setGeometry(QtCore.QRect(190, 100, 71, 21))&lt;br /&gt;
			self.Robot_PoseDegrees.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.Robot_PoseDegrees.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.Robot_PoseDegrees.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.Robot_PoseDegrees.setReadOnly(True)&lt;br /&gt;
			self.Robot_PoseDegrees.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.Robot_PoseDegrees.setDecimals(3)&lt;br /&gt;
			self.Robot_PoseDegrees.setMinimum(-999999.0)&lt;br /&gt;
			self.Robot_PoseDegrees.setMaximum(999999.99)&lt;br /&gt;
			self.Robot_PoseDegrees.setObjectName(&amp;quot;Robot_PoseDegrees&amp;quot;)&lt;br /&gt;
			self.label_125 = QtWidgets.QLabel(self.groupBox_3)&lt;br /&gt;
			self.label_125.setGeometry(QtCore.QRect(10, 130, 81, 21))&lt;br /&gt;
			self.label_125.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.label_125.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_125.setObjectName(&amp;quot;label_125&amp;quot;)&lt;br /&gt;
			self.tabWidget_2.addTab(self.tab_7, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.tabWidget.addTab(self.tab, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.tab_2 = QtWidgets.QWidget()&lt;br /&gt;
			self.tab_2.setObjectName(&amp;quot;tab_2&amp;quot;)&lt;br /&gt;
			self.frame_2 = QtWidgets.QFrame(self.tab_2)&lt;br /&gt;
			self.frame_2.setGeometry(QtCore.QRect(0, 0, 711, 421))&lt;br /&gt;
			self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.frame_2.setObjectName(&amp;quot;frame_2&amp;quot;)&lt;br /&gt;
			self.label_10 = QtWidgets.QLabel(self.frame_2)&lt;br /&gt;
			self.label_10.setGeometry(QtCore.QRect(10, 10, 101, 16))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_10.setFont(font)&lt;br /&gt;
			self.label_10.setObjectName(&amp;quot;label_10&amp;quot;)&lt;br /&gt;
			self.enableLineSensor_checkBox = QtWidgets.QCheckBox(self.frame_2)&lt;br /&gt;
			self.enableLineSensor_checkBox.setGeometry(QtCore.QRect(10, 30, 111, 17))&lt;br /&gt;
			self.enableLineSensor_checkBox.setObjectName(&amp;quot;enableLineSensor_checkBox&amp;quot;)&lt;br /&gt;
			self.frame_3 = QtWidgets.QFrame(self.frame_2)&lt;br /&gt;
			self.frame_3.setGeometry(QtCore.QRect(10, 50, 421, 231))&lt;br /&gt;
			self.frame_3.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.frame_3.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.frame_3.setObjectName(&amp;quot;frame_3&amp;quot;)&lt;br /&gt;
			self.line_bar_1 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.line_bar_1.setGeometry(QtCore.QRect(10, 20, 16, 181))&lt;br /&gt;
			self.line_bar_1.setMaximum(1000)&lt;br /&gt;
			self.line_bar_1.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.line_bar_1.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.line_bar_1.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.line_bar_1.setObjectName(&amp;quot;line_bar_1&amp;quot;)&lt;br /&gt;
			self.line_bar_4 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.line_bar_4.setGeometry(QtCore.QRect(70, 20, 16, 181))&lt;br /&gt;
			self.line_bar_4.setMaximum(1000)&lt;br /&gt;
			self.line_bar_4.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.line_bar_4.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.line_bar_4.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.line_bar_4.setObjectName(&amp;quot;line_bar_4&amp;quot;)&lt;br /&gt;
			self.line_bar_6 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.line_bar_6.setGeometry(QtCore.QRect(110, 20, 16, 181))&lt;br /&gt;
			self.line_bar_6.setMaximum(1000)&lt;br /&gt;
			self.line_bar_6.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.line_bar_6.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.line_bar_6.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.line_bar_6.setObjectName(&amp;quot;line_bar_6&amp;quot;)&lt;br /&gt;
			self.line_bar_5 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.line_bar_5.setGeometry(QtCore.QRect(90, 20, 16, 181))&lt;br /&gt;
			self.line_bar_5.setMaximum(1000)&lt;br /&gt;
			self.line_bar_5.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.line_bar_5.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.line_bar_5.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.line_bar_5.setObjectName(&amp;quot;line_bar_5&amp;quot;)&lt;br /&gt;
			self.line_bar_3 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.line_bar_3.setGeometry(QtCore.QRect(50, 20, 16, 181))&lt;br /&gt;
			self.line_bar_3.setMaximum(1000)&lt;br /&gt;
			self.line_bar_3.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.line_bar_3.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.line_bar_3.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.line_bar_3.setObjectName(&amp;quot;line_bar_3&amp;quot;)&lt;br /&gt;
			self.line_bar_7 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.line_bar_7.setGeometry(QtCore.QRect(130, 20, 16, 181))&lt;br /&gt;
			self.line_bar_7.setMaximum(1000)&lt;br /&gt;
			self.line_bar_7.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.line_bar_7.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.line_bar_7.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.line_bar_7.setObjectName(&amp;quot;line_bar_7&amp;quot;)&lt;br /&gt;
			self.line_bar_2 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.line_bar_2.setGeometry(QtCore.QRect(30, 20, 16, 181))&lt;br /&gt;
			self.line_bar_2.setMaximum(1000)&lt;br /&gt;
			self.line_bar_2.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.line_bar_2.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.line_bar_2.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.line_bar_2.setObjectName(&amp;quot;line_bar_2&amp;quot;)&lt;br /&gt;
			self.line_bar_8 = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.line_bar_8.setGeometry(QtCore.QRect(150, 20, 16, 181))&lt;br /&gt;
			self.line_bar_8.setMaximum(1000)&lt;br /&gt;
			self.line_bar_8.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.line_bar_8.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.line_bar_8.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.line_bar_8.setObjectName(&amp;quot;line_bar_8&amp;quot;)&lt;br /&gt;
			self.label_11 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_11.setGeometry(QtCore.QRect(10, 0, 151, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_11.setFont(font)&lt;br /&gt;
			self.label_11.setObjectName(&amp;quot;label_11&amp;quot;)&lt;br /&gt;
			self.label_12 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_12.setGeometry(QtCore.QRect(10, 200, 16, 21))&lt;br /&gt;
			self.label_12.setLayoutDirection(QtCore.Qt.RightToLeft)&lt;br /&gt;
			self.label_12.setObjectName(&amp;quot;label_12&amp;quot;)&lt;br /&gt;
			self.label_13 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_13.setGeometry(QtCore.QRect(30, 200, 16, 21))&lt;br /&gt;
			self.label_13.setObjectName(&amp;quot;label_13&amp;quot;)&lt;br /&gt;
			self.label_14 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_14.setGeometry(QtCore.QRect(110, 200, 16, 21))&lt;br /&gt;
			self.label_14.setObjectName(&amp;quot;label_14&amp;quot;)&lt;br /&gt;
			self.label_15 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_15.setGeometry(QtCore.QRect(50, 200, 16, 21))&lt;br /&gt;
			self.label_15.setObjectName(&amp;quot;label_15&amp;quot;)&lt;br /&gt;
			self.label_16 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_16.setGeometry(QtCore.QRect(70, 200, 16, 21))&lt;br /&gt;
			self.label_16.setObjectName(&amp;quot;label_16&amp;quot;)&lt;br /&gt;
			self.label_17 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_17.setGeometry(QtCore.QRect(90, 200, 20, 21))&lt;br /&gt;
			self.label_17.setObjectName(&amp;quot;label_17&amp;quot;)&lt;br /&gt;
			self.label_18 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_18.setGeometry(QtCore.QRect(130, 200, 16, 21))&lt;br /&gt;
			self.label_18.setObjectName(&amp;quot;label_18&amp;quot;)&lt;br /&gt;
			self.label_19 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_19.setGeometry(QtCore.QRect(150, 200, 16, 21))&lt;br /&gt;
			self.label_19.setObjectName(&amp;quot;label_19&amp;quot;)&lt;br /&gt;
			self.ls_right_side = QtWidgets.QDoubleSpinBox(self.frame_3)&lt;br /&gt;
			self.ls_right_side.setGeometry(QtCore.QRect(300, 170, 61, 21))&lt;br /&gt;
			self.ls_right_side.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Position of right edge (cm)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.ls_right_side.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.ls_right_side.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.ls_right_side.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_right_side.setReadOnly(True)&lt;br /&gt;
			self.ls_right_side.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.ls_right_side.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ls_right_side.setDecimals(3)&lt;br /&gt;
			self.ls_right_side.setMinimum(-100.0)&lt;br /&gt;
			self.ls_right_side.setMaximum(100.0)&lt;br /&gt;
			self.ls_right_side.setObjectName(&amp;quot;ls_right_side&amp;quot;)&lt;br /&gt;
			self.ls_left_side = QtWidgets.QDoubleSpinBox(self.frame_3)&lt;br /&gt;
			self.ls_left_side.setGeometry(QtCore.QRect(230, 170, 61, 21))&lt;br /&gt;
			self.ls_left_side.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Position of left edge (cm)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.ls_left_side.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.ls_left_side.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.ls_left_side.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_left_side.setReadOnly(True)&lt;br /&gt;
			self.ls_left_side.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.ls_left_side.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ls_left_side.setDecimals(3)&lt;br /&gt;
			self.ls_left_side.setMinimum(-100.0)&lt;br /&gt;
			self.ls_left_side.setMaximum(100.0)&lt;br /&gt;
			self.ls_left_side.setObjectName(&amp;quot;ls_left_side&amp;quot;)&lt;br /&gt;
			self.label_118 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_118.setGeometry(QtCore.QRect(230, 40, 61, 18))&lt;br /&gt;
			self.label_118.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_118.setObjectName(&amp;quot;label_118&amp;quot;)&lt;br /&gt;
			self.ls_left_edge = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.ls_left_edge.setEnabled(True)&lt;br /&gt;
			self.ls_left_edge.setGeometry(QtCore.QRect(240, 60, 41, 101))&lt;br /&gt;
			self.ls_left_edge.setMinimum(-3)&lt;br /&gt;
			self.ls_left_edge.setMaximum(103)&lt;br /&gt;
			self.ls_left_edge.setProperty(&amp;quot;value&amp;quot;, 24)&lt;br /&gt;
			self.ls_left_edge.setTextVisible(False)&lt;br /&gt;
			self.ls_left_edge.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.ls_left_edge.setInvertedAppearance(False)&lt;br /&gt;
			self.ls_left_edge.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.ls_left_edge.setObjectName(&amp;quot;ls_left_edge&amp;quot;)&lt;br /&gt;
			self.ls_right_edge = QtWidgets.QProgressBar(self.frame_3)&lt;br /&gt;
			self.ls_right_edge.setGeometry(QtCore.QRect(310, 60, 41, 101))&lt;br /&gt;
			self.ls_right_edge.setMinimum(-3)&lt;br /&gt;
			self.ls_right_edge.setMaximum(103)&lt;br /&gt;
			self.ls_right_edge.setProperty(&amp;quot;value&amp;quot;, 24)&lt;br /&gt;
			self.ls_right_edge.setTextVisible(False)&lt;br /&gt;
			self.ls_right_edge.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.ls_right_edge.setInvertedAppearance(False)&lt;br /&gt;
			self.ls_right_edge.setObjectName(&amp;quot;ls_right_edge&amp;quot;)&lt;br /&gt;
			self.label_121 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_121.setGeometry(QtCore.QRect(300, 40, 61, 18))&lt;br /&gt;
			self.label_121.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_121.setObjectName(&amp;quot;label_121&amp;quot;)&lt;br /&gt;
			self.label_20 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_20.setGeometry(QtCore.QRect(180, 0, 101, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_20.setFont(font)&lt;br /&gt;
			self.label_20.setObjectName(&amp;quot;label_20&amp;quot;)&lt;br /&gt;
			self.label_27 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_27.setGeometry(QtCore.QRect(290, 20, 48, 20))&lt;br /&gt;
			self.label_27.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_27.setObjectName(&amp;quot;label_27&amp;quot;)&lt;br /&gt;
			self.label_171 = QtWidgets.QLabel(self.frame_3)&lt;br /&gt;
			self.label_171.setGeometry(QtCore.QRect(176, 20, 48, 20))&lt;br /&gt;
			self.label_171.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_171.setObjectName(&amp;quot;label_171&amp;quot;)&lt;br /&gt;
			self.ls_crossing_cnt = QtWidgets.QLineEdit(self.frame_3)&lt;br /&gt;
			self.ls_crossing_cnt.setGeometry(QtCore.QRect(344, 20, 54, 20))&lt;br /&gt;
			self.ls_crossing_cnt.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ls_crossing_cnt.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.ls_crossing_cnt.setObjectName(&amp;quot;ls_crossing_cnt&amp;quot;)&lt;br /&gt;
			self.ls_line_valid_cnt = QtWidgets.QLineEdit(self.frame_3)&lt;br /&gt;
			self.ls_line_valid_cnt.setGeometry(QtCore.QRect(230, 20, 54, 20))&lt;br /&gt;
			self.ls_line_valid_cnt.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.ls_line_valid_cnt.setObjectName(&amp;quot;ls_line_valid_cnt&amp;quot;)&lt;br /&gt;
			self.groupBox_5 = QtWidgets.QGroupBox(self.frame_2)&lt;br /&gt;
			self.groupBox_5.setGeometry(QtCore.QRect(430, 10, 251, 361))&lt;br /&gt;
			self.groupBox_5.setObjectName(&amp;quot;groupBox_5&amp;quot;)&lt;br /&gt;
			self.CalibrateWhite_Cmd = QtWidgets.QPushButton(self.groupBox_5)&lt;br /&gt;
			self.CalibrateWhite_Cmd.setGeometry(QtCore.QRect(20, 310, 101, 31))&lt;br /&gt;
			self.CalibrateWhite_Cmd.setObjectName(&amp;quot;CalibrateWhite_Cmd&amp;quot;)&lt;br /&gt;
			self.label_217 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_217.setGeometry(QtCore.QRect(20, 75, 21, 16))&lt;br /&gt;
			self.label_217.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_217.setObjectName(&amp;quot;label_217&amp;quot;)&lt;br /&gt;
			self.label_218 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_218.setGeometry(QtCore.QRect(20, 117, 21, 16))&lt;br /&gt;
			self.label_218.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_218.setObjectName(&amp;quot;label_218&amp;quot;)&lt;br /&gt;
			self.label_219 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_219.setGeometry(QtCore.QRect(20, 159, 21, 16))&lt;br /&gt;
			self.label_219.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_219.setObjectName(&amp;quot;label_219&amp;quot;)&lt;br /&gt;
			self.label_221 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_221.setGeometry(QtCore.QRect(20, 96, 21, 16))&lt;br /&gt;
			self.label_221.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_221.setObjectName(&amp;quot;label_221&amp;quot;)&lt;br /&gt;
			self.label_223 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_223.setGeometry(QtCore.QRect(20, 138, 21, 16))&lt;br /&gt;
			self.label_223.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_223.setObjectName(&amp;quot;label_223&amp;quot;)&lt;br /&gt;
			self.label_224 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_224.setGeometry(QtCore.QRect(20, 33, 21, 16))&lt;br /&gt;
			self.label_224.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_224.setObjectName(&amp;quot;label_224&amp;quot;)&lt;br /&gt;
			self.label_225 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_225.setGeometry(QtCore.QRect(20, 180, 21, 16))&lt;br /&gt;
			self.label_225.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_225.setObjectName(&amp;quot;label_225&amp;quot;)&lt;br /&gt;
			self.label_226 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_226.setGeometry(QtCore.QRect(20, 54, 21, 16))&lt;br /&gt;
			self.label_226.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_226.setObjectName(&amp;quot;label_226&amp;quot;)&lt;br /&gt;
			self.ls_max_white_7 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_white_7.setGeometry(QtCore.QRect(50, 159, 50, 15))&lt;br /&gt;
			self.ls_max_white_7.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_white_7.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_white_7.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_white_7.setObjectName(&amp;quot;ls_max_white_7&amp;quot;)&lt;br /&gt;
			self.ls_max_white_8 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_white_8.setGeometry(QtCore.QRect(50, 180, 50, 15))&lt;br /&gt;
			self.ls_max_white_8.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_white_8.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_white_8.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_white_8.setObjectName(&amp;quot;ls_max_white_8&amp;quot;)&lt;br /&gt;
			self.ls_max_white_1 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_white_1.setGeometry(QtCore.QRect(50, 33, 50, 15))&lt;br /&gt;
			self.ls_max_white_1.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.ls_max_white_1.setAutoFillBackground(False)&lt;br /&gt;
			self.ls_max_white_1.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_white_1.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_white_1.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_white_1.setObjectName(&amp;quot;ls_max_white_1&amp;quot;)&lt;br /&gt;
			self.ls_max_white_3 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_white_3.setGeometry(QtCore.QRect(50, 75, 50, 15))&lt;br /&gt;
			self.ls_max_white_3.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_white_3.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_white_3.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_white_3.setObjectName(&amp;quot;ls_max_white_3&amp;quot;)&lt;br /&gt;
			self.ls_max_white_5 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_white_5.setGeometry(QtCore.QRect(50, 117, 50, 15))&lt;br /&gt;
			self.ls_max_white_5.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_white_5.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_white_5.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_white_5.setObjectName(&amp;quot;ls_max_white_5&amp;quot;)&lt;br /&gt;
			self.ls_max_white_2 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_white_2.setGeometry(QtCore.QRect(50, 54, 50, 15))&lt;br /&gt;
			self.ls_max_white_2.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_white_2.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_white_2.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_white_2.setObjectName(&amp;quot;ls_max_white_2&amp;quot;)&lt;br /&gt;
			self.ls_max_white_4 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_white_4.setGeometry(QtCore.QRect(50, 96, 50, 15))&lt;br /&gt;
			self.ls_max_white_4.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_white_4.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_white_4.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_white_4.setObjectName(&amp;quot;ls_max_white_4&amp;quot;)&lt;br /&gt;
			self.ls_max_white_6 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_white_6.setGeometry(QtCore.QRect(50, 138, 50, 15))&lt;br /&gt;
			self.ls_max_white_6.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_white_6.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_white_6.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_white_6.setObjectName(&amp;quot;ls_max_white_6&amp;quot;)&lt;br /&gt;
			self.ls_max_black_5 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_black_5.setGeometry(QtCore.QRect(150, 117, 50, 15))&lt;br /&gt;
			self.ls_max_black_5.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_black_5.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_black_5.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_black_5.setObjectName(&amp;quot;ls_max_black_5&amp;quot;)&lt;br /&gt;
			self.ls_max_black_4 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_black_4.setGeometry(QtCore.QRect(150, 96, 50, 15))&lt;br /&gt;
			self.ls_max_black_4.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_black_4.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_black_4.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_black_4.setObjectName(&amp;quot;ls_max_black_4&amp;quot;)&lt;br /&gt;
			self.ls_max_black_8 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_black_8.setGeometry(QtCore.QRect(150, 180, 50, 15))&lt;br /&gt;
			self.ls_max_black_8.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_black_8.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_black_8.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_black_8.setObjectName(&amp;quot;ls_max_black_8&amp;quot;)&lt;br /&gt;
			self.ls_max_black_1 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_black_1.setGeometry(QtCore.QRect(150, 33, 50, 15))&lt;br /&gt;
			self.ls_max_black_1.setMinimumSize(QtCore.QSize(0, 0))&lt;br /&gt;
			self.ls_max_black_1.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_black_1.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_black_1.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_black_1.setObjectName(&amp;quot;ls_max_black_1&amp;quot;)&lt;br /&gt;
			self.ls_max_black_2 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_black_2.setGeometry(QtCore.QRect(150, 54, 50, 15))&lt;br /&gt;
			self.ls_max_black_2.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_black_2.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_black_2.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_black_2.setObjectName(&amp;quot;ls_max_black_2&amp;quot;)&lt;br /&gt;
			self.ls_max_black_3 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_black_3.setGeometry(QtCore.QRect(150, 75, 50, 15))&lt;br /&gt;
			self.ls_max_black_3.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_black_3.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_black_3.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_black_3.setObjectName(&amp;quot;ls_max_black_3&amp;quot;)&lt;br /&gt;
			self.ls_max_black_6 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_black_6.setGeometry(QtCore.QRect(150, 138, 50, 15))&lt;br /&gt;
			self.ls_max_black_6.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_black_6.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_black_6.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_black_6.setObjectName(&amp;quot;ls_max_black_6&amp;quot;)&lt;br /&gt;
			self.ls_max_black_7 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.ls_max_black_7.setGeometry(QtCore.QRect(150, 159, 50, 15))&lt;br /&gt;
			self.ls_max_black_7.setFrameShape(QtWidgets.QFrame.Panel)&lt;br /&gt;
			self.ls_max_black_7.setFrameShadow(QtWidgets.QFrame.Plain)&lt;br /&gt;
			self.ls_max_black_7.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_max_black_7.setObjectName(&amp;quot;ls_max_black_7&amp;quot;)&lt;br /&gt;
			self.label_227 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_227.setGeometry(QtCore.QRect(50, 14, 51, 16))&lt;br /&gt;
			self.label_227.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_227.setObjectName(&amp;quot;label_227&amp;quot;)&lt;br /&gt;
			self.CalibrateBlack_Cmd = QtWidgets.QPushButton(self.groupBox_5)&lt;br /&gt;
			self.CalibrateBlack_Cmd.setGeometry(QtCore.QRect(130, 310, 101, 31))&lt;br /&gt;
			self.CalibrateBlack_Cmd.setObjectName(&amp;quot;CalibrateBlack_Cmd&amp;quot;)&lt;br /&gt;
			self.label_228 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_228.setGeometry(QtCore.QRect(150, 15, 51, 16))&lt;br /&gt;
			self.label_228.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_228.setObjectName(&amp;quot;label_228&amp;quot;)&lt;br /&gt;
			self.ls_swap_left_right = QtWidgets.QCheckBox(self.groupBox_5)&lt;br /&gt;
			self.ls_swap_left_right.setGeometry(QtCore.QRect(140, 220, 81, 20))&lt;br /&gt;
			self.ls_swap_left_right.setObjectName(&amp;quot;ls_swap_left_right&amp;quot;)&lt;br /&gt;
			self.ls_show_normalized = QtWidgets.QCheckBox(self.groupBox_5)&lt;br /&gt;
			self.ls_show_normalized.setGeometry(QtCore.QRect(30, 220, 101, 17))&lt;br /&gt;
			self.ls_show_normalized.setObjectName(&amp;quot;ls_show_normalized&amp;quot;)&lt;br /&gt;
			self.label_23 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_23.setGeometry(QtCore.QRect(130, 240, 75, 21))&lt;br /&gt;
			self.label_23.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_23.setObjectName(&amp;quot;label_23&amp;quot;)&lt;br /&gt;
			self.line_disp_max_value = QtWidgets.QSpinBox(self.groupBox_5)&lt;br /&gt;
			self.line_disp_max_value.setGeometry(QtCore.QRect(30, 260, 75, 20))&lt;br /&gt;
			self.line_disp_max_value.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Maximum value used as 100% in bar display.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.line_disp_max_value.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.line_disp_max_value.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.line_disp_max_value.setMaximum(4250)&lt;br /&gt;
			self.line_disp_max_value.setSingleStep(256)&lt;br /&gt;
			self.line_disp_max_value.setProperty(&amp;quot;value&amp;quot;, 4096)&lt;br /&gt;
			self.line_disp_max_value.setObjectName(&amp;quot;line_disp_max_value&amp;quot;)&lt;br /&gt;
			self.label_156 = QtWidgets.QLabel(self.groupBox_5)&lt;br /&gt;
			self.label_156.setGeometry(QtCore.QRect(34, 240, 61, 20))&lt;br /&gt;
			self.label_156.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.label_156.setObjectName(&amp;quot;label_156&amp;quot;)&lt;br /&gt;
			self.ls_crossing_detect = QtWidgets.QDoubleSpinBox(self.groupBox_5)&lt;br /&gt;
			self.ls_crossing_detect.setGeometry(QtCore.QRect(130, 260, 75, 20))&lt;br /&gt;
			self.ls_crossing_detect.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ls_crossing_detect.setDecimals(1)&lt;br /&gt;
			self.ls_crossing_detect.setMinimum(2.0)&lt;br /&gt;
			self.ls_crossing_detect.setMaximum(8.0)&lt;br /&gt;
			self.ls_crossing_detect.setSingleStep(0.1)&lt;br /&gt;
			self.ls_crossing_detect.setProperty(&amp;quot;value&amp;quot;, 4.5)&lt;br /&gt;
			self.ls_crossing_detect.setObjectName(&amp;quot;ls_crossing_detect&amp;quot;)&lt;br /&gt;
			self.line_2 = QtWidgets.QFrame(self.groupBox_5)&lt;br /&gt;
			self.line_2.setGeometry(QtCore.QRect(20, 200, 211, 21))&lt;br /&gt;
			self.line_2.setFrameShape(QtWidgets.QFrame.HLine)&lt;br /&gt;
			self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)&lt;br /&gt;
			self.line_2.setObjectName(&amp;quot;line_2&amp;quot;)&lt;br /&gt;
			self.line_3 = QtWidgets.QFrame(self.groupBox_5)&lt;br /&gt;
			self.line_3.setGeometry(QtCore.QRect(20, 290, 211, 21))&lt;br /&gt;
			self.line_3.setFrameShape(QtWidgets.QFrame.HLine)&lt;br /&gt;
			self.line_3.setFrameShadow(QtWidgets.QFrame.Sunken)&lt;br /&gt;
			self.line_3.setObjectName(&amp;quot;line_3&amp;quot;)&lt;br /&gt;
			self.ls_power_high = QtWidgets.QCheckBox(self.frame_2)&lt;br /&gt;
			self.ls_power_high.setGeometry(QtCore.QRect(230, 30, 91, 17))&lt;br /&gt;
			self.ls_power_high.setObjectName(&amp;quot;ls_power_high&amp;quot;)&lt;br /&gt;
			self.ls_line_white = QtWidgets.QCheckBox(self.frame_2)&lt;br /&gt;
			self.ls_line_white.setGeometry(QtCore.QRect(140, 30, 81, 17))&lt;br /&gt;
			self.ls_line_white.setObjectName(&amp;quot;ls_line_white&amp;quot;)&lt;br /&gt;
			self.tabWidget.addTab(self.tab_2, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.tab_8 = QtWidgets.QWidget()&lt;br /&gt;
			self.tab_8.setObjectName(&amp;quot;tab_8&amp;quot;)&lt;br /&gt;
			self.frame_37 = QtWidgets.QFrame(self.tab_8)&lt;br /&gt;
			self.frame_37.setGeometry(QtCore.QRect(0, 0, 521, 291))&lt;br /&gt;
			self.frame_37.setMinimumSize(QtCore.QSize(250, 0))&lt;br /&gt;
			self.frame_37.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.frame_37.setFrameShadow(QtWidgets.QFrame.Sunken)&lt;br /&gt;
			self.frame_37.setObjectName(&amp;quot;frame_37&amp;quot;)&lt;br /&gt;
			self.label_56 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_56.setGeometry(QtCore.QRect(10, 10, 104, 16))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setPointSize(10)&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_56.setFont(font)&lt;br /&gt;
			self.label_56.setObjectName(&amp;quot;label_56&amp;quot;)&lt;br /&gt;
			self.ir_bar_1 = QtWidgets.QProgressBar(self.frame_37)&lt;br /&gt;
			self.ir_bar_1.setGeometry(QtCore.QRect(320, 200, 171, 41))&lt;br /&gt;
			self.ir_bar_1.setMaximum(130000)&lt;br /&gt;
			self.ir_bar_1.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.ir_bar_1.setOrientation(QtCore.Qt.Horizontal)&lt;br /&gt;
			self.ir_bar_1.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.ir_bar_1.setFormat(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ir_bar_1.setObjectName(&amp;quot;ir_bar_1&amp;quot;)&lt;br /&gt;
			self.ir_bar_2 = QtWidgets.QProgressBar(self.frame_37)&lt;br /&gt;
			self.ir_bar_2.setGeometry(QtCore.QRect(280, 60, 41, 141))&lt;br /&gt;
			self.ir_bar_2.setMaximum(130000)&lt;br /&gt;
			self.ir_bar_2.setProperty(&amp;quot;value&amp;quot;, 0)&lt;br /&gt;
			self.ir_bar_2.setOrientation(QtCore.Qt.Vertical)&lt;br /&gt;
			self.ir_bar_2.setTextDirection(QtWidgets.QProgressBar.TopToBottom)&lt;br /&gt;
			self.ir_bar_2.setFormat(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ir_bar_2.setObjectName(&amp;quot;ir_bar_2&amp;quot;)&lt;br /&gt;
			self.ir_d1_meters = QtWidgets.QLineEdit(self.frame_37)&lt;br /&gt;
			self.ir_d1_meters.setGeometry(QtCore.QRect(400, 170, 81, 20))&lt;br /&gt;
			self.ir_d1_meters.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.ir_d1_meters.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.ir_d1_meters.setObjectName(&amp;quot;ir_d1_meters&amp;quot;)&lt;br /&gt;
			self.label_279 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_279.setGeometry(QtCore.QRect(360, 170, 41, 20))&lt;br /&gt;
			self.label_279.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
			self.label_279.setObjectName(&amp;quot;label_279&amp;quot;)&lt;br /&gt;
			self.label_281 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_281.setGeometry(QtCore.QRect(330, 140, 71, 18))&lt;br /&gt;
			self.label_281.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
			self.label_281.setObjectName(&amp;quot;label_281&amp;quot;)&lt;br /&gt;
			self.ir_d1_raw = QtWidgets.QDoubleSpinBox(self.frame_37)&lt;br /&gt;
			self.ir_d1_raw.setGeometry(QtCore.QRect(400, 140, 81, 20))&lt;br /&gt;
			self.ir_d1_raw.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Raw measured value 2Y0A21F sensor from A/D converter&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.ir_d1_raw.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.ir_d1_raw.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.ir_d1_raw.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ir_d1_raw.setReadOnly(True)&lt;br /&gt;
			self.ir_d1_raw.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.ir_d1_raw.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ir_d1_raw.setDecimals(0)&lt;br /&gt;
			self.ir_d1_raw.setMinimum(0.0)&lt;br /&gt;
			self.ir_d1_raw.setMaximum(1000000.0)&lt;br /&gt;
			self.ir_d1_raw.setObjectName(&amp;quot;ir_d1_raw&amp;quot;)&lt;br /&gt;
			self.label_287 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_287.setGeometry(QtCore.QRect(490, 170, 16, 16))&lt;br /&gt;
			self.label_287.setObjectName(&amp;quot;label_287&amp;quot;)&lt;br /&gt;
			self.ir_d2_meters = QtWidgets.QLineEdit(self.frame_37)&lt;br /&gt;
			self.ir_d2_meters.setGeometry(QtCore.QRect(402, 90, 81, 20))&lt;br /&gt;
			self.ir_d2_meters.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.ir_d2_meters.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.ir_d2_meters.setObjectName(&amp;quot;ir_d2_meters&amp;quot;)&lt;br /&gt;
			self.label_280 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_280.setGeometry(QtCore.QRect(360, 90, 51, 20))&lt;br /&gt;
			sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)&lt;br /&gt;
			sizePolicy.setHorizontalStretch(0)&lt;br /&gt;
			sizePolicy.setVerticalStretch(0)&lt;br /&gt;
			sizePolicy.setHeightForWidth(self.label_280.sizePolicy().hasHeightForWidth())&lt;br /&gt;
			self.label_280.setSizePolicy(sizePolicy)&lt;br /&gt;
			self.label_280.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
			self.label_280.setObjectName(&amp;quot;label_280&amp;quot;)&lt;br /&gt;
			self.ir_d2_raw = QtWidgets.QDoubleSpinBox(self.frame_37)&lt;br /&gt;
			self.ir_d2_raw.setGeometry(QtCore.QRect(400, 60, 81, 20))&lt;br /&gt;
			self.ir_d2_raw.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Raw measured value 2Y0A21F sensor from A/D converter&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.ir_d2_raw.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.ir_d2_raw.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.ir_d2_raw.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ir_d2_raw.setReadOnly(True)&lt;br /&gt;
			self.ir_d2_raw.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.ir_d2_raw.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ir_d2_raw.setDecimals(0)&lt;br /&gt;
			self.ir_d2_raw.setMinimum(0.0)&lt;br /&gt;
			self.ir_d2_raw.setMaximum(1000000.0)&lt;br /&gt;
			self.ir_d2_raw.setObjectName(&amp;quot;ir_d2_raw&amp;quot;)&lt;br /&gt;
			self.label_282 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_282.setGeometry(QtCore.QRect(330, 60, 71, 18))&lt;br /&gt;
			self.label_282.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
			self.label_282.setObjectName(&amp;quot;label_282&amp;quot;)&lt;br /&gt;
			self.label_288 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_288.setGeometry(QtCore.QRect(490, 90, 16, 16))&lt;br /&gt;
			self.label_288.setObjectName(&amp;quot;label_288&amp;quot;)&lt;br /&gt;
			self.ir_d2_80cm = QtWidgets.QDoubleSpinBox(self.frame_37)&lt;br /&gt;
			self.ir_d2_80cm.setGeometry(QtCore.QRect(170, 200, 85, 20))&lt;br /&gt;
			self.ir_d2_80cm.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Raw value at 50 cm from base of sensor (about 24000)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.ir_d2_80cm.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.ir_d2_80cm.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ir_d2_80cm.setReadOnly(False)&lt;br /&gt;
			self.ir_d2_80cm.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)&lt;br /&gt;
			self.ir_d2_80cm.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ir_d2_80cm.setDecimals(0)&lt;br /&gt;
			self.ir_d2_80cm.setMinimum(0.0)&lt;br /&gt;
			self.ir_d2_80cm.setMaximum(128000.0)&lt;br /&gt;
			self.ir_d2_80cm.setProperty(&amp;quot;value&amp;quot;, 24100.0)&lt;br /&gt;
			self.ir_d2_80cm.setObjectName(&amp;quot;ir_d2_80cm&amp;quot;)&lt;br /&gt;
			self.ir_d1_80cm = QtWidgets.QDoubleSpinBox(self.frame_37)&lt;br /&gt;
			self.ir_d1_80cm.setGeometry(QtCore.QRect(170, 148, 85, 20))&lt;br /&gt;
			self.ir_d1_80cm.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Raw value at 50cm from base of sensor (about 24000)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.ir_d1_80cm.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.ir_d1_80cm.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ir_d1_80cm.setReadOnly(False)&lt;br /&gt;
			self.ir_d1_80cm.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)&lt;br /&gt;
			self.ir_d1_80cm.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ir_d1_80cm.setDecimals(0)&lt;br /&gt;
			self.ir_d1_80cm.setMinimum(0.0)&lt;br /&gt;
			self.ir_d1_80cm.setMaximum(128000.0)&lt;br /&gt;
			self.ir_d1_80cm.setProperty(&amp;quot;value&amp;quot;, 24100.0)&lt;br /&gt;
			self.ir_d1_80cm.setObjectName(&amp;quot;ir_d1_80cm&amp;quot;)&lt;br /&gt;
			self.ir_d2_20cm = QtWidgets.QDoubleSpinBox(self.frame_37)&lt;br /&gt;
			self.ir_d2_20cm.setGeometry(QtCore.QRect(170, 174, 85, 20))&lt;br /&gt;
			self.ir_d2_20cm.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Raw value at 13 cm from base of sensor (about 80000)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.ir_d2_20cm.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.ir_d2_20cm.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ir_d2_20cm.setReadOnly(False)&lt;br /&gt;
			self.ir_d2_20cm.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)&lt;br /&gt;
			self.ir_d2_20cm.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ir_d2_20cm.setDecimals(0)&lt;br /&gt;
			self.ir_d2_20cm.setMinimum(0.0)&lt;br /&gt;
			self.ir_d2_20cm.setMaximum(128000.0)&lt;br /&gt;
			self.ir_d2_20cm.setProperty(&amp;quot;value&amp;quot;, 72300.0)&lt;br /&gt;
			self.ir_d2_20cm.setObjectName(&amp;quot;ir_d2_20cm&amp;quot;)&lt;br /&gt;
			self.label_284 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_284.setGeometry(QtCore.QRect(32, 148, 132, 20))&lt;br /&gt;
			self.label_284.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
			self.label_284.setObjectName(&amp;quot;label_284&amp;quot;)&lt;br /&gt;
			self.label_286 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_286.setGeometry(QtCore.QRect(32, 174, 132, 20))&lt;br /&gt;
			self.label_286.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
			self.label_286.setObjectName(&amp;quot;label_286&amp;quot;)&lt;br /&gt;
			self.label_285 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_285.setGeometry(QtCore.QRect(32, 200, 132, 20))&lt;br /&gt;
			self.label_285.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
			self.label_285.setObjectName(&amp;quot;label_285&amp;quot;)&lt;br /&gt;
			self.label_283 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_283.setGeometry(QtCore.QRect(32, 122, 132, 20))&lt;br /&gt;
			self.label_283.setMaximumSize(QtCore.QSize(16777215, 20))&lt;br /&gt;
			self.label_283.setObjectName(&amp;quot;label_283&amp;quot;)&lt;br /&gt;
			self.ir_d1_20cm = QtWidgets.QDoubleSpinBox(self.frame_37)&lt;br /&gt;
			self.ir_d1_20cm.setGeometry(QtCore.QRect(170, 122, 85, 20))&lt;br /&gt;
			self.ir_d1_20cm.setToolTip(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Raw value at 13 cm from base of sensor (about 80000)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;
			self.ir_d1_20cm.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))&lt;br /&gt;
			self.ir_d1_20cm.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.ir_d1_20cm.setReadOnly(False)&lt;br /&gt;
			self.ir_d1_20cm.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)&lt;br /&gt;
			self.ir_d1_20cm.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.ir_d1_20cm.setDecimals(0)&lt;br /&gt;
			self.ir_d1_20cm.setMinimum(0.0)&lt;br /&gt;
			self.ir_d1_20cm.setMaximum(128000.0)&lt;br /&gt;
			self.ir_d1_20cm.setProperty(&amp;quot;value&amp;quot;, 72000.0)&lt;br /&gt;
			self.ir_d1_20cm.setObjectName(&amp;quot;ir_d1_20cm&amp;quot;)&lt;br /&gt;
			self.checkBox_ir_use = QtWidgets.QCheckBox(self.frame_37)&lt;br /&gt;
			self.checkBox_ir_use.setGeometry(QtCore.QRect(30, 70, 132, 17))&lt;br /&gt;
			self.checkBox_ir_use.setObjectName(&amp;quot;checkBox_ir_use&amp;quot;)&lt;br /&gt;
			self.checkBox_ir_installed = QtWidgets.QCheckBox(self.frame_37)&lt;br /&gt;
			self.checkBox_ir_installed.setGeometry(QtCore.QRect(30, 44, 111, 17))&lt;br /&gt;
			self.checkBox_ir_installed.setObjectName(&amp;quot;checkBox_ir_installed&amp;quot;)&lt;br /&gt;
			self.label_277 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_277.setGeometry(QtCore.QRect(30, 100, 132, 13))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_277.setFont(font)&lt;br /&gt;
			self.label_277.setObjectName(&amp;quot;label_277&amp;quot;)&lt;br /&gt;
			self.ir_apply = QtWidgets.QPushButton(self.frame_37)&lt;br /&gt;
			self.ir_apply.setGeometry(QtCore.QRect(170, 230, 85, 23))&lt;br /&gt;
			self.ir_apply.setObjectName(&amp;quot;ir_apply&amp;quot;)&lt;br /&gt;
			self.label_278 = QtWidgets.QLabel(self.frame_37)&lt;br /&gt;
			self.label_278.setGeometry(QtCore.QRect(310, 30, 171, 16))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_278.setFont(font)&lt;br /&gt;
			self.label_278.setObjectName(&amp;quot;label_278&amp;quot;)&lt;br /&gt;
			self.ir_edit = QtWidgets.QPushButton(self.frame_37)&lt;br /&gt;
			self.ir_edit.setGeometry(QtCore.QRect(30, 230, 132, 23))&lt;br /&gt;
			self.ir_edit.setObjectName(&amp;quot;ir_edit&amp;quot;)&lt;br /&gt;
			self.ir_cancel = QtWidgets.QPushButton(self.frame_37)&lt;br /&gt;
			self.ir_cancel.setGeometry(QtCore.QRect(260, 230, 51, 23))&lt;br /&gt;
			self.ir_cancel.setObjectName(&amp;quot;ir_cancel&amp;quot;)&lt;br /&gt;
			self.tabWidget.addTab(self.tab_8, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.tab_3 = QtWidgets.QWidget()&lt;br /&gt;
			self.tab_3.setObjectName(&amp;quot;tab_3&amp;quot;)&lt;br /&gt;
			self.frame_4 = QtWidgets.QFrame(self.tab_3)&lt;br /&gt;
			self.frame_4.setGeometry(QtCore.QRect(0, 0, 711, 691))&lt;br /&gt;
			self.frame_4.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.frame_4.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.frame_4.setObjectName(&amp;quot;frame_4&amp;quot;)&lt;br /&gt;
			self.EnableCamera_checkBox = QtWidgets.QCheckBox(self.frame_4)&lt;br /&gt;
			self.EnableCamera_checkBox.setGeometry(QtCore.QRect(20, 20, 121, 17))&lt;br /&gt;
			self.EnableCamera_checkBox.setObjectName(&amp;quot;EnableCamera_checkBox&amp;quot;)&lt;br /&gt;
			self.frame_5 = QtWidgets.QFrame(self.frame_4)&lt;br /&gt;
			self.frame_5.setGeometry(QtCore.QRect(10, 430, 461, 211))&lt;br /&gt;
			self.frame_5.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.frame_5.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.frame_5.setObjectName(&amp;quot;frame_5&amp;quot;)&lt;br /&gt;
			self.Camera_Record_Cmd = QtWidgets.QPushButton(self.frame_5)&lt;br /&gt;
			self.Camera_Record_Cmd.setGeometry(QtCore.QRect(240, 10, 91, 23))&lt;br /&gt;
			self.Camera_Record_Cmd.setObjectName(&amp;quot;Camera_Record_Cmd&amp;quot;)&lt;br /&gt;
			self.Camera_StopRecord_Cmd = QtWidgets.QPushButton(self.frame_5)&lt;br /&gt;
			self.Camera_StopRecord_Cmd.setGeometry(QtCore.QRect(340, 10, 91, 23))&lt;br /&gt;
			self.Camera_StopRecord_Cmd.setObjectName(&amp;quot;Camera_StopRecord_Cmd&amp;quot;)&lt;br /&gt;
			self.Camera_Snapshot_Cmd = QtWidgets.QPushButton(self.frame_5)&lt;br /&gt;
			self.Camera_Snapshot_Cmd.setGeometry(QtCore.QRect(10, 10, 111, 23))&lt;br /&gt;
			self.Camera_Snapshot_Cmd.setObjectName(&amp;quot;Camera_Snapshot_Cmd&amp;quot;)&lt;br /&gt;
			self.Camera_SnapFilename = QtWidgets.QLineEdit(self.frame_5)&lt;br /&gt;
			self.Camera_SnapFilename.setGeometry(QtCore.QRect(240, 70, 191, 20))&lt;br /&gt;
			self.Camera_SnapFilename.setObjectName(&amp;quot;Camera_SnapFilename&amp;quot;)&lt;br /&gt;
			self.label_55 = QtWidgets.QLabel(self.frame_5)&lt;br /&gt;
			self.label_55.setGeometry(QtCore.QRect(240, 50, 121, 20))&lt;br /&gt;
			self.label_55.setObjectName(&amp;quot;label_55&amp;quot;)&lt;br /&gt;
			self.Camera_SnapSave_Cmd = QtWidgets.QPushButton(self.frame_5)&lt;br /&gt;
			self.Camera_SnapSave_Cmd.setGeometry(QtCore.QRect(340, 100, 91, 23))&lt;br /&gt;
			self.Camera_SnapSave_Cmd.setObjectName(&amp;quot;Camera_SnapSave_Cmd&amp;quot;)&lt;br /&gt;
			self.Snapshot_Label = QtWidgets.QLabel(self.frame_5)&lt;br /&gt;
			self.Snapshot_Label.setGeometry(QtCore.QRect(10, 40, 221, 161))&lt;br /&gt;
			self.Snapshot_Label.setLayoutDirection(QtCore.Qt.LeftToRight)&lt;br /&gt;
			self.Snapshot_Label.setAutoFillBackground(True)&lt;br /&gt;
			self.Snapshot_Label.setFrameShape(QtWidgets.QFrame.Box)&lt;br /&gt;
			self.Snapshot_Label.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.Snapshot_Label.setLineWidth(-2)&lt;br /&gt;
			self.Snapshot_Label.setText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.Snapshot_Label.setScaledContents(True)&lt;br /&gt;
			self.Snapshot_Label.setIndent(0)&lt;br /&gt;
			self.Snapshot_Label.setObjectName(&amp;quot;Snapshot_Label&amp;quot;)&lt;br /&gt;
			self.VideoRecording_Label = QtWidgets.QLabel(self.frame_5)&lt;br /&gt;
			self.VideoRecording_Label.setGeometry(QtCore.QRect(240, 30, 71, 16))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.VideoRecording_Label.setFont(font)&lt;br /&gt;
			self.VideoRecording_Label.setObjectName(&amp;quot;VideoRecording_Label&amp;quot;)&lt;br /&gt;
			self.Camera_ClearSnap_Cmd = QtWidgets.QPushButton(self.frame_5)&lt;br /&gt;
			self.Camera_ClearSnap_Cmd.setGeometry(QtCore.QRect(130, 10, 91, 23))&lt;br /&gt;
			self.Camera_ClearSnap_Cmd.setObjectName(&amp;quot;Camera_ClearSnap_Cmd&amp;quot;)&lt;br /&gt;
			self.frame_6 = QtWidgets.QFrame(self.frame_4)&lt;br /&gt;
			self.frame_6.setGeometry(QtCore.QRect(10, 40, 561, 381))&lt;br /&gt;
			self.frame_6.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.frame_6.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.frame_6.setObjectName(&amp;quot;frame_6&amp;quot;)&lt;br /&gt;
			self.Stream_Label = QtWidgets.QLabel(self.frame_6)&lt;br /&gt;
			self.Stream_Label.setGeometry(QtCore.QRect(10, 0, 541, 371))&lt;br /&gt;
			self.Stream_Label.setText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.Stream_Label.setScaledContents(True)&lt;br /&gt;
			self.Stream_Label.setObjectName(&amp;quot;Stream_Label&amp;quot;)&lt;br /&gt;
			self.tabWidget.addTab(self.tab_3, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.tab_4 = QtWidgets.QWidget()&lt;br /&gt;
			self.tab_4.setObjectName(&amp;quot;tab_4&amp;quot;)&lt;br /&gt;
			self.frame_7 = QtWidgets.QFrame(self.tab_4)&lt;br /&gt;
			self.frame_7.setGeometry(QtCore.QRect(0, 0, 541, 631))&lt;br /&gt;
			self.frame_7.setFrameShape(QtWidgets.QFrame.StyledPanel)&lt;br /&gt;
			self.frame_7.setFrameShadow(QtWidgets.QFrame.Raised)&lt;br /&gt;
			self.frame_7.setObjectName(&amp;quot;frame_7&amp;quot;)&lt;br /&gt;
			self.label_30 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_30.setGeometry(QtCore.QRect(10, 0, 151, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setPointSize(10)&lt;br /&gt;
			font.setBold(True)&lt;br /&gt;
			font.setWeight(75)&lt;br /&gt;
			self.label_30.setFont(font)&lt;br /&gt;
			self.label_30.setObjectName(&amp;quot;label_30&amp;quot;)&lt;br /&gt;
			self.enableServo1_checkBox = QtWidgets.QCheckBox(self.frame_7)&lt;br /&gt;
			self.enableServo1_checkBox.setGeometry(QtCore.QRect(10, 30, 111, 17))&lt;br /&gt;
			self.enableServo1_checkBox.setObjectName(&amp;quot;enableServo1_checkBox&amp;quot;)&lt;br /&gt;
			self.enableServo2_checkBox = QtWidgets.QCheckBox(self.frame_7)&lt;br /&gt;
			self.enableServo2_checkBox.setGeometry(QtCore.QRect(10, 150, 111, 17))&lt;br /&gt;
			self.enableServo2_checkBox.setObjectName(&amp;quot;enableServo2_checkBox&amp;quot;)&lt;br /&gt;
			self.enableServo3_checkBox = QtWidgets.QCheckBox(self.frame_7)&lt;br /&gt;
			self.enableServo3_checkBox.setGeometry(QtCore.QRect(10, 270, 111, 17))&lt;br /&gt;
			self.enableServo3_checkBox.setObjectName(&amp;quot;enableServo3_checkBox&amp;quot;)&lt;br /&gt;
			self.Servo1_Pos_dial = QtWidgets.QDial(self.frame_7)&lt;br /&gt;
			self.Servo1_Pos_dial.setGeometry(QtCore.QRect(210, 50, 81, 81))&lt;br /&gt;
			self.Servo1_Pos_dial.setMinimum(-920)&lt;br /&gt;
			self.Servo1_Pos_dial.setMaximum(920)&lt;br /&gt;
			self.Servo1_Pos_dial.setTracking(False)&lt;br /&gt;
			self.Servo1_Pos_dial.setObjectName(&amp;quot;Servo1_Pos_dial&amp;quot;)&lt;br /&gt;
			self.label_31 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_31.setGeometry(QtCore.QRect(10, 50, 81, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_31.setFont(font)&lt;br /&gt;
			self.label_31.setObjectName(&amp;quot;label_31&amp;quot;)&lt;br /&gt;
			self.servo1_current_pos = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.servo1_current_pos.setGeometry(QtCore.QRect(100, 50, 91, 20))&lt;br /&gt;
			self.servo1_current_pos.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.servo1_current_pos.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.servo1_current_pos.setObjectName(&amp;quot;servo1_current_pos&amp;quot;)&lt;br /&gt;
			self.label_33 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_33.setGeometry(QtCore.QRect(10, 80, 91, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_33.setFont(font)&lt;br /&gt;
			self.label_33.setObjectName(&amp;quot;label_33&amp;quot;)&lt;br /&gt;
			self.manualServoPos1_Val = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.manualServoPos1_Val.setGeometry(QtCore.QRect(100, 80, 91, 20))&lt;br /&gt;
			self.manualServoPos1_Val.setObjectName(&amp;quot;manualServoPos1_Val&amp;quot;)&lt;br /&gt;
			self.manualServoPos1_Cmd = QtWidgets.QPushButton(self.frame_7)&lt;br /&gt;
			self.manualServoPos1_Cmd.setGeometry(QtCore.QRect(100, 110, 91, 23))&lt;br /&gt;
			self.manualServoPos1_Cmd.setObjectName(&amp;quot;manualServoPos1_Cmd&amp;quot;)&lt;br /&gt;
			self.label_39 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_39.setGeometry(QtCore.QRect(220, 30, 61, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_39.setFont(font)&lt;br /&gt;
			self.label_39.setObjectName(&amp;quot;label_39&amp;quot;)&lt;br /&gt;
			self.Servo2_Pos_dial = QtWidgets.QDial(self.frame_7)&lt;br /&gt;
			self.Servo2_Pos_dial.setGeometry(QtCore.QRect(210, 170, 81, 81))&lt;br /&gt;
			self.Servo2_Pos_dial.setMinimum(-920)&lt;br /&gt;
			self.Servo2_Pos_dial.setMaximum(920)&lt;br /&gt;
			self.Servo2_Pos_dial.setTracking(False)&lt;br /&gt;
			self.Servo2_Pos_dial.setObjectName(&amp;quot;Servo2_Pos_dial&amp;quot;)&lt;br /&gt;
			self.label_40 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_40.setGeometry(QtCore.QRect(220, 150, 61, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_40.setFont(font)&lt;br /&gt;
			self.label_40.setObjectName(&amp;quot;label_40&amp;quot;)&lt;br /&gt;
			self.manualServoPos2_Val = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.manualServoPos2_Val.setGeometry(QtCore.QRect(100, 200, 91, 20))&lt;br /&gt;
			self.manualServoPos2_Val.setObjectName(&amp;quot;manualServoPos2_Val&amp;quot;)&lt;br /&gt;
			self.servo2_current_pos = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.servo2_current_pos.setGeometry(QtCore.QRect(100, 170, 91, 20))&lt;br /&gt;
			self.servo2_current_pos.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.servo2_current_pos.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.servo2_current_pos.setObjectName(&amp;quot;servo2_current_pos&amp;quot;)&lt;br /&gt;
			self.label_41 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_41.setGeometry(QtCore.QRect(10, 200, 91, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_41.setFont(font)&lt;br /&gt;
			self.label_41.setObjectName(&amp;quot;label_41&amp;quot;)&lt;br /&gt;
			self.manualServoPos2_Cmd = QtWidgets.QPushButton(self.frame_7)&lt;br /&gt;
			self.manualServoPos2_Cmd.setGeometry(QtCore.QRect(100, 230, 91, 23))&lt;br /&gt;
			self.manualServoPos2_Cmd.setObjectName(&amp;quot;manualServoPos2_Cmd&amp;quot;)&lt;br /&gt;
			self.label_43 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_43.setGeometry(QtCore.QRect(10, 170, 81, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_43.setFont(font)&lt;br /&gt;
			self.label_43.setObjectName(&amp;quot;label_43&amp;quot;)&lt;br /&gt;
			self.Servo3_Pos_dial = QtWidgets.QDial(self.frame_7)&lt;br /&gt;
			self.Servo3_Pos_dial.setGeometry(QtCore.QRect(210, 290, 81, 81))&lt;br /&gt;
			self.Servo3_Pos_dial.setMinimum(-920)&lt;br /&gt;
			self.Servo3_Pos_dial.setMaximum(920)&lt;br /&gt;
			self.Servo3_Pos_dial.setTracking(False)&lt;br /&gt;
			self.Servo3_Pos_dial.setObjectName(&amp;quot;Servo3_Pos_dial&amp;quot;)&lt;br /&gt;
			self.label_46 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_46.setGeometry(QtCore.QRect(220, 270, 61, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_46.setFont(font)&lt;br /&gt;
			self.label_46.setObjectName(&amp;quot;label_46&amp;quot;)&lt;br /&gt;
			self.manualServoPos3_Val = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.manualServoPos3_Val.setGeometry(QtCore.QRect(100, 320, 91, 20))&lt;br /&gt;
			self.manualServoPos3_Val.setObjectName(&amp;quot;manualServoPos3_Val&amp;quot;)&lt;br /&gt;
			self.servo3_current_pos = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.servo3_current_pos.setGeometry(QtCore.QRect(100, 290, 91, 20))&lt;br /&gt;
			self.servo3_current_pos.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.servo3_current_pos.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.servo3_current_pos.setObjectName(&amp;quot;servo3_current_pos&amp;quot;)&lt;br /&gt;
			self.label_47 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_47.setGeometry(QtCore.QRect(10, 320, 91, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_47.setFont(font)&lt;br /&gt;
			self.label_47.setObjectName(&amp;quot;label_47&amp;quot;)&lt;br /&gt;
			self.manualServoPos3_Cmd = QtWidgets.QPushButton(self.frame_7)&lt;br /&gt;
			self.manualServoPos3_Cmd.setGeometry(QtCore.QRect(100, 350, 91, 23))&lt;br /&gt;
			self.manualServoPos3_Cmd.setObjectName(&amp;quot;manualServoPos3_Cmd&amp;quot;)&lt;br /&gt;
			self.label_48 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_48.setGeometry(QtCore.QRect(10, 290, 81, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_48.setFont(font)&lt;br /&gt;
			self.label_48.setObjectName(&amp;quot;label_48&amp;quot;)&lt;br /&gt;
			self.enableServo4_checkBox = QtWidgets.QCheckBox(self.frame_7)&lt;br /&gt;
			self.enableServo4_checkBox.setGeometry(QtCore.QRect(10, 390, 111, 17))&lt;br /&gt;
			self.enableServo4_checkBox.setObjectName(&amp;quot;enableServo4_checkBox&amp;quot;)&lt;br /&gt;
			self.Servo4_Pos_dial = QtWidgets.QDial(self.frame_7)&lt;br /&gt;
			self.Servo4_Pos_dial.setGeometry(QtCore.QRect(210, 410, 81, 81))&lt;br /&gt;
			self.Servo4_Pos_dial.setMinimum(-920)&lt;br /&gt;
			self.Servo4_Pos_dial.setMaximum(920)&lt;br /&gt;
			self.Servo4_Pos_dial.setTracking(False)&lt;br /&gt;
			self.Servo4_Pos_dial.setObjectName(&amp;quot;Servo4_Pos_dial&amp;quot;)&lt;br /&gt;
			self.label_49 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_49.setGeometry(QtCore.QRect(220, 390, 61, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_49.setFont(font)&lt;br /&gt;
			self.label_49.setObjectName(&amp;quot;label_49&amp;quot;)&lt;br /&gt;
			self.manualServoPos4_Val = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.manualServoPos4_Val.setGeometry(QtCore.QRect(100, 440, 91, 20))&lt;br /&gt;
			self.manualServoPos4_Val.setObjectName(&amp;quot;manualServoPos4_Val&amp;quot;)&lt;br /&gt;
			self.servo4_current_pos = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.servo4_current_pos.setGeometry(QtCore.QRect(100, 410, 91, 20))&lt;br /&gt;
			self.servo4_current_pos.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.servo4_current_pos.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.servo4_current_pos.setObjectName(&amp;quot;servo4_current_pos&amp;quot;)&lt;br /&gt;
			self.label_50 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_50.setGeometry(QtCore.QRect(10, 440, 91, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_50.setFont(font)&lt;br /&gt;
			self.label_50.setObjectName(&amp;quot;label_50&amp;quot;)&lt;br /&gt;
			self.manualServoPos4_Cmd = QtWidgets.QPushButton(self.frame_7)&lt;br /&gt;
			self.manualServoPos4_Cmd.setGeometry(QtCore.QRect(100, 470, 91, 23))&lt;br /&gt;
			self.manualServoPos4_Cmd.setObjectName(&amp;quot;manualServoPos4_Cmd&amp;quot;)&lt;br /&gt;
			self.label_51 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_51.setGeometry(QtCore.QRect(10, 410, 81, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_51.setFont(font)&lt;br /&gt;
			self.label_51.setObjectName(&amp;quot;label_51&amp;quot;)&lt;br /&gt;
			self.enableServo5_checkBox = QtWidgets.QCheckBox(self.frame_7)&lt;br /&gt;
			self.enableServo5_checkBox.setGeometry(QtCore.QRect(10, 510, 111, 17))&lt;br /&gt;
			self.enableServo5_checkBox.setObjectName(&amp;quot;enableServo5_checkBox&amp;quot;)&lt;br /&gt;
			self.Servo5_Pos_dial = QtWidgets.QDial(self.frame_7)&lt;br /&gt;
			self.Servo5_Pos_dial.setGeometry(QtCore.QRect(210, 530, 81, 81))&lt;br /&gt;
			self.Servo5_Pos_dial.setMinimum(-920)&lt;br /&gt;
			self.Servo5_Pos_dial.setMaximum(920)&lt;br /&gt;
			self.Servo5_Pos_dial.setTracking(False)&lt;br /&gt;
			self.Servo5_Pos_dial.setObjectName(&amp;quot;Servo5_Pos_dial&amp;quot;)&lt;br /&gt;
			self.label_52 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_52.setGeometry(QtCore.QRect(220, 510, 61, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_52.setFont(font)&lt;br /&gt;
			self.label_52.setObjectName(&amp;quot;label_52&amp;quot;)&lt;br /&gt;
			self.manualServoPos5_Val = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.manualServoPos5_Val.setGeometry(QtCore.QRect(100, 560, 91, 20))&lt;br /&gt;
			self.manualServoPos5_Val.setObjectName(&amp;quot;manualServoPos5_Val&amp;quot;)&lt;br /&gt;
			self.servo5_current_pos = QtWidgets.QLineEdit(self.frame_7)&lt;br /&gt;
			self.servo5_current_pos.setGeometry(QtCore.QRect(100, 530, 91, 20))&lt;br /&gt;
			self.servo5_current_pos.setStatusTip(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.servo5_current_pos.setStyleSheet(&amp;quot;background-color: rgb(230,230,230);&amp;quot;)&lt;br /&gt;
			self.servo5_current_pos.setObjectName(&amp;quot;servo5_current_pos&amp;quot;)&lt;br /&gt;
			self.label_53 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_53.setGeometry(QtCore.QRect(10, 560, 91, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_53.setFont(font)&lt;br /&gt;
			self.label_53.setObjectName(&amp;quot;label_53&amp;quot;)&lt;br /&gt;
			self.manualServoPos5_Cmd = QtWidgets.QPushButton(self.frame_7)&lt;br /&gt;
			self.manualServoPos5_Cmd.setGeometry(QtCore.QRect(100, 590, 91, 23))&lt;br /&gt;
			self.manualServoPos5_Cmd.setObjectName(&amp;quot;manualServoPos5_Cmd&amp;quot;)&lt;br /&gt;
			self.label_54 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_54.setGeometry(QtCore.QRect(10, 530, 81, 21))&lt;br /&gt;
			font = QtGui.QFont()&lt;br /&gt;
			font.setBold(False)&lt;br /&gt;
			font.setWeight(50)&lt;br /&gt;
			self.label_54.setFont(font)&lt;br /&gt;
			self.label_54.setObjectName(&amp;quot;label_54&amp;quot;)&lt;br /&gt;
			self.Servo1Steering_checkBox = QtWidgets.QCheckBox(self.frame_7)&lt;br /&gt;
			self.Servo1Steering_checkBox.setGeometry(QtCore.QRect(110, 30, 111, 17))&lt;br /&gt;
			self.Servo1Steering_checkBox.setObjectName(&amp;quot;Servo1Steering_checkBox&amp;quot;)&lt;br /&gt;
			self.val_servo1_offset = QtWidgets.QDoubleSpinBox(self.frame_7)&lt;br /&gt;
			self.val_servo1_offset.setGeometry(QtCore.QRect(300, 50, 81, 18))&lt;br /&gt;
			self.val_servo1_offset.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.val_servo1_offset.setReadOnly(False)&lt;br /&gt;
			self.val_servo1_offset.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)&lt;br /&gt;
			self.val_servo1_offset.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.val_servo1_offset.setDecimals(0)&lt;br /&gt;
			self.val_servo1_offset.setMinimum(-200.0)&lt;br /&gt;
			self.val_servo1_offset.setMaximum(200.0)&lt;br /&gt;
			self.val_servo1_offset.setSingleStep(0.01)&lt;br /&gt;
			self.val_servo1_offset.setProperty(&amp;quot;value&amp;quot;, 10.0)&lt;br /&gt;
			self.val_servo1_offset.setObjectName(&amp;quot;val_servo1_offset&amp;quot;)&lt;br /&gt;
			self.label_servo_6 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_servo_6.setGeometry(QtCore.QRect(310, 70, 75, 13))&lt;br /&gt;
			self.label_servo_6.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_servo_6.setObjectName(&amp;quot;label_servo_6&amp;quot;)&lt;br /&gt;
			self.val_servo1_scale = QtWidgets.QDoubleSpinBox(self.frame_7)&lt;br /&gt;
			self.val_servo1_scale.setGeometry(QtCore.QRect(300, 130, 81, 18))&lt;br /&gt;
			self.val_servo1_scale.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.val_servo1_scale.setReadOnly(False)&lt;br /&gt;
			self.val_servo1_scale.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)&lt;br /&gt;
			self.val_servo1_scale.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.val_servo1_scale.setDecimals(1)&lt;br /&gt;
			self.val_servo1_scale.setMinimum(-180.0)&lt;br /&gt;
			self.val_servo1_scale.setMaximum(180.0)&lt;br /&gt;
			self.val_servo1_scale.setSingleStep(1.0)&lt;br /&gt;
			self.val_servo1_scale.setProperty(&amp;quot;value&amp;quot;, 160.0)&lt;br /&gt;
			self.val_servo1_scale.setObjectName(&amp;quot;val_servo1_scale&amp;quot;)&lt;br /&gt;
			self.label_servo_9 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_servo_9.setGeometry(QtCore.QRect(300, 110, 87, 13))&lt;br /&gt;
			self.label_servo_9.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_servo_9.setObjectName(&amp;quot;label_servo_9&amp;quot;)&lt;br /&gt;
			self.label_servo_8 = QtWidgets.QLabel(self.frame_7)&lt;br /&gt;
			self.label_servo_8.setGeometry(QtCore.QRect(303, 31, 81, 13))&lt;br /&gt;
			self.label_servo_8.setAlignment(QtCore.Qt.AlignCenter)&lt;br /&gt;
			self.label_servo_8.setObjectName(&amp;quot;label_servo_8&amp;quot;)&lt;br /&gt;
			self.val_steer_distance = QtWidgets.QDoubleSpinBox(self.frame_7)&lt;br /&gt;
			self.val_steer_distance.setGeometry(QtCore.QRect(300, 90, 81, 18))&lt;br /&gt;
			self.val_steer_distance.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)&lt;br /&gt;
			self.val_steer_distance.setReadOnly(False)&lt;br /&gt;
			self.val_steer_distance.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)&lt;br /&gt;
			self.val_steer_distance.setSpecialValueText(&amp;quot;&amp;quot;)&lt;br /&gt;
			self.val_steer_distance.setDecimals(3)&lt;br /&gt;
			self.val_steer_distance.setMinimum(-1.0)&lt;br /&gt;
			self.val_steer_distance.setMaximum(1.0)&lt;br /&gt;
			self.val_steer_distance.setSingleStep(0.01)&lt;br /&gt;
			self.val_steer_distance.setProperty(&amp;quot;value&amp;quot;, 0.16)&lt;br /&gt;
			self.val_steer_distance.setObjectName(&amp;quot;val_steer_distance&amp;quot;)&lt;br /&gt;
			self.tabWidget.addTab(self.tab_4, &amp;quot;&amp;quot;)&lt;br /&gt;
			self.tab_5 = QtWidgets.QWidget()&lt;br /&gt;
			self.tab_5.setObjectName(&amp;quot;tab_5&amp;quot;)&lt;br /&gt;
			self.tabWidget.addTab(self.tab_5, &amp;quot;&amp;quot;)&lt;br /&gt;
			MainWindow.setCentralWidget(self.centralwidget)&lt;br /&gt;
			self.menubar = QtWidgets.QMenuBar(MainWindow)&lt;br /&gt;
			self.menubar.setGeometry(QtCore.QRect(0, 0, 1122, 21))&lt;br /&gt;
			self.menubar.setObjectName(&amp;quot;menubar&amp;quot;)&lt;br /&gt;
			self.menuFile = QtWidgets.QMenu(self.menubar)&lt;br /&gt;
			self.menuFile.setObjectName(&amp;quot;menuFile&amp;quot;)&lt;br /&gt;
			self.menuHelp = QtWidgets.QMenu(self.menubar)&lt;br /&gt;
			self.menuHelp.setObjectName(&amp;quot;menuHelp&amp;quot;)&lt;br /&gt;
			self.menuTools = QtWidgets.QMenu(self.menubar)&lt;br /&gt;
			self.menuTools.setObjectName(&amp;quot;menuTools&amp;quot;)&lt;br /&gt;
			MainWindow.setMenuBar(self.menubar)&lt;br /&gt;
			self.statusbar = QtWidgets.QStatusBar(MainWindow)&lt;br /&gt;
			self.statusbar.setObjectName(&amp;quot;statusbar&amp;quot;)&lt;br /&gt;
			MainWindow.setStatusBar(self.statusbar)&lt;br /&gt;
			self.menubar.addAction(self.menuFile.menuAction())&lt;br /&gt;
			self.menubar.addAction(self.menuTools.menuAction())&lt;br /&gt;
			self.menubar.addAction(self.menuHelp.menuAction())&lt;br /&gt;
&lt;br /&gt;
			self.retranslateUi(MainWindow)&lt;br /&gt;
			self.tabWidget.setCurrentIndex(2)&lt;br /&gt;
			self.tabWidget_2.setCurrentIndex(0)&lt;br /&gt;
			QtCore.QMetaObject.connectSlotsByName(MainWindow)&lt;br /&gt;
&lt;br /&gt;
		def retranslateUi(self, MainWindow):&lt;br /&gt;
			_translate = QtCore.QCoreApplication.translate&lt;br /&gt;
			MainWindow.setWindowTitle(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;MainWindow&amp;quot;))&lt;br /&gt;
			self.label_2.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Network Status: &amp;quot;))&lt;br /&gt;
			self.Disconnect_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Disconnect&amp;quot;))&lt;br /&gt;
			self.connect_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Connect&amp;quot;))&lt;br /&gt;
			self.label.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;IP adress:&amp;quot;))&lt;br /&gt;
			self.IP_NetworkStatus_label.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Status&amp;quot;))&lt;br /&gt;
			self.label_7.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;INTERNET CONNECTION&amp;quot;))&lt;br /&gt;
			self.SidebarMenu.setItemText(self.SidebarMenu.indexOf(self.SidebarMenuPage1), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Robot Connection&amp;quot;))&lt;br /&gt;
			self.send_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Send&amp;quot;))&lt;br /&gt;
			self.TX_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;TX&amp;quot;))&lt;br /&gt;
			self.RX_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;RX&amp;quot;))&lt;br /&gt;
			self.ConsoleHelp_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Help&amp;quot;))&lt;br /&gt;
			self.ConsoleClear_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Clear&amp;quot;))&lt;br /&gt;
			self.SidebarMenu.setItemText(self.SidebarMenu.indexOf(self.SidebarMenuPage2), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Debugger&amp;quot;))&lt;br /&gt;
			self.main_start.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Start&amp;quot;))&lt;br /&gt;
			self.main_remote_ctrl.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Remote control is active,&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;and mission paused.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.main_remote_ctrl.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Remote-Control&amp;quot;))&lt;br /&gt;
			self.main_stop.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Stop&amp;quot;))&lt;br /&gt;
			self.main_mission_state.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Shows that a mission is active.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Number is&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0: not active&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;1: waiting 1 second to start&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;2: running mission line&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;6,7: listening to button (more button presses)&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;8: stop mission&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.main_mission_state.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Mission&amp;quot;))&lt;br /&gt;
			self.SidebarMenu.setItemText(self.SidebarMenu.indexOf(self.SidebarMenuPage3), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Mission&amp;quot;))&lt;br /&gt;
			self.label_5.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Manual Speed:&amp;quot;))&lt;br /&gt;
			self.enableKeyboardRC_checkBox.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Enable Remote Keyboard control&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.enableKeyboardRC_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable keyboard control&amp;quot;))&lt;br /&gt;
			self.guiRC_Reverse_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;V&amp;quot;))&lt;br /&gt;
			self.guiRC_Forward_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;^&amp;quot;))&lt;br /&gt;
			self.enableGuiRC_checkBox.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Enable Remote - GUI control&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.enableGuiRC_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable GUI control&amp;quot;))&lt;br /&gt;
			self.guiRC_Right_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;gt;&amp;quot;))&lt;br /&gt;
			self.guiRC_Left_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;&amp;quot;))&lt;br /&gt;
			self.label_8.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Motor Remote GUI control&amp;quot;))&lt;br /&gt;
			self.label_9.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Robot Motor hardware parameters&amp;quot;))&lt;br /&gt;
			self.label_21.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Number of encoder pulses on each motor revolution (normally 48)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_21.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enc. pulse per rev:&amp;quot;))&lt;br /&gt;
			self.label_22.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Gear ratio [N]:&amp;quot;))&lt;br /&gt;
			self.Robot_GearRatio.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Gear ratio from motor to wheel axle (9.68).&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_24.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Wheel radius (Left) [m]:&amp;quot;))&lt;br /&gt;
			self.label_127.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Robot ID:&amp;quot;))&lt;br /&gt;
			self.label_128.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Hardware:&amp;quot;))&lt;br /&gt;
			self.label_25.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Wheel base is distance between wheels (mid-wheel to mid-wheel) for turn calculations.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_25.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Wheel-base [m]:&amp;quot;))&lt;br /&gt;
			self.Robot_PulsePerRev.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Number of encoder ticks per motor revolution (48)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_26.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Wheel radius (Right) [m]:&amp;quot;))&lt;br /&gt;
			self.Robot_RevEncoder_Right.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Checking this makes odometry go the other way, i.e. encoder pins swapped (right motor).&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;This should NOT be checked on normal REGBOT.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.Robot_RevEncoder_Right.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Reverse Right Encoder&amp;quot;))&lt;br /&gt;
			self.Robot_RevMotor.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Checking this makes wheels go the other way when orderd forward.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;This should NOT be checked on normal REGBOT.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.Robot_RevMotor.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Reverse Motor&amp;quot;))&lt;br /&gt;
			self.Robot_RevEncoder_Left.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Checking this makes odometry go the other way, i.e. encoder pins swapped.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;This should NOT be checked on normal REGBOT.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.Robot_RevEncoder_Left.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Reverse Left Encoder&amp;quot;))&lt;br /&gt;
			self.label_222.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Battery is assumed to be of type LiPo 3cell.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Any battery with a normal voltage between 9 and 15V should do.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_222.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Go idle at [V]:&amp;quot;))&lt;br /&gt;
			self.save_id_on_robot.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Save&amp;quot;))&lt;br /&gt;
			self.label_129.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Tilt offset [Rad]:&amp;quot;))&lt;br /&gt;
			self.Robot_edit.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Edit&amp;quot;))&lt;br /&gt;
			self.Robot_OnBattery.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Robot On Battery&amp;quot;))&lt;br /&gt;
			self.robot_cancel.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Cancel&amp;quot;))&lt;br /&gt;
			self.label_36.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Right&amp;quot;))&lt;br /&gt;
			self.label_37.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Left&amp;quot;))&lt;br /&gt;
			self.label_42.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Current [A]:&amp;quot;))&lt;br /&gt;
			self.label_28.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Encoder:&amp;quot;))&lt;br /&gt;
			self.label_38.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Drive information&amp;quot;))&lt;br /&gt;
			self.label_32.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Distance [m]:&amp;quot;))&lt;br /&gt;
			self.label_29.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Wheel Speed [m/s]:&amp;quot;))&lt;br /&gt;
			self.tabWidget_2.setTabText(self.tabWidget_2.indexOf(self.tab_6), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Drive Info&amp;quot;))&lt;br /&gt;
			self.label_76.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;degrees&amp;quot;))&lt;br /&gt;
			self.label_45.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Pose (relative to starting/reset point)&amp;quot;))&lt;br /&gt;
			self.Robot_PoseReset.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;reset&amp;quot;))&lt;br /&gt;
			self.label_44.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Heading [rad]:&amp;quot;))&lt;br /&gt;
			self.label_34.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;X (forward) [m]:&amp;quot;))&lt;br /&gt;
			self.label_35.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Y (left) [m]:&amp;quot;))&lt;br /&gt;
			self.label_125.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Tilt [rad]:&amp;quot;))&lt;br /&gt;
			self.tabWidget_2.setTabText(self.tabWidget_2.indexOf(self.tab_7), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Pose Info&amp;quot;))&lt;br /&gt;
			self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Motor&amp;quot;))&lt;br /&gt;
			self.label_10.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Line Sensor&amp;quot;))&lt;br /&gt;
			self.enableLineSensor_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Line Sensor&amp;quot;))&lt;br /&gt;
			self.line_bar_1.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
			self.line_bar_4.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
			self.line_bar_6.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
			self.line_bar_5.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
			self.line_bar_3.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
			self.line_bar_7.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
			self.line_bar_2.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
			self.line_bar_8.setFormat(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;%p%&amp;quot;))&lt;br /&gt;
			self.label_11.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Sensor readings:&amp;quot;))&lt;br /&gt;
			self.label_12.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.label_13.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L2&amp;quot;))&lt;br /&gt;
			self.label_14.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L6&amp;quot;))&lt;br /&gt;
			self.label_15.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L3&amp;quot;))&lt;br /&gt;
			self.label_16.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L4&amp;quot;))&lt;br /&gt;
			self.label_17.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L5&amp;quot;))&lt;br /&gt;
			self.label_18.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L7&amp;quot;))&lt;br /&gt;
			self.label_19.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L8&amp;quot;))&lt;br /&gt;
			self.label_118.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Left edge&amp;quot;))&lt;br /&gt;
			self.ls_left_edge.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Left edge position&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Gray if an edge is not detected&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.ls_right_edge.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Right edge position&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Either no edge is valid or both are valid.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_121.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Right edge&amp;quot;))&lt;br /&gt;
			self.label_20.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Edge Analysis:&amp;quot;))&lt;br /&gt;
			self.label_27.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Crossing&amp;quot;))&lt;br /&gt;
			self.label_171.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Line valid&amp;quot;))&lt;br /&gt;
			self.ls_crossing_cnt.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 20.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no crossing, 20 = crossing detected in 20ms. &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Counts +1 when detection is above &amp;amp;quot;Crossing det.&amp;amp;quot; value.&amp;lt;br/&amp;gt;else -1.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.ls_line_valid_cnt.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 5.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no line, 5=line seen in 5ms.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.groupBox_5.setTitle(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Sensor Calibration&amp;quot;))&lt;br /&gt;
			self.CalibrateWhite_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Calibrate White&amp;quot;))&lt;br /&gt;
			self.label_217.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L3&amp;quot;))&lt;br /&gt;
			self.label_218.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L5&amp;quot;))&lt;br /&gt;
			self.label_219.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L7&amp;quot;))&lt;br /&gt;
			self.label_221.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L4&amp;quot;))&lt;br /&gt;
			self.label_223.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L6&amp;quot;))&lt;br /&gt;
			self.label_224.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.label_225.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L8&amp;quot;))&lt;br /&gt;
			self.label_226.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L2&amp;quot;))&lt;br /&gt;
			self.ls_max_white_7.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_white_8.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_white_1.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_white_3.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_white_5.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_white_2.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_white_4.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_white_6.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_black_5.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_black_4.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_black_8.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_black_1.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_black_2.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_black_3.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_black_6.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.ls_max_black_7.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;L1&amp;quot;))&lt;br /&gt;
			self.label_227.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;White&amp;quot;))&lt;br /&gt;
			self.CalibrateBlack_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Calibrate Black &amp;quot;))&lt;br /&gt;
			self.label_228.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Black&amp;quot;))&lt;br /&gt;
			self.ls_swap_left_right.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Swap the detected left and right edge.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Check this if LED\&#039;s are (mostly) in the forward (X) direction.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;(Normally the LED end of the line sensor PCB is closer to driving wheel axis).&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.ls_swap_left_right.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Swap Sides&amp;quot;))&lt;br /&gt;
			self.ls_show_normalized.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;If line is not white (bright), then assumed black (dark)&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;The normalized value should be shown as 75%.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.ls_show_normalized.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Show normalized&amp;quot;))&lt;br /&gt;
			self.label_23.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Crossing det.&amp;quot;))&lt;br /&gt;
			self.label_156.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Max RAW&amp;quot;))&lt;br /&gt;
			self.ls_crossing_detect.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Distance between left and right edge in a 0 to 8 scale.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Value depends distance between edges with an extra value if width increse fast. Therefore the width can temorarily be higher than 6 (edge values are from -3 to 3)&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;this should make it easier to detect a crossing.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;For Regbot in balance (and normal tape (38mm)), then about 4.8 cm to trigger crossing line.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;For Robobot this could be as low as 3-4, as the linesensor is wider.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.ls_power_high.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;If not in auto mode, should the line sensor LED power be high (ca. 30mA), else low (ca 10mA)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.ls_power_high.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;High power&amp;quot;))&lt;br /&gt;
			self.ls_line_white.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;If line is not white (bright), then assumed black (dark)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.ls_line_white.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;White line&amp;quot;))&lt;br /&gt;
			self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Edge Detection&amp;quot;))&lt;br /&gt;
			self.label_56.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Distance sensor&amp;quot;))&lt;br /&gt;
			self.label_279.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D1 right&amp;quot;))&lt;br /&gt;
			self.label_281.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D1 right RAW&amp;quot;))&lt;br /&gt;
			self.label_287.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;m&amp;quot;))&lt;br /&gt;
			self.label_280.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D2 front&amp;quot;))&lt;br /&gt;
			self.label_282.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D2 front RAW&amp;quot;))&lt;br /&gt;
			self.label_288.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;m&amp;quot;))&lt;br /&gt;
			self.label_284.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D1 right (50cm)&amp;quot;))&lt;br /&gt;
			self.label_286.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D2 front (13cm)&amp;quot;))&lt;br /&gt;
			self.label_285.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D2 front (50cm)&amp;quot;))&lt;br /&gt;
			self.label_283.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;D1 right (13cm)&amp;quot;))&lt;br /&gt;
			self.checkBox_ir_use.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Should be turned on at power on (will be turned on automatically in missions that use the sensor - if installed). If not installed, then distance is  10m&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.checkBox_ir_use.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Sensor Enable&amp;quot;))&lt;br /&gt;
			self.checkBox_ir_installed.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Is the sensor installed (can not be turned on if not installed)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.checkBox_ir_installed.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Sensor Installed&amp;quot;))&lt;br /&gt;
			self.label_277.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Calibrate values (RAW)&amp;quot;))&lt;br /&gt;
			self.ir_apply.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Apply&amp;quot;))&lt;br /&gt;
			self.label_278.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Distance values (measured)&amp;quot;))&lt;br /&gt;
			self.ir_edit.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Edit&amp;quot;))&lt;br /&gt;
			self.ir_cancel.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Cancel&amp;quot;))&lt;br /&gt;
			self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_8), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;IR Distance&amp;quot;))&lt;br /&gt;
			self.EnableCamera_checkBox.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;To enable Camera stream, install mjpeg-streamer and run follwing commands:&amp;lt;br/&amp;gt;cd mjpg-streamer/mjpg-streamer-experimental/ &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;./mjpg_streamer -o &amp;amp;quot;output_http.so -w ./www&amp;amp;quot; -i &amp;amp;quot;input_raspicam.so&amp;amp;quot;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.EnableCamera_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Camera&amp;quot;))&lt;br /&gt;
			self.Camera_Record_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Record&amp;quot;))&lt;br /&gt;
			self.Camera_StopRecord_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Stop Recording&amp;quot;))&lt;br /&gt;
			self.Camera_Snapshot_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Take Snapshot&amp;quot;))&lt;br /&gt;
			self.label_55.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Snapshot Save Filename&amp;quot;))&lt;br /&gt;
			self.Camera_SnapSave_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Save&amp;quot;))&lt;br /&gt;
			self.VideoRecording_Label.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;&amp;lt;span style=\&amp;quot; color:#ff0000;\&amp;quot;&amp;gt;RECORDING...&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.Camera_ClearSnap_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Clear Snap&amp;quot;))&lt;br /&gt;
			self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Camera&amp;quot;))&lt;br /&gt;
			self.label_30.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Servo Controls&amp;quot;))&lt;br /&gt;
			self.enableServo1_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Servo 1&amp;quot;))&lt;br /&gt;
			self.enableServo2_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Servo 2&amp;quot;))&lt;br /&gt;
			self.enableServo3_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Servo 3&amp;quot;))&lt;br /&gt;
			self.label_31.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Current Position&amp;quot;))&lt;br /&gt;
			self.servo1_current_pos.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 20.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no crossing, 20 = crossing detected in 20ms. &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Counts +1 when detection is above &amp;amp;quot;Crossing det.&amp;amp;quot; value.&amp;lt;br/&amp;gt;else -1.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_33.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Command Position&amp;quot;))&lt;br /&gt;
			self.manualServoPos1_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Apply&amp;quot;))&lt;br /&gt;
			self.label_39.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Position Dial&amp;quot;))&lt;br /&gt;
			self.label_40.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Position Dial&amp;quot;))&lt;br /&gt;
			self.servo2_current_pos.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 20.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no crossing, 20 = crossing detected in 20ms. &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Counts +1 when detection is above &amp;amp;quot;Crossing det.&amp;amp;quot; value.&amp;lt;br/&amp;gt;else -1.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_41.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Command Position&amp;quot;))&lt;br /&gt;
			self.manualServoPos2_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Apply&amp;quot;))&lt;br /&gt;
			self.label_43.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Current Position&amp;quot;))&lt;br /&gt;
			self.label_46.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Position Dial&amp;quot;))&lt;br /&gt;
			self.servo3_current_pos.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 20.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no crossing, 20 = crossing detected in 20ms. &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Counts +1 when detection is above &amp;amp;quot;Crossing det.&amp;amp;quot; value.&amp;lt;br/&amp;gt;else -1.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_47.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Command Position&amp;quot;))&lt;br /&gt;
			self.manualServoPos3_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Apply&amp;quot;))&lt;br /&gt;
			self.label_48.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Current Position&amp;quot;))&lt;br /&gt;
			self.enableServo4_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Servo 4&amp;quot;))&lt;br /&gt;
			self.label_49.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Position Dial&amp;quot;))&lt;br /&gt;
			self.servo4_current_pos.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 20.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no crossing, 20 = crossing detected in 20ms. &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Counts +1 when detection is above &amp;amp;quot;Crossing det.&amp;amp;quot; value.&amp;lt;br/&amp;gt;else -1.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_50.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Command Position&amp;quot;))&lt;br /&gt;
			self.manualServoPos4_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Apply&amp;quot;))&lt;br /&gt;
			self.label_51.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Current Position&amp;quot;))&lt;br /&gt;
			self.enableServo5_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Servo 5&amp;quot;))&lt;br /&gt;
			self.label_52.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Position Dial&amp;quot;))&lt;br /&gt;
			self.servo5_current_pos.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Value from 0 to 20.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;0 = no crossing, 20 = crossing detected in 20ms. &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Counts +1 when detection is above &amp;amp;quot;Crossing det.&amp;amp;quot; value.&amp;lt;br/&amp;gt;else -1.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_53.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Command Position&amp;quot;))&lt;br /&gt;
			self.manualServoPos5_Cmd.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Apply&amp;quot;))&lt;br /&gt;
			self.label_54.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Current Position&amp;quot;))&lt;br /&gt;
			self.Servo1Steering_checkBox.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Enable Steering&amp;quot;))&lt;br /&gt;
			self.val_servo1_offset.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Steering offset to align steering wheel with straight forward.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Units is control units, i.e. about 50 is 5 degrees.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Zero offset means forward is 1.5ms pulse (center for servo)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_servo_6.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;distance&amp;quot;))&lt;br /&gt;
			self.val_servo1_scale.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Servo angle change from 1ms to 2ms PWM pulse to servo.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;(typically 160 deg for hitec7235 and 90 deg for &amp;amp;quot;old&amp;amp;quot; servos)&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.label_servo_9.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;scale&amp;quot;))&lt;br /&gt;
			self.label_servo_8.setText(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Offset&amp;quot;))&lt;br /&gt;
			self.val_steer_distance.setToolTip(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Distance from drive axle to steering wheel.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Positive is forward (front wheel). In meters.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Typically 0.16 m for ROBOBOT.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;))&lt;br /&gt;
			self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_4), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Servo&amp;quot;))&lt;br /&gt;
			self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_5), _translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Mission&amp;quot;))&lt;br /&gt;
			self.menuFile.setTitle(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;File&amp;quot;))&lt;br /&gt;
			self.menuHelp.setTitle(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Help&amp;quot;))&lt;br /&gt;
			self.menuTools.setTitle(_translate(&amp;quot;MainWindow&amp;quot;, &amp;quot;Tools&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
		import sys&lt;br /&gt;
		app = QtWidgets.QApplication(sys.argv)&lt;br /&gt;
		MainWindow = QtWidgets.QMainWindow()&lt;br /&gt;
		ui = Ui_MainWindow()&lt;br /&gt;
		ui.setupUi(MainWindow)&lt;br /&gt;
		MainWindow.show()&lt;br /&gt;
		sys.exit(app.exec_())&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Source_Code&amp;diff=5104</id>
		<title>Source Code</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Source_Code&amp;diff=5104"/>
		<updated>2020-12-06T17:38:15Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* User Interface Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==User Interface Code==&lt;br /&gt;
&lt;br /&gt;
[[User Interface Code]]&lt;br /&gt;
&lt;br /&gt;
==Main Application Code==&lt;br /&gt;
&lt;br /&gt;
==Linesensor==&lt;br /&gt;
&lt;br /&gt;
==IR distance sensors==&lt;br /&gt;
&lt;br /&gt;
==Servo==&lt;br /&gt;
&lt;br /&gt;
==Video Threads==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Source_Code&amp;diff=5103</id>
		<title>Source Code</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Source_Code&amp;diff=5103"/>
		<updated>2020-12-06T17:37:56Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Source Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==User Interface Code==&lt;br /&gt;
&lt;br /&gt;
==Main Application Code==&lt;br /&gt;
&lt;br /&gt;
==Linesensor==&lt;br /&gt;
&lt;br /&gt;
==IR distance sensors==&lt;br /&gt;
&lt;br /&gt;
==Servo==&lt;br /&gt;
&lt;br /&gt;
==Video Threads==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Source_Code&amp;diff=5102</id>
		<title>Source Code</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Source_Code&amp;diff=5102"/>
		<updated>2020-12-06T17:34:47Z</updated>

		<summary type="html">&lt;p&gt;S192320: Created page with &amp;quot;==Source Code==&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Source Code==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5101</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5101"/>
		<updated>2020-12-06T17:34:31Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Source Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
The following functions are crucial to actually display the image onto the label of the Qlabel object. The covert_cv_qt(cv_img) takes the output image of the mjpeg streamer and converts it to the qt format so it can be displayed in our Qt Gui application.&lt;br /&gt;
&lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
The update_image() actually updates the image and displays it to the Qlabel. As you can notice, this function uses the convert_cv_qt function to transform the received image in order to display it properly.&lt;br /&gt;
&lt;br /&gt;
    def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) &lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) &lt;br /&gt;
            self.take_snapshot = False&lt;br /&gt;
&lt;br /&gt;
Current setup for the Camera includes also the functionality to take a picture and record a video with the camera. It is a little more simple with taking a picture from the stream because we are just taking last snapped image from the update_image() function and save the image under the entered filename using snapshot_save() function. As it can be seen below, the picture by default saves the &amp;quot;.jpg&amp;quot; file with the current date and time. Otherwise, an user can enter the filename that he would like to use and save it under that. And using the opencv function cv2.imwrite() we save the picture.&lt;br /&gt;
&lt;br /&gt;
 def snapshot_save(self):    &lt;br /&gt;
        t = time.gmtime()&lt;br /&gt;
        if self.ui.Camera_SnapFilename.text() == &amp;quot;&amp;quot;:&lt;br /&gt;
            filename = &#039;snapshot_&#039; + str(t.tm_year) + str(t.tm_mon) + str(t.tm_mday) + &amp;quot;_&amp;quot; + str(t.tm_hour) + str(t.tm_min) + str(t.tm_sec) +&#039;.jpg&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            filename = str(self.ui.Camera_SnapFilename.text() + &#039;.jpg&#039;)&lt;br /&gt;
        cv2.imwrite(filename, self.snapshot_image)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To record the video from the video stream, the other class was implemented which took care of the video recording functionality. &lt;br /&gt;
The key two functions to ensure the video starting/stopping and saving the video are the following. video_StartRecording() functions connects the update_image() function with the video reocrding thread and starts it. The video_recording flag is True. &lt;br /&gt;
&lt;br /&gt;
    def video_StartRecording(self):&lt;br /&gt;
        self.video_recording = True&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread = VideoRecordingThread()&lt;br /&gt;
            self.VideoRecordingThread.change_pixmap_signal.connect(self.update_image)&lt;br /&gt;
            self.VideoRecordingThread.start()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
 &lt;br /&gt;
Meanwhile, the recording is happening, the toogleRecLabel(), makes the recirding status label visible, which indicates that the recoding is happening.    &lt;br /&gt;
&lt;br /&gt;
    def toogleRecLabel(self):&lt;br /&gt;
        self.toogleSignalON = not self.toogleSignalON&lt;br /&gt;
        if self.toogleSignalON:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        else:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(False)&lt;br /&gt;
&lt;br /&gt;
Similarly, the video_StopRecording() function stops the video recording by stopping the video thread and setting video_recording flag to False.&lt;br /&gt;
            &lt;br /&gt;
    def video_StopRecording(self):&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread.stop()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording Stopping...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True            &lt;br /&gt;
        self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        self.video_recording = False     &lt;br /&gt;
&lt;br /&gt;
The actual Video recording class that controls the video recording can be seen below. The function &#039;&#039;out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&#039;&#039; defines the output object and the &#039;&#039;cv_img = cap.read()&#039;&#039; and &#039;&#039;out.write(cv_img) &#039;&#039; gets the actual image from the cv2.VideoCapture() and then writes the image to the actual output video file.&lt;br /&gt;
&lt;br /&gt;
 class VideoRecordingThread(QThread): &lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;)&lt;br /&gt;
        frame_width = int(cap.get(3))&lt;br /&gt;
        frame_height = int(cap.get(4))&lt;br /&gt;
        out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                out.write(cv_img)  &lt;br /&gt;
        cap.release()&lt;br /&gt;
        out.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== Source Code ==&lt;br /&gt;
The Full Source code can be seen in the following link -&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Source Code]]&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5100</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5100"/>
		<updated>2020-12-06T17:34:02Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
The following functions are crucial to actually display the image onto the label of the Qlabel object. The covert_cv_qt(cv_img) takes the output image of the mjpeg streamer and converts it to the qt format so it can be displayed in our Qt Gui application.&lt;br /&gt;
&lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
The update_image() actually updates the image and displays it to the Qlabel. As you can notice, this function uses the convert_cv_qt function to transform the received image in order to display it properly.&lt;br /&gt;
&lt;br /&gt;
    def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) &lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) &lt;br /&gt;
            self.take_snapshot = False&lt;br /&gt;
&lt;br /&gt;
Current setup for the Camera includes also the functionality to take a picture and record a video with the camera. It is a little more simple with taking a picture from the stream because we are just taking last snapped image from the update_image() function and save the image under the entered filename using snapshot_save() function. As it can be seen below, the picture by default saves the &amp;quot;.jpg&amp;quot; file with the current date and time. Otherwise, an user can enter the filename that he would like to use and save it under that. And using the opencv function cv2.imwrite() we save the picture.&lt;br /&gt;
&lt;br /&gt;
 def snapshot_save(self):    &lt;br /&gt;
        t = time.gmtime()&lt;br /&gt;
        if self.ui.Camera_SnapFilename.text() == &amp;quot;&amp;quot;:&lt;br /&gt;
            filename = &#039;snapshot_&#039; + str(t.tm_year) + str(t.tm_mon) + str(t.tm_mday) + &amp;quot;_&amp;quot; + str(t.tm_hour) + str(t.tm_min) + str(t.tm_sec) +&#039;.jpg&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            filename = str(self.ui.Camera_SnapFilename.text() + &#039;.jpg&#039;)&lt;br /&gt;
        cv2.imwrite(filename, self.snapshot_image)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To record the video from the video stream, the other class was implemented which took care of the video recording functionality. &lt;br /&gt;
The key two functions to ensure the video starting/stopping and saving the video are the following. video_StartRecording() functions connects the update_image() function with the video reocrding thread and starts it. The video_recording flag is True. &lt;br /&gt;
&lt;br /&gt;
    def video_StartRecording(self):&lt;br /&gt;
        self.video_recording = True&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread = VideoRecordingThread()&lt;br /&gt;
            self.VideoRecordingThread.change_pixmap_signal.connect(self.update_image)&lt;br /&gt;
            self.VideoRecordingThread.start()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
 &lt;br /&gt;
Meanwhile, the recording is happening, the toogleRecLabel(), makes the recirding status label visible, which indicates that the recoding is happening.    &lt;br /&gt;
&lt;br /&gt;
    def toogleRecLabel(self):&lt;br /&gt;
        self.toogleSignalON = not self.toogleSignalON&lt;br /&gt;
        if self.toogleSignalON:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        else:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(False)&lt;br /&gt;
&lt;br /&gt;
Similarly, the video_StopRecording() function stops the video recording by stopping the video thread and setting video_recording flag to False.&lt;br /&gt;
            &lt;br /&gt;
    def video_StopRecording(self):&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread.stop()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording Stopping...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True            &lt;br /&gt;
        self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        self.video_recording = False     &lt;br /&gt;
&lt;br /&gt;
The actual Video recording class that controls the video recording can be seen below. The function &#039;&#039;out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&#039;&#039; defines the output object and the &#039;&#039;cv_img = cap.read()&#039;&#039; and &#039;&#039;out.write(cv_img) &#039;&#039; gets the actual image from the cv2.VideoCapture() and then writes the image to the actual output video file.&lt;br /&gt;
&lt;br /&gt;
 class VideoRecordingThread(QThread): &lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;)&lt;br /&gt;
        frame_width = int(cap.get(3))&lt;br /&gt;
        frame_height = int(cap.get(4))&lt;br /&gt;
        out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                out.write(cv_img)  &lt;br /&gt;
        cap.release()&lt;br /&gt;
        out.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== Source Code ==&lt;br /&gt;
[[Source Code]]&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5099</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5099"/>
		<updated>2020-12-06T17:26:56Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* The Source Files */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
The following functions are crucial to actually display the image onto the label of the Qlabel object. The covert_cv_qt(cv_img) takes the output image of the mjpeg streamer and converts it to the qt format so it can be displayed in our Qt Gui application.&lt;br /&gt;
&lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
The update_image() actually updates the image and displays it to the Qlabel. As you can notice, this function uses the convert_cv_qt function to transform the received image in order to display it properly.&lt;br /&gt;
&lt;br /&gt;
    def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) &lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) &lt;br /&gt;
            self.take_snapshot = False&lt;br /&gt;
&lt;br /&gt;
Current setup for the Camera includes also the functionality to take a picture and record a video with the camera. It is a little more simple with taking a picture from the stream because we are just taking last snapped image from the update_image() function and save the image under the entered filename using snapshot_save() function. As it can be seen below, the picture by default saves the &amp;quot;.jpg&amp;quot; file with the current date and time. Otherwise, an user can enter the filename that he would like to use and save it under that. And using the opencv function cv2.imwrite() we save the picture.&lt;br /&gt;
&lt;br /&gt;
 def snapshot_save(self):    &lt;br /&gt;
        t = time.gmtime()&lt;br /&gt;
        if self.ui.Camera_SnapFilename.text() == &amp;quot;&amp;quot;:&lt;br /&gt;
            filename = &#039;snapshot_&#039; + str(t.tm_year) + str(t.tm_mon) + str(t.tm_mday) + &amp;quot;_&amp;quot; + str(t.tm_hour) + str(t.tm_min) + str(t.tm_sec) +&#039;.jpg&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            filename = str(self.ui.Camera_SnapFilename.text() + &#039;.jpg&#039;)&lt;br /&gt;
        cv2.imwrite(filename, self.snapshot_image)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To record the video from the video stream, the other class was implemented which took care of the video recording functionality. &lt;br /&gt;
The key two functions to ensure the video starting/stopping and saving the video are the following. video_StartRecording() functions connects the update_image() function with the video reocrding thread and starts it. The video_recording flag is True. &lt;br /&gt;
&lt;br /&gt;
    def video_StartRecording(self):&lt;br /&gt;
        self.video_recording = True&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread = VideoRecordingThread()&lt;br /&gt;
            self.VideoRecordingThread.change_pixmap_signal.connect(self.update_image)&lt;br /&gt;
            self.VideoRecordingThread.start()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
 &lt;br /&gt;
Meanwhile, the recording is happening, the toogleRecLabel(), makes the recirding status label visible, which indicates that the recoding is happening.    &lt;br /&gt;
&lt;br /&gt;
    def toogleRecLabel(self):&lt;br /&gt;
        self.toogleSignalON = not self.toogleSignalON&lt;br /&gt;
        if self.toogleSignalON:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        else:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(False)&lt;br /&gt;
&lt;br /&gt;
Similarly, the video_StopRecording() function stops the video recording by stopping the video thread and setting video_recording flag to False.&lt;br /&gt;
            &lt;br /&gt;
    def video_StopRecording(self):&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread.stop()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording Stopping...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True            &lt;br /&gt;
        self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        self.video_recording = False     &lt;br /&gt;
&lt;br /&gt;
The actual Video recording class that controls the video recording can be seen below. The function &#039;&#039;out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&#039;&#039; defines the output object and the &#039;&#039;cv_img = cap.read()&#039;&#039; and &#039;&#039;out.write(cv_img) &#039;&#039; gets the actual image from the cv2.VideoCapture() and then writes the image to the actual output video file.&lt;br /&gt;
&lt;br /&gt;
 class VideoRecordingThread(QThread): &lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;)&lt;br /&gt;
        frame_width = int(cap.get(3))&lt;br /&gt;
        frame_height = int(cap.get(4))&lt;br /&gt;
        out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                out.write(cv_img)  &lt;br /&gt;
        cap.release()&lt;br /&gt;
        out.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5098</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5098"/>
		<updated>2020-12-06T17:20:53Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
The following functions are crucial to actually display the image onto the label of the Qlabel object. The covert_cv_qt(cv_img) takes the output image of the mjpeg streamer and converts it to the qt format so it can be displayed in our Qt Gui application.&lt;br /&gt;
&lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
The update_image() actually updates the image and displays it to the Qlabel. As you can notice, this function uses the convert_cv_qt function to transform the received image in order to display it properly.&lt;br /&gt;
&lt;br /&gt;
    def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) &lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) &lt;br /&gt;
            self.take_snapshot = False&lt;br /&gt;
&lt;br /&gt;
Current setup for the Camera includes also the functionality to take a picture and record a video with the camera. It is a little more simple with taking a picture from the stream because we are just taking last snapped image from the update_image() function and save the image under the entered filename using snapshot_save() function. As it can be seen below, the picture by default saves the &amp;quot;.jpg&amp;quot; file with the current date and time. Otherwise, an user can enter the filename that he would like to use and save it under that. And using the opencv function cv2.imwrite() we save the picture.&lt;br /&gt;
&lt;br /&gt;
 def snapshot_save(self):    &lt;br /&gt;
        t = time.gmtime()&lt;br /&gt;
        if self.ui.Camera_SnapFilename.text() == &amp;quot;&amp;quot;:&lt;br /&gt;
            filename = &#039;snapshot_&#039; + str(t.tm_year) + str(t.tm_mon) + str(t.tm_mday) + &amp;quot;_&amp;quot; + str(t.tm_hour) + str(t.tm_min) + str(t.tm_sec) +&#039;.jpg&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            filename = str(self.ui.Camera_SnapFilename.text() + &#039;.jpg&#039;)&lt;br /&gt;
        cv2.imwrite(filename, self.snapshot_image)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To record the video from the video stream, the other class was implemented which took care of the video recording functionality. &lt;br /&gt;
The key two functions to ensure the video starting/stopping and saving the video are the following. video_StartRecording() functions connects the update_image() function with the video reocrding thread and starts it. The video_recording flag is True. &lt;br /&gt;
&lt;br /&gt;
    def video_StartRecording(self):&lt;br /&gt;
        self.video_recording = True&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread = VideoRecordingThread()&lt;br /&gt;
            self.VideoRecordingThread.change_pixmap_signal.connect(self.update_image)&lt;br /&gt;
            self.VideoRecordingThread.start()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
 &lt;br /&gt;
Meanwhile, the recording is happening, the toogleRecLabel(), makes the recirding status label visible, which indicates that the recoding is happening.    &lt;br /&gt;
&lt;br /&gt;
    def toogleRecLabel(self):&lt;br /&gt;
        self.toogleSignalON = not self.toogleSignalON&lt;br /&gt;
        if self.toogleSignalON:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        else:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(False)&lt;br /&gt;
&lt;br /&gt;
Similarly, the video_StopRecording() function stops the video recording by stopping the video thread and setting video_recording flag to False.&lt;br /&gt;
            &lt;br /&gt;
    def video_StopRecording(self):&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread.stop()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording Stopping...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True            &lt;br /&gt;
        self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        self.video_recording = False     &lt;br /&gt;
&lt;br /&gt;
The actual Video recording class that controls the video recording can be seen below. The function &#039;&#039;out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&#039;&#039; defines the output object and the &#039;&#039;cv_img = cap.read()&#039;&#039; and &#039;&#039;out.write(cv_img) &#039;&#039; gets the actual image from the cv2.VideoCapture() and then writes the image to the actual output video file.&lt;br /&gt;
&lt;br /&gt;
 class VideoRecordingThread(QThread): &lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;)&lt;br /&gt;
        frame_width = int(cap.get(3))&lt;br /&gt;
        frame_height = int(cap.get(4))&lt;br /&gt;
        out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                out.write(cv_img)  &lt;br /&gt;
        cap.release()&lt;br /&gt;
        out.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5097</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5097"/>
		<updated>2020-12-06T17:07:27Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
The following functions are crucial to actually display the image onto the label of the Qlabel object. The covert_cv_qt(cv_img) takes the output image of the mjpeg streamer and converts it to the qt format so it can be displayed in our Qt Gui application.&lt;br /&gt;
&lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
The update_image() actually updates the image and displays it to the Qlabel. As you can notice, this function uses the convert_cv_qt function to transform the received image in order to display it properly.&lt;br /&gt;
&lt;br /&gt;
    def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) &lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) &lt;br /&gt;
            self.take_snapshot = False&lt;br /&gt;
&lt;br /&gt;
Current setup for the Camera includes also the functionality to take a picture and record a video with the camera. It is a little more simple with taking a picture from the stream because we are just taking last snapped image from the update_image() function and save the image under the entered filename using snapshot_save() function. As it can be seen below, the picture by default saves the &amp;quot;.jpg&amp;quot; file with the current date and time. Otherwise, an user can enter the filename that he would like to use and save it under that. And using the opencv function cv2.imwrite() we save the picture.&lt;br /&gt;
&lt;br /&gt;
 def snapshot_save(self):    &lt;br /&gt;
        t = time.gmtime()&lt;br /&gt;
        if self.ui.Camera_SnapFilename.text() == &amp;quot;&amp;quot;:&lt;br /&gt;
            filename = &#039;snapshot_&#039; + str(t.tm_year) + str(t.tm_mon) + str(t.tm_mday) + &amp;quot;_&amp;quot; + str(t.tm_hour) + str(t.tm_min) + str(t.tm_sec) +&#039;.jpg&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            filename = str(self.ui.Camera_SnapFilename.text() + &#039;.jpg&#039;)&lt;br /&gt;
        cv2.imwrite(filename, self.snapshot_image)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To record the video from the video stream, the other class was implemented which took care of the video recording functionality. &lt;br /&gt;
The key two functions to ensure the video starting/stopping and saving the video are the following. video_StartRecording() functions connects the update_image() function with the video reocrding thread and starts it. The video_recording flag is True. &lt;br /&gt;
&lt;br /&gt;
    def video_StartRecording(self):&lt;br /&gt;
        self.video_recording = True&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread = VideoRecordingThread()&lt;br /&gt;
            self.VideoRecordingThread.change_pixmap_signal.connect(self.update_image)&lt;br /&gt;
            self.VideoRecordingThread.start()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
 &lt;br /&gt;
Meanwhile, the recording is happening, the toogleRecLabel(), makes the recirding status label visible, which indicates that the recoding is happening.    &lt;br /&gt;
&lt;br /&gt;
    def toogleRecLabel(self):&lt;br /&gt;
        self.toogleSignalON = not self.toogleSignalON&lt;br /&gt;
        if self.toogleSignalON:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        else:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(False)&lt;br /&gt;
&lt;br /&gt;
Similarly, the video_StopRecording() function stops the video recording by stopping the video thread and setting video_recording flag to False.&lt;br /&gt;
            &lt;br /&gt;
    def video_StopRecording(self):&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread.stop()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording Stopping...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True            &lt;br /&gt;
        self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        self.video_recording = False     &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 class VideoRecordingThread(QThread): &lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # maybe replace with something else?&lt;br /&gt;
        frame_width = int(cap.get(3))&lt;br /&gt;
        frame_height = int(cap.get(4))&lt;br /&gt;
        out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                out.write(cv_img)  &lt;br /&gt;
        cap.release()&lt;br /&gt;
        out.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5096</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5096"/>
		<updated>2020-12-06T17:05:55Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
The following functions are crucial to actually display the image onto the label of the Qlabel object. The covert_cv_qt(cv_img) takes the output image of the mjpeg streamer and converts it to the qt format so it can be displayed in our Qt Gui application.&lt;br /&gt;
&lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
The update_image() actually updates the image and displays it to the Qlabel. As you can notice, this function uses the convert_cv_qt function to transform the received image in order to display it properly.&lt;br /&gt;
&lt;br /&gt;
    def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) &lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) &lt;br /&gt;
            self.take_snapshot = False&lt;br /&gt;
&lt;br /&gt;
Current setup for the Camera includes also the functionality to take a picture and record a video with the camera. It is a little more simple with taking a picture from the stream because we are just taking last snapped image from the update_image() function and save the image under the entered filename using snapshot_save() function. As it can be seen below, the picture by default saves the &amp;quot;.jpg&amp;quot; file with the current date and time. Otherwise, an user can enter the filename that he would like to use and save it under that. And using the opencv function cv2.imwrite() we save the picture.&lt;br /&gt;
&lt;br /&gt;
 def snapshot_save(self):    &lt;br /&gt;
        t = time.gmtime()&lt;br /&gt;
        if self.ui.Camera_SnapFilename.text() == &amp;quot;&amp;quot;:&lt;br /&gt;
            filename = &#039;snapshot_&#039; + str(t.tm_year) + str(t.tm_mon) + str(t.tm_mday) + &amp;quot;_&amp;quot; + str(t.tm_hour) + str(t.tm_min) + str(t.tm_sec) +&#039;.jpg&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            filename = str(self.ui.Camera_SnapFilename.text() + &#039;.jpg&#039;)&lt;br /&gt;
        cv2.imwrite(filename, self.snapshot_image)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To record the video from the video stream, the other class was implemented which took care of the video recording functionality. &lt;br /&gt;
The key two functions to ensure the video starting/stopping and saving the video are the following. video_StartRecording() functions connects the update_image() function with the video reocrding thread and starts it. The video_recording flag is True. Meanwhile, the recording is happening, the roogleRecLabel(), makes the recirding status label visible, which indicates that the recoding is happening. &lt;br /&gt;
&lt;br /&gt;
    def video_StartRecording(self):&lt;br /&gt;
        self.video_recording = True&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread = VideoRecordingThread()&lt;br /&gt;
            self.VideoRecordingThread.change_pixmap_signal.connect(self.update_image)&lt;br /&gt;
            self.VideoRecordingThread.start()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
    &lt;br /&gt;
    def toogleRecLabel(self):&lt;br /&gt;
        self.toogleSignalON = not self.toogleSignalON&lt;br /&gt;
        if self.toogleSignalON:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        else:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(False)&lt;br /&gt;
            &lt;br /&gt;
    def video_StopRecording(self):&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread.stop()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording Stopping...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True            &lt;br /&gt;
        self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        self.video_recording = False     &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 class VideoRecordingThread(QThread): &lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # maybe replace with something else?&lt;br /&gt;
        frame_width = int(cap.get(3))&lt;br /&gt;
        frame_height = int(cap.get(4))&lt;br /&gt;
        out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                out.write(cv_img)  &lt;br /&gt;
        cap.release()&lt;br /&gt;
        out.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5095</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5095"/>
		<updated>2020-12-06T16:50:19Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
The following functions are crucial to actually display the image onto the label of the Qlabel object. The covert_cv_qt(cv_img) takes the output image of the mjpeg streamer and converts it to the qt format so it can be displayed in our Qt Gui application.&lt;br /&gt;
&lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
The update_image() actually updates the image and displays it to the Qlabel. As you can notice, this function uses the convert_cv_qt function to transform the received image in order to display it properly.&lt;br /&gt;
&lt;br /&gt;
    def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) &lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) &lt;br /&gt;
            self.take_snapshot = False&lt;br /&gt;
&lt;br /&gt;
Current setup for the Camera includes also the functionality to take a picture and record a video with the camera. It is a little more simple with taking a picture from the stream because we are just taking last snapped image from the update_image() function and save the image under the entered filename using snapshot_save() function. As it can be seen below, the picture by default saves the &amp;quot;.jpg&amp;quot; file with the current date and time. Otherwise, an user can enter the filename that he would like to use and save it under that. And using the opencv function cv2.imwrite() we save the picture.&lt;br /&gt;
&lt;br /&gt;
 def snapshot_save(self):    &lt;br /&gt;
        t = time.gmtime()&lt;br /&gt;
        if self.ui.Camera_SnapFilename.text() == &amp;quot;&amp;quot;:&lt;br /&gt;
            filename = &#039;snapshot_&#039; + str(t.tm_year) + str(t.tm_mon) + str(t.tm_mday) + &amp;quot;_&amp;quot; + str(t.tm_hour) + str(t.tm_min) + str(t.tm_sec) +&#039;.jpg&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            filename = str(self.ui.Camera_SnapFilename.text() + &#039;.jpg&#039;)&lt;br /&gt;
        cv2.imwrite(filename, self.snapshot_image)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To record the video from the video stream, the other class was implemented which took care of the video recording functionality. &lt;br /&gt;
The key two functions to enure the actual video saving an&lt;br /&gt;
&lt;br /&gt;
    def video_StartRecording(self):&lt;br /&gt;
        self.video_recording = True&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread = VideoRecordingThread()&lt;br /&gt;
            self.VideoRecordingThread.change_pixmap_signal.connect(self.update_image)&lt;br /&gt;
            self.VideoRecordingThread.start()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
    &lt;br /&gt;
    def toogleRecLabel(self):&lt;br /&gt;
        self.toogleSignalON = not self.toogleSignalON&lt;br /&gt;
        if self.toogleSignalON:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        else:&lt;br /&gt;
            self.ui.VideoRecording_Label.setHidden(False)&lt;br /&gt;
            &lt;br /&gt;
    def video_StopRecording(self):&lt;br /&gt;
        if self.video_recording and self.wifiConnected:&lt;br /&gt;
            self.VideoRecordingThread.stop()&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Recording Stopping...\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True            &lt;br /&gt;
        self.ui.VideoRecording_Label.setHidden(True)&lt;br /&gt;
        self.video_recording = False     &lt;br /&gt;
&lt;br /&gt;
 out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&lt;br /&gt;
 out.write(cv_img)&lt;br /&gt;
&lt;br /&gt;
 class VideoRecordingThread(QThread): &lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # maybe replace with something else?&lt;br /&gt;
        frame_width = int(cap.get(3))&lt;br /&gt;
        frame_height = int(cap.get(4))&lt;br /&gt;
        out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                out.write(cv_img)  &lt;br /&gt;
        cap.release()&lt;br /&gt;
        out.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5094</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5094"/>
		<updated>2020-12-06T16:46:23Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
The following functions are crucial to actually display the image onto the label of the Qlabel object. The covert_cv_qt(cv_img) takes the output image of the mjpeg streamer and converts it to the qt format so it can be displayed in our Qt Gui application.&lt;br /&gt;
&lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
The update_image() actually updates the image and displays it to the Qlabel. As you can notice, this function uses the convert_cv_qt function to transform the received image in order to display it properly.&lt;br /&gt;
&lt;br /&gt;
    def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) &lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) &lt;br /&gt;
            self.take_snapshot = False&lt;br /&gt;
&lt;br /&gt;
Current setup for the Camera includes also the functionality to take a picture and record a video with the camera. It is a little more simple with taking a picture from the stream because we are just taking last snapped image from the update_image() function and save the image under the entered filename using snapshot_save() function. As it can be seen below, the picture by default saves the &amp;quot;.jpg&amp;quot; file with the current date and time. Otherwise, an user can enter the filename that he would like to use and save it under that. And using the opencv function cv2.imwrite() we save the picture.&lt;br /&gt;
&lt;br /&gt;
 def snapshot_save(self):    &lt;br /&gt;
        t = time.gmtime()&lt;br /&gt;
        if self.ui.Camera_SnapFilename.text() == &amp;quot;&amp;quot;:&lt;br /&gt;
            filename = &#039;snapshot_&#039; + str(t.tm_year) + str(t.tm_mon) + str(t.tm_mday) + &amp;quot;_&amp;quot; + str(t.tm_hour) + str(t.tm_min) + str(t.tm_sec) +&#039;.jpg&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            filename = str(self.ui.Camera_SnapFilename.text() + &#039;.jpg&#039;)&lt;br /&gt;
        cv2.imwrite(filename, self.snapshot_image)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To record the video from the video stream, the other class was implemented which took care of the video recording functionality.&lt;br /&gt;
&lt;br /&gt;
 class VideoRecordingThread(QThread): &lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # maybe replace with something else?&lt;br /&gt;
        frame_width = int(cap.get(3))&lt;br /&gt;
        frame_height = int(cap.get(4))&lt;br /&gt;
        out = cv2.VideoWriter(&#039;PiVideo_1.avi&#039;,cv2.VideoWriter_fourcc(&#039;M&#039;,&#039;J&#039;,&#039;P&#039;,&#039;G&#039;), 10, (frame_width,frame_height))&lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                out.write(cv_img)  &lt;br /&gt;
        cap.release()&lt;br /&gt;
        out.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5093</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5093"/>
		<updated>2020-12-06T16:43:06Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
The following functions are crucial to actually display the image onto the label of the Qlabel object. The covert_cv_qt(cv_img) takes the output image of the mjpeg streamer and converts it to the qt format so it can be displayed in our Qt Gui application.&lt;br /&gt;
&lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
The update_image() actually updates the image and displays it to the Qlabel. As you can notice, this function uses the convert_cv_qt function to transform the received image in order to display it properly.&lt;br /&gt;
&lt;br /&gt;
    def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) &lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) &lt;br /&gt;
            self.take_snapshot = False&lt;br /&gt;
&lt;br /&gt;
Current setup for the Camera includes also the functionality to take a picture and record a video with the camera. It is a little more simple with taking a picture from the stream because we are just taking last snapped image from the update_image() function and save the image under the entered filename using snapshot_save() function. As it can be seen below, the picture by default saves the &amp;quot;.jpg&amp;quot; file with the current date and time. Otherwise, an user can enter the filename that he would like to use and save it under that. And using the opencv function cv2.imwrite() we save the picture.&lt;br /&gt;
&lt;br /&gt;
 def snapshot_save(self):    &lt;br /&gt;
        t = time.gmtime()&lt;br /&gt;
        if self.ui.Camera_SnapFilename.text() == &amp;quot;&amp;quot;:&lt;br /&gt;
            filename = &#039;snapshot_&#039; + str(t.tm_year) + str(t.tm_mon) + str(t.tm_mday) + &amp;quot;_&amp;quot; + str(t.tm_hour) + str(t.tm_min) + str(t.tm_sec) +&#039;.jpg&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            filename = str(self.ui.Camera_SnapFilename.text() + &#039;.jpg&#039;)&lt;br /&gt;
        cv2.imwrite(filename, self.snapshot_image)&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5092</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5092"/>
		<updated>2020-12-02T18:59:11Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
The following functions are crucial to actually displaying immage onto the label of the Qlabel object. The covert_cv_qt(cv_img) takes the output immage of the mjpeg streamer and converts to the qt format so it can be displayed in our Qt Gui application.&lt;br /&gt;
&lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
The update_image() actually updates the image and displays it to the Qlabel. As you can notice, this function uses the convert_cv_qt function to transform the received image in order to display it properly.&lt;br /&gt;
&lt;br /&gt;
    def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) &lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) &lt;br /&gt;
            self.take_snapshot = False&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5091</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5091"/>
		<updated>2020-12-02T18:52:21Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  def update_image(self, cv_img):&lt;br /&gt;
        if not self.camera_enabled:&lt;br /&gt;
            cv_img = cv2.imread(&#039;nosignal.png&#039;)  &lt;br /&gt;
        qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
        self.ui.Stream_Label.setPixmap(qt_img) &lt;br /&gt;
        if self.camera_enabled and self.take_snapshot: &lt;br /&gt;
            qt_img = self.convert_cv_qt(cv_img)&lt;br /&gt;
            self.snapshot_image = cv_img&lt;br /&gt;
            self.ui.Snapshot_Label.setPixmap(qt_img) &lt;br /&gt;
            self.take_snapshot = False        &lt;br /&gt;
    def convert_cv_qt(self, cv_img):&lt;br /&gt;
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)&lt;br /&gt;
        h, w, ch = rgb_image.shape&lt;br /&gt;
        bytes_per_line = ch * w&lt;br /&gt;
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)&lt;br /&gt;
        p = convert_to_Qt_format.scaled(500, 500, Qt.KeepAspectRatio)&lt;br /&gt;
        return QPixmap.fromImage(p)&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5090</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5090"/>
		<updated>2020-12-02T18:48:51Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
As the Video stream will need to happen simultaneously to other robot actions, we pass the &#039;&#039;Qthread&#039;&#039; as an argument for this class, which will treat this function as a parallel thread to the main function.&lt;br /&gt;
Using the open-cv library we are able to capture a video stream and converting it to the Qt format in which we will be able to display the actual image to the &#039;&#039;Qlabel&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5089</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5089"/>
		<updated>2020-12-02T18:36:38Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 $ pip install opencv-python&lt;br /&gt;
&lt;br /&gt;
 import cv2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5088</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5088"/>
		<updated>2020-12-02T18:35:29Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
To implement the mjpeg-streamer also requires &#039;&#039;open cv&#039;&#039; library installed on your machine. This way the image can be displayed using the open-cv framework.&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # &lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5087</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5087"/>
		<updated>2020-12-02T18:33:33Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 class VideoThread(QThread):&lt;br /&gt;
    change_pixmap_signal = pyqtSignal(np.ndarray)&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        super().__init__()&lt;br /&gt;
        self._run_flag = True&lt;br /&gt;
&lt;br /&gt;
    def run(self):&lt;br /&gt;
        cap = cv2.VideoCapture(&#039;http://cleo.local:8080/?action=stream&#039;) # maybe replace with something else?&lt;br /&gt;
        while self._run_flag:&lt;br /&gt;
            ret, cv_img = cap.read()&lt;br /&gt;
            if ret:&lt;br /&gt;
                self.change_pixmap_signal.emit(cv_img) #cv_img    &lt;br /&gt;
        cap.release()&lt;br /&gt;
&lt;br /&gt;
    def stop(self):&lt;br /&gt;
        self._run_flag = False&lt;br /&gt;
        self.wait()&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5086</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5086"/>
		<updated>2020-12-02T18:29:23Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5085</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5085"/>
		<updated>2020-12-02T18:28:45Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
To get a snapshot of the current stream use this:&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=snapshot&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5084</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5084"/>
		<updated>2020-12-02T18:27:27Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
It is possible to also see the stream using the Web-browser using the host address. This is convinient way how to test if the streamer works correctly.&lt;br /&gt;
&lt;br /&gt;
 http://&#039;robotname&#039;.local:8080/?action=stream&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5083</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5083"/>
		<updated>2020-12-02T18:24:48Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If the picture is reversed, use this function to display to run the stream:&lt;br /&gt;
&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so -hf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5082</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5082"/>
		<updated>2020-12-02T18:23:53Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
Now everything is set to runt the streamer:&lt;br /&gt;
&lt;br /&gt;
 *If you are running from the root folder, remember to go to the folder -&amp;gt;&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ ./mjpg_streamer -o &amp;quot;output_http.so -w ./www&amp;quot; -i &amp;quot;input_raspicam.so&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5081</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5081"/>
		<updated>2020-12-02T18:21:43Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
To download the project you will need a source control system called git. It may not be installed on a fresh image. I know it’s not on the lite image. So you may need to install it.&lt;br /&gt;
 $ sudo apt-get install git&lt;br /&gt;
&lt;br /&gt;
Now that you have git installed, use it to clone a copy of the mjpg-streamer to your Pi.&lt;br /&gt;
&lt;br /&gt;
 $ git clone https://github.com/jacksonliam/mjpg-streamer.git&lt;br /&gt;
&lt;br /&gt;
After the cloned copy of the mjpeg-stream has been coppied to the raspberry-pi, follow these steps.&lt;br /&gt;
&lt;br /&gt;
 $ cd mjpg-streamer/&lt;br /&gt;
 $ cd mjpg-streamer-experimental/&lt;br /&gt;
 $ sudo apt-get install cmake&lt;br /&gt;
 $ sudo apt-get install python-imaging&lt;br /&gt;
 $ sudo apt-get install libjpeg-dev&lt;br /&gt;
 $ make CMAKE_BUILD_TYPE=Debug&lt;br /&gt;
 $ sudo make install &lt;br /&gt;
&lt;br /&gt;
When all the necessary libraries have been installed, it is necessary to run the following script to make this setting permanent.&lt;br /&gt;
&lt;br /&gt;
 $ export LD_LIBRARY_PATH=.&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5080</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5080"/>
		<updated>2020-12-02T18:07:44Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System. *Note that you have to be connected to the network in order to update the software. In some cases, it might be successful, as the robot has not got internet access even though it should have been connected to the internet wirelessly -  the best and reliable solution is to use an Ethernet cable and connect to the DTU network using some ports, that can be found around the Campus.&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5079</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5079"/>
		<updated>2020-12-02T18:04:39Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, that we are going to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;How to set up mjpeg streamer on Raspbery-Pi&#039;&#039;&#039;&lt;br /&gt;
Connect to your Raspberry-Pi&lt;br /&gt;
 $ ssh local@IP(or robot name)&lt;br /&gt;
&lt;br /&gt;
Update the Operating System&lt;br /&gt;
 $ sudo apt-get update&lt;br /&gt;
Enable the camera and Launch raspi-config:&lt;br /&gt;
&lt;br /&gt;
 $ sudo raspi-config&lt;br /&gt;
From the menu options:&lt;br /&gt;
&lt;br /&gt;
 Select:&lt;br /&gt;
 6. Enable Camera&lt;br /&gt;
 Click: Yes&lt;br /&gt;
 Click: OK&lt;br /&gt;
 Click: Finish&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5078</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5078"/>
		<updated>2020-12-02T17:55:41Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
The designed Camera window has been designed as in the following picture. &lt;br /&gt;
The key tip designing the mjpeg-streamer in Qt-designer, is to use &#039;&#039;QLabel&#039;&#039; object for the actual frame where to display the actual live-video footage.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5077</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5077"/>
		<updated>2020-12-02T17:51:56Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5076</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5076"/>
		<updated>2020-12-02T17:51:47Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5075</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5075"/>
		<updated>2020-12-02T17:50:23Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
&lt;br /&gt;
[[File:Camera.JPG]]&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=File:Camera.JPG&amp;diff=5074</id>
		<title>File:Camera.JPG</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=File:Camera.JPG&amp;diff=5074"/>
		<updated>2020-12-02T17:50:07Z</updated>

		<summary type="html">&lt;p&gt;S192320: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5073</id>
		<title>Python interface</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Python_interface&amp;diff=5073"/>
		<updated>2020-12-02T17:50:02Z</updated>

		<summary type="html">&lt;p&gt;S192320: /* Implementation of the Video Stream and Camera */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Qt GUI interface design ==&lt;br /&gt;
&lt;br /&gt;
The Regbot desktop application is designed using the Qt-Designer app which uses the widgets from the Qt GUI framework. The application gives a possibility to quickly build interfaces using the drag-and-drop feature for placing necessary components in your interface.&lt;br /&gt;
The Qt designer produces .ui files that can be translated to C++ or Python interface code.&lt;br /&gt;
This user interface was designed using Python 3.7.9 and Qt version 5.&lt;br /&gt;
&#039;&#039;To some extent, it was possible to use Visual Studio, but I ran into some problems with finding the necessary libraries for some tasks I wanted to use (like cv2, keyboard and some PyQT libraries). A Safe choice was to use one Python development platform on your PC, for example, Spyder (as it was necessary to use this for other courses). &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:UI.JPG | 900px]]&lt;br /&gt;
&lt;br /&gt;
The translated UI file will be autogenerated with all the parameters that were set in Qt-Designer app like - object name, size, and many other parameters that correspond to each specific object.&lt;br /&gt;
At first, to use Qt-Designer version 5 with python it is necessary to install the necessary python packages:&lt;br /&gt;
 # Installing PyQt5 package&lt;br /&gt;
 pip install pyqt5-tools&lt;br /&gt;
 pip install pyqt5&lt;br /&gt;
&lt;br /&gt;
After installing and designing your user-interface it is necessary to convert your user interface .ui file to a .py file, that can be run in your program.&lt;br /&gt;
The translated .ui files can be auto-generated using the following commands:&lt;br /&gt;
 # Generating Python interface file from Qt-Designer .ui file&lt;br /&gt;
 pyuic5 -x &amp;quot;filename&amp;quot;.ui -o &amp;quot;filename&amp;quot;.py&lt;br /&gt;
&lt;br /&gt;
Note that this file is generated in the directory where the terminal was opened, so make sure that you know where this file is located after using this command.&lt;br /&gt;
&lt;br /&gt;
In the picture below, you can see an example of an auto-generated python interface file, that can later be included in the rest of your application as a user interface.&lt;br /&gt;
By knowing the name of the object, the actual user interface now can also be tweaked by editing the Python code.&lt;br /&gt;
&lt;br /&gt;
[[File:UIpy.JPG | 400px]]&lt;br /&gt;
&lt;br /&gt;
If this auto-generated python file is run on its own, the user interface is executed as an output of this program.&lt;br /&gt;
&lt;br /&gt;
[[File:gui.JPG | 700px]]&lt;br /&gt;
&lt;br /&gt;
To add functionality to the object of the user interface (like buttons, checkboxes, text fields, etc.), we can attach a function to the interface object to be executed, for example, if the check box is checked, or button is pressed. This can be done by taking the user interface object in the python .py file and passing your function to it as an argument. &lt;br /&gt;
&lt;br /&gt;
By running the python class script, the first function that is run is &amp;quot;__init__&amp;quot;. In &amp;quot;__init__&amp;quot; usually we define all other python classes that need to be initialized and defined.&lt;br /&gt;
The following code is a template example of how to connect the user-interface object with the function in your program. The specific object-related function (clicked, released, pressed) varies from object to object.&lt;br /&gt;
 # self.&amp;quot;user-interface-file&amp;quot;.&amp;quot;object-name&amp;quot;.&amp;quot;function&amp;quot;.connect(self.&amp;quot;function name&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
See the example below:&lt;br /&gt;
&lt;br /&gt;
[[File:code1.JPG | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Programming GUI ==&lt;br /&gt;
&lt;br /&gt;
As the whole Regbot firmware and bridge software is already programmed (see section &amp;quot;Navigation box software&amp;quot; on the main robobot page). And accessing the local@IP port 24001 we are able to receive and send byte-type messages to the network socket. Thus, by building a GUI, it is possible to control the robot remotely, read/test sensor values, read and write robot system parameters to its configuration.&lt;br /&gt;
&lt;br /&gt;
The first thing that is needed to do is install the socket library and define/initialize is the network socket (internet connection) to the robobot bridge.&lt;br /&gt;
&lt;br /&gt;
 # import socket&lt;br /&gt;
   class GUIMainWindow(QtWidgets.QMainWindow):&lt;br /&gt;
        socket = socket.socket()&lt;br /&gt;
&lt;br /&gt;
When that is done, we connect the actual &amp;quot;connect&amp;quot; button to the connectClient() function do all the necessary steps to establish a connection with the chosen IP address.&lt;br /&gt;
The Connect client functions &lt;br /&gt;
   self.ui.connect_Cmd.clicked.connect(self.connectClient)&lt;br /&gt;
&lt;br /&gt;
[[File:Connectclient.JPG]]&lt;br /&gt;
&lt;br /&gt;
The connectClient() function implementation can be seen below. To make a stable socket connection, at first, it is required to get the address information parameters like - Adress Family, Socket Type and the Adress Info (IP and port). Then using the previously extracted parameters we define the actual network socket and connect with the IP adress using &#039;&#039;socket.socket.connect(IP)&#039;&#039;. If the attempt to make a connection is faulted, it is necessary to close a connection using &#039;&#039;socket.close()&#039;&#039; function. In the code below some extra functions are implemented aswell, for example to change the frame color or text in the status label to indicate that the connection has successfully been made or not. &lt;br /&gt;
&lt;br /&gt;
  def connectClient(self):&lt;br /&gt;
        if not self.wifiConnected:&lt;br /&gt;
            IP = str(&#039;192.168.43.101&#039;) # Connect automatically to this address&lt;br /&gt;
            #IP = str(self.ui.IP_Adress_field.text()) # Manual IP adress input&lt;br /&gt;
            # Getting address info for the connection&lt;br /&gt;
            for addressInfo in socket.getaddrinfo(IP, 24001, socket.AF_UNSPEC, socket.SOCK_STREAM): &lt;br /&gt;
                print(&amp;quot;Socket Info &amp;quot; + str(addressInfo)) # Printing the whole adress info &lt;br /&gt;
                AddresFamily = addressInfo[0] # Extracting the Adress Family type&lt;br /&gt;
                print(&amp;quot;# Adress Family &amp;quot;, AddresFamily)&lt;br /&gt;
                SocketType = addressInfo[1] # Extracting the Socket Type&lt;br /&gt;
                print(&amp;quot;# Socket Type &amp;quot; , SocketType)&lt;br /&gt;
                IP = addressInfo[4] # both IP and port number&lt;br /&gt;
                print(&amp;quot;# IP adress and port &amp;quot;, IP)&lt;br /&gt;
            try:      &lt;br /&gt;
                self.socket = socket.socket(AddresFamily, SocketType, 0) # Making a actual networ socket with necessary adress parameters.&lt;br /&gt;
                self.socket.settimeout(5)&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                print(&amp;quot;# Network Connection timeout - retry&amp;quot;)&lt;br /&gt;
            try:  &lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Connecting to Client...&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
                print(&amp;quot;Connecting to Client...&amp;quot;)&lt;br /&gt;
                self.socket.connect(IP) # Connecting to the adress&lt;br /&gt;
                self.wifiConnected = True&lt;br /&gt;
                self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(0,255,0,255))&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Connected&#039;)&lt;br /&gt;
                print(&amp;quot;Connected&amp;quot;)&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Network is Connected\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;Socket Info &amp;quot; + str(addressInfo) + &amp;quot;\n&amp;quot;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
            except OSError as msg:&lt;br /&gt;
                self.socket.close() # closing the connection if faulted&lt;br /&gt;
                self.ui.IP_NetworkStatus_label.setText(&#039;Faulted&#039;)&lt;br /&gt;
&#039;&#039;&#039;To avoid the confusion, why the connection with the Raspberry Pi suddenly faulted, it is required to continuously send &amp;quot;alive&amp;quot; or heartbeat command&amp;quot;u5&amp;quot;. The Pi is configured in a way, if it doesn&#039;t receive anything in the connection socket for couple of seconds, the connection is terminated.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a similar fashion the &#039;&#039;Disconnect&#039;&#039; button in the GUI is connected with the &#039;&#039;disconnectClient()&#039;&#039; function. To correctly close down the connection, it is required to shut down the socket using &#039;&#039;socket.shutdow()&#039;&#039; and then close the connection using &#039;&#039;socket.close()&#039;&#039;. As one can notice, other different functions are called in &#039;&#039;disconnectClient()&#039;&#039; function, which stops other features in the programm.&lt;br /&gt;
&lt;br /&gt;
  def disconnectClient(self):&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            print(&amp;quot;Disconnecting...&amp;quot;)&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnecting&#039;)&lt;br /&gt;
            self.wifiConnected = False&lt;br /&gt;
            self.video_StopRecording()&lt;br /&gt;
            self.socket.shutdown(2) # Shutting down the socket&lt;br /&gt;
            self.socket.close() # Closing down the connection&lt;br /&gt;
            self.setConnectFrameColor(self.ui.IP_Connect_frame,QtGui.QColor(200,0,0,255))&lt;br /&gt;
            self.ui.IP_NetworkStatus_label.setText(&#039;Disconnected&#039;)&lt;br /&gt;
            print(&amp;quot;Disconnected&amp;quot;)&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network is Disconnected\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The messages in the code to the socket can be written using the socket function &#039;&#039;socket.send(&amp;quot;MESSAGE-TEXT&amp;quot;)&#039;&#039;. The most important thing that the string message has to be converted to a type byte to be able to successfully send the message to the socket.&lt;br /&gt;
&lt;br /&gt;
  def connectionWrite(self,MESSAGE):&lt;br /&gt;
        MESSAGE = bytes(MESSAGE, &#039;utf-8&#039;)&lt;br /&gt;
        if self.wifiConnected:&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        if (self.wifiConnected):&lt;br /&gt;
            if (self.ui.TX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(tx)-&amp;gt;&amp;quot;+ str(MESSAGE) +&#039;\n&#039;&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        else:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;Network not connected, could not send message -&amp;quot; + str(MESSAGE)&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
Here is visible the screenshot of the debugging message window in the GUI. Here using TX and RX checkboxes we can see what messages are sent to the connection and received back. As described before, the GUI button &amp;quot;Send&amp;quot; is connected with the text input field and function &#039;&#039;connectionWrite()&#039;&#039;.&lt;br /&gt;
By pressing the Help button, the &amp;quot;help&amp;quot; message is sent to the socket, and then as the reply, we are receiving back the reply from the connection. This reply consists of a list of function messages that can be written in the socket in order to receive actual data from the robobot bridge. &lt;br /&gt;
&lt;br /&gt;
[[File:Debugger.JPG]]&lt;br /&gt;
&lt;br /&gt;
By sending the specific predifined command to the socket, the reply will be provided that later can be used as an actual value for editing the robot configuration, displaying the sensor values etc. However, the reply is sent in a form one string, so the decoding function is needed to be implemented to seperate the values. The list of these messages can be seen here:&lt;br /&gt;
 (rx)-&amp;gt; # help for special second parameter:&lt;br /&gt;
 (rx)-&amp;gt; #   get          Gets the value of the tata item&lt;br /&gt;
 (rx)-&amp;gt; #   meta         Gets &#039;key meta r vs s p description&#039;: r=responder, vs: 0=val 1=seq, s=source, p=priority&lt;br /&gt;
 (rx)-&amp;gt; #   subscribe p  Set subscription priority 0=none, 1=fast (10ms), 5 = slow (6sec), 6=all updates &lt;br /&gt;
 (rx)-&amp;gt; #   status       Sends status &#039;key status c T n p p p p ...&#039; c: update count, T: since last (sec), n=clients slots, p client priority&lt;br /&gt;
 (rx)-&amp;gt; #   name xxx     Sets name or description for data item&lt;br /&gt;
 (rx)-&amp;gt; #   logopen      Opens a (new) logfile and log all updates with timestamp (key.txt)&lt;br /&gt;
 (rx)-&amp;gt; #   logclose     Closes logfile (if open)&lt;br /&gt;
 (rx)-&amp;gt; #   h            This help&lt;br /&gt;
 (rx)-&amp;gt; &lt;br /&gt;
 (rx)-&amp;gt; # NB! you now subscribed to all &#039;#&#039; messages&lt;br /&gt;
 (rx)-&amp;gt; # (use &#039;robot silent&#039; or &#039;# subscribe 0&#039; to undo)&lt;br /&gt;
 (rx)-&amp;gt; # Robot connection has these subcommand options:&lt;br /&gt;
 (rx)-&amp;gt; #     clogopen   open communication log for all traffic to and from Regbot&lt;br /&gt;
 (rx)-&amp;gt; #     clogclose  close these logfiles&lt;br /&gt;
 (rx)-&amp;gt; #     help       This help text&lt;br /&gt;
 (rx)-&amp;gt; #     Regbot communication open=1&lt;br /&gt;
 (rx)-&amp;gt; # RegBot commands ($Id: command.cpp 1175 2020-01-04 15:40:53Z jcan $)&lt;br /&gt;
 (rx)-&amp;gt; # M=N         Select mission 0=user mission, 1.. = hard coded mission (M=0)&lt;br /&gt;
 (rx)-&amp;gt; # i=1         Interactive: 0: GUI info, 1: use local echo of all commands, -1:no info  (i=0)&lt;br /&gt;
 (rx)-&amp;gt; # s=N A       Log interval in milliseconds (s=5) and allow A=1 (is 1)&lt;br /&gt;
 (rx)-&amp;gt; # lcl 0 0 0 0 0 0 0 0 0  Log control: vel,head,pos,edge,walldist,fwd-dist,bal,bal-vel,bal-pos (is 100000000)&lt;br /&gt;
 (rx)-&amp;gt; # log start    Start logging to 70kB RAM (is=0, logged 0/1458 lines)&lt;br /&gt;
 (rx)-&amp;gt; # log get      Transfer log to USB (active=0)&lt;br /&gt;
 (rx)-&amp;gt; # motw m1 m2   Set motor PWM -1024..1024 (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # motv m1 m2   Set motor voltage -6.0 .. 6.0 (is=0.00 0.00)&lt;br /&gt;
 (rx)-&amp;gt; # mote m1 m2   Set motor enable (left right) (is=0 0)&lt;br /&gt;
 (rx)-&amp;gt; # u0..u8       Status: u0:ver,u1:measure,u2:mission, u3:log,u4:robot,u5:heartbeat,u6:mag,u8:motorVA&lt;br /&gt;
 (rx)-&amp;gt; # u9..u14      Status: Line sensor u9=limit w, u10=limits B, u11=value, u12=raw, u13=pos and x, u14=ADC&lt;br /&gt;
 (rx)-&amp;gt; # u15..u24     Status:  15=acc, 16=gyro, 17=gyro offs, 18=motor (A), 19=enc, 20=vel(m/s), 21=pos(m), 22=pose, 23=button, 24=voltage (V)&lt;br /&gt;
 (rx)-&amp;gt; # u25..u31     Status:  25,26 encoder calibrate raw values (cpu clock units), u27,28 calibrate factors, u29 enc status, u30 line sensor, u31 line sensor gain&lt;br /&gt;
 (rx)-&amp;gt; # v0..2        Status: v0:IR sensor data, v1:wifi status, v2 wifi clients&lt;br /&gt;
 (rx)-&amp;gt; # xID          Get Control params, ID: cvel=wheel vel, ctrn=turn, cwve=wall vel, cwth=wall turn, cpos=position,&lt;br /&gt;
 (rx)-&amp;gt; # cedg=edge, cbal=balance, cbav=bal vel, ctrl=rate limit&lt;br /&gt;
 (rx)-&amp;gt; # ID x x ...   Set controler parameters (ID use Kp ... see format below)&lt;br /&gt;
 (rx)-&amp;gt; # format: ID use kp iuse itau ilim Lfuse LfNum LfDen Lbuse LbNum LbDen preUse preNum preDen&lt;br /&gt;
 (rx)-&amp;gt; # preIuse preItau preIlim ffUse ffKp ffFuse ffFnum ffFden LimUse Lim&lt;br /&gt;
 (rx)-&amp;gt; # rid=d d d d d d d d d d  Robot ID set: ID wheelBase gear PPR RadLeft RadRight balanceOffset flags batt_low HW_version&lt;br /&gt;
 (rx)-&amp;gt; # eew          Save configuration to EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeW          Get configuration as string&lt;br /&gt;
 (rx)-&amp;gt; # eer          Read configuration from EE-Prom&lt;br /&gt;
 (rx)-&amp;gt; # eeR=X        Read config and mission from hard coded set X=0: empty, X=1 follow wall&lt;br /&gt;
 (rx)-&amp;gt; # sub s p m    Subscribe s=1, unsubscribe s=0, p=priority 0 (high) .. 4(low), m=msg:0=hbt, 1=pose,2=IR,3=edge,4=acc,5=gyro,6=current,7=vel,8=mission&lt;br /&gt;
 (rx)-&amp;gt; # sut t p      msg interval for priority, t=time in ms 1.., p=priority 0..4 (is 0=1 1=5 2=25 3=125 4=625)&lt;br /&gt;
 (rx)-&amp;gt; # posec        Reset pose and position&lt;br /&gt;
 (rx)-&amp;gt; # gyroo        Make gyro offset calibration&lt;br /&gt;
 (rx)-&amp;gt; # mem          Some memory usage info&lt;br /&gt;
 (rx)-&amp;gt; # start        start mission now&lt;br /&gt;
 (rx)-&amp;gt; # stop         terminate mission now&lt;br /&gt;
 (rx)-&amp;gt; # rc=A V T [B] Remote control A=0/1 (is 0), V=m/s (is 0), T=vel difference (is 0), B=1 try keep balance&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;add user-mission-line&amp;gt;     add a user mission line (3 lines loaded in 1 threads)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;mod T L user-mission-line  Modify line L in thread T to new user-mission-line&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;clear&amp;gt;      Clear all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;get&amp;gt;        Get all user mission lines&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;event=X&amp;gt;    Make an event number X (from 0 to 31)&lt;br /&gt;
 (rx)-&amp;gt; # &amp;lt;token&amp;gt;      Get all user mission lines as tokens&lt;br /&gt;
 (rx)-&amp;gt; # :xxxx        Send data (AT commands) to wifi board (all except the &#039;:&#039;) \r\n is added&lt;br /&gt;
 (rx)-&amp;gt; # link=L,data  Send data to socket link L&lt;br /&gt;
 (rx)-&amp;gt; # wifi use port SSID PW   Wifi setup (e.g. &#039;wifi 1 24001 &amp;quot;device&amp;quot; &amp;quot;&amp;quot;&#039;)&lt;br /&gt;
 (rx)-&amp;gt; # wifi e/n     Echo all received from 8266 to USB link&lt;br /&gt;
 (rx)-&amp;gt; # halt         Turn 12V power off (on by halt=0) (is 0)&lt;br /&gt;
 (rx)-&amp;gt; # alive        Alive command - reply: &amp;lt;alive last=&amp;quot;0.0xxx&amp;quot;/&amp;gt;&lt;br /&gt;
 (rx)-&amp;gt; # iron 1       IR sensor on (1 = on, 0 = off) is=0&lt;br /&gt;
 (rx)-&amp;gt; # irc 2 8 2 8 u i  IR set 20cm 80cm 20cm 80cm on installed&lt;br /&gt;
 (rx)-&amp;gt; # servo N P V  Set servo N=1..3 (4,5) P (position):-512..+512 (&amp;gt;1024 = disable), V (velocity): 0=full, 1..10 P-values/per ms&lt;br /&gt;
 (rx)-&amp;gt; # (status: (servo 1,2,3) enabled 0,0,0, P= 0, 0, 0, V= 2, 2, 2)&lt;br /&gt;
 (rx)-&amp;gt; # servo -1 frw Set PWM frequency&lt;br /&gt;
 (rx)-&amp;gt; # svo          Get servo status (same format as below)&lt;br /&gt;
 (rx)-&amp;gt; # svo s1 p1 v1 s2 p2 v2 s3 p3 v3 s4 p4 v4 s5 p5 v5  Set servo status sx=enable px=position (-1000..1000), vx velocity 0 (full) 1..10 (slower)&lt;br /&gt;
 (rx)-&amp;gt; # encc, enci     Encoder calibrate: enc: calibrate now, eni: Find calibration index now&lt;br /&gt;
 (rx)-&amp;gt; # eneX=1       enec=1/0 enable timing collect, eneu=1/0 use calibration, enea=1/0 auto calibrate&lt;br /&gt;
 (rx)-&amp;gt; # silent=1     Should USB be silent, if no communication (1=auto silent)&lt;br /&gt;
 (rx)-&amp;gt; # help         This help text.&lt;br /&gt;
 (rx)-&amp;gt; # ADC use=1, seq=0, resetcnt=0, reset&lt;br /&gt;
&lt;br /&gt;
== Receiving, Decoding and Translating received messages ==&lt;br /&gt;
&lt;br /&gt;
When the correct message is sent, and the bridge has understood the message, it provides a reply. The reply usually is in the form as show in the following string - &amp;quot;keyword parameter1 parameter2 parameter3 ... parameter_n&amp;quot;.&lt;br /&gt;
For example, sending the message &amp;quot;u5&amp;quot;, we receive :&lt;br /&gt;
 &#039;&#039;(rx)-&amp;gt; hbt 6542.6 11.5319 0 0 0 221&#039;&#039;&lt;br /&gt;
 where [0] element - is the keyword&lt;br /&gt;
 [1] element - regbot time since startup&lt;br /&gt;
 [2] element - battery voltage&lt;br /&gt;
 [3] element - control active flag&lt;br /&gt;
 [4] element - mission state&lt;br /&gt;
 [5] element - remote control flag&lt;br /&gt;
 [6] element - cpu load&lt;br /&gt;
&lt;br /&gt;
The translation and the meaning of each element can be found in the previous &amp;quot;help&amp;quot; section.&lt;br /&gt;
&lt;br /&gt;
It is possible to get a continuous stream of data of the selected parameter of by sending a subscription command - &amp;quot;keyword subscribe x&amp;quot;, where x is the priority of the subscribtion stream.&lt;br /&gt;
For example sending a subscribrion command to subscribe to linesensor values:&lt;br /&gt;
 liv subscribe 6&lt;br /&gt;
 (rx)-&amp;gt; liv 3035 3279 3067 3178 3322 2683 3045 2761&lt;br /&gt;
 *RAW values of the line sensor&lt;br /&gt;
&lt;br /&gt;
To decode the received message, it is necessary to create a function that separates the values of the received string.&lt;br /&gt;
Using the readReply() function, firstly make sure that we have a valid, stable connection and then with &#039;&#039;socket.recv(1).decode()&#039;&#039; we read the reply from the socket chatacter-by-character and putting them all together to get a string message. When the full message has been stacked together, we pass the &amp;quot;message&amp;quot; in the function decodeCommand() which will seperate the necessary values.&lt;br /&gt;
     def readReply(self):&lt;br /&gt;
        c = &#039;\0&#039;&lt;br /&gt;
        self.socket.settimeout(1)&lt;br /&gt;
        while (not self.stop.is_set()):&lt;br /&gt;
            if self.wifiConnected:&lt;br /&gt;
                n = 0&lt;br /&gt;
                if (c == &#039;\n&#039;):&lt;br /&gt;
                    MESSAGE = &amp;quot;&amp;quot;&lt;br /&gt;
                c = &#039;\0&#039;&lt;br /&gt;
                try: &lt;br /&gt;
                      while (c != &#039;\n&#039; and self.wifiConnected):  &lt;br /&gt;
                            c = self.socket.recv(1).decode()&lt;br /&gt;
                            if (len(c) &amp;gt; 0):&lt;br /&gt;
                                    if (c &amp;gt;= &#039; &#039; or c == &#039;\n&#039;):&lt;br /&gt;
                                        MESSAGE = MESSAGE + str(c)&lt;br /&gt;
                      n = len(MESSAGE)&lt;br /&gt;
                except:&lt;br /&gt;
                      time.sleep(0.01)&lt;br /&gt;
                if (n &amp;gt; 0):&lt;br /&gt;
                    self.decodeCommand(MESSAGE, n)&lt;br /&gt;
            else:&lt;br /&gt;
                  time.sleep(0.1)&lt;br /&gt;
        pass           &lt;br /&gt;
&lt;br /&gt;
The &amp;quot;message&amp;quot; then is passed around, until it reaches the function that recognizes the initial keyword. *The Function also is printed on the GUI message terminal.&lt;br /&gt;
For example, if the decodeCommand() function recognizes that the message belongs to linesensor.readData(), the message is treated further in this function.&lt;br /&gt;
&lt;br /&gt;
    def decodeCommand(self, MESSAGE, n):&lt;br /&gt;
        if n &amp;gt; 0:&lt;br /&gt;
            if (self.ui.RX_checkBox.isChecked()):&lt;br /&gt;
                self.ConsoleMessage += &amp;quot;(rx)-&amp;gt; &amp;quot; + str(MESSAGE)&lt;br /&gt;
                self.ConsoleMessagePush = True&lt;br /&gt;
        if n &amp;gt; 1:&lt;br /&gt;
            gg = MESSAGE.split()&lt;br /&gt;
            &lt;br /&gt;
            if gg[0] == &amp;quot;hbt&amp;quot;: &lt;br /&gt;
                print(gg, &#039;hbt&#039;)&lt;br /&gt;
            elif self.info.readData(gg):&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.lineSensor.readData(gg): &amp;lt;------------------&lt;br /&gt;
                pass&lt;br /&gt;
            elif self.servo.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
            if self.irdist.readData(gg): &lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As we received the message that belongs to linesensor.readData() function, we are able to split the array of string parameters and assign them to our own variables, that we can pass to our GUI objects.&lt;br /&gt;
&lt;br /&gt;
 # readData function from linesensor class&lt;br /&gt;
 def readData(self, MESSAGE):&lt;br /&gt;
        used = True&lt;br /&gt;
        try:&lt;br /&gt;
            if MESSAGE[0] == &amp;quot;liv&amp;quot;:&lt;br /&gt;
                self.dataReadLiv = True&lt;br /&gt;
                if self.ui.ls_show_normalized.isChecked():&lt;br /&gt;
                    maxraw = self.ui.line_disp_max_value.value()&lt;br /&gt;
                    self.lineValue[0] = maxraw * (int(MESSAGE[1],0) - self.lineMaxBlack[0])/(self.lineMaxWhite[0] - self.lineMaxBlack[0])&lt;br /&gt;
                    self.lineValue[1] = maxraw * (int(MESSAGE[2],0) - self.lineMaxBlack[1])/(self.lineMaxWhite[1] - self.lineMaxBlack[1])&lt;br /&gt;
                    self.lineValue[2] = maxraw * (int(MESSAGE[3],0) - self.lineMaxBlack[2])/(self.lineMaxWhite[2] - self.lineMaxBlack[2])&lt;br /&gt;
                    self.lineValue[3] = maxraw * (int(MESSAGE[4],0) - self.lineMaxBlack[3])/(self.lineMaxWhite[3] - self.lineMaxBlack[3])&lt;br /&gt;
                    self.lineValue[4] = maxraw * (int(MESSAGE[5],0) - self.lineMaxBlack[4])/(self.lineMaxWhite[4] - self.lineMaxBlack[4])&lt;br /&gt;
                    self.lineValue[5] = maxraw * (int(MESSAGE[6],0) - self.lineMaxBlack[5])/(self.lineMaxWhite[5] - self.lineMaxBlack[5])&lt;br /&gt;
                    self.lineValue[6] = maxraw * (int(MESSAGE[7],0) - self.lineMaxBlack[6])/(self.lineMaxWhite[6] - self.lineMaxBlack[6])&lt;br /&gt;
                    self.lineValue[7] = maxraw * (int(MESSAGE[8],0) - self.lineMaxBlack[7])/(self.lineMaxWhite[7] - self.lineMaxBlack[7])&lt;br /&gt;
                else:    &lt;br /&gt;
                    self.lineValue[0] = int(MESSAGE[1],0)&lt;br /&gt;
                    self.lineValue[1] = int(MESSAGE[2],0)&lt;br /&gt;
                    self.lineValue[2] = int(MESSAGE[3],0)&lt;br /&gt;
                    self.lineValue[3] = int(MESSAGE[4],0)&lt;br /&gt;
                    self.lineValue[4] = int(MESSAGE[5],0)&lt;br /&gt;
                    self.lineValue[5] = int(MESSAGE[6],0)&lt;br /&gt;
                    self.lineValue[6] = int(MESSAGE[7],0)&lt;br /&gt;
                    self.lineValue[7] = int(MESSAGE[8],0)&lt;br /&gt;
&lt;br /&gt;
These values can be stored and assigned to line sensor GUI object element for displaying its value.&lt;br /&gt;
By subscribing to different parameters like - liv,lib lip, we can get the calibrated values and configuration parameters for edge detection, crossing detection etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Linesensor.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Manual Robot Control from the GUI ==&lt;br /&gt;
&lt;br /&gt;
Just like it is possible to start the robot mission and control it remotely from the game-controller, it is possible to manually control the motor movement remotely from the user-interface or computer keyboard.&lt;br /&gt;
As it can be seen in the picture below, the two checkboxes correspond to each mode - GUI control, where pressing the arrow buttons in the user-interface, will control the robot with the speed specified in the text-field.&lt;br /&gt;
&lt;br /&gt;
Similarly, the robot can be controlled using the computer keyboard arrows, with &#039;&#039;keyboard control&#039;&#039; enabled and manual speed specified in the manual speed text-field. *If the manual speed is not specified, the remote control is not possible.&lt;br /&gt;
&lt;br /&gt;
[[File:Rc.JPG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The function that controls the remote manual control from the computer keyboard is as follows. This requires to installation and import python &#039;&#039;keyboard&#039;&#039; library.&lt;br /&gt;
The basic principle of controlling the robot is as discussed before - by sending the &amp;quot;rc = enable fwd_speed turn_speed&amp;quot; string to the IP address port 24001 connection.&lt;br /&gt;
 &amp;gt;&amp;gt;&amp;gt;pip install keyboard &lt;br /&gt;
&lt;br /&gt;
     import keyboard &lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC_enable(self):&lt;br /&gt;
        self.keyboardRC_enabled = not self.keyboardRC_enabled&lt;br /&gt;
        if self.keyboardRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*Keyboard Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        self.keyboardRC()&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     def keyboardRC(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.keyboardRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            if keyboard.is_pressed(&#039;up arrow&#039;):  # if key &#039;up&#039; is pressed &lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;down arrow&#039;):  # if key &#039;down&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)  &lt;br /&gt;
            elif keyboard.is_pressed(&#039;left arrow&#039;):  # if key &#039;left&#039; is pressed&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
            elif keyboard.is_pressed(&#039;right arrow&#039;):  # if key &#039;right&#039; is pressed&lt;br /&gt;
                rev = float(speed)*(-1)&lt;br /&gt;
                MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
                self.socket.send(MESSAGE) &lt;br /&gt;
            elif keyboard.is_pressed(&#039;esc&#039;):  # if key &#039;esc&#039; is pressed &lt;br /&gt;
                self.ui.enableKeyboardRC_checkBox.setChecked(False) &lt;br /&gt;
                self.keyboardRC_enable()&lt;br /&gt;
            else:&lt;br /&gt;
                MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
                self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
For the robot to follow the input from the user as the action has been done without huge time delays, it is necessary for some conditions to be checked periodically and continuously. That&#039;s why the &#039;&#039;timer&#039;&#039; is used.&lt;br /&gt;
The timer is set to start counting and until the defined value in milliseconds has been reached and the timeout signal has been provided which runs the assigned functions periodically.&lt;br /&gt;
&lt;br /&gt;
        self.timer = QtCore.QTimer(self)&lt;br /&gt;
        self.timer.start(50) # Start&#039;s the timer with 50 ms interval&lt;br /&gt;
        self.timer.timeout.connect(self.keyboardRC) # After each timout, the function is executed.&lt;br /&gt;
        self.timer.timeout.connect(self.timerUpdate)&lt;br /&gt;
&lt;br /&gt;
The functions for remote control using GUI, is developed in similar fashion, by sending the manual control string message to the IP port connection. The code can be seen below.&lt;br /&gt;
&lt;br /&gt;
     # Assigning the button the function for remote control.&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.pressed.connect(self.guiRC_fwd)&lt;br /&gt;
     self.ui.guiRC_Forward_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.pressed.connect(self.guiRC_rev)&lt;br /&gt;
     self.ui.guiRC_Reverse_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.pressed.connect(self.guiRC_right)&lt;br /&gt;
     self.ui.guiRC_Right_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.pressed.connect(self.guiRC_left)&lt;br /&gt;
     self.ui.guiRC_Left_Cmd.released.connect(self.guiRC_idle)&lt;br /&gt;
    &lt;br /&gt;
     def guiRC_enable(self):&lt;br /&gt;
        self.guiRC_enabled = not self.guiRC_enabled&lt;br /&gt;
        if self.guiRC_enabled:&lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control enabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True&lt;br /&gt;
        else:    &lt;br /&gt;
            self.ConsoleMessage += &amp;quot;*GUI Remote control disabled\n&amp;quot;&lt;br /&gt;
            self.ConsoleMessagePush = True    &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_fwd(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed): &lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+speed+&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
        &lt;br /&gt;
    def guiRC_rev(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 &#039;+ str(rev) +&#039; 0\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_right(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            rev = float(speed)*(-1)&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ str(rev) +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE)  &lt;br /&gt;
        &lt;br /&gt;
    def guiRC_left(self):&lt;br /&gt;
        speed = self.ui.manualGuiSpeed_Cmd.text()&lt;br /&gt;
        if self.guiRC_enabled and speed != &amp;quot;&amp;quot; and float(speed):&lt;br /&gt;
            MESSAGE = bytes(&#039;rc=1 0 &#039;+ speed +&#039;\n&#039;, &#039;utf-8&#039;)&lt;br /&gt;
            self.socket.send(MESSAGE) &lt;br /&gt;
            &lt;br /&gt;
    def guiRC_idle(self):&lt;br /&gt;
        if self.guiRC_enabled:    &lt;br /&gt;
            MESSAGE = b&#039;rc=1 0 0\n&#039;&lt;br /&gt;
            self.socket.send(MESSAGE)&lt;br /&gt;
&lt;br /&gt;
== Implementation of the Video Stream and Camera ==&lt;br /&gt;
&lt;br /&gt;
In this section, it is going to be described how to implement the video stream in your python Qt Gui application using mjpeg-streamer.&lt;br /&gt;
The first thing to do is to design the layout for your mjpeg-streamer. The mjpg-streamer is a command-line application that copies JPEG frames from one or more input plugins to multiple output plugins.&lt;br /&gt;
[[File:Camera.JPG]]&lt;br /&gt;
&lt;br /&gt;
== The Source Files ==&lt;/div&gt;</summary>
		<author><name>S192320</name></author>
	</entry>
</feed>