Python – Comunicação serial com Arduino e pyserial

ArduinoPythonComSerialTopo

Finalmente escrevi o post que de tempos em tempos recebia alguma pergunta ou notava essa questão em fóruns e listas de discussão, sobre como comunicar Python com interface serial, nada melhor do que um exemplo e aproveitando o caso utilizei o próprio Arduino UNO e o modulo pyserial.

 

Vamos ver do que precisamos e em poucas linhas como comunicamos via serial:

(*) Python instalado, eu estou usando a versão 2.7.2
(*) A library python-serial (pyserial)
(*) Arduino UNO
(*) Firmware já gravado no Arduino, no caso usei este aqui que escrevi a um tempo atras

 

 

PySerial

 

No firmware exemplo ele irá me retornar a temperatura quando eu enviar o caractere ‘t’ pela serial.
Confirmando a versão do Python e do Pyserial:

cleiton@linuxVM:~/projetos/python/communicationSerial$ python --version
Python 2.7.2+
cleiton@linuxVM:~/projetos/python/communicationSerial$ pip freeze | grep serial
pyserial==2.5

Caso não aparecer nada com o comando pip freeze acima você deve instalar com o comando abaixo:

cleiton@linuxVM:~/projetos/python/communicationSerial$ sudo apt-get install -f python-serial

Agora um exemplo básico para trabalhar com o modulo pyserial:

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

import serial

ser = serial.Serial('/dev/ttyUSB0', 9600)
ser.write('5')
#ser.write(b'5') #Prefixo b necessario se estiver utilizando Python 3.X
ser.read()

Vamos entender como funciona, primeiro na linha 4 antes de qualquer coisa eu importo o modulo serial, depois deve iniciar a conexão com a serial com serial.Serial(PORTA_SERIAL, BAUD_RATE), no caso estou usando Linux e o Arduino é um SerialUSB CDC então minha serial é /dev/ttyUSB0 no caso de Windows não tentei mas me parece que é apenas o numero sem o COM, enfim após essa configuração e o baudrate correto no meu caso é 9600 na linha seguinte 7 envio o carácter 5 ao dispositivo pela serial, se tudo ocorrer bem e algo estiver programado para responder na linha 9 é realizado a leitura do que chegar da serial para o computador. Facil não? Python é demais!

Agora utilizando o firmware que já fiz aqui vamos escrever uma aplicação para comunicar com ele.

Código:

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

import time
import serial

# Iniciando conexao serial
comport = serial.Serial('/dev/ttyUSB0', 9600)
#comport = serial.Serial('/dev/ttyUSB0', 9600, timeout=1) # Setando timeout 1s para a conexao

PARAM_CARACTER='t'
PARAM_ASCII=str(chr(116)) # Equivalente 116 = t

# Time entre a conexao serial e o tempo para escrever (enviar algo)
time.sleep(1.8) # Entre 1.5s a 2s

#comport.write(PARAM_CARACTER)
comport.write(PARAM_ASCII)

VALUE_SERIAL=comport.readline()

print 'nRetorno da serial: %s' % (VALUE_SERIAL)

# Fechando conexao serial
comport.close()

Saída:

cleiton@linuxVM:~/projetos/python/communicationSerial$ ./exemplo01.py

Retorno da serial: 27.37

cleiton@linuxVM:~/projetos/python/communicationSerial$

Beleza, funcionou! Agora vamos entender o que mudou nesse script porque tem coisa nova, eu importei o modulo time que vou explicar já já porque, para iniciar a serial foi do mesmo jeito porém logo abaixo tem o mesmo modulo comentado e com acréscimo de um parâmetro novo o timeout=1, porque no caso se eu mandar conectar e o dispositivo não responder ele vai ficar ocioso aguardando, já se eu setar timeout=1 tem um tempo de 1s para a conexão responder.
Ae criei duas variáveis uma com o carácter ‘t’ na linha 11 e na linha 12 a mesma coisa só que usando o valor em ASCII, na linha 15 implementei algo que não achei na documentação mas como já havia passado por isso que é o tempo de conexão do mesmo e para escrever, deve haver um delay nesse intervalo no caso deste exemplo entre 1.5s e 1.8s fico perfeito, mas já teve caso de .2 ou .5 ficar certo também, e na linha 17 ou 18 escrevo na serial usando ASCII ou o carácter logo na sequencia vem o comport.readline() na linha 20 que irá receber o valor e armazenar na minha variável que logo em seguida é dado um print para mostrar na tela, tudo isso ocorrendo sem grandes emoções finalizamos com o fim da conexão com comport.close() na linha 25.
Só mais um detalhe na conexão da linha 8, que as ações com a serial serão manipuladas pela variável que recebe essa conexão podemos dizer nosso file descriptor da conexão até o momento em que encerramos a comunicação.

Agora se você prestar atenção no exemplo básico que citei e no nosso programa a parte de recepção é diferente, no primeiro instante eu usei ser.read() e depois comport.readline(), é a mesma coisa? Não!

Vamos ver o que muda:

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

import time
import serial

# Iniciando conexao serial
comport = serial.Serial('/dev/ttyUSB0', 9600)
#comport = serial.Serial('/dev/ttyUSB0', 9600, timeout=1) # Setando timeout 1s para a conexao

PARAM_CARACTER='t'
PARAM_ASCII=str(chr(116)) # Equivalente 116 = t

# Time entre a conexao serial e o tempo para escrever (enviar algo)
time.sleep(1.8) # Entre 1.5s a 2s

#comport.write(PARAM_CARACTER)
comport.write(PARAM_ASCII)

#VALUE_SERIAL=comport.readline()
VALUE_SERIAL=comport.read()
#VALUE_SERIAL=comport.read(3)

print 'nRetorno da serial: %s' % (VALUE_SERIAL)

# Fechando conexao serial
comport.close()

Saída:

cleiton@linuxVM:~/projetos/python/communicationSerial$ ./exemplo02.py

Retorno da serial: 2
cleiton@linuxVM:~/projetos/python/communicationSerial$

Agora usando o terceiro modo:

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

import time
import serial

# Iniciando conexao serial
comport = serial.Serial('/dev/ttyUSB0', 9600)
#comport = serial.Serial('/dev/ttyUSB0', 9600, timeout=1) # Setando timeout 1s para a conexao

PARAM_CARACTER='t'
PARAM_ASCII=str(chr(116)) # Equivalente 116 = t

# Time entre a conexao serial e o tempo para escrever (enviar algo)
time.sleep(1.8) # Entre 1.5s a 2s

#comport.write(PARAM_CARACTER)
comport.write(PARAM_ASCII)

#VALUE_SERIAL=comport.readline()
#VALUE_SERIAL=comport.read()
VALUE_SERIAL=comport.read(3)

print 'nRetorno da serial: %s' % (VALUE_SERIAL)

# Fechando conexao serial
comport.close()

Saída:

cleiton@linuxVM:~/projetos/python/communicationSerial$ ./exemplo02.py

Retorno da serial: 26.
cleiton@linuxVM:~/projetos/python/communicationSerial$

Conseguiu notar a diferença?
comport.readline() : Ira ler até o n ou a linha toda
comport.read() : Ira ler 1 byte
comport.read(N) : Ira ler N bytes informados

No nosso caso 27.37 são 5 caracteres então 5 bytes, vamos tirar a prova.

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

import time
import serial

# Iniciando conexao serial
comport = serial.Serial('/dev/ttyUSB0', 9600)
#comport = serial.Serial('/dev/ttyUSB0', 9600, timeout=1) # Setando timeout 1s para a conexao

PARAM_CARACTER='t'
PARAM_ASCII=str(chr(116)) # Equivalente 116 = t

# Time entre a conexao serial e o tempo para escrever (enviar algo)
time.sleep(1.8) # Entre 1.5s a 2s

#comport.write(PARAM_CARACTER)
comport.write(PARAM_ASCII)

#VALUE_SERIAL=comport.readline()
#VALUE_SERIAL=comport.read()
VALUE_SERIAL=comport.read(5)

print 'nRetorno da serial: %s' % (VALUE_SERIAL)

# Fechando conexao serial
comport.close()

Saída:

cleiton@linuxVM:~/projetos/python/communicationSerial$ ./exemplo02.py

Retorno da serial: 27.41
cleiton@linuxVM:~/projetos/python/communicationSerial$

Além dessas opções para o read() temos outras que poucos conhecem/utilizam quando se usa pyserial que são isOpen, name, o próprio file descriptor e as N opções de configuração da conexão, vamos analisar:

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

import sys
import time
import serial

"""
VARIAVEIS GLOBAIS (NESTE EXEMPLO)
"""
DEVICE='/dev/ttyUSB0'

def InfoComSerial():
print 'nObtendo informacoes sobre a comunicacao serialn'

# Iniciando conexao serial
comport = serial.Serial(DEVICE, 9600, timeout=1)

time.sleep(1.8) # Entre 1.5s a 2s

print 'nStatus Porta: %s ' % (comport.isOpen())
print 'Device conectado: %s ' % (comport.name)
print 'Dump da configuracao:n %s ' % (comport)

print 'n###############################################n'

# Fechando conexao serial
comport.close()

""" main """
if __name__ == '__main__':

InfoComSerial()

Saída:

cleiton@linuxVM:~/projetos/python/communicationSerial$ ./exemplo03.py

Obtendo informacoes sobre a comunicacao serial

Status Porta: True
Device conectado: /dev/ttyUSB0
Dump da configuracao:
Serial<id=0x9438f4c, open=True>(port='/dev/ttyUSB0', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=1, xonxoff=False, rtscts=False, dsrdtr=False)

###############################################

cleiton@linuxVM:~/projetos/python/communicationSerial$

Opções interessantes, pois com isOpen podemos ver se a porta já esta aberta antes de prosseguir algo, name obtemos o device name da porta conectada e usando o nome do file descriptor da conexão temos um print das configurações que estão sendo utilizadas.
E se eu quiser mudar algum parâmetro da configuração da conexão serial?

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

import sys
import time
import serial

"""
VARIAVEIS GLOBAIS (NESTE EXEMPLO)
"""
DEVICE='/dev/ttyUSB0'
BAUD_RATE='9600'
TIMEOUT='1'
PARITY='N'
STOPBITS='1'
BYTESIZE='8'

def InfoComSerial():
print 'nObtendo informacoes sobre a comunicacao serialn'

# Iniciando conexao serial
#comport = serial.Serial(DEVICE, 9600, timeout=1)
comport = serial.Serial(DEVICE,
int(BAUD_RATE),
timeout=int(TIMEOUT),
bytesize=int(BYTESIZE),
stopbits=int(STOPBITS),
parity=PARITY)
# Alem das opcoes rtscts=BOOL, xonxoff=BOOL, e dsrdtr=BOOL
# Link: http://pyserial.sourceforge.net/pyserial_api.html#constants

time.sleep(1.8) # Entre 1.5s a 2s

print 'nStatus Porta: %s ' % (comport.isOpen())
print 'Device conectado: %s ' % (comport.name)
print 'Dump da configuracao:n %s ' % (comport)

print 'n###############################################n'

# Fechando conexao serial
comport.close()

""" main """
if __name__ == '__main__':

InfoComSerial()

Eu adicionei alguns variáveis globais com as configurações desejadas que são da linha 11 a 16 e apliquei meus parâmetros em serial.Serial().
Bom acho que abordei um conteúdo bom sobre conexão serial em Python e que todos consigam usar, qualquer coisa post a duvida nos comentários.

Até a próxima!

Referências

http://pyserial.sourceforge.net/
http://playground.arduino.cc/interfacing/python#.UzNiv86Gcno

Share Button

CC BY-NC-SA 4.0 Python – Comunicação serial com Arduino e pyserial by Cleiton Bueno is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.