PyQt Lab est un logiciel open source qui permet aux utilisateurs de créer des applications graphiques pour le contrôle et l’automatisation des systèmes embarqués. Dans ce tutoriel, nous allons voir comment utiliser PyQt Lab pour contrôler un Arduino à l’aide d’un port série et d’un bouton poussoir pour allumer et éteindre une LED. Nous verrons comment configurer le port série, comment envoyer des données à l’Arduino et comment créer un programme pour contrôler la LED.
PyQt Lab’ : Port Série : en envoi : Allumer une LED côté Arduino avec un PushButton en mode ON/OFF.
Par X. HINAULT – Juin 2013

Ce que l’on va faire ici
- Dans ce code PyQt, une interface qui permet d’allumer une LED côté Arduino avec un PushButton en mode ON/OFF.
Pré-requis
- python 2.7
- pyqt4.x
- modules :
- python-serial
Téléchargement :
- L’archive des codes est disponible ici : ..
Le fichier d’interface *.py
- Fichier obtenu automatiquement avec l’utilitaire pyuic4 à partir du fichier *.ui créé avec QtDesigner :
# Form implementation generated from reading ui file
#
# Created: Wed Jan 16 12:06:34 2013
# by: PyQt4 UI code generator 4.9.1
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName(_fromUtf8(« Form »))
Form.resize(432, 255)
self.comboBoxPort = QtGui.QComboBox(Form)
self.comboBoxPort.setGeometry(QtCore.QRect(10, 20, 191, 24))
self.comboBoxPort.setEditable(True)
self.comboBoxPort.setObjectName(_fromUtf8(« comboBoxPort »))
self.comboBoxPort.addItem(_fromUtf8(« »))
self.comboBoxPort.addItem(_fromUtf8(« »))
self.comboBoxPort.addItem(_fromUtf8(« »))
self.comboBoxPort.addItem(_fromUtf8(« »))
self.labelPort = QtGui.QLabel(Form)
self.labelPort.setGeometry(QtCore.QRect(10, 5, 101, 16))
self.labelPort.setObjectName(_fromUtf8(« labelPort »))
self.comboBoxDebit = QtGui.QComboBox(Form)
self.comboBoxDebit.setGeometry(QtCore.QRect(10, 65, 106, 24))
self.comboBoxDebit.setObjectName(_fromUtf8(« comboBoxDebit »))
self.comboBoxDebit.addItem(_fromUtf8(« »))
self.comboBoxDebit.addItem(_fromUtf8(« »))
self.comboBoxDebit.addItem(_fromUtf8(« »))
self.comboBoxDebit.addItem(_fromUtf8(« »))
self.comboBoxDebit.addItem(_fromUtf8(« »))
self.comboBoxDebit.addItem(_fromUtf8(« »))
self.comboBoxDebit.addItem(_fromUtf8(« »))
self.comboBoxDebit.addItem(_fromUtf8(« »))
self.labelDebit = QtGui.QLabel(Form)
self.labelDebit.setGeometry(QtCore.QRect(10, 50, 111, 16))
self.labelDebit.setObjectName(_fromUtf8(« labelDebit »))
self.pushButtonInitSerial = QtGui.QPushButton(Form)
self.pushButtonInitSerial.setGeometry(QtCore.QRect(125, 55, 81, 41))
self.pushButtonInitSerial.setObjectName(_fromUtf8(« pushButtonInitSerial »))
self.textEditReception = QtGui.QTextEdit(Form)
self.textEditReception.setGeometry(QtCore.QRect(10, 130, 186, 121))
self.textEditReception.setObjectName(_fromUtf8(« textEditReception »))
self.labelReception = QtGui.QLabel(Form)
self.labelReception.setGeometry(QtCore.QRect(10, 110, 191, 16))
self.labelReception.setObjectName(_fromUtf8(« labelReception »))
self.line_2 = QtGui.QFrame(Form)
self.line_2.setGeometry(QtCore.QRect(10, 95, 186, 16))
self.line_2.setFrameShape(QtGui.QFrame.HLine)
self.line_2.setFrameShadow(QtGui.QFrame.Sunken)
self.line_2.setObjectName(_fromUtf8(« line_2 »))
self.pushButtonLED = QtGui.QPushButton(Form)
self.pushButtonLED.setGeometry(QtCore.QRect(235, 41, 176, 166))
self.pushButtonLED.setCheckable(True)
self.pushButtonLED.setObjectName(_fromUtf8(« pushButtonLED »))
self.line = QtGui.QFrame(Form)
self.line.setGeometry(QtCore.QRect(203, 5, 16, 246))
self.line.setFrameShape(QtGui.QFrame.VLine)
self.line.setFrameShadow(QtGui.QFrame.Sunken)
self.line.setObjectName(_fromUtf8(« line »))
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
Form.setWindowTitle(QtGui.QApplication.translate(« Form », « PyQt + PySerial : Terminal Série : pushButton allume LED », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxPort.setItemText(0, QtGui.QApplication.translate(« Form », « /dev/ttyACM0 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxPort.setItemText(1, QtGui.QApplication.translate(« Form », « /dev/ttyACM1 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxPort.setItemText(2, QtGui.QApplication.translate(« Form », « /dev/ttyUSB0 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxPort.setItemText(3, QtGui.QApplication.translate(« Form », « /dev/ttyUSB1 », None, QtGui.QApplication.UnicodeUTF8))
self.labelPort.setText(QtGui.QApplication.translate(« Form », « Port Série : », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxDebit.setItemText(0, QtGui.QApplication.translate(« Form », « 115200 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxDebit.setItemText(1, QtGui.QApplication.translate(« Form », « 57600 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxDebit.setItemText(2, QtGui.QApplication.translate(« Form », « 38400 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxDebit.setItemText(3, QtGui.QApplication.translate(« Form », « 28800 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxDebit.setItemText(4, QtGui.QApplication.translate(« Form », « 19200 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxDebit.setItemText(5, QtGui.QApplication.translate(« Form », « 14400 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxDebit.setItemText(6, QtGui.QApplication.translate(« Form », « 9600 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxDebit.setItemText(7, QtGui.QApplication.translate(« Form », « 4800 », None, QtGui.QApplication.UnicodeUTF8))
self.labelDebit.setText(QtGui.QApplication.translate(« Form », « Débit Série (bauds) : », None, QtGui.QApplication.UnicodeUTF8))
self.pushButtonInitSerial.setText(QtGui.QApplication.translate(« Form », « Initialiser », None, QtGui.QApplication.UnicodeUTF8))
self.labelReception.setText(QtGui.QApplication.translate(« Form », « Réception sur le port série : « , None, QtGui.QApplication.UnicodeUTF8))
self.pushButtonLED.setText(QtGui.QApplication.translate(« Form », « ALLUMER LED », None, QtGui.QApplication.UnicodeUTF8))
if __name__ == « __main__ »:
import sys
app = QtGui.QApplication(sys.argv)
Form = QtGui.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())
Le fichier d’application *Main.py
# -*- coding: utf-8 -*-
# par X. HINAULT – Janv 2013 – Tous droits réservés
# GPLv3 – www.mon-club-elec.fr
# modules a importer
from PyQt4.QtGui import *
from PyQt4.QtCore import * # inclut QTimer..
import os,sys
import serial # communication serie
from tuto_pyqt_pyserial_terminal_bp_allume_led import * # fichier obtenu à partir QtDesigner et pyuic4
class myApp(QWidget, Ui_Form): # la classe reçoit le Qwidget principal ET la classe définie dans test.py obtenu avec pyuic4
def __init__(self, parent=None):
QWidget.__init__(self) # initialise le Qwidget principal
self.setupUi(parent) # Obligatoire
#Ici, personnalisez vos widgets si nécessaire
#Réalisez les connexions supplémentaires entre signaux et slots
# connecte le signal Clicked de l’objet bouton à l’appel de la fonction voulue
self.connect(self.pushButtonInitSerial, SIGNAL(« clicked() »), self.pushButtonInitSerialClicked)
self.connect(self.pushButtonLED, SIGNAL(« clicked() »), self.pushButtonLEDClicked)
#initialisation Timer
self.timer=QTimer() # déclare un timer Qt
self.connect(self.timer, SIGNAL(« timeout() »), self.timerEvent) # connecte le signal timeOut de l’objet timer à l’appel de la fonction voulue
#— déclaration utiles —
self.serialPort=None # déclaration initiale
# les fonctions appelées, utilisées par les signaux
def pushButtonLEDClicked(self): # lors appui bouton envoi série
print(« Bouton LED appuyé »)
# côté Arduino : H allume LED et L : éteint LED
if self.serialPort: # seulement si le port série existe – n’existe pas (=None) tant que pas initialisé
if self.pushButtonLED.isChecked(): # si bouton appuyé
# self.serialPort.write(str(self.lineEditChaineEnvoi.text())+’\n’ ) # envoie la chaine sur le port serie
self.serialPort.write(‘H’ ) # envoie la chaine sur le port serie + saut de ligne (pas obligatoire ici – cf prog Arduino)
print(« Envoi Série : « + ‘H’ )
else: # si bouton pas appuyé
# self.serialPort.write(str(self.lineEditChaineEnvoi.text())+’\n’ ) # envoie la chaine sur le port serie
self.serialPort.write(‘L’ ) # envoie la chaine sur le port serie + saut de ligne (pas obligatoire ici – cf prog Arduino)
print(« Envoi Série : « + ‘L’ )
def pushButtonInitSerialClicked(self): # lors appui bouton initialisation série
print(« Bouton Init cliqué »)
if self.serialPort: # si le port existe déjà
self.serialPort.close() # ferme le port si existe
# — initialise paramètres initialisation
if self.comboBoxPort.currentText()==« » : # si le champ d’initialisation Port est vide = initialisation par défaut
strPortInit=« /dev/ttyACM0 » # port par défaut
else :
strPortInit=str(self.comboBoxPort.currentText()) #sinon utilise paramètre champ texte pour le port
strDebitInit=str(self.comboBoxDebit.currentText()) # paramètre champ texte pour debit
#— initialisation série avec gestion erreur —
try: # essaie d’exécuter les instructions
self.serialPort=serial.Serial(strPortInit, strDebitInit) # initialise port serie
self.serialPort.flushInput() # vide la file d’attente série
print(« Initialisation Port Série : « + strPortInit + » @ « + strDebitInit + » = OK « ) # affiche debug
#– change aspect bouton init
self.pushButtonInitSerial.setStyleSheet(QString.fromUtf8(« background-color: rgb(0, 255, 0); »)) # bouton en vert
self.pushButtonInitSerial.setText(« Connexion OK ») # change titre bouton
except: # si erreur initialisation
print(« Erreur initialisation Série »)
#– change aspect bouton init
self.pushButtonInitSerial.setStyleSheet(QString.fromUtf8(« background-color: rgb(255, 127, 0); »)) # bouton en orange
self.pushButtonInitSerial.setText(QString.fromUtf8(« Non connecté »)) # change titre bouton
self.timer.start(10) # lance le timer avec délai en ms – 10 pour réception rapide
def timerEvent(self): # fonction appelée lors de la survenue d’un évènement Timer – nom fonction indiférrent
#– variables de réception —
self.chaineIn=« »;
self.char=« »;
# lecture des données reçues
if self.serialPort: # seulement si le port série existe
self.timer.stop() # stoppe le timer le temps de lire les caractères et éviter « réentrée »
if (self.serialPort.inWaiting()): # si au moins un caractère en réception
while self.char!=‘\n‘: # tant que dernier différent de saut de ligne
self.char=self.serialPort.read() # on lit le caractère
if self.char!=‘\n‘: # tant que c’est pas le saut de ligne, on l’ajoute à la chaine
self.chaineIn=self.chaineIn+self.char
else:
#print(« saut ligne reçu ») # debug
break
if len(self.chaineIn)>0: # … pour ne pas avoir d’affichage si « »
print(self.chaineIn) # affiche la chaîne
self.textEditReception.append(self.chaineIn[:-1]) # ajoute le texte au textEdit en enlevant le dernier caractère
self.timer.start() # redémarre le timer
def main(args):
a=QApplication(args) # crée l’objet application
f=QWidget() # crée le QWidget racine
c=myApp(f) # appelle la classe contenant le code de l’application
f.show() # affiche la fenêtre QWidget
r=a.exec_() # lance l’exécution de l’application
return r
if __name__==« __main__ »: # pour rendre le code exécutable
main(sys.argv) # appelle la fonction main
Code Arduino d’exemple
- Ce programme fonctionne avec le code Arduino suivant :
// Trame de code générée par le générateur de code Arduino
// du site www.mon-club-elec.fr
// Auteur du Programme : X. HINAULT – Tous droits réservés
// Programme écrit le : 29/1/2011.
// ——- Licence du code de ce programme —–
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License,
// or any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// //////////////////// PRESENTATION DU PROGRAMME ////////////////////
// ——– Que fait ce programme ? ———
/* Ce programme permet d’allumer une LED connectée sur une broche de la carte Arduino à partir d’un bouton inverseur graphique dans l’interface Processing côté PC. */
// — Fonctionnalités utilisées —
// Utilise la connexion série vers le PC
// ——– Circuit à réaliser ———
// La connexion série vers le PC utilise les broches 0 et 1 (via le câble USB)
// Broche 2 : Une LED connectée en série avec sa résistance
// /////////////////////////////// 1. Entête déclarative ///////////////////////
// A ce niveau sont déclarées les librairies incluses, les constantes, les variables, les objets utiles…
// — Déclaration des constantes —
// — Inclusion des librairies —
// — Déclaration des constantes utiles —
// — Déclaration des constantes des broches E/S numériques —
const int LED=2; // Constante pour la broche 2
// — Déclaration des constantes des broches analogiques —
// — Déclaration des variables globales —
int octetReception=0; // variable de stockage des valeurs reçues sur le port Série
String chaineReception=« »; // déclare un objet String vide pour reception chaine
// — Déclaration des objets utiles pour les fonctionnalités utilisées —
// ////////////////////////// 2. FONCTION SETUP = Code d’initialisation //////////////////////////
// La fonction setup() est exécutée en premier et 1 seule fois, au démarrage du programme
void setup() { // debut de la fonction setup()
// — ici instructions à exécuter 1 seule fois au démarrage du programme —
// ——- Initialisation fonctionnalités utilisées ——-
Serial.begin(115200); // initialise connexion série à 115200 bauds
// IMPORTANT : régler le terminal côté PC avec la même valeur de transmission
// ——- Broches en sorties numériques ——-
pinMode (LED,OUTPUT); // Broche LED configurée en sortie
// ——- Broches en entrées numériques ——-
// ——- Activation si besoin du rappel au + (pullup) des broches en entrées numériques ——-
// ——- Initialisation des variables utilisées ——-
} // fin de la fonction setup()
// ********************************************************************************
////////////////////////////////// 3. FONCTION LOOP = Boucle sans fin = coeur du programme //////////////////
// la fonction loop() s’exécute sans fin en boucle aussi longtemps que l’Arduino est sous tension
void loop(){ // debut de la fonction loop()
// — ici instructions à exécuter par le programme principal —
//—- code type réception valeur sur le port série —
if (Serial.available()>0) { // si un octet en réception
octetReception=Serial.read(); // Lit le 1er octet reçu et le met dans la variable
if (octetReception==‘H’) { // si Octet reçu est le H
digitalWrite(LED, HIGH); // allume la LED
}
if (octetReception==‘L’) { // si octet reçu est le L
digitalWrite(LED, LOW); // éteint la LED
}
} // fin si octet réception
} // fin de la fonction loop() – le programme recommence au début de la fonction loop sans fin
// ********************************************************************************
// ////////////////////////// FONCTIONS DE GESTION DES INTERRUPTIONS ////////////////////
// ////////////////////////// AUTRES FONCTIONS DU PROGRAMME ////////////////////
// ////////////////////////// Fin du programme ////////////////////
Matériel Nécessaire
L’espace de développement Arduino
- … pour éditer, compiler le programme et programmer la carte Arduino.

Le matériel suivant pour réaliser le montage associé
- une plaque d’essai pour montage sans soudures,

- des straps,

- une LED rouge 5mm,

- une résistance 1/4w de 200 Ohms environ,

Instructions de montage
- La connexion série vers le PC utilise les broches 0 et 1 (via le câble USB)
- Broche 2 : Une LED connectée en série avec sa résistance
Le schéma théorique du montage
Le schéma théorique du montage (cliquer pour agrandir)
Le circuit du montage
Le schéma du montage à réaliser (cliquer pour agrandir)
Fonctionnement du programme
Structure globale du programme
Ce programme simple comprend :
- une entête déclarative
- une partie « configuration » qui ne sera exécutée qu’une fois (fonction setup( ) )
- une partie constituée d’une boucle sans fin que le programme répètera à l’infini ( fonction loop( ) ) : c’est le coeur du programme.
Déroulement du programme
Le programme se déroule de la façon suivante :
- Après avoir pris en compte les instructions de la partie déclarative,
- puis après avoir exécuté la partie configuration ( fonction setup( ) ),
- le programme bouclera sans fin ( fonction loop ( ) ), exécutant de façon répétée le code compris dans la boucle sans fin.

Le déroulement du programme
Explication du programme
Au niveau de la partie déclarative :
- On déclare la broche de LED
Au niveau de la fonction d’initialisation setup( ) :
- On met la broche en sortie
Au niveau de la boucle principale, la fonction loop ( ) :
- On teste si un caractère est disponible sur le port série :
- si ce caractères est ‘H’, on allume la LED
- si ce caractère est ‘L’, on éteint la LED
Utilisation
- Les 2 fichiers suivants sont à enregistrer dans un même répertoire, l’un en nom.py et l’autre en nomMain.py.
- Puis lancer l’application depuis Geany ou équivalent, en exécutant le fichier nomMain.py
- L’appui sur le bouton poussoir allume/éteint la LED par l’envoi de la lettre H ou L sur le port série.
Articles Liés
- PyQt Lab' : Graphiques Math : Pyqtgraph + Pyserial : Afficher une courbe à partir de valeurs reçues sur le port série, avec widgets de paramétrage des axes
PyQt Lab est un outil puissant qui permet aux utilisateurs de créer des graphiques mathématiques…
- Javascript : Graphique Dygraphs simple
Le Javascript est un langage de programmation très populaire et puissant qui permet aux développeurs…
- Javascript : Afficher 6 widgets graphiques fournis par une librairie graphique externe.
Le Javascript est un langage de programmation très populaire qui permet aux développeurs de créer…