Etiquetas: , ,
0
(0)

Cuando se desarrolla un proyecto con Arduino, Raspberry Pi o cualquier microcontrolador, sin duda habrá que crear una interfaz gráfica como, un monitor en serie, para la gestión del sistema (depuración, observación de las mediciones, lanzamiento de acciones, etc.). Hay muchas herramientas para crear interfaces gráficas. En este proyecto, crearemos una interfaz con Python usando el marco QT(PySide2).

Objetivo

Para este proyecto, queremos crear una interfaz gráfica en Python, bajo Windows, que se comporte de forma similar al monitor de serie del IDE de Arduino. Para ello, tendremos que realizar las siguientes funciones

  • Área de selección del puerto USB
  • Botón de conexión
  • Campo de contabilización para el pedido
  • Botón de envío
  • La consola que muestra los datos enviados por Arduino

Código Arduino

El código de Arduino para la comunicación en serie es bastante simple, definimos la comunicación en serie y escuchamos en el puerto serie en cada bucle. Si aparece una información, el algoritmo devuelve lo que ha recibido.

/*---------------------------------------------------------------------- 
* Project : Test 
* Author : AranaCorp 
* Version : V01 
* Date : 7/4/2020 
* 
* Summary : 
* Hardware : 
   - Arduino UNO x1 
   - usbserial x1 
   - usbserialIn x1     https://www.aranacorp.com/fr/communiquez-avec-votre-arduino/ 
* 
* www.aranacorp.com 
----------------------------------------------------------------------*/ 
//Variables 
String msg ; 
 
void setup(){ 
//Init Serial USB 
Serial.begin(115200); 
delay(500);
Serial.println(F(">> Initialize System")); 
} 
 
void loop(){ 
readSerialPort(); 
} 
 
void readSerialPort(){/* function readSerialPort */ 
////Write something into the serial monitor to test serial communication 
while (Serial.available()) { 
   delay(10); 
   if (Serial.available() >0) { 
     char c = Serial.read();  //gets one byte from serial buffer 
     msg += c; //makes the string readString 
   } 
 } 
 if (msg.length() >0){ 
    Serial.println(">> "+msg); 
    msg=""; 
 } 
} 
 
 

Para este proyecto usamos el Arduino UNO, pero este código puede ser aplicado a todos los microcontroladores que pueden ser programados usando el Arduino IDE.

Recuperar los dispositivos USB conectados

Para recuperar los puertos USB usados, está la biblioteca serial.tools.list_ports

def find_USB_device():
	myports = [tuple(p) for p in list(serial.tools.list_ports.comports())]
	print(myports)
	usb_port_list = [p[0] for p in myports]
	
	return usb_port_list

Bajo linux, es posible usar un subproceso con el comando lsusb.

Código Python

Primero crearemos la ventana de la aplicación que llamamos Serial Interface que se comportará como el monitor en serie.

#!/usr/bin/python3
# -*-coding:Utf-8 -*

import sys,os, time
import platform
from random import randint
import serial,serial.tools.list_ports

#interface import
from PySide2.QtWidgets import QApplication, QMainWindow,QDesktopWidget, QTextEdit, QLineEdit, QPushButton, QMessageBox, QWidget, QGridLayout, QTextEdit, QGroupBox, QVBoxLayout,QHBoxLayout, QComboBox, QLabel
from PySide2.QtGui import QIcon, QScreen

class SerialInterface(QMainWindow):
	def __init__(self, parent=None):
		super().__init__(parent)
		self.width=650
		self.height=350
		
		self.resize(self.width, self.height)
		self.setWindowIcon(QIcon('./resources/logo-100.png'))
		self.setWindowTitle( 'Serial Monitor')
		
		#center window on screen
		qr = self.frameGeometry()
		cp = QScreen().availableGeometry().center()
		qr.moveCenter(cp)
		
		
		#init layout
		centralwidget = QWidget(self)
		centralLayout=QHBoxLayout(centralwidget)
		self.setCentralWidget(centralwidget)
		
		#add connect group
		#self.connectgrp=GroupClass(self)
		#centralLayout.addWidget(self.connectgrp)

if __name__ == "__main__":
	app = QApplication(sys.argv)
	frame = SerialInterface()
	frame.show()
	sys.exit(app.exec_())
	

Luego crearemos una clase que contendrá todos los Widgets que necesitamos para crear el Monitor Serial Python (QLineEdit,QTextEdit,QButton).

class GroupClass(QGroupBox):
	def __init__(self,widget,title="Connection Configuration"):
		super().__init__(widget)
		self.widget=widget
		self.title=title
		self.sep="-"
		self.id=-1
		self.name=''
		self.items=find_USB_device()
		self.serial=None
		self.init()
		
	def init(self):
		self.setTitle(self.title)
		
		self.selectlbl = QLabel("Select port:")
		#label
		self.typeBox=QComboBox()
		self.typeBox.addItems(self.items)#database getMotionType()
		self.typeBox.setCurrentIndex(self.typeBox.count()-1)
		
		#btn
		button = QPushButton("Connect")
		button.clicked.connect(self.connect)
		sendBtn = QPushButton("send")
		sendBtn.clicked.connect(self.sendData)
		
		titlelbl=  QLabel("Enter")
		self.title = QLineEdit("")
		desclbl=QLabel("Console")
		self.desc = QTextEdit("")
			
		self.fields=QGridLayout()
		self.fields.addWidget(self.selectlbl,0,0,1,1)
		self.fields.addWidget(self.typeBox,0,1,1,1)
		self.fields.addWidget(button,0,2,1,1)
		
		self.fields.addWidget(titlelbl,1,0,1,1)
		self.fields.addWidget(self.title,1,1,1,1)
		self.fields.addWidget(sendBtn,1,2,1,1)
		self.fields.addWidget(desclbl,2,0,1,1)
		self.fields.addWidget(self.desc,3,1,1,1)
		self.setLayout(self.fields)

Descomponga estas dos líneas para añadirlo a la ventana principal:

#self.connectgrp=GroupClass(self)
#centralLayout.addWidget(self.connectgrp)

Una vez que nuestros widgets estén en su lugar, seremos capaces de explotarlos. Primero, nos encargaremos de la conexión. Para ello, enlazaremos el botón «Conectar» con la función de la siguiente manera:

button.clicked.connect(self.connect)

Luego definiremos la función correspondiente que comprobará si el puerto serie no está ya abierto para evitar errores. Entonces crea y abre el puerto serie, para leer los datos enviados por Arduino.

	def connect(self):
		self.desc.setText("")
		self.desc.setText(">> trying to connect to port %s ..." % self.typeBox.currentText())
		if self.serial is None:
			self.serial=serial.Serial(self.typeBox.currentText(), 115200, timeout=1)
			time.sleep(0.05)
			#self.serial.write(b'hello')
			answer=self.readData()
			if answer!="":
				self.desc.setText(self.desc.toPlainText()+"\n>> Connected!\n"+answer)
		else:
			self.desc.setText(">> {} already Opened!\n".format(self.typeBox.currentText()))

Para recuperar y poner los datos enviados por Arduino en el formato correcto, crearemos otra función self.readData. Como podemos ver, almacenará los datos enviados por Arduino mientras haya datos disponibles en el buffer.

	def readData(self):
		self.serial.flush() # it is buffering. required to get the data out *now*
		answer=""
		while  self.serial.inWaiting()>0: #self.serial.readable() and
			answer += "\n"+str(self.serial.readline()).replace("\\r","").replace("\\n","").replace("'","").replace("b","")
		return answer	

Una vez que la conexión y la reproducción sea operacional, crearemos el comando sendData para poder enviar comandos al Arduino a través del monitor serial Python. La función recupera el texto del QLineEdit correspondiente y lo envía al puerto serie. Luego usamos la función readData para recuperar la respuesta del Arduino.

	def sendData(self):
		if self.serial.isOpen():
			if self.title.text() != "":
				self.serial.write(self.title.text().encode())
				answer=self.readData()
				self.desc.setText(self.desc.toPlainText()+"\n"+answer)

Código completo

#!/usr/bin/python3
# -*-coding:Utf-8 -*

import sys,os, time
import platform
from random import randint
import serial,serial.tools.list_ports

#interface import
import PySide2


from PySide2.QtWidgets import QApplication, QMainWindow,QDesktopWidget, QTextEdit, QLineEdit, QPushButton, QMessageBox, QWidget, QGridLayout, QTextEdit, QGroupBox, QVBoxLayout,QHBoxLayout, QComboBox, QLabel

from PySide2.QtGui import QIcon, QScreen


__prgm__ = 'Serial Monitor'
__version__ = '0.0.2'

def find_USB_device(USB_DEV_NAME=None):
    myports = [tuple(p) for p in list(serial.tools.list_ports.comports())]
	print(myports)
	usb_port_list = [p[0] for p in myports]
		usb_device_list = [p[1] for p in myports]
		print(usb_device_list)

		if USB_DEV_NAME is None:
			return myports
		else:
			USB_DEV_NAME=str(USB_DEV_NAME).replace("'","").replace("b","")
			for device in usb_device_list:
				print("{} -> {}".format(USB_DEV_NAME,device))
				print(USB_DEV_NAME in device)
				if USB_DEV_NAME in device:
					print(device)
					usb_id = device[device.index("COM"):device.index("COM")+4]
			
					print("{} port is {}".format(USB_DEV_NAME,usb_id))
					return usb_id
				
class GroupClass(QGroupBox):
	def __init__(self,widget,title="Connection Configuration"):
		super().__init__(widget)
		self.widget=widget
		self.title=title
		self.sep="-"
		self.id=-1
		self.name=''
		self.portlist=find_USB_device()
		self.items=[p[0] for p in self.portlist]#["COM1","COM2"]
		self.serial=None
		#self.motionDict={"POSITION BASED":" Describe motion based on position","VELOCITY BASED":" Describe motion based on velocity", "LOOP":" Describe loop motion", "PINGPONG":" Describe pingpong motion", "INTERACTIF":" Describe interactive motion"}
		self.init()
		
	def init(self):
		self.setTitle(self.title)
		
		self.selectlbl = QLabel("Select port:")
		#label
		self.typeBox=QComboBox()
		self.typeBox.addItems(self.items)#database getMotionType()
		self.typeBox.setCurrentIndex(self.typeBox.count()-1)
		
		#btn
		button = QPushButton("Connect")
		button.clicked.connect(self.connect)
		#hbox.addWidget(button)
		sendBtn = QPushButton("send")
		sendBtn.clicked.connect(self.sendData)
		#hbox.addWidget(button)
		
		titlelbl=  QLabel("Enter")
		self.title = QLineEdit("")
		desclbl=QLabel("Console")
		self.desc = QTextEdit("")
		
		#self.add=QPushButton("Ajouter/Modifier")
		#self.add.clicked.connect(self.addItem)
		#self.rem=QPushButton("Supprimer")
		#self.rem.clicked.connect(self.remItem)
			
		self.fields=QGridLayout()
		self.fields.addWidget(self.selectlbl,0,0,1,1)
		self.fields.addWidget(self.typeBox,0,1,1,1)
		self.fields.addWidget(button,0,2,1,1)
		
		self.fields.addWidget(titlelbl,1,0,1,1)
		self.fields.addWidget(self.title,1,1,1,1)
		self.fields.addWidget(sendBtn,1,2,1,1)
		self.fields.addWidget(desclbl,2,0,1,1)
		self.fields.addWidget(self.desc,3,1,1,1)
		#self.fields.addWidget(self.add,2,2,1,1)
		#self.fields.addWidget(self.rem,3,2,1,1)
		self.setLayout(self.fields)
	
	def connect(self):
		
		self.desc.setText("")
		self.desc.setText(">> trying to connect to port %s ..." % self.typeBox.currentText())
		#with serial.Serial(self.typeBox.currentText(), 115200, timeout=1) as self.serial:
		if self.serial is None:
			self.serial=serial.Serial(self.typeBox.currentText(), 115200, timeout=1)
			time.sleep(0.05)
			#self.serial.write(b'hello')
			answer=self.readData()
			if answer!="":
				self.desc.setText(self.desc.toPlainText()+"\n>> Connected!\n"+answer)
		else:
			self.desc.setText(">> {} already Opened!\n".format(self.typeBox.currentText()))
			
	def sendData(self):
		if self.serial.isOpen():
			if self.title.text() != "":
				self.serial.write(self.title.text().encode())
				answer=self.readData()
				if(self.title.text().encode()=="scan"):
					print("scanning results -> "+answer.find("0x"))
				else:
					print(answer.find("0x"))
				self.desc.setText(self.desc.toPlainText()+"\n"+answer)
					
	def readData(self):
		#self.serial.flush() # it is buffering. required to get the data out *now*
		answer=""
		while  self.serial.inWaiting()>0: #self.serial.readable() and
			
			print(self.serial.inWaiting())
			answer += "\n"+str(self.serial.readline()).replace("\\r","").replace("\\n","").replace("'","").replace("b","")
			#print(self.serial.inWaiting())
		#self.desc.setText(self.desc.toPlainText()+"\n"+answer)
		return answer	
	
			
class SerialInterface(QMainWindow):
	def __init__(self, parent=None):
		super().__init__(parent)
		self.width=650
		self.height=350
		
		self.resize(self.width, self.height)
		self.setWindowIcon(QIcon('./resources/logo-100.png'))
		self.setWindowTitle(__prgm__)
		
		#center window on screen
		qr = self.frameGeometry()
		cp = QScreen().availableGeometry().center()
		qr.moveCenter(cp)
		
		
		#init layout
		centralwidget = QWidget(self)
		centralLayout=QHBoxLayout(centralwidget)
		self.setCentralWidget(centralwidget)
		
		#add connect group
		self.connectgrp=GroupClass(self)
		centralLayout.addWidget(self.connectgrp)
		
			

if __name__ == "__main__":
	app = QApplication(sys.argv)
	frame = SerialInterface()
	frame.show()
	sys.exit(app.exec_())
	

Fuentes

¿De cuánta utilidad te ha parecido este contenido?

¡Haz clic en una estrella para puntuar!

Promedio de puntuación 0 / 5. Recuento de votos: 0

Hasta ahora, ¡no hay votos!. Sé el primero en puntuar este contenido.

Ya que has encontrado útil este contenido...

¡Sígueme en los medios sociales!

¡Siento que este contenido no te haya sido útil!

¡Déjame mejorar este contenido!

Dime, ¿cómo puedo mejorar este contenido?