Guias · 17 min de leitura

Comunicação serial entre Arduino e Python: pyserial, CSV e gráficos ao vivo

Comunicação serial entre Arduino e Python com pyserial: como instalar, encontrar a porta, salvar dados de sensores em CSV, enviar comandos e desenhar gráficos ao vivo com matplotlib.

Comunicação serial entre Arduino e Python significa um programa em Python rodando no seu computador trocando dados com um Arduino pelo cabo USB. O Arduino mede um sensor e escreve o resultado na porta serial; o Python lê essas linhas com a biblioteca pyserial, salva tudo em um arquivo CSV, desenha um gráfico ou toma uma decisão e manda um comando de volta para o Arduino. Assim você junta o controle preciso de hardware do Arduino com a força do Python em processamento de dados, gráficos, IA e automação. E você não precisa de nenhum módulo extra: o mesmo cabo USB que você usa para carregar os sketches já basta.

Neste guia você vai aprender passo a passo a instalar o pyserial, encontrar automaticamente a porta do Arduino, salvar dados de sensores com marcação de tempo em um arquivo CSV, enviar comandos de LED e PWM do Python para o Arduino e desenhar um gráfico ao vivo com matplotlib. Depois vamos ver como escolher o formato dos dados e o final de linha, os dois problemas que mais confundem (o erro de porta ocupada e a placa reiniciando quando a porta é aberta) e o que muda ao fazer o mesmo trabalho em MicroPython em um Raspberry Pi Pico. Os exemplos foram escritos para o Arduino Uno; as mudanças necessárias para o ESP32 e o Pico aparecem separadamente.

Placa Arduino Uno R3 e o conector USB
Arduino Uno: o conector USB serve tanto para carregar o código quanto para a comunicação serial com o Python; os LEDs TX e RX piscam enquanto os dados passam.

Como o Arduino conversa com o Python?

O chip conversor USB-serial do Arduino Uno (um ATmega16U2 nas placas originais, geralmente um CH340 nos clones) aparece no seu computador como uma porta serial virtual: COM3, COM4 e assim por diante no Windows; /dev/cu.usbmodem… ou /dev/cu.usbserial-… no macOS; /dev/ttyACM0 ou /dev/ttyUSB0 no Linux. Cada caractere que o Arduino escreve com Serial.print() chega ao computador por essa porta na forma de bytes; do lado do Python, o pyserial abre a porta como se fosse um arquivo, lê dela e escreve nela. Uma distinção importante: o Arduino Uno não roda código Python. A placa roda código Arduino escrito em C++, o computador roda Python, e os dois só conversam pela linha serial. Para que os dois lados falem a mesma língua, estas configurações precisam ser iguais:

ConfiguraçãoLado do ArduinoLado do PythonAtenção
PortaCriada automaticamente pela USBserial.Serial("COM3", ...)O nome da porta depende do sistema operacional
Velocidade (baud)Serial.begin(115200)serial.Serial(port, 115200)Se forem diferentes, chegam caracteres sem sentido
Quadro8 bits de dados, sem paridade, 1 bit de parada (8N1)O padrão do pyserial também é 8N1Raramente é preciso mudar
Final de linhaSerial.println() acrescenta \r\n no finalreadline() lê até encontrar \nNão esqueça de acrescentar \n ao enviar pelo Python
Tempo limiteSerial.setTimeout(), padrão de 1000 mstimeout=1 (segundos)Com timeout=None, o readline() espera para sempre

Instalando o pyserial (Windows, macOS, Linux)

  1. Confira se o Python 3 está instalado. No Windows, marque a caixa Add python.exe to PATH na primeira tela do instalador baixado do site oficial do Python; no macOS e no Linux, o Python 3 geralmente já vem instalado.
  2. Abra um terminal (Prompt de Comando ou PowerShell no Windows) e digite python -m pip install pyserial. Se o Windows não reconhecer o comando python, tente py -m pip install pyserial; no macOS e no Linux o comando pode ser python3 -m pip install pyserial.
  3. Verifique a instalação: python -c "import serial; print(serial.__version__)" deve mostrar um número de versão como 3.5.
  4. A seção do gráfico ao vivo também precisa do matplotlib: python -m pip install matplotlib.
  5. No Linux, adicione o seu usuário ao grupo dialout para poder abrir a porta: sudo usermod -a -G dialout $USER, depois saia da sessão e entre de novo. Se o pip mostrar o erro externally-managed-environment no Raspberry Pi OS ou nas versões recentes do Ubuntu, use sudo apt install python3-serial ou crie um ambiente virtual com python3 -m venv e rode o pip dentro dele.
No PyPI existe outro pacote chamado serial que não tem nada a ver com portas seriais. Se você digitar pip install serial, o seu código passa da linha import serial, mas depois falha com AttributeError: module 'serial' has no attribute 'Serial'. O pacote certo é o pyserial; remova o errado com pip uninstall serial e instale o pyserial de novo. O mesmo erro aparece se você der ao seu próprio arquivo o nome serial.py, porque aí o Python importa o seu arquivo em vez da biblioteca.

Em qual porta está o Arduino? Encontrando a porta com Python

O jeito mais fácil é olhar o menu Ferramentas > Porta da Arduino IDE, mas o próprio Python também consegue listar as portas. Digite python -m serial.tools.list_ports -v em um terminal e você verá o nome, a descrição e o ID de hardware (VID:PID) de cada porta. Se não der para saber qual linha é o Arduino, desconecte a placa, rode o comando de novo e anote a porta que sumiu. No macOS a mesma placa aparece tanto como /dev/tty… quanto como /dev/cu…; no Python, use a que começa com cu.

Módulo conversor USB-TTL CH340
O chip CH340: na maioria das placas Arduino clones, é este chip que faz a ponte entre a USB e a porta serial; o seu ID de fabricante (VID) é 1A86.
Chip USBOnde você encontraVID:PIDNome exibido no WindowsNome da porta no Linux
ATmega16U2Uno R3 original, Mega 25602341:0043 (Uno)Arduino Uno (COMx)/dev/ttyACM0
CH340Placas Uno, Nano e Mega clones1A86:7523USB-SERIAL CH340 (COMx)/dev/ttyUSB0
CP2102ESP32 DevKit, NodeMCU10C4:EA60Silicon Labs CP210x USB to UART Bridge (COMx)/dev/ttyUSB0
FT232RNanos antigos, cabos FTDI0403:6001USB Serial Port (COMx)/dev/ttyUSB0
RP2040 (USB integrada)Raspberry Pi Pico com MicroPython2E8A:0005Dispositivo Serial USB (COMx)/dev/ttyACM0

A função abaixo lista todas as portas e, se reconhecer uma placa pelo VID, devolve o nome da porta dessa placa. Salve o código como port_bul.py e, nos seus outros scripts, você pode escrever from port_bul import port_bul e PORT = port_bul() em vez de editar o nome da porta à mão toda vez.

Python
1import serial.tools.list_ports
2
3# Bilinen USB yongalarının üretici kimlikleri (VID) / Vendor IDs of common USB chips
4BILINEN = {
5    0x2341: "Arduino (orijinal / genuine)",
6    0x2A03: "Arduino (orijinal / genuine)",
7    0x1A86: "CH340 (klon / clone)",
8    0x10C4: "CP210x (ESP32, NodeMCU)",
9    0x0403: "FTDI",
10    0x2E8A: "Raspberry Pi Pico",
11}
12
13
14def port_bul():
15    portlar = sorted(serial.tools.list_ports.comports())
16    for p in portlar:
17        print(p.device, "|", p.description, "|", p.hwid)   # tüm portları listele / list every port
18    for p in portlar:
19        if p.vid in BILINEN:
20            print("Bulundu / found:", p.device, "->", BILINEN[p.vid])
21            return p.device
22    return None
23
24
25if __name__ == "__main__":
26    if port_bul() is None:
27        print("Kart yok: kabloyu ve surucuyu kontrol et / no board: check cable and driver")

Enviando dados do Arduino: o primeiro sketch

Os exemplos usam um potenciômetro ligado ao pino A0: o pino do meio é ligado ao A0, um dos pinos das pontas ao 5V e o outro ao GND. No lugar do potenciômetro você também pode montar um LDR com um resistor de 10 kΩ como divisor de tensão. O sketch faz uma leitura a cada 200 ms e envia o resultado em uma única linha no formato tempo,valor:

C++ (Arduino IDE)
1// A0 değerini "millis,deger" CSV satırı olarak gönderir / Sends the A0 reading as a "millis,value" CSV line
2const int SENSOR_PIN = A0;            // potansiyometrenin orta ucu / potentiometer wiper
3const unsigned long ARALIK = 200;     // gönderim aralığı, ms / send interval, ms
4unsigned long sonGonderim = 0;
5
6void setup() {
7  Serial.begin(115200);               // Python'daki hızla aynı olmalı / must match the baud rate in Python
8}
9
10void loop() {
11  if (millis() - sonGonderim >= ARALIK) {   // delay() olmadan zamanlama / timing without delay()
12    sonGonderim = millis();
13    int deger = analogRead(SENSOR_PIN);     // 0-1023
14    Serial.print(sonGonderim);              // zaman damgası / timestamp
15    Serial.print(',');                      // ayırıcı / separator
16    Serial.println(deger);                  // sonuna \r\n ekler / appends \r\n
17  }
18}

Usar millis() em vez de delay() facilita acrescentar mais tarde, no mesmo loop, a leitura de comandos vindos do Python; veja millis ou delay para mais detalhes. Depois de carregar o código, abra o Monitor Serial em 115200 baud e você verá passar linhas como 2573,512. Isso é só uma verificação: sempre feche o Monitor Serial antes de rodar o script em Python, senão a porta fica ocupada. Se você está começando com o Monitor Serial, o guia do Monitor Serial do Arduino vai ajudar.

Lendo os dados do Arduino com Python e salvando em CSV

Python
1import csv
2import time
3from datetime import datetime
4
5import serial
6
7PORT = "COM3"     # macOS: /dev/cu.usbmodem14101, Linux: /dev/ttyACM0
8BAUD = 115200     # Arduino'daki Serial.begin ile aynı / same as Serial.begin on the Arduino
9
10with serial.Serial(PORT, BAUD, timeout=1) as ser, \
11        open("olcumler.csv", "w", newline="", encoding="utf-8") as dosya:
12    time.sleep(2)               # Uno port açılınca resetlenir / the Uno resets when the port opens
13    ser.reset_input_buffer()    # yarım kalmış eski veriyi at / drop stale, partial data
14    yazici = csv.writer(dosya)
15    yazici.writerow(["tarih", "zaman_ms", "deger", "volt"])   # başlık satırı / header row
16    print("Kayit basladi, durdurmak icin Ctrl+C / logging, press Ctrl+C to stop")
17    try:
18        while True:
19            satir = ser.readline().decode("utf-8", errors="ignore").strip()
20            parcalar = satir.split(",")
21            if len(parcalar) != 2:
22                continue                    # boş ya da beklenmeyen satır / empty or unexpected line
23            try:
24                zaman_ms, deger = int(parcalar[0]), int(parcalar[1])
25            except ValueError:
26                continue                    # bozuk satır / corrupted line
27            volt = deger * 5.0 / 1023       # Uno ADC: 0-1023 -> 0-5 V
28            yazici.writerow([datetime.now().isoformat(timespec="seconds"),
29                             zaman_ms, deger, round(volt, 3)])
30            dosya.flush()                   # satırı hemen diske yaz / write the row to disk now
31            print(zaman_ms, deger, f"{volt:.2f} V")
32    except KeyboardInterrupt:
33        print("Durduruldu / stopped")

Este script abre a porta com timeout=1: o readline() espera no máximo 1 segundo por um final de linha, devolve um objeto bytes vazio se nada chegar, e o laço nunca trava. Os dados que chegam são do tipo bytes; o decode() os transforma em texto, o strip() remove o \r\n do final e o split(",") divide a linha em colunas. O time.sleep(2) e o reset_input_buffer() logo depois de abrir a porta descartam as meias linhas que chegam enquanto o Uno reinicia; você vai ver o porquê mais abaixo. Abrir o arquivo com newline="" evita linhas em branco no Windows, e o flush() mantém no disco as leituras salvas até ali, mesmo que o programa feche de repente. Pressione Ctrl+C no terminal para parar a gravação.

Se o arquivo CSV abrir no Excel em uma única coluna, a causa são as configurações regionais: no Windows em português do Brasil, como em muitas configurações europeias, o Excel espera ponto e vírgula como separador de lista e vírgula como separador decimal. Use csv.writer(dosya, delimiter=";") e escreva a tensão com str(round(volt, 3)).replace(".", ","), ou importe o arquivo no Excel com Dados > De Texto/CSV e escolha o separador você mesmo. Para outras formas de levar os dados para o Excel, veja como enviar dados do Arduino para o Excel.

Enviando comandos do Python para o Arduino

A comunicação funciona nos dois sentidos. O sketch do Arduino abaixo espera comandos que chegam linha por linha: LED ON e LED OFF acendem e apagam o LED embutido, PWM 128 ajusta o brilho de um LED no pino 9 e READ devolve o valor do A0. Cada comando recebe uma resposta curta, para que o Python saiba que o comando foi mesmo processado. Você encontra o resistor certo para o LED do pino 9 com a calculadora de resistor para LED; o texto O que é PWM? explica como o PWM funciona.

C++ (Arduino IDE)
1// Python'dan gelen satır komutlarını işler / Handles line-based commands from Python
2const int LED_PIN = LED_BUILTIN;      // Uno'da pin 13 / pin 13 on the Uno
3const int PWM_PIN = 9;                // 220 ohm dirençli LED / LED with a 220 ohm resistor
4
5void setup() {
6  pinMode(LED_PIN, OUTPUT);
7  pinMode(PWM_PIN, OUTPUT);
8  Serial.begin(115200);
9  Serial.setTimeout(50);              // readStringUntil en fazla 50 ms bekler / waits at most 50 ms
10  Serial.println("HAZIR");            // Python bu satırı bekler / Python waits for this line
11}
12
13void loop() {
14  if (Serial.available() > 0) {
15    String komut = Serial.readStringUntil('\n');   // satır sonuna kadar oku / read up to the newline
16    komut.trim();                                  // \r ve boşlukları sil / strip \r and spaces
17
18    if (komut == "LED ON") {
19      digitalWrite(LED_PIN, HIGH);
20      Serial.println("OK LED ON");
21    } else if (komut == "LED OFF") {
22      digitalWrite(LED_PIN, LOW);
23      Serial.println("OK LED OFF");
24    } else if (komut.startsWith("PWM ")) {
25      int deger = komut.substring(4).toInt();     // "PWM 128" -> 128
26      deger = constrain(deger, 0, 255);           // 0-255 aralığında tut / keep it within 0-255
27      analogWrite(PWM_PIN, deger);
28      Serial.print("OK PWM ");
29      Serial.println(deger);
30    } else if (komut == "READ") {
31      Serial.println(analogRead(A0));              // A0 ölçümünü gönder / send the A0 reading
32    } else if (komut.length() > 0) {
33      Serial.print("ERR ");                        // bilinmeyen komut / unknown command
34      Serial.println(komut);
35    }
36  }
37}
Python
1import time
2
3import serial
4
5
6def komut_gonder(ser, komut):
7    ser.write((komut + "\n").encode("ascii"))       # satır sonu şart / the newline is required
8    return ser.readline().decode("ascii", errors="ignore").strip()   # cevabı bekle / wait for the reply
9
10
11with serial.Serial("COM3", 115200, timeout=2) as ser:
12    bitis = time.time() + 3                         # en fazla 3 sn bekle / wait at most 3 s
13    while time.time() < bitis:
14        if ser.readline().strip() == b"HAZIR":      # Arduino açıldı / the Arduino is up
15            break
16    print(komut_gonder(ser, "LED ON"))              # OK LED ON
17    time.sleep(1)
18    print(komut_gonder(ser, "PWM 128"))             # OK PWM 128
19    print("A0 =", komut_gonder(ser, "READ"))        # A0 = 512
20    print(komut_gonder(ser, "LED OFF"))             # OK LED OFF

O erro mais comum do lado do Python é esquecer o \n no final do comando: quando o readStringUntil('\n') não encontra o final de linha, o Arduino espera o tempo definido com Serial.setTimeout() e só então processa o comando. Neste sketch esse tempo é de 50 ms; com o padrão de 1000 ms, cada comando atrasaria um segundo. O write() só aceita bytes, então você precisa converter o texto com encode() ou colocando um b na frente dele (b"LED ON\n"). O script primeiro espera até 3 segundos pela linha HAZIR (“pronto” em turco): como o Uno reinicia quando a porta é aberta, um comando enviado logo de cara vai para o bootloader e se perde. Ler a resposta depois de cada comando, um padrão de pedido e resposta, mantém os dois lados sincronizados e impede que os buffers encham; o buffer de recepção do Uno tem só 64 bytes.

Formato dos dados e final de linha: qual escolher?

A linha serial só entrega um fluxo de bytes; cabe a você marcar onde uma leitura termina e a próxima começa. O jeito mais robusto é escrever cada leitura em uma linha própria e terminar todas as linhas com \n. O formato usado dentro da linha depende do tamanho do seu projeto:

FormatoLinha de exemploVantagensDesvantagens
Valor único512O formato mais simples; o Plotter Serial do Arduino desenha diretoCom vários valores, fica confuso saber qual é qual
CSV2573,512,23.5Curto; o Excel e o pandas leem diretoA ordem das colunas precisa ser a mesma nos dois lados
Chave:valortemp:23.5,umid:41Legível; o Plotter Serial mostra os rótulosMais longo e um pouco mais trabalhoso de interpretar
JSON{"t":23.5,"h":41}Decodificado em uma linha com json.loads() no PythonO formato mais longo; pesa na RAM limitada do Uno
Binário2 bytes: 0x02 0x00A opção mais rápida e mais curtaNão é legível para humanos; precisa do módulo struct e de um byte de início

Os finais de linha são outra fonte frequente de confusão. O Serial.println() acrescenta dois caracteres no fim de cada linha: retorno de carro (\r, CR) e nova linha (\n, LF). O readline() do Python espera só pelo \n, então sobra um \r no fim da linha; o strip() remove os dois. A lista de final de linha do Monitor Serial da Arduino IDE oferece Sem final de linha, Nova linha, Retorno de carro e Ambos, NL e CR; quando você testar o sketch do Arduino no Monitor Serial antes de usar o Python, escolha Nova linha para que seja enviado o mesmo caractere que o Python envia. Para números decimais, informe a quantidade de casas, como em Serial.print(temperatura, 1); no Uno, o sprintf não suporta o formato de ponto flutuante (%f), então tentar imprimir decimais com sprintf gera um ponto de interrogação no lugar do número.

Baud rateBytes por segundo (8N1)Linhas de 12 bytes por segundoQuando usar
960096080Sensores lentos, cabos longos, sketches de exemplo antigos
576005.760480Medições de velocidade média
11520011.520960Gráficos ao vivo, o ESP32 e a maioria dos projetos novos

Na linha serial, cada byte viaja como 10 bits, contando os bits de início e de parada, então 115200 baud significa cerca de 11.520 bytes por segundo. Para uma linha de 12 bytes formada por 25730,1023 mais o final de linha (\r\n), cabem até 960 linhas por segundo. Se a velocidade for diferente nos dois lados, passam caracteres sem sentido pela linha e o Python gera um UnicodeDecodeError. Entre 5 e 20 leituras por segundo é mais do que suficiente para o olho humano e para a maioria dos sensores; enviar com muito mais frequência do que o necessário deixa o gráfico lento e incha o arquivo.

Desenhando um gráfico ao vivo com matplotlib

Python
1from collections import deque
2
3import matplotlib.pyplot as plt
4import serial
5from matplotlib.animation import FuncAnimation
6
7ser = serial.Serial("COM3", 115200, timeout=0.1)
8N = 200                                          # ekrandaki ölçüm sayısı / readings on screen
9veri = deque([0] * N, maxlen=N)                  # eskiler kendiliğinden düşer / old values drop out
10
11fig, ax = plt.subplots()
12cizgi, = ax.plot(range(N), list(veri))
13ax.set_ylim(0, 1023)                             # Uno ADC aralığı / Uno ADC range
14ax.set_xlabel("Ornek / sample")
15ax.set_ylabel("A0")
16
17
18def guncelle(_):
19    while ser.in_waiting:                        # biriken bütün satırları oku / read every queued line
20        parca = ser.readline().decode("utf-8", errors="ignore").strip().split(",")
21        if len(parca) == 2 and parca[1].isdigit():
22            veri.append(int(parca[1]))
23    cizgi.set_ydata(list(veri))
24    return (cizgi,)
25
26
27ani = FuncAnimation(fig, guncelle, interval=50, blit=True, cache_frame_data=False)   # ani silinmemeli / keep ani
28plt.show()
29ser.close()

O gráfico é construído sobre um deque que guarda as últimas 200 leituras: cada novo valor entra pela direita e o mais antigo sai pela esquerda automaticamente. O FuncAnimation chama a função guncelle (atualizar) a cada 50 ms. O laço while ser.in_waiting dentro da função é importante: por enquanto o Arduino envia 5 linhas por segundo, mas quando você aumentar a taxa, ler só uma linha por quadro faz o gráfico ficar atrás do tempo real; esse laço lê todas as linhas que estão na fila e mantém o gráfico em dia. Não apague a variável ani: se nenhuma variável apontar para o objeto da animação, o coletor de lixo do Python o descarta e a janela congela. Use set_ylim(0, 4095) para o ESP32 e set_ylim(0, 65535) para o Pico. Se você só quer dar uma olhada rápida, o Plotter Serial da Arduino IDE resolve; a vantagem do Python é poder salvar o gráfico, combinar várias fontes e processar os dados ao mesmo tempo.

O erro de porta ocupada e o problema do reset automático

Uma porta serial só pode ser aberta por um programa de cada vez. Se o Monitor Serial ou o Plotter Serial da Arduino IDE estiver aberto enquanto o seu script em Python roda, se outro script em Python estiver segurando a porta ou se algum software que varre as portas, como programas de impressora 3D, estiver rodando em segundo plano, você recebe could not open port 'COM3': PermissionError(13, 'Access is denied.', None, 5) no Windows e Resource busy ou Device or resource busy no macOS e no Linux. O contrário também vale: enquanto um script em Python mantém a porta aberta, a Arduino IDE não consegue carregar o código. Por isso, use a construção with serial.Serial(...) as ser: nos seus scripts; a porta fecha sozinha quando o bloco termina ou quando acontece um erro. Crie o hábito de parar o script com Ctrl+C antes de carregar.

O segundo problema é mais traiçoeiro: no Arduino Uno, Nano e Mega, a linha DTR do chip USB está ligada ao pino RESET do processador por meio de um capacitor de 100 nF. Esse circuito existe para que a IDE possa reiniciar a placa automaticamente na hora de carregar o código, mas toda vez que o pyserial abre a porta o DTR também muda e a placa reinicia. A placa primeiro espera um ou dois segundos no bootloader e depois roda o seu sketch desde o começo. Os comandos enviados nesse intervalo se perdem, e você pode perder as primeiras linhas que o Arduino imprime no setup(). A solução é esperar com time.sleep(2) depois de abrir a porta ou, de forma mais robusta, esperar o Arduino enviar uma linha como HAZIR; o exemplo de comandos acima faz exatamente isso. Abra a porta uma vez no início do programa e mantenha aberta até o fim: um laço que abre e fecha a porta a cada leitura reinicia a placa sem parar.

Se você quiser evitar o reset por completo, há dois caminhos. Por software: crie a porta fechada com ser = serial.Serial(), defina ser.port e ser.baudrate, escreva ser.dtr = False e só então chame ser.open(). Isso costuma funcionar no Windows; no Linux e no macOS o sistema operacional ainda pode mexer no DTR quando a porta é aberta. Por hardware: coloque um capacitor de 10 µF entre os pinos RESET e GND do Uno, com o terminal positivo no RESET. Placas com USB integrada, como o Leonardo, o Micro e o Raspberry Pi Pico, não reiniciam quando a porta é aberta; nessas placas, as linhas que o seu código imprime antes de o Python se conectar podem se perder, então é mais seguro enviar as mensagens importantes quando chegar um comando.

Com o capacitor de 10 µF instalado, a Arduino IDE não consegue carregar código, porque o carregamento depende do mesmo circuito de reset automático; retire o capacitor antes de carregar. Se o Python mostrar device reports readiness to read but returned no data, a placa perdeu a conexão ou dois programas estão usando a porta ao mesmo tempo. Se a placa reinicia sozinha durante o funcionamento, a causa costuma ser alimentação insuficiente; alimente servos e motores com uma fonte separada em vez dos 5V da USB.

Comunicação serial com Python no ESP32

Todo o código Python deste texto funciona também com o ESP32; você só precisa mudar algumas coisas do lado do Arduino. Para a entrada analógica, use um dos pinos do ADC1 (por exemplo, GPIO 34). O ADC do ESP32 tem 12 bits e converte 0–3,3 V em 0–4095, então calcule a tensão no Python com deger * 3.3 / 4095. Ao ligar, o ESP32 imprime as próprias mensagens de inicialização em 115200 baud, como rst:0x1 (POWERON_RESET),boot:0x13; os scripts acima ignoram linhas que não podem ser convertidas em números, então essas mensagens não causam problema. Dependendo do circuito de reset automático da placa e do sistema operacional, o ESP32 também pode reiniciar quando a porta é aberta, então a espera e a linha HAZIR ajudam aqui também. O chip USB geralmente é um CP2102 ou um CH340. Se preferir dispensar o cabo, o ESP32 pode enviar os mesmos dados por Wi-Fi; veja os guias ESP32 WebSocket e ESP32 Google Sheets. Se for o seu primeiro ESP32, leia antes O que é o ESP32?.

Raspberry Pi Pico e MicroPython: qual é a diferença?

Placa de desenvolvimento Raspberry Pi Pico
Raspberry Pi Pico: graças ao periférico USB integrado do RP2040, ele não precisa de um chip USB-serial separado; com o MicroPython instalado, aparece no computador diretamente como uma porta serial.

Outra forma de juntar Python e hardware é rodar o Python direto na placa. O Raspberry Pi Pico pode ser programado em MicroPython: ler sensores, acionar LEDs e controlar o tempo, tudo é feito dentro do Pico com a sintaxe do Python. Só que o MicroPython é uma versão enxuta do Python de computador; matplotlib, pandas e OpenCV não rodam no Pico. Então, se você quer registrar dados, desenhar gráficos ou processá-los com IA, também liga o Pico ao Python do computador. O código pyserial deste texto funciona com o Pico também; você só precisa adaptar o nome da porta, a faixa de valores e a forma de ler as respostas dos comandos, como explicado abaixo.

CaracterísticaArduino + Python no PCPico + MicroPython (sozinho)Pico + Python no PC
Onde o Python roda?Só no computador; a placa roda C++Dentro do PicoNos dois lados: MicroPython no Pico, Python no computador
Precisa de computador?Sim, os dados são processados no computadorNão, funciona com alimentação USB ou bateriaSim
Bibliotecas disponíveispyserial, matplotlib, pandas, numpy e OpenCVMódulos limitados, como machine e timeTodas as bibliotecas do computador
Baud rateImportante, precisa ser igual nos dois ladosNão usa porta serialIrrelevante, os dados passam na velocidade da USB
Reset ao abrir a portaSim no Uno, Nano e MegaNenhumNão
Quando escolherVocê já tem um Arduino e precisa de bibliotecas prontasDispositivos simples e independentesVocê quer uma só linguagem (Python) nos dois lados

O código MicroPython abaixo é a versão para Pico do exemplo de Arduino acima: ele lê o potenciômetro no GP26 a cada 200 ms, envia o resultado no mesmo formato tempo,valor e aceita os mesmos comandos de LED. Aqui, o equivalente à Serial do Arduino são o print() e o sys.stdin; graças ao select.poll(), o laço de medição continua rodando enquanto espera comandos.

MicroPython
1# Pico'ya main.py adıyla kaydet / save it on the Pico as main.py
2import sys
3import select
4import time
5from machine import ADC, Pin
6
7led = Pin("LED", Pin.OUT)          # Pico ve Pico W dahili LED / onboard LED on Pico and Pico W
8pot = ADC(26)                      # GP26 = ADC0, potansiyometre orta ucu / potentiometer wiper
9okuyucu = select.poll()
10okuyucu.register(sys.stdin, select.POLLIN)   # USB'den veri gelince haber ver / notify when USB data arrives
11
12son = time.ticks_ms()
13while True:
14    if okuyucu.poll(0):                       # bekleyen komut var mı? / is a command waiting?
15        komut = sys.stdin.readline().strip()
16        if komut == "LED ON":
17            led.on()
18            print("OK LED ON")
19        elif komut == "LED OFF":
20            led.off()
21            print("OK LED OFF")
22    if time.ticks_diff(time.ticks_ms(), son) >= 200:   # 200 ms'de bir ölçüm / one reading every 200 ms
23        son = time.ticks_ms()
24        print("{},{}".format(son, pot.read_u16()))     # zaman,değer (0-65535) / time,value (0-65535)

Salve o código no Pico como main.py pelo Thonny, depois feche o Thonny e desconecte e reconecte o cabo USB do Pico: o Thonny usa a própria porta serial do Pico, então o seu script em Python não consegue abrir a porta enquanto ele está conectado, e ele ainda para o programa em execução ao se conectar; depois do reinício, o main.py roda automaticamente. O baud rate não importa na porta USB do Pico; seja qual for o valor que você colocar no Python, os dados passam na velocidade da USB. O read_u16() devolve valores de 0 a 65535, então calcule a tensão com deger * 3.3 / 65535. Como o Pico envia linhas de medição sem parar, o readline() do script de comandos acima pode devolver uma medição em vez da resposta; com o Pico, continue lendo até receber uma linha que comece com OK. Este exemplo não tem linha HAZIR nem os comandos PWM e READ; acrescente-os ao código do Pico se precisar. Cuidado se você enviar dados binários do computador: o byte 0x03 significa Ctrl+C no MicroPython e para o seu programa; dá para desativar isso com micropython.kbd_intr(-1). Se você está começando com o Pico, O que é o Raspberry Pi Pico? e Fundamentos de MicroPython são um bom começo.

Ideias de projetos com Arduino e Python

  • Registrador de temperatura e umidade: grave as leituras de um DHT11 em CSV uma vez por minuto e faça o gráfico de uma semana do clima do seu quarto no Excel ou com pandas.
  • Controle de lâmpada pelo computador: acione um módulo relé com comandos ON e OFF enviados pelo Python e combine isso com um temporizador ou com comandos de voz.
  • Gráfico de distância ao vivo: desenhe com matplotlib a distância medida por um HC-SR04. Se você girar o sensor com um servo, o mesmo método também permite desenhar uma tela de radar.
  • Painel de status do PC: faça o Python ler o uso de CPU e de memória com a biblioteca psutil e enviar para o Arduino, que mostra tudo em um display OLED.
  • Câmera que segue rostos: o OpenCV encontra a posição de um rosto na imagem, o Python converte isso em um ângulo e envia, e o Arduino gira a câmera com um servo motor.
  • Seu próprio teclado de atalhos: faça os botões do Arduino enviarem uma linha quando forem pressionados, e o Python ler essa linha e abrir um programa ou executar um atalho no computador.

Erros comuns e como resolver

Erro ou sintomaCausaSolução
ModuleNotFoundError: No module named 'serial' (módulo não encontrado)O pyserial não está instalado no interpretador Python que roda o script; o VS Code ou o Thonny podem estar usando outro PythonRode python -m pip install pyserial com o mesmo interpretador e selecione o interpretador certo no seu editor
AttributeError: module 'serial' has no attribute 'Serial' (o módulo não tem Serial)O pacote serial errado está instalado ou o seu arquivo se chama serial.pypip uninstall serial e pip install pyserial; renomeie o seu arquivo
could not open port 'COM3': PermissionError(13, 'Access is denied.', None, 5) (acesso negado)A porta está aberta em outro programa: Monitor Serial, Plotter Serial ou um segundo scriptFeche o outro programa; use a construção with no seu script
could not open port 'COM5': FileNotFoundError(2, 'The system cannot find the file specified.', None, 2) (porta não encontrada)Nome de porta errado ou a placa não está conectadaEncontre o nome certo com python -m serial.tools.list_ports -v
[Errno 13] Permission denied: '/dev/ttyACM0' (permissão negada)O seu usuário do Linux não tem permissão para acessar a porta serialsudo usermod -a -G dialout $USER, depois saia da sessão e entre de novo
Caracteres sem sentido ou UnicodeDecodeErrorBaud rates diferentes ou ruído durante a inicializaçãoUse a mesma velocidade nos dois lados; acrescente decode(errors="ignore") e reset_input_buffer()
readline() sempre devolve b''O Arduino não envia final de linha (print em vez de println), a porta está errada ou a placa ainda está inicializandoUse println, confira a porta e a velocidade, aumente o timeout
ValueError: invalid literal for int() with base 10 (valor inválido para int)Uma linha incompleta, vazia ou inesperada foi convertida em númeroColoque a conversão dentro de um try/except e confira o número de colunas
Os primeiros comandos são ignoradosO Uno reiniciou quando a porta foi aberta e o comando foi para o bootloaderEspere a linha HAZIR ou espere 2 segundos depois de abrir a porta
O gráfico fica atrás do tempo realSó uma linha é lida por quadro, então os dados se acumulam no bufferLeia todas as linhas com o laço in_waiting e envie com menos frequência
O código não carrega enquanto o script está rodandoO Python está segurando a portaPare o script com Ctrl+C e depois carregue
Quando algo der errado, primeiro tire o Python da jogada e teste o sketch do Arduino no Monitor Serial com o mesmo baud rate. Se as linhas aparecerem certinhas ali, o problema está do lado do Python (nome da porta, velocidade, final de linha ou interpretação dos dados); se também estiverem embaralhadas ali, corrija primeiro o sketch do Arduino. Se o código não carregar de jeito nenhum, siga os passos do guia erro ao carregar código no Arduino. Se você tem curiosidade sobre o lado de hardware da comunicação serial, ou seja, TX, RX e UART, dê uma olhada em comunicação serial UART.

Fazer o Arduino e o Python conversarem pela porta serial é o caminho mais rápido para transformar um projeto simples com sensor em um sistema que registra dados, desenha gráficos e pode ser controlado pelo computador. Primeiro leia um único valor e mostre na tela, depois salve em CSV e, por último, acrescente comandos e gráficos; em cada passo, confirme que o baud rate e o final de linha são iguais nos dois lados. No app BlueGrays você encontra projetos passo a passo, com esquemas do circuito e código, para medir luz com um LDR, temperatura e umidade com um DHT11, distância com um HC-SR04 e para comunicação serial entre dois Arduinos, assim você monta rapidamente a fonte dos dados que vai ler com o Python.

Perguntas frequentes

O Arduino pode ser programado em Python?

O Python não roda dentro de um Arduino Uno, Nano ou Mega; essas placas são programadas na linguagem Arduino, baseada em C++, pela Arduino IDE. O Python roda no computador e conversa com a placa pela porta serial usando o pyserial; você também pode carregar o exemplo StandardFirmata e controlar os pinos direto do Python com uma biblioteca como a pyFirmata2. Se você quer que o Python rode na própria placa, escolha um Raspberry Pi Pico ou um ESP32, que suportam MicroPython.

Como instalar o pyserial?

Rode python -m pip install pyserial em um terminal e escreva import serial no seu código. O pacote se chama pyserial, mas o módulo que você importa se chama serial; pip install serial instala um pacote completamente diferente e leva a um AttributeError. No Linux, você também precisa adicionar o seu usuário ao grupo dialout.

Como descobrir em qual porta COM está o meu Arduino?

Olhe em Ferramentas > Porta na Arduino IDE ou rode python -m serial.tools.list_ports -v em um terminal. No Windows, a seção Portas (COM e LPT) do Gerenciador de Dispositivos também mostra a porta. Se estiver em dúvida, desconecte a placa e liste as portas de novo; a que sumir é a do Arduino.

Como enviar dados do Python para o Arduino?

Abra a porta com serial.Serial e envie bytes, como em ser.write(b"LED ON\n"); não esqueça de converter o texto com encode() e de acrescentar um final de linha. Do lado do Arduino, verifique se chegaram dados com Serial.available() e leia a linha com Serial.readStringUntil('\n'). Como o Uno reinicia quando a porta é aberta, espere uns 2 segundos antes de enviar o primeiro comando.

Dá para usar o Monitor Serial do Arduino e o Python ao mesmo tempo?

Não, uma porta serial só pode ser aberta por um programa de cada vez; o segundo programa recebe um erro Access is denied ou Resource busy. Você pode processar os dados que chegam no Python e, ao mesmo tempo, mostrá-los na tela com print. Se precisar de dois canais separados, ligue um segundo conversor USB-TTL ao Arduino e crie uma segunda porta serial com SoftwareSerial.

Por que o meu Arduino reinicia quando o Python abre a porta?

No Uno, Nano e Mega, a linha DTR do chip USB está ligada ao pino RESET por meio de um capacitor, então a placa reinicia toda vez que a porta é aberta; esse circuito é necessário para que a IDE consiga carregar o código. Espere 2 segundos depois de abrir a porta ou espere uma linha do Arduino avisando que está pronto. Para evitar o reset por completo, você pode colocar um capacitor de 10 µF entre RESET e GND, mas precisa retirá-lo antes de carregar.

Ver todos os projetos
Medição da intensidade da luz com LDR
#11Iniciante

Medição da intensidade da luz com LDR

Quer ver a luminosidade do seu quarto em números? O LDR, um pequeno componente sensível à luz, mede a luz ambiente, e o Arduino exibe o valor na tela do computador a cada meio segundo. Cubra o sensor

4.6(81)
139
Medição de distância com HC-SR04 (Monitor Serial)
#27Iniciante

Medição de distância com HC-SR04 (Monitor Serial)

O sensor HC-SR04 funciona como um morcego que se orienta no escuro: emite um som que você não consegue ouvir, escuta o eco e mede a distância até o objeto à sua frente. O Arduino escreve essa distânci

4.6(46)
71
Temperatura e umidade com DHT11
#55Intermediário

Temperatura e umidade com DHT11

Qual é a temperatura do seu quarto? O ar está úmido ou seco? O sensor DHT11 mede a temperatura e a umidade, e o Arduino mostra os valores em um display LCD de duas linhas, atualizando a cada poucos se

4.7(47)
73
Leitura da intensidade da luz com módulo LDR e ESP32
#113Iniciante

Leitura da intensidade da luz com módulo LDR e ESP32

Acompanhe em tempo real no Monitor Serial o valor bruto do ADC do módulo LDR e a porcentagem relativa de luz calculada a partir de referências de escuridão e claridade. Essa porcentagem não é uma medida em lux. O projeto ensina a lógica de calibração do módulo. É ideal fazê-lo antes do projeto 10, pois ajuda a determinar o valor do limiar.

4.6(36)
55
Comunicação unidirecional entre 2 Arduinos
#57Intermediário
Premium

Comunicação unidirecional entre 2 Arduinos

Faça dois Arduinos conversarem por um único fio: ao pressionar o botão de um, o LED do outro acende; ao pressionar de novo, ele apaga. A placa transmissora informa que o botão foi pressionado com uma

4.4(37)
50
Comunicação bidirecional entre 2 Arduinos
#58Intermediário
Premium

Comunicação bidirecional entre 2 Arduinos

Dois Arduinos se conectam por fios e conversam entre si: ao pressionar o botão de um, a luz do outro acende; ao pressionar novamente, ela apaga. As duas placas executam o mesmo programa, então ambas e

4.5(20)
26
Controle de LED pelo celular via Bluetooth
#34Iniciante
Premium

Controle de LED pelo celular via Bluetooth

Um pequeno ensaio para acender e apagar as luzes do seu quarto pelo celular. O módulo Bluetooth repassa ao Arduino as letras enviadas pelo celular; uma letra acende o LED vermelho, outra acende o verd

4.8(68)
89
Registrador de temperatura com ESP32 (DHT22 + DS1307 RTC + microSD + OLED)
#144Avançado
Premium

Registrador de temperatura com ESP32 (DHT22 + DS1307 RTC + microSD + OLED)

Monte um registrador de temperatura com data e hora usando ESP32! Leia a temperatura e a umidade do DHT22 a cada minuto, obtenha a hora real do DS1307 RTC e grave os dados em CSV no cartão microSD. O OLED exibe a temperatura atual em letras grandes, a porcentagem de umidade, a data e a hora e o contador de registros. O arquivo /temp_log.csv no cartão SD pode ser aberto no Excel, Google Sheets ou pandas do Python para análise — ideal para monitorar estufas, acompanhar a temperatura de casa ou…

4.7(7)
12