Terminal RS232 :

Ce logiciel écrit en Python pour Linux est un outil d'aide au développement de logiciels pilotant des appareils connectés par la voie série (RS232).

Il permet :

  •  d'envoyer des ordres (en Ascii ou en décimal) à l'appareil ;
  • de récupérer les données renvoyées par cet appareil. Selon le type de données qu'il renvoie, on pourra sélectionner le mode de visualisation adéquat : Ascii, Décimal ou Hexadécimal.
  • de positionner au niveau 0 ou 1 les lignes DTR et/ou RTS

L'interface est inspirée du logiciel fourni par Philippe André dans son ouvrage "La liaison RS232" aux Editions Dunod/ETSF. (A lire pour qui souhaiterait en savoir plus sur la communication série RS232).

L'utilisation de la communication série sous Linux peut nécessiter quelques éclaircissements que l'on trouvera dans le premier onglet ci-dessous :

Port série sous Linux

Dépendance :

Pour fonctionner, le logiciel nécessite l'installation d'un module python de communication série (python-serial) : dans le Gestionnaire de paquets, vérifier sa présence ; l'installer si nécessaire (ici c'est la version 2.5-2.1 qui est installée) :

python-serial.png

 

status_unknown.pngAttention : Linux étant un système sécurisé, vous n'avez peut-être pas les droits pour accéder en tant qu'utlisateur aux ports série.

Ouvrir un Terminal et entrer la commande :

ls -l /dev/ttyS*

Voici un exemple de retour :

droits_tty.png

Pour des convertisseurs USB-série, taper la commande :

ls -l /dev/ttyU* 

Voici un retour pour deux câbles USB-série (le grand luxe !) :

droits_ttyUsb.png

On voit qu'il faut appartenir au groupe "dialout" pour accéder en tant qu'utilisateur à ces différents ports série.

Pour savoir à quels groupes on appartient, entrer dans un terminal la commande :

groups

Un exemple de retour (réalisé sur une distribution Debian toute neuve dans une VirtualBox) :

groups.png

On voit que l'utilisateur "vincent" n'appartient pas au group "dialout"

Appartenir au groupe "dialout" :

Pour que cet utilisateur "vincent" puisse appartenir au groupe "dialout", entrer les commandes suivantes dans un Terminal :

su     ( il faudra ensuite entrer son mot de passe administrateur)

usermod -a -G dialout vincent  (remplacer bien sûr l'utilisateur "vincent" par le vôtre !)


(Rque : sur une distribution SuSE Linux, la commande à entrer est légèrement différente :

usermode -A dialout vincent           )...

Voici l'ensemble de la séquence (à remarquer : la commande groups réitérée après usermod ne montre pas l'appartenance de l'utilisateur "vincent" au groupe "dialout". Il faudra faire un redémarrage pour que cela soit pris en compte :

usermod_avant_redemarage.png

Après redémarrage :

usermod_apres_reem.png

Remarque : l'ensemble des groupes disponibles se trouve dans le fichier etc/group. On aurait pu le lire avec gedit par exemple, voire même le modifier directement (en mode superutilisateur ... mais attention à ne pas faire d'erreurs !) :

c67ab3ba9b56073038c451f57b61c6c3_f812.png

 

 

 

Utilisation du Terminal

Ci-dessous, le logiciel en fonctionnement : une connexion a été réalisée vers l'alimentation AL991S (décrite ici). Un exemple d'ordre est réalisé :

En mode Ascii, on envoie le code R? (voir la zone "Emission") auquel on ajoute un code de retour chariot (\r) (Cf notice de l'alimentation : AL991S_Com_Protocole.pdf).

L'alimentation répond en Ascii :

Capture_du_2013-04-28_001633.png

Ce logiciel m'a permis de développer plus facilement le code nécessaire à la gestion de cette alimentation. Voir la page Pilotage AL991S), mais aussi le logiciel Rigolinux pour le pilotage d'oscilloscopes de la marque Rigol, ou le logiciel Balance (Windows par la fenêtre...).

Le logiciel de communication pour la carte Quatre jauges sur USB a été également développé à l'aide du logiciel Terminal. La carte retourne 4 octets résultants de la conversion analogique-numérique réalisée sur les 4 voies d'acquisition lorsqu'elle reçoit un octet correspondant à la lettre Ascii M. On a alors intérêt dans ce type de situation à se mettre en Ascii pour la fenêtre Emission et en Décimal pour la fenêtre Réception :

Capture_du_2013-04-28_001532.png

En effet, le mode Ascii en réception n'est absolument pas parlant pour ce cas de figure :

Capture_du_2013-04-28_001507.png

 

Le code source

#!/usr/bin/python
#-*- coding: iso-8859-15 -*-

from __future__ import division
from gi.repository import Gtk, GObject
import os, sys
import serial
import string
from time import sleep
import glob


class Terminal:
	def __init__(self):

		self.builder = Gtk.Builder()
		self.builder.add_from_file("Terminal.glade")

		#recuperation des widgets utilises par le programme :
		self.btOuvrir = self.builder.get_object("btOuvrir")
		self.btFermer = self.builder.get_object("btFermer")
		self.btQuitter = self.builder.get_object("btQuitter")
		self.btRafraichir = self.builder.get_object("btRafraichir")
		self.comboPort = self.builder.get_object("comboPort")
		self.comboBauds = self.builder.get_object("comboBauds")
		self.rb7bits = self.builder.get_object("rb7bits")
		self.rb8bits = self.builder.get_object("rb8bits")
		self.rbStop1 = self.builder.get_object("rbStop1")
		self.rbStop2 = self.builder.get_object("rbStop2")
		self.comboParite = self.builder.get_object("comboParite")
		self.swDTR = self.builder.get_object("swDTR")
		self.swRTS = self.builder.get_object("swRTS")
		self.textEmission = self.builder.get_object("textEmission")
		self.rb_Emission_Ascii = self.builder.get_object("rb_Emission_Ascii")
		self.rb_Emission_Deci = self.builder.get_object("rb_Emission_Deci")

		self.btEffaceTexteEmis = self.builder.get_object("btEffaceTexteEmis")
		self.cb_CR = self.builder.get_object("cb_CR")
		self.cb_LF = self.builder.get_object("cb_LF")
		self.btEnvoyer = self.builder.get_object("btEnvoyer")
		self.textReception = self.builder.get_object("textReception")
		self.rb_Reception_Ascii = self.builder.get_object("rb_Reception_Ascii")
		self.rb_Reception_Deci = self.builder.get_object("rb_Reception_Deci")
		self.rb_Reception_Hexa = self.builder.get_object("rb_Reception_Hexa")
		self.btEffaceTexteRecu = self.builder.get_object("btEffaceTexteRecu")
		
		self.textAide = self.builder.get_object("textAide")
		self.textAPropos = self.builder.get_object("textAPropos")

		self.textAide = self.builder.get_object("textAide")
		self.textAPropos = self.builder.get_object("textAPropos")

		self.buffer_aide = self.textAide.get_buffer()
		fichier_aide = open("Aide.txt", "r")
		if fichier_aide:
			texte_aide = fichier_aide.read()
			fichier_aide.close()
			self.buffer_aide.set_text(texte_aide)
			
		fichier_apropos = open("A_propos.txt", "r")
		if fichier_apropos:
			texte_apropos = fichier_apropos.read()
			fichier_apropos.close()
			self.buffer_apropos = self.textAPropos.get_buffer()
			self.buffer_apropos.set_text(texte_apropos)

		self.buffer_emission = self.textEmission.get_buffer()
		self.buffer_reception = self.textReception.get_buffer()

		self.builder.connect_signals(self)

		window = self.builder.get_object('window')
		window.show_all()

		self.lister_ports()

		#initialisations :
		self.sortie =""
		self.port_serie = " "
		self.port_choisi = " "
		self.data_format = " "
		self.debit = 9600
		self.parite = ""

		self.n_data = 0
		self.data_deci = []
		self.char_ascii = []
		self.char_hexa = []
		self.liste = []
		self.btFermer.set_sensitive(False)
		self.btQuitter.set_sensitive(False)
		self.btEnvoyer.set_sensitive(False)

	def lister_ports(self) :
		  list_port =  glob.glob('/dev/ttyUSB*') + glob.glob('/dev/ttyS*')
		  i = 0
		  while i < len(list_port):
			self.comboPort.append_text(list_port[i])
			i=i+1

	def on_btRafraichir_clicked(self,widget):
		#Rafraichir la liste des ports série disponibles
		self.comboPort.remove_all()
		self.lister_ports()

	def on_btOuvrir_clicked(self,widget):
		# recuperation des parametres choisis :
		self.port_choisi = self.comboPort.get_active_text()

		debit = self.comboBauds.get_active_text()

		if self.rb8bits.get_active():
		  data_format = serial.EIGHTBITS
		else:
		  data_format = serial.SEVENBITS

		if self.rbStop1.get_active():
		  stop = serial.STOPBITS_ONE
		else:
		  stop = serial.STOPBITS_TWO

		if self.comboParite.get_active() == 0:
		  parite = serial.PARITY_NONE
		if self.comboParite.get_active() == 1:
		  parite = serial.PARITY_ODD
		if self.comboParite.get_active() == 2:
		  parite = serial.PARITY_EVEN

		try:
		  # desactivation de widgets (ceux qui ne doivent pas
		  # etre modifies une fois le port serie ouvert) :
		  self.comboPort.set_sensitive(False)
		  self.comboBauds.set_sensitive(False)
		  self.comboParite.set_sensitive(False)
		  self.rb7bits.set_sensitive(False)
		  self.rb8bits.set_sensitive(False)
		  self.rbStop1.set_sensitive(False)
		  self.rbStop2.set_sensitive(False)
		  self.btRafraichir.set_sensitive(False)
		  self.btQuitter.set_sensitive(True)

		  #ouverture du port serie :
		  self.port_serie = serial.Serial(
			port=self.port_choisi,
			baudrate = debit,
			bytesize = data_format,
			parity = parite,
			stopbits = stop,
			timeout=2
		  )

		  #activation/desactivation de widgets :
		  self.btFermer.set_sensitive(True)
		  self.btEnvoyer.set_sensitive(True)
		  self.btOuvrir.set_sensitive(False)

		except :

		  self.comboPort.set_sensitive(True)
		  self.comboBauds.set_sensitive(True)
		  self.comboParite.set_sensitive(True)
		  self.rb7bits.set_sensitive(True)
		  self.rb8bits.set_sensitive(True)
		  self.rbStop1.set_sensitive(True)
		  self.rbStop2.set_sensitive(True)

		  self.btOuvrir.set_sensitive(True)
		  self.btEnvoyer.set_sensitive(False)
		  self.btRafraichir.set_sensitive(True)

		  #self.bt_fermer.set_sensitive(False)
		  self.ouvert = False
		  msg = "Erreur lors de l'ouverture du port série : ce port n'est peut-être pas valide, ou bien vous n'avez pas les droits pour accéder aux ports série"
		  dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, msg)
			# Montre le dialog
		  dialog.run()
			# Destruction du dialog
		  dialog.destroy()

	def on_swDTR_button_press_event(self,widget,event):
		if self.swDTR.get_active() ==True :
			self.port_serie.setDTR(level=1)
		else:
			self.port_serie.setDTR(level=0)
			
	def on_swRTS_button_press_event(self,widget,event):
		if self.swRTS.get_active() ==True :
			self.port_serie.setRTS(level=1)
		else:
			self.port_serie.setRTS(level=0)
			

	def on_btEnvoyer_clicked(self,widget):
		iterdebut = self.buffer_emission.get_start_iter()
		iterfin = self.buffer_emission.get_end_iter()
		#envoi des donnees sur le port serie
		if (self.rb_Emission_Ascii.get_active() == True):
			texte_emis = self.buffer_emission.get_text(iterdebut,iterfin,True)

		if (self.rb_Emission_Deci.get_active() == True):
			#texte_emis = (re.findall(r'\b\d+\b', self.buffer_emission.get_text(iterdebut,iterfin,True)))
			self.liste = [int(s) for s in self.buffer_emission.get_text(iterdebut,iterfin,True).split() if s.isdigit()]
			new_list = [chr(i) for i in self.liste]
			texte_emis = ''.join(new_list)

		 # avec ou sans ajout de \r et/ou \n (CR et/ou LF) :
		if (self.cb_CR.get_active()== False) and (self.cb_LF.get_active() == False):
			self.port_serie.write(texte_emis)

		if (self.cb_CR.get_active()== True) and (self.cb_LF.get_active() == True) :
			self.port_serie.write(texte_emis + '\r' + '\n')

		else :
		  if (self.cb_CR.get_active()== True):
			self.port_serie.write(texte_emis + '\r' )

		  if self.cb_LF.get_active() == True :
			self.port_serie.write(texte_emis + '\n')

		sleep(1)

		self.data_deci = []
		self.char_ascii = []
		self.char_hexa = []
		self.n_data = self.port_serie.inWaiting()
		for i in range (self.n_data):
		  self.data_deci.append(ord(self.port_serie.read(1)))

		if self.rb_Reception_Deci.get_active() == True:
		  texte_recu = str(self.data_deci)

		if self.rb_Reception_Hexa.get_active() == True :
		  texte_recu = ""
		  for i in range (self.n_data):
			self.char_hexa.append(hex(self.data_deci[i]))
			texte_recu += str(self.char_hexa[i]) + "  "

		if self.rb_Reception_Ascii.get_active() == True:
		  texte_recu = ""
		  for i in range (self.n_data) :
			if 32 <= self.data_deci[i] <= 126:
			  self.char_ascii.append(chr(self.data_deci[i]))
			else:
			  self.char_ascii.append(".")

			texte_recu += str(self.char_ascii[i])

		self.buffer_reception.set_text(texte_recu)



	def on_rb_Emission_Ascii_clicked(self,widget):
		self.buffer_emission.set_text('')

	def on_rb_Emission_Deci_clicked(self,widget):
		self.buffer_emission.set_text('')

	def on_rb_Reception_Hexa_clicked(self,widget):
		texte_recu = ""
		for i in range (self.n_data):
			self.char_hexa.append(hex(self.data_deci[i]))
			texte_recu += str(self.char_hexa[i]) + "  "
		self.buffer_reception.set_text(texte_recu)

	def on_rb_Reception_Deci_clicked(self,widget):
		texte_recu = str(self.data_deci)
		self.buffer_reception.set_text(texte_recu)

	def on_rb_Reception_Ascii_clicked(self,widget):
		texte_recu = ""
		for i in range (self.n_data) :
			if 32 <= self.data_deci[i] <= 126:
				self.char_ascii.append(chr(self.data_deci[i]))
			else:
				self.char_ascii.append(".")
			texte_recu += str(self.char_ascii[i])
		self.buffer_reception.set_text(texte_recu)

	def on_btEffaceTexteEmis_clicked(self,widget):
		self.buffer_emission.set_text('')
		self.port_serie.setDTR(level=0)



	def on_btEffaceTexteRecu_clicked(self,widget):
		self.data_deci = []
		self.n_data = 0
		self.buffer_reception.set_text('')


	def on_btFermer_clicked(self,widget):
		#fermeture du port série ouvert
		try :
			if self.port_serie.isOpen() == True:
				self.port_serie.close()
				self.comboPort.set_sensitive(True)
				self.btOuvrir.set_sensitive(True)
				self.btRafraichir.set_sensitive(True)
				self.btFermer.set_sensitive(False)
				self.comboBauds.set_sensitive(True)
				self.comboParite.set_sensitive(True)
				self.rb7bits.set_sensitive(True)
				self.rb8bits.set_sensitive(True)
				self.rbStop1.set_sensitive(True)
				self.rbStop2.set_sensitive(True)

				self.btEnvoyer.set_sensitive(False)


		except :
			msgFermer = "Erreur lors de la fermeture du port s©rie !"
			dlgFermer = Gtk.MessageDialog(None, 0, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, msgFermer)
			dlgFermer.run()
			dlgFermer.destroy()

	def on_btQuitter_clicked(self,widget):
		Gtk.main_quit()

	def destroy(window, self):
		Gtk.main_quit()

def main():
	app = Terminal()
	Gtk.main()

if __name__ == "__main__":
	sys.exit(main())

 katuberling.png Sur cette page :

- un logiciel libre écrit en Python (avec le listing source fourni) : Terminal.zip

- Connaissances Linux apportées :

  •  commandes : ls, groups, usermod
  •  fichier : etc/group 
  •     communication série (python-serial)