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.

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ção | Lado do Arduino | Lado do Python | Atenção |
|---|---|---|---|
| Porta | Criada automaticamente pela USB | serial.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 |
| Quadro | 8 bits de dados, sem paridade, 1 bit de parada (8N1) | O padrão do pyserial também é 8N1 | Raramente é preciso mudar |
| Final de linha | Serial.println() acrescenta \r\n no final | readline() lê até encontrar \n | Não esqueça de acrescentar \n ao enviar pelo Python |
| Tempo limite | Serial.setTimeout(), padrão de 1000 ms | timeout=1 (segundos) | Com timeout=None, o readline() espera para sempre |
Instalando o pyserial (Windows, macOS, Linux)
- 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.
- 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.
- Verifique a instalação: python -c "import serial; print(serial.__version__)" deve mostrar um número de versão como 3.5.
- A seção do gráfico ao vivo também precisa do matplotlib: python -m pip install matplotlib.
- 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.
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.

| Chip USB | Onde você encontra | VID:PID | Nome exibido no Windows | Nome da porta no Linux |
|---|---|---|---|---|
| ATmega16U2 | Uno R3 original, Mega 2560 | 2341:0043 (Uno) | Arduino Uno (COMx) | /dev/ttyACM0 |
| CH340 | Placas Uno, Nano e Mega clones | 1A86:7523 | USB-SERIAL CH340 (COMx) | /dev/ttyUSB0 |
| CP2102 | ESP32 DevKit, NodeMCU | 10C4:EA60 | Silicon Labs CP210x USB to UART Bridge (COMx) | /dev/ttyUSB0 |
| FT232R | Nanos antigos, cabos FTDI | 0403:6001 | USB Serial Port (COMx) | /dev/ttyUSB0 |
| RP2040 (USB integrada) | Raspberry Pi Pico com MicroPython | 2E8A:0005 | Dispositivo 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.
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:
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
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.
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.
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}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 OFFO 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:
| Formato | Linha de exemplo | Vantagens | Desvantagens |
|---|---|---|---|
| Valor único | 512 | O formato mais simples; o Plotter Serial do Arduino desenha direto | Com vários valores, fica confuso saber qual é qual |
| CSV | 2573,512,23.5 | Curto; o Excel e o pandas leem direto | A ordem das colunas precisa ser a mesma nos dois lados |
| Chave:valor | temp:23.5,umid:41 | Legível; o Plotter Serial mostra os rótulos | Mais longo e um pouco mais trabalhoso de interpretar |
| JSON | {"t":23.5,"h":41} | Decodificado em uma linha com json.loads() no Python | O formato mais longo; pesa na RAM limitada do Uno |
| Binário | 2 bytes: 0x02 0x00 | A opção mais rápida e mais curta | Nã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 rate | Bytes por segundo (8N1) | Linhas de 12 bytes por segundo | Quando usar |
|---|---|---|---|
| 9600 | 960 | 80 | Sensores lentos, cabos longos, sketches de exemplo antigos |
| 57600 | 5.760 | 480 | Medições de velocidade média |
| 115200 | 11.520 | 960 | Grá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
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.
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?

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ística | Arduino + Python no PC | Pico + MicroPython (sozinho) | Pico + Python no PC |
|---|---|---|---|
| Onde o Python roda? | Só no computador; a placa roda C++ | Dentro do Pico | Nos dois lados: MicroPython no Pico, Python no computador |
| Precisa de computador? | Sim, os dados são processados no computador | Não, funciona com alimentação USB ou bateria | Sim |
| Bibliotecas disponíveis | pyserial, matplotlib, pandas, numpy e OpenCV | Módulos limitados, como machine e time | Todas as bibliotecas do computador |
| Baud rate | Importante, precisa ser igual nos dois lados | Não usa porta serial | Irrelevante, os dados passam na velocidade da USB |
| Reset ao abrir a porta | Sim no Uno, Nano e Mega | Nenhum | Não |
| Quando escolher | Você já tem um Arduino e precisa de bibliotecas prontas | Dispositivos simples e independentes | Você 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.
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 sintoma | Causa | Soluçã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 Python | Rode 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.py | pip 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 script | Feche 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á conectada | Encontre 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 serial | sudo usermod -a -G dialout $USER, depois saia da sessão e entre de novo |
| Caracteres sem sentido ou UnicodeDecodeError | Baud rates diferentes ou ruído durante a inicialização | Use 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á inicializando | Use 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úmero | Coloque a conversão dentro de um try/except e confira o número de colunas |
| Os primeiros comandos são ignorados | O Uno reiniciou quando a porta foi aberta e o comando foi para o bootloader | Espere a linha HAZIR ou espere 2 segundos depois de abrir a porta |
| O gráfico fica atrás do tempo real | Só uma linha é lida por quadro, então os dados se acumulam no buffer | Leia 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á rodando | O Python está segurando a porta | Pare o script com Ctrl+C e depois carregue |
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.
Projetos para montar depois desta leitura
Ver todos os projetos
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

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

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

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.

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

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

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

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…
Mais artigos
App BlueGrays
Monte este circuito passo a passo pelo celular
Esquema do circuito, código e lista de componentes em uma só tela. Mais de 200 projetos com Arduino, ESP32 e Pico prontos para montar.
Escaneie com seu celular
Aponte a câmera para o código para abrir a loja de aplicativos do seu celular.
ou abrir a loja

