Você está na página 1de 10

•Proyectos

De BASIC a Python (3)


Comunicación con el ElektorBus
En las dos primeras partes describíamos las
diferencias entre BASIC y Python, así
Jean-Claude Feltes
(Luxemburgo) como la representación de diagramas, la
implementación de funciones matemáticas
como la síntesis de Fourier y los interfaces
gráficos de usuario. En la tercera parte
mostramos cómo realizar una conexión
con el ElektorBus. Naturalmente no
será sólo teórica, en blanco y negro, sino
eminentemente práctica y en color).

La combinación de elec- tación hexadecimal y agrupa distintas funciones


trónica + Python + PC es reutilizables en módulos.
ideal para enviar datos En el ElektorBus los datos se transmiten en for-
a un ordenador desde mato binario. También puede tratarse de carac-
hardware externo, ya teres no imprimibles que normalmente no se
sea casero o comer- representan correctamente en la ventana del ter-
cial, y representar- minal. Por ello, una función ha de mostrar los
los en él. El flujo de datos recibidos en formato hexadecimal.
datos en sentido
contrario también Este es el propósito del código mostrado en el
tiene bastante lógica, pues así listado 1, incluido en el archivo Hexfunctions.
podemos controlar hardware externo. Para la py. La función puede utilizarse también en otros
comunicación con el hardware externo pode- programas. Si fuera necesario, el módulo puede
mos utilizar los clásicos puertos serie, o bien, ampliarse con otras funciones. Lo típico en Python
si necesitamos más funciones, servirnos de un sería incorporar en el módulo su propia función
bus. Por ello, el ElektorBus puede servir muy de test. Al ejecutarse directamente la variable
bien de ejemplo a la hora de implementar un __name__ contiene el valor “__main__”, y la
sistema como este. parte de abajo realiza de forma automática la
El ElektorBus se detallaba en una serie de once conversión para que podamos ver inmediata-
artículos [1] publicados en Elektor entre enero mente el resultado. Obtenemos:
de 2011 y enero de 2012. El hardware utilizado HELLO
se mostraba en la parte 6 [2], se trata de una 48 45 4C 4C 4F 0A
placa con microcontrolador, LEDs y pulsadores,
a modo de nodo experimental, conectada al PC La función puede utilizarse también en otros pro-
mediante un conversor USB/RS485 (figura 1). gramas. Por ejemplo en Test_hexfunctions.py:

Funciones hexadecimales en módulos from hexfunctions import *


Primero un poco de calentamiento: un script s=”HELLO\n”
incorpora una función auxiliar para la represen- print s, translate2hex(s)

24 | julio / agosto 2013 | www.elektor-magazine.es


lenguajes de programación

El módulo importado ha de estar en la misma


carpeta que el programa principal o en el direc- ELEKTOR BUS
torio de búsqueda de Python. Utilizar funciones
externas en módulos resulta muy práctico, pues
aumenta la claridad del código. Aparte, los módu-
los pueden reutilizarse.
USB
RS485 NODE 5
GUI con wxPython CONVERTER
Digamos que todos los caminos llevan a los
entornos gráficos. En Python no hay nada más
cómodo que el Forms Designer de Visual Basic.
Esta aplicación otorga control completo sobre los 8

resultados, y tras un poco de práctica funciona


USB
bastante bien. En mi caso, he dado los prime-
ros pasos con Boa Constructor. Sin embargo,
ponerse a depurar sin ningún conocimiento es
Figura 1.
bastante tedioso. Python Card resulta algo más
El PC recibe datos a través
sencillo. De hecho, si no queremos profundizar
PC: MASTER 10 del ElektorBus (RS485)
es muy práctico, aunque lamentablemente haya de una pequeña placa
que tener instalado siempre en cualquier orde- con controlador que tiene
120744 - 11
nador Python Card. conectado un sensor
Tkinter es la librería de GUIs instalada en Python. fotoeléctrico.
Es bastante sencilla, pero incluye menos objetos
que wxPython. Ya que carece de control sobre el de wx.Frame todas las características como
portapapeles, al final he optado por wxPython. las funciones para maximizar y minimizar,
El código del listado 2 incluye el framework moverse, etc. Las propiedades de la ven-
básico para entornos gráficos. Este programa tana están definidas en la función __init__.
irá ampliándose poco a poco. Para que funcione, Aquí se establecen los elementos gráficos, en
lógicamente tendremos que instalar wxPython. este caso un campo de texto y un botón. Por
La ventana principal se define como una clase sencillez, se generan con un tamaño y posi-
orientada a objetos, MyFrame, que hereda ción fijos. Con los llamados “sizers” también

Listado 1: Hexfunctions.py
def translate2hex(c):
“”” translate character string c to hex representation string
e.g ABC -> 41 42 43 “””

h=””
for ch in c: # iterate over all characters
b=hex(ord(ch)) # get hex value
b=b.replace(“0x”,””) # take away leading “0x for better overview”
b=b.upper() # all in upper characters
if len(b)<=1:
b=”0”+b # e.g. make “0A” out of “A”
h=h+b+” “ # separate bytes by space

return h

# test:
if __name__ == “__main__”:
s=”HELLO\n”
print s, translate2hex(s)

www.elektor-magazine.es | julio / agosto 2013 | 25


•Proyectos

Listado 2: GUI_template.py
import wx

# GUI
class MyFrame(wx.Frame):

def __init__(self, **kwargs):


# create frame
wx.Frame.__init__(self, None, **kwargs)

# text box with fixed width font for nice data representation
self.textbox=wx.TextCtrl(self, style = wx.TE_MULTILINE,
pos = (5,5),size=(300, 200))
myfont = wx.Font(12, wx.MODERN, wx.NORMAL, wx.BOLD, False, u’Courier’)
self.textbox.SetFont(myfont)

self.button=wx.Button(self, -1, “TEST”, pos=(100,230))

# Bindings
self.Bind(wx.EVT_IDLE, self.OnIdle)
self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)

Listado 3: Klasse Serialthread


class Serialthread(serial.Serial):
def __init__(self, port, baud, **kwargs):
# Initialization of port + baudrate
serial.Serial.__init__(self)
self.sCOM =serial.Serial(port)
self.sCOM.setBaudrate(baud)

# open port if not already open


if self.sCOM.isOpen()==False:
self.sCOM.open()
if self.sCOM.isOpen()==True:
print „connected to“, self.sCOM.port
else:
print „Error opening port“

# Counter for received data blocks


self.ctr=0

# Create stop event (to terminate endless receiving loop)


# and message queue for thread (to transmit received text to TextCtrl)
self.stopevent=threading.Event()
self.msgQueue=Queue.Queue()

def disconnect(self):
# set stop event so endless receiving loop can be interrupted
self.stopevent.set()

def connect(self):
# create a new thread object that runs serial thread

26 | julio / agosto 2013 | www.elektor-magazine.es


lenguajes de programación

self.Bind(wx.EVT_BUTTON, self.OnButton)

def OnIdle( self, event):


# if nothing else to do, update text from message queue
pass

def OnDestroy(self, event):


print “Exit”

def OnButton(self, event):


self.textbox.AppendText (“Button pressed\n”)
#-----------------------------------------------------------------------

# Main program
if __name__ == “__main__”:
app = wx.App(redirect = False)
frame = MyFrame(title=”GUI”, size = (320,270))
frame.Show(True)
frame.Centre()
app.MainLoop()

# to read serial characters


self.serialthread = threading.Thread(target=self.readSerial)

# clear stopevent and Connect thread


self.stopevent.clear()
self.serialthread.start()

def readSerial(self):
# endless receiving loop
while not self.stopevent.isSet():
data=““

# read from port


c = self.sCOM.read(1)

# synchronize
if ord(c) == 0xAA:
self.ctr += 1
rest = self.sCOM.read(15)
data=c+rest

# format c to 16 bytes output


datastring=str(self.ctr) + „\t“ + translate2hex(data) + „\n“

# update message queue


self.msgQueue.put(datastring)
wx.WakeUpIdle() # wake up to update text

# end serial thread


print „disconnected“
self.sCOM.close()

www.elektor-magazine.es | julio / agosto 2013 | 27


•Proyectos

ls /dev/tt*U*

Como resultado obtenemos por ejemplo:

/dev/ttyUSB0

También funciona hasta el pequeño script para


escanear los puertos en Python de la primera
parte [3] (el código de ScanSerial.py en la edi-
Figura 2. ción impresa contenía un error en el layout, pero
Ventana generada por el la versión descargable es correcta).
código del listado 2. Si no funcionase ninguno de los siguientes scripts,
entonces habrá que verificar que el puerto es
es posible generar un diseño que se adapta el correcto. Al experimentar, el sistema opera-
automáticamente. tivo podría haber cambiado “a escondidas” el
En el siguiente paso se crean tres manejadores número de puerto. Esto ocurre por ejemplo si
de eventos, dos de los cuales necesitaremos des- retiramos el conector USB mientras ejecutamos
pués. Por tradición, el nombre de un manejador scripts con ttyUSB0 y lo conectamos otra vez.
siempre empieza con “On”. Con Bind estas funcio- En este caso el conversor se asigna a ttyUSB1.
nes responden a eventos externos. Por ejemplo, Entonces el script ya no podrá recibir más datos.
OnButton reacciona a un click del ratón. No obstante, en un funcionamiento normal esto
Definimos la clase MyFrame, que puede utilizarse raramente ocurre.
en el programa principal. Ésta genera inicialmente
un objeto wx.App, que se encarga principalmente La plantilla de GUI del listado 2 puede ampliarse
de gestionar eventos. Después se muestra nues- gradualmente. Al principio han de cargarse los
tra ventana y se centra. Los eventos se manejan módulos requeridos y fijarse los parámetros del
en el bucle infinito app.MainLoop. puerto:
Tras iniciar el programa aparecerá ventana de la
figura 2. Ésta responde al hacer click sobre ella COMport = “/dev/ttyUSB0” # ¡Adaptarlo
y permite varias funciones de edición: al hacer según nuestro puerto!
click con el botón derecho del ratón se desple- Baud = 9600
gará un menú con las opciones de seleccionar,
copiar, borrar y pegar. import threading, Queue
Este framework puede ampliarse fácilmente con import serial
una opción para guardar texto en un archivo, import time
por ejemplo.
El módulo threading es necesario porque la recep-
ElektorBus: lectura ción serie debe ejecutarse en un proceso o thread
El nodo experimental está conectado al PC separado, ya que para recibir necesita ejecutar
mediante el conversor USB/RS485. La pequeña un bucle infinito que entraría en conflicto con el
placa está equipada con un microcontrola- bucle principal de wx. Esta es la parte más com-
dor ATmega328 y puede programarse con el pleja de la programación.
archivo hexadecimal descargable [4], grabando Para acceder al puerto creamos la clase propia
el código en la memoria flash. En el conector Serialthread (listado 3). Un objeto de esta clase
de expansión del nodo experimental (ADC0) abre el puerto, lee los datos entrantes, los for-
podemos colocar por ejemplo un potencióme- matea y los envía mediante una cola de mensa-
tro o una fotoresistencia, como se explica en jes (Message Queue) a otra parte del programa.
[2a] y [2b]. Esto se ejecuta en un bucle infinito hasta que el
Pero primero hay que identificar el puerto serie thread sea detenido.
utilizado en el PC. En Windows, hemos de con- Un objeto del thread serie hereda todas las pro-
sultar el administrador de dispositivos, y ver qué piedades y métodos de la clase básica Serial, con
nuevo puerto aparece al conectar el conversor. En el nombre del puerto, la tasa de transferencia,
Linux se notifica mediante la línea de comandos: las funciones de lectura y escritura, etc. En la

28 | julio / agosto 2013 | www.elektor-magazine.es


lenguajes de programación

función __init__ se genera un objeto serie que se


encarga de todas las operaciones del puerto. Si
aún no está abierto, se abre y se ajusta la tasa
de transferencia.
El contador self.ctr no es importante. Se utiliza
posteriormente para numerar bloques de datos.
Hay dos objetos importantes utilizados por el
thread recientemente creado: un evento de stop
para abandonar el thread y una Message Queue
para transmitir datos al GUI.
Desde fuera podemos iniciar y detener el thread
mediante los métodos connect y disconnect. Por
lo tanto, disconnect es sólo el evento de stop. Con
connect iniciamos un nuevo thread en el que se COMPort y escribe todos los datos entrantes en la Figura 3.
ejecuta la función readserial. Ésta lee bytes del Message Queue. Como nos habremos percatado, Los datos recibidos se
puerto en un bucle infinito hasta que se active el texto debe extraerse de la Message Queue y entregan con formato
el evento de stop. Después el thread finaliza y escribirse en el campo de texto. Para ello, utili- hexadecimal.
el puerto se cierra. zamos una función que ya existe, OnIdle:
Dentro de readSerial hay un mecanismo para sin-
cronizar los datos. En el ElektorBus cada paquete class MyFrame(wx.Frame):
de datos empieza con 0xAA. Cada vez que apa-
rece dicho valor se incrementa el contador y se def __init__(self, **kwargs):
leen 15 bytes de datos. Posteriormente se for- ...
matean y se sitúan en una cadena de caracteres
en la Message Queue. Con wx.WakeUpIdle() se def OnIdle( self, event):
indica al GUI del programa que puede leer la # if nothing else to do, update text
Message Queue en caso de que no tenga nada from message queue
más que hacer. while not self.serialreceive.msg-
Hablemos ahora de Serialthread. Con esta clase Queue.empty():
pueden leerse datos continuamente sin alterar msg=self.serialreceive.msg-
el flujo del programa. Serialthread se ejecuta Queue.get()
casi en paralelo con otras partes del programa. self.textbox.AppendText(msg)
Para ello ha de crearse e iniciarse un objeto de
la clase Serialthread en el programa principal. Ha La instrucción pass sólo se trataba de un place-
de ampliarse el código de MyFrame (del script de holder (o “marcador de posición”) y se ha eli-
la plantilla de GUI, en la ventana principal). En la minado. Con estos cambios los datos entrantes
función __init__ (tras los “Bindings”) añadimos: deberían aparecer como en la figura 3 con el
número visible en el campo de texto.
class MyFrame(wx.Frame): Para evitar los molestos mensajes de error en
la ventana aún tenemos que añadir una última
def __init__(self, **kwargs): cosa en el procedimiento OnDestroy:
...
def OnDestroy(self, event):
# Bindings self.serialreceive.disconnect()
... time.sleep(1)

# serial thread Antes de cerrar la ventana ha de finalizarse el


self.serialreceive = Serialthread(- thread serie. Esto podría llevar cierto tiempo con
COMport, Baud) lo que se ha añadido una breve pausa con time.
self.serialreceive.connect() sleep(1). Los cambios llevados a cabo en este capí-
tulo, junto con el nuevo programa Serialreceive1.
Ahora tenemos un objeto serialreceive, conec- py, y el resto de códigos pueden descargarse gra-
tado al puerto COM especificado por la variable tuitamente en la página de este artículo [4].

www.elektor-magazine.es | julio / agosto 2013 | 29


•Proyectos

Para dividir todo correctamente en módulos, la def __init__(self, **kwargs):


definición de la clase Serialthread puede guar- # create frame
darse por separado en el módulo Serialthread.py, ....
si no queremos cambiar nada más. Se importará buttonOn=wx.Button(self, -1, “LED
en el programa principal. De esta manera queda ON”, pos=(100,230))
todo más claro. buttonOff=wx.Button(self, -1, “LED
Aún queda una cosa más, aquí hay que importar OFF”, pos=(200,230))
los módulos necesarios para Serialthread, y no
en el programa principal. El archivo Serialthread. # Bindings
py incluye también el siguiente código: ....
buttonOn.Bind(wx.EVT_BUTTON, self.
import threading, Queue OnButtonOn)
import serial buttonOff.Bind(wx.EVT_BUTTON, self.
import time OnButtonOff)
from hexfunctions import *
import wx El manejador de evento llama a las funciones
self.OnButtonOn y self.OnButtonOff. Éstas han
class Serialthread(serial.Serial): de reemplazarse por:
def __init__(self, port, baud, **kwargs):
# Initialization od port + baudrate def OnButtonOn(self, event):
serial.Serial.__init__(self) self.textbox.AppendText (“LED
.... ON\n”)
# end serial thread data=b”\xAA\x00\x00\x05\x00\x0A\
print “disconnected” x00\x00\x00\x00\x60\x01\x00\x00\x00\x00”
self.sCOM.close() self.serial_thread.sCOM.write(data)

En el programa principal resultante Serialre- def OnButtonOff(self, event):


ceive2.py puede omitirse el bloque de definición self.textbox.AppendText (“LED
de la clase Serialthread completo. Tampoco se OFF\n”)
necesitan las líneas de import, ya que éstos se data=b”\xAA\x00\x00\x05\x00\x0A\
hacen en Serialthread.py. x00\x00\x00\x00\x60\x00\x00\x00\x00\x00”
Siendo quisquillosos esta división aún no sería self.serial_thread.sCOM.write(data)
del todo correcta, pues el módulo Serialthread.
py es ciertamente específico para este proyecto Si echamos un vistazo al código fuente de la
y no de uso general, pero en este caso así es clase Serialthread, nos preguntaremos dónde está
suficiente. definida la función write. No hace falta definirla
explícitamente, pues se hereda como clase básica
ElektorBus: escritura de serial.Serial y por lo tanto está disponible
Los nodos experimentales incorporan un LED rojo. automáticamente.
Éstos se encienden o se apagan desde el PC con Con estos cambios obtenemos el programa
dos botones. Para ello habrá que enviar una cierta Receive_send.py. Ahora sólo falta encender y
secuencia de bytes a través del ElektorBus (ver apagar el LED rojo del nodo experimental desde
las especificaciones del bus [1]): el PC. El software no es en absoluto perfecto y
carece de una sincronización con el nodo. Si se
• Encender: AA 00 00 05 00 0A 00 00 00 00 diera el caso de que el PC enviase datos al mismo
60 01 00 00 00 00 tiempo, podrían aparecer colisiones en el bus, ori-
• Apagar: AA 00 00 05 00 0A 00 00 00 00 ginando errores en los datos. Esto puede evitarse
60 00 00 00 00 00 si el mensaje desde el PC se envía únicamente
tras haber recibido correctamente los datos del
En el programa del GUI ha de añadirse un nodo (véase el DirectMode, en las especificacio-
segundo botón y otro manejador de evento, y nes del ElektorBus [1]). Podemos por ejemplo
ponerles nombres lógicos a los botones. Para ello activar un flag tras recibir los datos, y que éste
editamos __init__ de la clase MyFrame: sea necesario para realizar cualquier envío.

30 | julio / agosto 2013 | www.elektor-magazine.es


lenguajes de programación

Diagramas # synchronize
Ahora sólo nos falta mostrar los datos, aparte de if ord(c) == 0xAA:
como texto, también con un diagrama, y visua- self.ctr += 1
lizar los valores generados por el conversor A/D rest = self.sCOM.read(15)
del nodo. data=c+rest
La librería estándar para la representación gráfica
es Matplotlib. Ante todo habrá que instalarla. En la ## update x,y
parte 1 de esta serie de artículos trabajamos con lbyte = ord(rest[6])
el sencillo interfaz de pyplot, con el cual pueden hbyte = ord(rest[5]) & 7
generarse rápidamente diagramas simples. Pero adc = lbyte + hbyte *256
si queremos insertarlos en un entorno gráfico, t=time.time()-self.starttime
la cosa se complica: habrá que utilizar el inter- self.x.append(t)
faz orientado a objetos, que básicamente ofrece self.y.append( adc)
más posibilidades. La documentación relacionada print t,adc
está disponible en la página de Matplotlib [5]. Si
somos principiantes, es posible que el enorme # format c to 16 bytes output
abanico de posibilidades nos supere. En [6] pode- ....
mos encontrar un ejemplo de inserción en wx. wx.WakeUpIdle() #
Antes de modificar el módulo Serialthread es wake up to update text
mejor guardarlo con otro nombre, por ejemplo
Serialthread_diagram.py. Los valores del con- Una vez convertidos, el módulo suministra ade-
versor A/D a representar se encuentran en los más los valores hexadecimales para el diagrama.
bytes 5 y 6 del bloque de datos recibido. Han de
extraerse y transferirse al programa principal. Ahora veamos los cambios en el programa prin-
Las posibilidades son muchas. Una de ellas con- cipal. Ya que hemos cambiado el nombre del
siste en pasarle los arrays x e y como atributos módulo Serialthread, también habrá que llevar
en el objeto Serialthread. Estas coordenadas se a cabo el cambio aquí:
corresponden con los valores x e y del diagrama.
En __init__ añadimos: from serialthread_diagram import *

class Serialthread(serial.Serial): Para representar diagramas primero han de


def __init__(self, port, baud, **kwargs): importarse los siguientes módulos necesarios:
...
from matplotlib.backends.backend_wxagg import
## init arrays and timer for data FigureCanvasWxAgg as FCanvas
self.x=[] from matplotlib.figure import Figure
self.y=[]
self.starttime=time.time() Matplotlib funciona con los llamados backends,
... que se encargan de la verdadera tarea gráfica.
El backend es distinto si hay que representar por
Aquí tenemos dos arrays vacíos para los valores ejemplo un diagrama en la pantalla con wx, en
de x e y. El atributo adicional starttime sirve para otro sistema, en una impresora o en un archivo
calcular el tiempo en segundos, y que el valor gráfico. La primera fila genera un objeto FCan-
pueda utilizarse para el eje de abscisas. vas para wx, que puede dibujarse con Matplot-
En la función de recepción se extraen los valo- lib. FCanvas hereda métodos como el ajuste del
res del ADC, se combinan y se almacenan en los tamaño y la posición de wxPanel.
arrays x e y: El objeto Figure es un contenedor en el cual se
dibuja. No obstante, Figure no es el verdadero
def readSerial(self): diagrama. En un objeto Figure puede haber uno o
varios objetos Axes embebidos. Estos sí son el la
# endless receiving loop verdadera área de representación del diagrama.
while not self.stopevent.isSet(): Esto resulta un poco confuso, pero se compensa
... con las posibilidades que ofrece Matplotlib.

www.elektor-magazine.es | julio / agosto 2013 | 31


•Proyectos

Por favor, ¡hay que vigilar siempre la indentación en los listados!


Si no es correcta podrían aparecer errores, o que el programa
se comporte de forma extraña.

Para que todas las ventanas se muestren correc- figure.add_subplot(numrows, numcols,


tamente hemos hecho algunos ajustes en el fignumber)
layout y el tamaño de la fuente. En el listado
pueden verse algunos detalles. En nuestro caso sólo tenemos un diagrama (fig-
number = 1) y por lo tanto sólo una fila (numrows
La ventana del diagrama (ver la figura 4) = 1) y una columna (numcols = 1). En las últi-
se genera en la función __init__ de la clase mas tres líneas se genera con el objeto Fcanvas
MyFrame: la superficie del backend de wx, se sitúa y se
ajusta el tamaño. Ahora ya puede dibujarse un
class MyFrame(wx.Frame): diagrama.

def __init__(self, **kwargs): Hay otro cambio más en la función OnIdle. Ésta
# create frame es llamada el programa no tiene nada más que
wx.Frame.__init__(self, None, hacer, especialmente cuando hay datos nuevos
**kwargs) aún pendientes.

# text box with fixed width font for def OnIdle( self, event):
nice data representation # if nothing else to do, update text
self.textbox=wx.TextCtrl(self, style from message queue
= wx.TE_MULTILINE, while not self.serial_thread.msg-
pos = (5,5),size=(420, 200)) Queue.empty():
myfont = wx.Font(10, wx.MODERN, wx. msg=self.serial_thread.msg-
NORMAL, wx.BOLD, False, u’Courier’) Queue.get()
self.textbox.SetFont(myfont) self.textbox.AppendText(msg)

buttonOn=wx.Button(self, -1, “LED ## display values in diagram


ON”, pos=(100,230)) self.axes.plot(self.serial_
buttonOff=wx.Button(self, -1, “LED thread.x, self.serial_thread.y)
OFF”, pos=(200,230)) self.canvas.draw()

## diagram Aquí se representan los valores de x e y recibi-


self.figure = Figure() dos anteriormente (el array x incluye el tiempo
self.axes = self.figure. en segundos). El gráfico puede verse mediante
add_subplot(111) el comando canvas.draw().
self.canvas = FCanvas(self, -1,
self.figure) Los cambios realizados en esta sección dan
self.canvas.SetPosition( (450,5)) como resultado el programa Diagram.py. En
self.canvas.SetSize((300,250)) este sencillo ejemplo hemos decidido no man-
.... tener fijo el escalado del diagrama. Éste se
adapta continuamente según los datos. Aparte,
Primero se genera un Subplot. Por ejemplo, un la curva cambia de color con cada diagrama
diagrama puede obtenerse de representar varios nuevo. Para adaptar los colores y el formato a
Subplots mediante múltiples series de datos. La nuestro gusto, es recomendable consultar la
sintaxis sería: ayuda de Matplotlib.

32 | julio / agosto 2013 | www.elektor-magazine.es


lenguajes de programación

Conclusiones y planes para el futuro


Ahora ya deberíamos tener una idea básica sobre
el tema. El software que explicamos está muy
lejos de ser perfecto, por ejemplo, la gestión de
errores es bastante rudimentaria. Por lo menos
el intérprete imprime los mensajes inmediata-
mente, de modo que podamos ver cuál puede
ser el problema.
Uno de los inconvenientes es que el programa
puede bloquearse al transcurrir cierto tiempo.
Esto se debe a que los nodos experimentales
envían nuevos valores dos veces por segundo.
Esto conduce a que tarde o temprano los arrays
x e y se saturen de valores y el diagrama tarde [3] De BASIC a Python – Parte 1: Figura 4.
www.elektor.es/110483 Ventana del terminal
mucho tiempo en dibujarse. Mientras se está
con valores numéricos y
generando una representación, si llegan nuevos [4] De BASIC a Python – Parte 3:
representación gráfica en
valores el thread del GUI se bloquea y la ventana www.elektor.es/120744
X e Y.
se pone gris. Es curioso, porque sin embargo la [5] Documentación de Matplotlib:
ventana del terminal sí sigue activa con el thread http://matplotlib.org/contents.html
de recepción y se ejecuta independientemente.
[6] Sandro Tosi: Matplotlib for Python Developers
En cualquier caso, este problema puede solucio-
narse estableciendo unos límites al programa, de [7] Página del autor:
modo que el gráfico sólo se refresque tras unos http://staff.ltam.lu/feljc/home.html
segundos. ¡Ahora te toca a ti probar! [8] Python para electrónicos:
(120744) Andrew Pratt: „Python Programming and
GUIs for Electronic Engineers“ http://www.
elektor.es/products/books/programming/
python-programming-and-guis-for-electro-
nic.1320886.lynkx

Enlaces y bibliografía
[1] Página del ElektorBus:
www.elektor.com/elektorbus
[2a] ElektorBus, Parte 6:
www.elektor.es/110258
[2b] ElektorBus, Parte 8:
www.elektor.es/110428

Sobre el autor
Jean-Claude Feltes imparte clases de
electrónica en el Lycée Technique des Arts
et Métiers en Luxemburg. Esta escuela de
tecnología y artes otorga cualificaciones
profesionales a aprendices y técnicos.
Dedica gran parte de su tiempo libre a su
pasión por la electrónica y la programación
(véase [7]).

www.elektor-magazine.es | julio / agosto 2013 | 33

Você também pode gostar