Componentes · 19 min de leitura

Sensor de batimentos cardíacos com Arduino: guia de BPM com Pulse Sensor e KY-039

Guia do sensor de batimentos cardíacos com Arduino: ligação do Pulse Sensor e do KY-039, BPM com a PulseSensorPlayground, onda de pulso no Plotter Serial, OLED e código para ESP32 e Pico.

Um sensor de pulso para Arduino, também chamado de sensor de batimentos cardíacos, é um pequeno módulo que acompanha com luz o fluxo de sangue na ponta do dedo ou no lóbulo da orelha e mede quantas vezes o seu coração bate por minuto, o BPM (beats per minute, batimentos por minuto). Os dois modelos mais comuns na eletrônica de hobby são o Pulse Sensor, de placa redonda, e o KY-039, com LED infravermelho. Os dois são ligados com três fios e lidos por um único pino analógico, mas diferem muito na qualidade do sinal e na dificuldade de escrever um código confiável para eles.

Neste guia você vai aprender, passo a passo, em qual princípio físico um sensor de pulso se baseia, como ver uma onda de pulso real no Plotter Serial, como calcular o BPM com a biblioteca PulseSensorPlayground, como tratar o sinal fraco do KY-039 com um algoritmo sem biblioteca e como mostrar o resultado em um display OLED com um ícone de coração. O código para ESP32 e Raspberry Pi Pico, os fatores que estragam uma medição e a diferença para o MAX30102 também estão aqui. Uma observação importante logo de início: esses sensores não são dispositivos médicos e não devem ser usados em nenhuma decisão relacionada à saúde.

Sensor de batimentos cardíacos Pulse Sensor com cabo de ligação
Módulo Pulse Sensor: uma placa redonda de cerca de 16 mm de diâmetro que reúne um LED verde, um sensor de luz e o circuito que amplifica o sinal; ele vem com um cabo de 3 pinos.

Como funciona um sensor de pulso? Fotopletismografia (PPG)

Todos os sensores de pulso funcionam com um método chamado fotopletismografia (PPG). Cada vez que o coração se contrai, ele envia uma onda de sangue para as artérias; quando essa onda chega aos pequenos vasos da ponta do dedo, o volume de sangue no tecido aumenta por um instante. Como a hemoglobina do sangue absorve luz, quanto mais sangue houver, menos luz é refletida de volta pelo tecido ou o atravessa. O sensor ilumina o tecido com um LED, e um sensor de luz mede a luz que volta; essa pequena oscilação regular na quantidade de luz é o próprio pulso.

A parte difícil é que quase toda a luz medida é constante. A pele, os músculos, os ossos e o sangue das veias absorvem sempre a mesma quantidade de luz; isso é chamado de componente DC do sinal. A componente AC, que muda a cada batimento, costuma ser só cerca de 1–2% da luz total. Ou seja, o verdadeiro trabalho do sensor é separar e amplificar uma pequena onda que viaja sobre um nível grande e estável. Uma onda de PPG mostra uma subida rápida até um pico (o pico sistólico), seguida de uma descida lenta; o pequeno entalhe no meio da descida e a pequena elevação logo depois dele (a incisura dicrótica) vêm do fechamento da valva aórtica. Um bom algoritmo conta cada batimento uma única vez e não confunde essa incisura com um segundo batimento.

A forma como a luz é enviada ao tecido também importa. O Pulse Sensor usa o método reflexivo: o LED e o sensor de luz ficam na mesma face e medem a luz que a pele espalha de volta. A luz verde dele é fortemente absorvida pela hemoglobina e dá um sinal claro dos vasos logo abaixo da pele; é também por isso que você vê LEDs verdes na parte de trás dos smartwatches. O KY-039 usa o método transmissivo: um LED infravermelho ilumina um lado do dedo e um fototransistor do outro lado mede a luz que o atravessa. A luz infravermelha penetra mais fundo no tecido, mas, como o KY-039 não tem circuito para amplificar o sinal, a onda resultante é muito pequena.

CaracterísticaValor no Pulse Sensor
Método de mediçãoPPG reflexiva
Fonte de luzLED verde
Sensor de luzAPDS-9008, sensibilidade máxima em 565 nm
Alimentação3–5 V
CorrenteCerca de 4 mA (em 5 V)
SaídaAnalógica; metade da tensão de alimentação em repouso (~2,5 V em 5 V, ~512 no Uno)
Diâmetro da placaCerca de 16 mm (0,625 polegada)
FiosNo original: vermelho +, preto GND, roxo sinal; as cores podem mudar nos clones

Pulse Sensor, KY-039 ou MAX30102: diferenças

Sensor de batimentos KY-039 (LED infravermelho e fototransistor)
KY-039: em cima, o LED infravermelho dobrado sobre o fototransistor; embaixo, o fototransistor; o dedo fica entre os dois. Não há amplificador na placa, então o sinal vai direto para o pino analógico.
CaracterísticaPulse SensorKY-039MAX30102
MétodoPPG reflexivaPPG transmissiva (dedo no meio)PPG reflexiva
LuzLED verdeLED infravermelhoVermelho (660 nm) + infravermelho (880 nm)
Amplificador e filtroSim (amplificador operacional)NãoADC interno de 18 bits, cancelamento de luz ambiente
SaídaAnalógicaAnalógica (muito fraca)Digital, I2C (endereço 0x57)
Alimentação3–5 V3,3–5 V3,3–5 V, conforme o módulo
Qualidade do sinalBoaFraca, sujeita a ruídoMuito boa
SpO2 (oxigênio)NãoNãoSim (com um algoritmo)
BibliotecaPulseSensor PlaygroundSem biblioteca prontaSparkFun MAX3010x

Se você está começando e só quer medir o pulso, o Pulse Sensor é a escolha mais fácil: como o sinal é amplificado e filtrado na própria placa, você vê uma onda nítida no Plotter Serial já na primeira tentativa. O amplificador operacional na parte de trás eleva essa pequena variação a um nível que o Arduino consegue ler; você pode testar como esse ganho é calculado na ferramenta Amplificador operacional. O KY-039 é barato e vem na maioria dos kits de sensores, mas a saída dele varia só alguns passos do ADC, então um código simples com limite fixo não dá resultados confiáveis. Se você também quer medir a saturação de oxigênio no sangue (SpO2), a escolha certa é o MAX30102; falamos dele em uma seção própria mais abaixo.

Ligação do sensor de pulso: Arduino Uno, ESP32 e Pico

Pino do sensorArduino UnoESP32Raspberry Pi Pico
+ (VCC, vermelho)5V3V33V3(OUT), pino 36
− (GND, preto)GNDGNDGND, pino 38
S (sinal, roxo)A0GPIO 34 (ADC1)GP26 (ADC0), pino 31
LED de batimento (opcional)D13 (LED embutido)GPIO 2 (LED embutido)LED da placa
As entradas analógicas do ESP32 e do Pico suportam no máximo 3,3 V. Como a saída do Pulse Sensor pode chegar até a tensão de alimentação, nessas placas alimente o sensor pelo pino 3V3, não pelo 5V nem pelo VBUS. Em placas clone, a ordem dos pinos e as cores dos fios podem ser diferentes; sempre leia as marcações S, + e − na placa antes de ligá-la.

A mesma tabela vale para o KY-039: o pino S vai para a entrada analógica, o + para a alimentação e o − para o GND. Confira se o LED infravermelho está dobrado sobre o fototransistor e se o seu dedo cabe entre os dois. Os componentes na parte de trás do Pulse Sensor ficam expostos; se um dedo ou uma superfície suada encostar atrás, isso gera ruído. Por isso é um bom hábito colar na frente o adesivo transparente que vem no kit e cobrir a parte de trás com cola quente. Se você usar um LED externo, calcule o resistor dele com a ferramenta Resistor para LED; você também pode ver o mapa de pinos da sua placa na ferramenta Pinagem.

Primeiro teste: ver a onda de pulso bruta no Plotter Serial

Antes de passar para o BPM, confira se o sensor realmente enxerga um pulso. O código abaixo não usa nenhuma biblioteca; ele lê o sinal 100 vezes por segundo e o envia para o Plotter Serial. Carregue o código, abra Ferramentas > Plotter Serial na Arduino IDE e ajuste a velocidade para 115200 baud. Se você nunca usou o Plotter Serial, o artigo Monitor Serial do Arduino explica como a janela funciona.

C++ (Arduino IDE)
1// Nabız sensörü ham sinyal testi (Seri Çizici) / Pulse sensor raw signal test (Serial Plotter)
2const int PULSE_PIN = A0;              // sensörün S (mor) kablosu / sensor S (purple) wire
3const unsigned long PERIOD_MS = 10;    // 10 ms = saniyede 100 örnek / 10 ms = 100 samples per second
4
5unsigned long lastSample = 0;
6
7void setup() {
8  Serial.begin(115200);                // Seri Çizici'de de 115200 seç / pick 115200 in the Serial Plotter too
9  lastSample = millis();
10}
11
12void loop() {
13  if (millis() - lastSample >= PERIOD_MS) {
14    lastSample += PERIOD_MS;           // sabit aralıkla örnekle / sample at a fixed interval
15    int raw = analogRead(PULSE_PIN);   // 0-1023 arası değer / value between 0 and 1023
16    Serial.print("Sinyal:");
17    Serial.print(raw);
18    Serial.println(",Orta:512");       // boştaki orta seviye çizgisi / idle midpoint line
19  }
20}

Sem dedo, a linha do Pulse Sensor fica reta em torno de 512, com uma leve tremedeira; isso é metade da alimentação e mostra que o sensor está ligado corretamente. Quando você apoia de leve a ponta do dedo no sensor, em poucos segundos aparece uma onda que sobe rápido e desce devagar a cada batimento; a amplitude dela costuma ficar entre 100 e 300 passos. Se você não vê a onda, não avance para o código de BPM: primeiro corrija a ligação, a alimentação e a pressão do dedo. Se os picos viram uma linha reta grudada em 0 ou em 1023, o sinal está saturando (clipping); geralmente o dedo está se mexendo ou o sensor está sendo apertado demais. Se você tem curiosidade de saber de onde vem a faixa 0–1023 de uma leitura analógica, veja o artigo ADC e conversão analógico-digital.

Medindo o BPM com a PulseSensorPlayground

  1. Abra o Gerenciador de Bibliotecas na Arduino IDE, busque PulseSensor Playground e instale a biblioteca. Para mais detalhes sobre como instalar bibliotecas, veja o artigo como instalar uma biblioteca no Arduino.
  2. Ligue o sensor como na tabela: + no 5V, − no GND e S no A0.
  3. Carregue o código abaixo e abra o Plotter Serial em 115200 baud; a primeira linha mostra o sinal bruto e a segunda mostra o valor de BPM.
  4. Coloque o dedo no sensor, apoie a mão na mesa e fique parado por 10 segundos; a biblioteca ajusta a média nos primeiros batimentos.
  5. Se preferir ver os números linha por linha no Monitor Serial, mude a constante PLOTTER do código para false; assim, o BPM e o tempo entre batimentos (IBI) são impressos a cada batimento.
C++ (Arduino IDE)
1// PulseSensor Playground ile BPM ölçme / Measuring BPM with PulseSensor Playground
2#include <PulseSensorPlayground.h>
3
4const int PULSE_PIN = A0;              // sensörün S (mor) kablosu / sensor S (purple) wire
5const int LED_PIN = LED_BUILTIN;       // her atışta yanan LED (D13) / LED lit on each beat (D13)
6const int THRESHOLD = 550;             // başlangıç eşiği, boşta ~512 / starting threshold, idle ~512
7const bool PLOTTER = true;             // true: Seri Çizici, false: Seri Monitör / true: Serial Plotter, false: Serial Monitor
8
9PulseSensorPlayground pulseSensor;     // kütüphane nesnesi / library object
10unsigned long lastPrint = 0;
11
12void setup() {
13  Serial.begin(115200);
14  pulseSensor.analogInput(PULSE_PIN);
15  pulseSensor.blinkOnPulse(LED_PIN);
16  pulseSensor.setThreshold(THRESHOLD);
17  if (!pulseSensor.begin()) {          // 2 ms'lik zamanlayıcıyı başlatır / starts the 2 ms timer
18    Serial.println(F("PulseSensor baslatilamadi / could not start"));
19    while (true) {}
20  }
21}
22
23void loop() {
24  // Atış anı: BPM ve atışlar arası süre / moment of a beat: BPM and inter-beat interval
25  if (pulseSensor.sawStartOfBeat() && !PLOTTER) {
26    Serial.print(F("BPM: "));
27    Serial.print(pulseSensor.getBeatsPerMinute());
28    Serial.print(F("  IBI: "));
29    Serial.print(pulseSensor.getInterBeatIntervalMs());
30    Serial.println(F(" ms"));
31  }
32
33  // 20 ms'de bir çizim satırı / one plotter line every 20 ms
34  if (PLOTTER && millis() - lastPrint >= 20) {
35    lastPrint = millis();
36    Serial.print(F("Sinyal:"));
37    Serial.print(pulseSensor.getLatestSample());
38    Serial.print(F(",BPM:"));
39    Serial.println(pulseSensor.getBeatsPerMinute());
40  }
41}
FunçãoO que faz
analogInput(pino)Seleciona o pino analógico em que o sensor está ligado (A0 por padrão)
blinkOnPulse(pino)Acende o LED deste pino enquanto o sinal está acima do limite, ou seja, durante cada batimento
fadeOnPulse(pino)Acende o LED de um pino PWM a cada batimento e o apaga devagar
setThreshold(valor)Limite inicial; quando nenhum batimento é encontrado, o limite volta a esse valor
begin() (inicialização)Inicia a amostragem; retorna true se der certo
sawStartOfBeat() (novo batimento)Retorna true se um novo batimento começou desde a última chamada
getBeatsPerMinute() (BPM)BPM calculado pela média dos últimos 10 intervalos entre batimentos
getInterBeatIntervalMs() (IBI)Tempo entre os dois últimos batimentos (IBI), em milissegundos
getLatestSample() (última leitura)A última leitura bruta (0–1023)
isInsideBeat() (dentro do batimento)Retorna true enquanto o sinal está acima do limite

A biblioteca lê o sensor em segundo plano com uma interrupção de timer a cada 2 ms (500 vezes por segundo), então a amostragem continua estável mesmo que você faça tarefas longas no loop(). A cada batimento, ela registra o pico e o vale da onda e move o limite exatamente para o meio entre os dois; em outras palavras, o valor que você passa para setThreshold() é só um ponto de partida. Ela ignora subidas que chegam em menos de 250 ms depois do batimento anterior ou antes de passarem três quintos do último intervalo entre batimentos, para que a incisura dicrótica não seja contada como um segundo batimento. O BPM é calculado pela média dos últimos 10 intervalos entre batimentos; se nenhum batimento for encontrado por 2,5 segundos, tudo é zerado e getBeatsPerMinute() retorna 0.

Escolha o limite inicial olhando o sinal bruto: 30–50 passos acima do maior valor que a linha atinge sem dedo é um bom começo (entre 530 e 600 na maioria dos sensores). Se o limite for baixo demais, o ruído é contado como batimento mesmo sem dedo; se for alto demais, pulsos fracos passam despercebidos. Mais um aviso: no Arduino Uno a biblioteca usa o Timer1 para a amostragem, então o analogWrite() não funciona nos pinos 9 e 10. Se você adicionar a biblioteca Servo ao projeto, a biblioteca passa para o Timer2; aí quem é afetado são o PWM dos pinos 3 e 11 e a função tone().

Para uma leitura precisa: pressão do dedo, luz ambiente e movimento

A maior parte do que estraga uma leitura de pulso tem a ver com a forma de segurar o sensor, não com o código. Teste os pontos abaixo um por um com o Plotter Serial aberto; ver como cada um muda a onda é o jeito mais rápido de entender o sensor.

  • Pressão do dedo: pressione de leve, mas de forma constante. Se você encostar de leve demais, cada tremor do dedo gera um ruído maior que a onda; se apertar demais, você comprime os capilares, o fluxo de sangue cai e a onda achata ou até desaparece. Os melhores resultados vêm prendendo o sensor no dedo com uma fita de velcro.
  • Luz ambiente: a luz do sol e das lâmpadas entra no sensor pelas laterais. Na rede de 60 Hz do Brasil, muitas lâmpadas piscam 120 vezes por segundo (100 vezes em redes de 50 Hz), e essa cintilação entra no sinal. Cubra totalmente a área em volta do sensor com o dedo ou enrole uma faixa escura por cima.
  • Movimento: um cabo balançando, falar, mexer o dedo ou andar produz sinais muito maiores que o pulso. Apoie a mão na mesa e fique parado durante a medição.
  • Mãos frias: em um dedo frio, os vasos se contraem e o sinal fica mais fraco. Aqueça as mãos ou prenda o sensor no lóbulo da orelha; o lóbulo também sofre menos com o movimento.
  • Suor e oleosidade: a pele úmida espalha a luz de outro jeito e suja a superfície do sensor. Use o adesivo protetor transparente na frente e limpe o sensor de vez em quando com um pano seco.
  • Tempo de estabilização: nos primeiros 5–10 segundos depois que você coloca o dedo, o sinal se estabiliza e a média se completa; ignore os valores que aparecerem nesse período.
Existe um jeito simples de saber se o seu sensor mede corretamente: conte os batimentos no punho ou no pescoço por 30 segundos, multiplique por 2 e compare com o BPM da tela. Em repouso, uma diferença de 3–5 BPM é normal; se a diferença for maior, primeiro corrija a pressão do dedo e a luz ambiente e depois ajuste o limite.

Monitor cardíaco em um display OLED

O Plotter Serial serve bem na mesa, mas um monitor cardíaco portátil precisa de uma telinha. O código abaixo desenha na parte de cima de um OLED SSD1306 de 0,96 polegada o BPM em números grandes e um ícone de coração que cresce a cada batimento, e na parte de baixo uma onda de pulso ao vivo que varre a tela da esquerda para a direita. Ligue o OLED nos pinos A4 (SDA) e A5 (SCL) do Uno e a alimentação dele no 5V e no GND; a instalação da biblioteca do display e o ajuste do endereço são explicados em detalhes no artigo display OLED de 0,96 polegada (SSD1306).

C++ (Arduino IDE)
1// Pulse Sensor + 0,96" OLED: BPM, kalp simgesi ve nabız dalgası / BPM, heart icon and pulse wave
2#include <Wire.h>
3#include <Adafruit_GFX.h>
4#include <Adafruit_SSD1306.h>
5#include <PulseSensorPlayground.h>
6
7Adafruit_SSD1306 display(128, 64, &Wire, -1);  // 128x64, reset pini yok / no reset pin
8PulseSensorPlayground pulseSensor;
9
10const int PULSE_PIN = A0;              // sensörün S pini / sensor S pin
11const int THRESHOLD = 550;             // başlangıç eşiği / starting threshold
12const int GRAPH_TOP = 24;              // dalga alanının üst kenarı / top edge of the wave area
13const int GRAPH_H = 40;                // dalga alanının yüksekliği / height of the wave area
14
15int x = 0;                             // tarama sütunu (0-127) / sweep column (0-127)
16int lastY = GRAPH_TOP + GRAPH_H / 2;   // önceki noktanın y'si / y of the previous point
17int lo = 1023, hi = 0;                 // bu taramadaki en küçük/büyük değer / min/max in this sweep
18int scaleLo = 400, scaleHi = 624;      // çizimde kullanılan aralık / range used for drawing
19unsigned long lastDraw = 0, beatTime = 0;
20
21// İki daire ve bir üçgenle kalp çiz / draw a heart from two circles and a triangle
22void drawHeart(int cx, int cy, bool big) {
23  int r = big ? 6 : 4;                 // atış anında büyük kalp / bigger heart on a beat
24  display.fillCircle(cx - r, cy - r / 2, r, SSD1306_WHITE);
25  display.fillCircle(cx + r, cy - r / 2, r, SSD1306_WHITE);
26  display.fillTriangle(cx - 2 * r, cy, cx + 2 * r, cy, cx, cy + 2 * r, SSD1306_WHITE);
27}
28
29void setup() {
30  Serial.begin(115200);
31  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {  // I2C adresi 0x3C / I2C address 0x3C
32    Serial.println(F("SSD1306 allocation failed"));
33    while (true) {}
34  }
35  display.clearDisplay();
36  pulseSensor.analogInput(PULSE_PIN);
37  pulseSensor.setThreshold(THRESHOLD);
38  pulseSensor.begin();                 // örnekleme kesmeyle sürer / sampling runs in an interrupt
39}
40
41void loop() {
42  if (pulseSensor.sawStartOfBeat()) beatTime = millis();  // atış anını sakla / store the beat time
43
44  if (millis() - lastDraw < 40) return; // saniyede 25 kare / 25 frames per second
45  lastDraw = millis();
46
47  // Alt bölüm: soldan sağa taranan nabız dalgası / bottom: pulse wave swept left to right
48  int s = pulseSensor.getLatestSample();
49  if (s < lo) lo = s;
50  if (s > hi) hi = s;
51  int y = map(constrain(s, scaleLo, scaleHi), scaleLo, scaleHi, GRAPH_TOP + GRAPH_H - 1, GRAPH_TOP);
52  display.drawFastVLine(x, GRAPH_TOP, GRAPH_H, SSD1306_BLACK);              // eski sütunu sil / erase the old column
53  display.drawFastVLine((x + 1) % 128, GRAPH_TOP, GRAPH_H, SSD1306_BLACK);  // önündeki boşluk / gap ahead
54  if (x > 0) display.drawLine(x - 1, lastY, x, y, SSD1306_WHITE);
55  lastY = y;
56  if (++x >= 128) {                    // tarama bitti: ölçeği yenile / sweep done: refresh the scale
57    x = 0;
58    if (hi - lo > 20) { scaleLo = lo; scaleHi = hi; }
59    lo = 1023;
60    hi = 0;
61  }
62
63  // Üst bölüm: kalp ve BPM / top: heart and BPM
64  display.fillRect(0, 0, 128, GRAPH_TOP, SSD1306_BLACK);
65  drawHeart(14, 10, millis() - beatTime < 150);
66  int bpm = pulseSensor.getBeatsPerMinute();
67  display.setTextColor(SSD1306_WHITE);
68  display.setTextSize(3);
69  display.setCursor(40, 0);
70  if (bpm > 0) display.print(bpm);
71  else display.print(F("--"));         // parmak yok / no finger
72  display.setTextSize(1);
73  display.setCursor(104, 14);
74  display.print(F("BPM"));
75  display.display();                   // tamponu ekrana gönder / push the buffer to the screen
76}

O código atualiza a tela 25 vezes por segundo e acrescenta um ponto à onda em cada quadro, então a largura da tela mostra cerca de 5 segundos de registro do pulso. No fim de cada varredura, a onda é reescalada pelo menor e pelo maior valor daquela varredura, para que um sinal fraco ou forte caiba na tela. Enviar um quadro ao display pelo I2C leva cerca de 25–30 ms, mas, como a biblioteca faz a amostragem em uma interrupção, nenhum batimento se perde nesse meio-tempo. No Uno, o buffer do display ocupa 1 KB de RAM; como o código desenha a onda direto na tela, sem guardá-la em um array, a memória é suficiente, embora sobrem só cerca de 450 bytes livres.

Medindo o pulso com o KY-039

No KY-039, o fototransistor está ligado em série com um resistor, e a tensão de saída muda com a luz que ele recebe: mais luz significa tensão mais baixa, menos luz, tensão mais alta. Não há amplificador nem filtro no meio. Com o dedo no lugar, um batimento costuma mover essa tensão só 2–10 passos do ADC, cerca de 10–50 mV; até uma pequena mudança na luz ambiente é maior que isso. Por isso, o código escrito para o Pulse Sensor e um limite fixo como 550 não funcionam no KY-039; é preciso primeiro limpar o sinal e depois usar um limite que se adapta ao tamanho da própria onda.

O código abaixo faz isso em quatro passos. Primeiro, ele lê o pino analógico sem parar durante 20 ms (cerca de 175 leituras) e tira a média; isso reduz o ruído aleatório e, como 20 ms é exatamente um período da rede de 50 Hz, cancela quase totalmente a cintilação das lâmpadas (na rede de 60 Hz do Brasil, use a janela de 60 Hz da tabela abaixo). Depois, suaviza o sinal com um filtro passa-baixa leve e remove o nível DC com uma média móvel bem lenta, deixando só a onda de pulso. No terceiro passo, calcula duas envoltórias que acompanham os picos e os vales da onda e coloca o limite no meio entre elas. Por fim, um batimento é contado quando o sinal cruza o limite subindo, e o BPM é obtido pela média dos últimos 8 intervalos entre batimentos.

C++ (Arduino IDE)
1// KY-039 ile nabız ölçme (kütüphanesiz) / Heart rate with the KY-039 (no library)
2const int SENSOR_PIN = A0;             // KY-039 S pini / KY-039 S pin
3const int LED_PIN = LED_BUILTIN;       // atışta yanan LED / LED lit on each beat
4const bool PLOTTER = true;             // true: Seri Çizici, false: Seri Monitör / true: Serial Plotter, false: Serial Monitor
5const unsigned long WINDOW_US = 20000; // 20 ms = 50 Hz şebekenin bir periyodu / one period of 50 Hz mains
6const float MIN_AMP = 1.2;             // tepe-tepe gürültü sınırı (ADC birimi) / peak-to-peak noise limit (ADC units)
7const int AVG_BEATS = 8;               // BPM için ortalanan aralık sayısı / intervals averaged for BPM
8
9float smooth, baseline;                // süzülmüş sinyal ve DC seviye / filtered signal and DC level
10float posEnv = 0, negEnv = 0;          // tepe ve çukur zarfları / peak and trough envelopes
11bool armed = true;                     // yeni atışa hazır mı / ready for a new beat
12unsigned long lastBeat = 0;            // son atışın zamanı / time of the last beat
13unsigned int ibi[AVG_BEATS];           // atışlar arası süreler (ms) / inter-beat intervals (ms)
14int ibiCount = 0, ibiIndex = 0, bpm = 0;
15
16// 20 ms boyunca okuyup ortalama al / read for 20 ms and average
17float readWindow() {
18  unsigned long start = micros();
19  long sum = 0;
20  int n = 0;
21  while (micros() - start < WINDOW_US) {
22    sum += analogRead(SENSOR_PIN);
23    n++;
24  }
25  return (float)sum / n;
26}
27
28float averageIbi() {
29  if (ibiCount == 0) return 500.0;     // geçmiş yoksa 120 BPM varsay (ilk bekleme 350 ms) / assume 120 BPM without history (first wait 350 ms)
30  unsigned long sum = 0;
31  for (int i = 0; i < ibiCount; i++) sum += ibi[i];
32  return (float)sum / ibiCount;
33}
34
35void addInterval(unsigned int ms) {
36  ibi[ibiIndex] = ms;                  // halka tampon / ring buffer
37  ibiIndex = (ibiIndex + 1) % AVG_BEATS;
38  if (ibiCount < AVG_BEATS) ibiCount++;
39  bpm = (int)(60000.0 / averageIbi() + 0.5);
40}
41
42void setup() {
43  Serial.begin(115200);
44  pinMode(LED_PIN, OUTPUT);
45  smooth = baseline = readWindow();    // başlangıç seviyesi / starting level
46}
47
48void loop() {
49  float x = readWindow();              // saniyede 50 örnek / 50 samples per second
50  unsigned long now = millis();
51
52  smooth += (x - smooth) * 0.5;        // hafif alçak geçiren süzgeç / light low-pass filter
53  baseline += (smooth - baseline) * 0.05;  // yavaş DC takibi (~0,4 s) / slow DC tracking (~0.4 s)
54  float ac = smooth - baseline;        // yalnız nabız dalgası / pulse wave only
55
56  // Zarflar: uçları hemen yakala, yavaşça bırak / envelopes: catch extremes fast, release slowly
57  posEnv = (ac > posEnv) ? ac : posEnv * 0.99;
58  negEnv = (ac < negEnv) ? ac : negEnv * 0.99;
59  float amp = posEnv - negEnv;         // tepe-tepe genlik / peak-to-peak amplitude
60  float mid = (posEnv + negEnv) / 2;   // eşik: dalganın ortası / threshold: middle of the wave
61
62  unsigned long sinceBeat = now - lastBeat;
63  float refractory = constrain(averageIbi() * 0.7, 250.0, 600.0);  // dikrotik çentiği atla / skip the dicrotic notch
64
65  if (armed && amp > MIN_AMP && ac > mid && sinceBeat > refractory) {
66    armed = false;                     // atış bulundu / beat found
67    digitalWrite(LED_PIN, HIGH);
68    if (sinceBeat < 2000) addInterval(sinceBeat);  // 30 BPM'den yavaşı alma / ignore gaps slower than 30 BPM
69    lastBeat = now;
70    if (!PLOTTER && bpm > 0) {
71      Serial.print(F("BPM: "));
72      Serial.println(bpm);
73    }
74  }
75  if (!armed && ac < mid - amp * 0.25) {  // dalga yeterince indi: yeniden hazır / wave fell enough: re-arm
76    armed = true;
77    digitalWrite(LED_PIN, LOW);
78  }
79  if (sinceBeat > 2500 && ibiCount > 0) {  // 2,5 s atış yok: sıfırla / no beat for 2.5 s: reset
80    ibiCount = 0;
81    bpm = 0;
82  }
83
84  if (PLOTTER) {
85    Serial.print(F("Nabiz:"));
86    Serial.print(ac);
87    Serial.print(F(",Esik:"));
88    Serial.println(mid);
89  }
90}
Constante / valorO que faz
WINDOW_US = 20000 (janela)Janela da média; cancela a cintilação da rede de 50 Hz (use 16667 em países de 60 Hz, como o Brasil)
0.5 (smooth, suavização)Filtro passa-baixa leve; suaviza saltos bruscos
0.05 (baseline, linha de base)Acompanha o nível DC em cerca de 0,4 segundo; remove mudanças lentas na pressão do dedo
0.99 (envoltórias)Velocidade de liberação das envoltórias de pico e de vale (cerca de 2 segundos)
MIN_AMP = 1.2 (amplitude mínima)Uma onda abaixo dessa amplitude pico a pico é considerada ruído
refractory (período refratário)70% do último intervalo médio (250–600 ms; 350 ms enquanto ainda não há histórico, para não perder um pulso rápido); impede que a incisura dicrótica conte como um segundo batimento
mid - amp * 0.25 (rearme)Nível de rearme; nenhum novo batimento é procurado até a onda descer o suficiente

Quando você carregar o código e abrir o Plotter Serial, vai ver duas linhas: Nabiz (pulso) mostra a onda limpa e Esik (limite), o limite adaptativo que passa pelo meio dela. Veja quanto a linha Nabiz oscila sem dedo e ajuste MIN_AMP um pouco acima desse valor pico a pico; diminua-o se o seu sinal for muito fraco. Para saber mais sobre como suavizar sinais, veja o artigo filtragem de dados de sensores. O mesmo código também funciona com o Pulse Sensor; nesse caso, basta aumentar MIN_AMP para cerca de 20.

Sensor de pulso com o ESP32

No ESP32, alimente o sensor pelo pino 3V3 e ligue o sinal ao GPIO 34. Esse pino pertence ao grupo ADC1; os pinos do ADC2 não podem ser lidos com o Wi-Fi ligado, então, se você for enviar os dados do pulso pela internet, precisa escolher um dos pinos do ADC1 (GPIO 32–39). O ADC do ESP32 é de 12 bits (0–4095) e, com alimentação de 3,3 V, o Pulse Sensor dá um valor em torno de 2000 em repouso.

As versões recentes da PulseSensorPlayground também são compatíveis com o ESP32, mas a biblioteca usa as funções de timer da versão 3.x do pacote de placas do ESP32 e não compila com a versão 2.x. Nos exemplos, ela reduz o ADC para 10 bits com analogReadResolution(10), para que o limite seja dado na mesma escala 0–1023 do Uno (685 no exemplo do ESP32). Para não depender das diferenças de versão, abaixo está um sketch sem biblioteca que usa o mesmo algoritmo da seção do KY-039; ele funciona com as duas versões do pacote de placas.

C++ (Arduino IDE)
1// ESP32 + Pulse Sensor: kütüphanesiz BPM / BPM without a library
2const int SENSOR_PIN = 34;             // ADC1 pini, Wi-Fi açıkken de çalışır / ADC1 pin, works with Wi-Fi on
3const int LED_PIN = 2;                 // çoğu DevKit'te dahili LED / built-in LED on most DevKits
4const unsigned long PERIOD_MS = 10;    // saniyede 100 örnek / 100 samples per second
5const float MIN_AMP = 80;              // 12 bitte tepe-tepe gürültü sınırı / peak-to-peak noise limit in 12-bit units
6const int AVG_BEATS = 8;               // BPM için ortalanan aralık sayısı / intervals averaged for BPM
7
8float smooth, baseline, posEnv = 0, negEnv = 0;
9bool armed = true;
10unsigned long lastSample = 0, lastBeat = 0;
11unsigned long ibi[AVG_BEATS];
12int ibiCount = 0, ibiIndex = 0, bpm = 0;
13
14// 8 okumanın ortalaması: ESP32 ADC'si gürültülüdür / average of 8 reads: the ESP32 ADC is noisy
15float readSensor() {
16  long sum = 0;
17  for (int i = 0; i < 8; i++) sum += analogRead(SENSOR_PIN);
18  return sum / 8.0;
19}
20
21float averageIbi() {
22  if (ibiCount == 0) return 500.0;     // geçmiş yoksa 120 BPM varsay (ilk bekleme 350 ms) / assume 120 BPM without history (first wait 350 ms)
23  unsigned long sum = 0;
24  for (int i = 0; i < ibiCount; i++) sum += ibi[i];
25  return (float)sum / ibiCount;
26}
27
28void setup() {
29  Serial.begin(115200);
30  pinMode(LED_PIN, OUTPUT);
31  analogReadResolution(12);            // 0-4095 arası değer / values from 0 to 4095
32  smooth = baseline = readSensor();
33  lastSample = millis();
34}
35
36void loop() {
37  if (millis() - lastSample < PERIOD_MS) return;
38  lastSample += PERIOD_MS;             // sabit aralıkla örnekle / sample at a fixed interval
39  unsigned long now = millis();
40
41  float x = readSensor();
42  smooth += (x - smooth) * 0.3;        // alçak geçiren süzgeç (~6 Hz) / low-pass filter (~6 Hz)
43  baseline += (smooth - baseline) * 0.02;  // DC takibi (~0,5 s) / DC tracking (~0.5 s)
44  float ac = smooth - baseline;
45
46  posEnv = (ac > posEnv) ? ac : posEnv * 0.995;  // tepe zarfı / peak envelope
47  negEnv = (ac < negEnv) ? ac : negEnv * 0.995;  // çukur zarfı / trough envelope
48  float amp = posEnv - negEnv;
49  float mid = (posEnv + negEnv) / 2;
50
51  unsigned long sinceBeat = now - lastBeat;
52  float refractory = constrain(averageIbi() * 0.7, 250.0, 600.0);
53
54  if (armed && amp > MIN_AMP && ac > mid && sinceBeat > refractory) {
55    armed = false;
56    digitalWrite(LED_PIN, HIGH);
57    if (sinceBeat < 2000) {            // 30-240 BPM aralığı / 30-240 BPM range
58      ibi[ibiIndex] = sinceBeat;
59      ibiIndex = (ibiIndex + 1) % AVG_BEATS;
60      if (ibiCount < AVG_BEATS) ibiCount++;
61      bpm = (int)(60000.0 / averageIbi() + 0.5);
62      Serial.printf("BPM: %d  IBI: %lu ms\n", bpm, sinceBeat);
63    }
64    lastBeat = now;
65  }
66  if (!armed && ac < mid - amp * 0.25) {
67    armed = true;
68    digitalWrite(LED_PIN, LOW);
69  }
70  if (sinceBeat > 2500 && ibiCount > 0) {  // parmak çekildi / finger removed
71    ibiCount = 0;
72    bpm = 0;
73    Serial.println("Parmak yok / no finger");
74  }
75}

Neste código, o sinal é lido 100 vezes por segundo e, como o ADC do ESP32 é ruidoso, cada amostra é a média de 8 leituras. Os valores de limite e das envoltórias estão ajustados para a escala de 12 bits, então MIN_AMP é 80 (cerca de 65 mV). O verdadeiro trunfo do ESP32 é o Wi-Fi: você pode transmitir o BPM ao vivo para uma página web por WebSocket, enviar uma mensagem no Telegram quando o pulso passar de um limite ou registrar as suas leituras no Google Sheets.

Medindo o pulso com o Raspberry Pi Pico e MicroPython

No Raspberry Pi Pico, ligue o pino + do sensor ao 3V3(OUT) (pino 36), o − ao GND (pino 38) e o S ao GP26 (ADC0, pino 31). No MicroPython, read_u16() retorna um valor de 0 a 65535; o código o desloca 4 bits para a direita para trazê-lo à mesma escala 0–4095 do ESP32, assim os valores de limite continuam iguais nas duas placas. Se você salvar o código no Pico como main.py pelo Thonny, ele roda sozinho toda vez que a placa é ligada.

MicroPython
1# Raspberry Pi Pico + Pulse Sensor: MicroPython ile BPM / BPM with MicroPython
2from machine import ADC, Pin
3import time
4
5sensor = ADC(26)                   # GP26 = ADC0, sensörün S pini / sensor S pin
6led = Pin("LED", Pin.OUT)          # kartın üzerindeki LED / on-board LED
7
8PERIOD_MS = 10                     # saniyede 100 örnek / 100 samples per second
9MIN_AMP = 80                       # 12 bitte tepe-tepe gürültü sınırı / peak-to-peak noise limit (12-bit)
10AVG_BEATS = 8                      # BPM için ortalanan aralık / intervals averaged for BPM
11
12def read12():
13    # 4 okumanın ortalaması, 0-4095 / average of 4 reads, 0-4095
14    s = 0
15    for _ in range(4):
16        s += sensor.read_u16() >> 4
17    return s / 4
18
19smooth = baseline = read12()
20pos_env = neg_env = 0.0
21armed = True
22ibis = []
23bpm = 0
24last_beat = time.ticks_ms()
25next_t = time.ticks_ms()
26
27while True:
28    while time.ticks_diff(time.ticks_ms(), next_t) < 0:
29        pass                       # bir sonraki örnek zamanını bekle / wait for the next sample time
30    next_t = time.ticks_add(next_t, PERIOD_MS)
31    now = time.ticks_ms()
32
33    x = read12()
34    smooth += (x - smooth) * 0.3            # alçak geçiren süzgeç / low-pass filter
35    baseline += (smooth - baseline) * 0.02  # DC takibi / DC tracking
36    ac = smooth - baseline
37
38    pos_env = ac if ac > pos_env else pos_env * 0.995
39    neg_env = ac if ac < neg_env else neg_env * 0.995
40    amp = pos_env - neg_env
41    mid = (pos_env + neg_env) / 2
42
43    since = time.ticks_diff(now, last_beat)
44    avg_ibi = sum(ibis) / len(ibis) if ibis else 500   # geçmiş yoksa 120 BPM varsay / assume 120 BPM without history
45    refractory = min(max(avg_ibi * 0.7, 250), 600)   # dikrotik çentiği atla / skip the dicrotic notch
46
47    if armed and amp > MIN_AMP and ac > mid and since > refractory:
48        armed = False
49        led.on()
50        if since < 2000:                    # 30 BPM'den yavaşı alma / ignore gaps slower than 30 BPM
51            ibis.append(since)
52            if len(ibis) > AVG_BEATS:
53                ibis.pop(0)
54            bpm = round(60000 / (sum(ibis) / len(ibis)))
55            print("BPM:", bpm, "IBI:", since)
56        last_beat = now
57    if not armed and ac < mid - amp * 0.25:
58        armed = True
59        led.off()
60    if since > 2500 and ibis:               # parmak çekildi / finger removed
61        ibis = []
62        bpm = 0
63        print("Parmak yok / no finger")

O código funciona com a mesma lógica da versão para ESP32: ele faz uma amostragem a cada 10 ms, remove o nível DC, encontra o batimento com um limite adaptativo e calcula o BPM pela média dos últimos 8 intervalos. A cada batimento, o LED da placa acende, e o BPM e o intervalo entre batimentos são impressos na janela Shell do Thonny. Se a sintaxe do Python é novidade para você, o artigo fundamentos do MicroPython é um bom ponto de partida.

Diferença para o MAX30102: medindo o oxigênio no sangue (SpO2)

O MAX30102 é um sensor de chip único que a Analog Devices (antiga Maxim Integrated) projetou para medir a frequência cardíaca e a saturação de oxigênio no sangue. Dentro dele há dois LEDs, vermelho (660 nm) e infravermelho (880 nm), um fotodiodo, um circuito de cancelamento de luz ambiente e um ADC de 18 bits. Ele mesmo faz a medição, guarda as amostras em uma memória de 32 amostras (FIFO) e as envia como números pelo barramento I2C (endereço 0x57). Como o ruído captado pelos fios analógicos e o limite do ADC de 10 bits do Arduino deixam de importar, o sinal dele é muito mais limpo que o do Pulse Sensor.

O segredo da medição de SpO2 é usar luz de duas cores diferentes. A hemoglobina que transporta oxigênio absorve mais luz infravermelha e menos luz vermelha; a hemoglobina que já liberou o oxigênio faz o contrário. O sensor mede as componentes AC e DC da onda de pulso para as duas luzes e calcula a razão R = (AC_vermelho / DC_vermelho) / (AC_infravermelho / DC_infravermelho); a SpO2 é obtida a partir dessa razão com uma curva de calibração empírica. O Pulse Sensor e o KY-039 usam luz de uma só cor, então não conseguem calcular essa razão, ou seja, não medem oxigênio. A biblioteca mais popular para o MAX30102 no Arduino é a SparkFun MAX3010x; o exemplo Example5_HeartRate calcula a frequência cardíaca e o Example8_SPO2 calcula a saturação de oxigênio.

Em algumas placas baratas de MAX30102, os resistores de pull-up do I2C estão ligados a 1,8 V; um Arduino Uno de 5 V não reconhece esse nível como HIGH, então o sensor não é encontrado. Se a sua placa tiver um jumper de solda para escolher entre 1,8 V e 3,3 V, passe-o para o lado de 3,3 V. Esses módulos também são muito sensíveis à pressão do dedo e ao movimento.

Um sensor de pulso não é um dispositivo médico

Os circuitos e os códigos deste artigo são para fins educativos e de hobby. Os módulos Pulse Sensor, KY-039 e MAX30102 não foram testados nem aprovados como dispositivos médicos; não faça diagnósticos nem tome decisões sobre remédios ou tratamentos com base nas leituras deles. Se você tiver dor no peito, palpitações, tontura, desmaio ou falta de ar, não confie em um sensor: procure atendimento médico imediatamente e, em uma emergência, ligue para o número de emergência local (no Brasil, o SAMU, 192).

Há motivos concretos para isso. Monitores cardíacos e oxímetros de pulso médicos são calibrados dentro de limites de precisão definidos e testados em diferentes tons de pele, com baixo fluxo sanguíneo e em movimento; os módulos de hobby e os algoritmos que escrevemos não passam por esses testes. Além disso, medir o pulso não é o mesmo que avaliar o ritmo cardíaco: para entender problemas de ritmo é preciso um ECG, que registra o sinal elétrico do coração. Como informação geral, a frequência cardíaca de repouso de um adulto costuma ficar entre 60 e 100 batimentos por minuto; em quem pratica exercícios regularmente, ela pode cair para 40–60. Se a sua leitura ficar fora dessa faixa, confira primeiro o sensor e o código; se continuar preocupado, converse com um médico.

Ideias de projetos com sensor de pulso

  • Luz que pulsa com o coração: a cada batimento, acenda em vermelho um anel de LEDs WS2812B e apague devagar; mude a cor do azul para o vermelho à medida que o BPM sobe.
  • Monitor cardíaco com bipe: toque um bipe curto em um buzzer toda vez que sawStartOfBeat() retornar true; um projeto divertido que imita o som dos monitores de hospital.
  • Monitor cardíaco portátil: coloque o código do OLED em uma caixa com uma bateria e um circuito de carga; desligue a tela quando a medição terminar para a bateria durar mais.
  • Teste de recuperação pós-exercício: meça logo depois de terminar o exercício e calcule quantos batimentos o pulso cai no primeiro minuto; registre o valor todos os dias e acompanhe a sua evolução.
  • Registrador de frequência cardíaca: grave os valores de BPM com data e hora em um cartão SD ou envie-os para o Excel e faça um gráfico.
  • Jogo de exercício de respiração: mostre na tela como o pulso muda enquanto você respira de forma lenta e profunda; você vence quando leva o pulso abaixo de um valor-alvo.

Erros comuns e como corrigi-los

SintomaCausa provávelSolução
BPM sempre 0 ou “--”Nenhum batimento detectado: limite alto demais, sem dedo ou pino S na entrada erradaConfira a onda com o sketch do sinal bruto; mova o limite para o meio da onda
BPM muito alto, como 150–220Ruído ou a incisura dicrótica contados como um segundo batimentoAumente o limite, firme o dedo, bloqueie a luz ambiente
BPM aparece mesmo sem dedoLimite abaixo do ruído em repouso; cintilação das lâmpadas entrando no sinalAjuste o limite 30–50 passos acima do maior valor em repouso e cubra o sensor
Sinal parado, reto em 0 ou 1023+ e − invertidos, fio partido, pino S no lugar errado ou sensor com defeitoConfira a ligação com a tabela; use 3V3 no ESP32 e no Pico
Há onda, mas ela vive se desfazendoDedo se mexendo ou pressão mudandoPrenda o sensor com uma fita de velcro, apoie a mão na mesa e fique parado
Nenhuma onda no KY-039LED não dobrado sobre o fototransistor ou luz ambiente forte saturando o fototransistorDobre o LED para que fique sobre o dedo, use o sensor na sombra, diminua MIN_AMP
“too few arguments to function 'hw_timer_t* timerBegin(uint8_t, uint16_t, bool)'” no ESP32 (argumentos de menos para timerBegin)A PulseSensorPlayground está sendo compilada com a versão 2.x do pacote de placas do ESP32Atualize o pacote de placas para a 3.x ou use o código para ESP32 sem biblioteca deste artigo
analogWrite() não funciona no pino 9 ou 10A biblioteca usa o Timer1 no UnoUse os pinos 3, 5, 6 ou 11 para PWM
Na hora de resolver problemas, comece sempre pelo código mais simples: se o teste do sinal bruto mostra uma onda limpa, o hardware está bom e o problema está no limite ou no código; se não há onda, mudar o código não adianta, então corrija primeiro a ligação e o contato do sensor com o dedo.

Um sensor de pulso é um daqueles raros projetos que ensinam leitura analógica, filtragem de sinais, interrupções de timer e gráficos em tempo real, tudo ao mesmo tempo com um único pino analógico, e você vê o resultado no próprio corpo. Se você quer um projeto para iniciantes que também mede luz, o sensor de luz LDR é um bom próximo passo. No app BlueGrays você encontra projetos que usam as peças deste artigo, como ler a intensidade da luz com um LDR, o buzzer, projetos com ESP32 e display OLED e um efeito de respiração com PWM no Pico, com esquemas do circuito e código passo a passo.

Perguntas frequentes

Qual é a precisão de um sensor de pulso com Arduino?

Quando bem posicionado e com a pessoa parada, um Pulse Sensor costuma medir a frequência cardíaca de repouso com erro de poucos BPM. Movimento, luz ambiente, mãos frias e a pressão do dedo aumentam o erro rapidamente. O sinal do KY-039 é bem mais fraco, então as leituras oscilam mais; nenhum desses sensores serve para medição médica.

Qual é a diferença entre o Pulse Sensor e o KY-039?

O Pulse Sensor usa um LED verde e o método reflexivo, amplifica e filtra o sinal com um amplificador operacional na placa e fica em repouso na metade da tensão de alimentação. O KY-039 mede a luz infravermelha que atravessa o dedo, não tem amplificador na placa e a saída dele varia só alguns passos do ADC. Por isso o Pulse Sensor funciona facilmente com uma biblioteca pronta, enquanto o KY-039 precisa de filtragem e de um limite adaptativo.

Um sensor de pulso consegue medir o oxigênio no sangue (SpO2)?

O Pulse Sensor e o KY-039, não, porque o cálculo de SpO2 depende de comparar a luz em dois comprimentos de onda diferentes, vermelho e infravermelho. Para isso, você precisa de um sensor com dois LEDs, como o MAX30102. Mesmo a leitura do MAX30102 é uma estimativa não calibrada e não substitui um oxímetro médico.

Por que o meu sensor de pulso mostra valores muito altos ou instáveis?

A causa mais comum é o ruído ou a incisura dicrótica da onda de pulso serem contados como um segundo batimento; nesse caso, o BPM sai mais ou menos o dobro do valor real. Aumente o limite, prenda o dedo no lugar com uma fita de velcro e proteja o sensor da luz. Olhar a onda bruta no Plotter Serial mostra a origem do problema na hora.

O Pulse Sensor funciona com o ESP32 e o Raspberry Pi Pico?

Sim, mas nas duas placas você deve alimentar o sensor pelo pino 3V3, porque as entradas analógicas delas não suportam mais de 3,3 V. No ESP32, use um pino do ADC1, como o GPIO 34, para não conflitar com o Wi-Fi; no Pico, use o GP26, o GP27 ou o GP28. A PulseSensorPlayground exige a versão 3.x do pacote de placas do ESP32 e, no MicroPython, você escreve o seu próprio algoritmo, como mostrado neste artigo.

Qual é a frequência cardíaca normal?

Em adultos em repouso, a frequência cardíaca costuma ficar entre 60 e 100 batimentos por minuto; em quem pratica exercícios regularmente pode ser de 40–60 e, em crianças, é mais alta. O pulso sobe naturalmente com exercício, estresse, cafeína e febre. Esta é uma informação geral; converse com um médico sobre as suas leituras ou qualquer sintoma.

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
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
Uso do buzzer
#13Iniciante

Uso do buzzer

Já se perguntou como uma campainha ou um alarme produz som? O Arduino faz um pequeno buzzer emitir bipes em intervalos curtos: bipe, pausa, bipe, pausa. Você vê como a eletricidade produz som e dá o p

4.7(89)
161
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
Voltímetro digital com display LCD
#56Intermediário
Premium

Voltímetro digital com display LCD

Descubra a tensão de uma pilha ou de um adaptador de alimentação com um medidor feito por você. Um divisor formado por dois resistores reduz a tensão alta a um nível que a placa consegue ler, e o Ardu

4.5(27)
36
ESP32 HTTPClient + API de clima (com display OLED SSD1306)
#134Avançado
Premium

ESP32 HTTPClient + API de clima (com display OLED SSD1306)

Neste projeto, usamos o ESP32 para fazer uma chamada REST à API de clima wttr.in pela internet com HTTPClient.h. Diferentemente da versão anterior com LCD I2C, aqui usamos um display OLED SSD1306 (128×64), que permite usar fontes grandes, ícones vetoriais e uma apresentação visual rica em uma única tela. Sete ícones vetoriais de condições do tempo (ensolarado, parcialmente nublado, nublado, chuvoso, com neve, com tempestade e com neblina) são desenhados durante a execução, sem arquivos bitmap.

4.3(10)
19
ESP32 em sono profundo — Economia de bateria (OLED + contador na RAM RTC)
#135Avançado
Premium

ESP32 em sono profundo — Economia de bateria (OLED + contador na RAM RTC)

O modo de sono profundo do ESP32 reduz o consumo de corrente para ~10 µA ao desligar a CPU e a maioria dos periféricos, sendo essencial para dispositivos IoT alimentados por bateria. Neste projeto, usamos três fontes de despertar: um temporizador (15 segundos), um pino externo (EXT0 — botão no GPIO 33) e toque (T0/GPIO 4). As variáveis marcadas com RTC_DATA_ATTR são preservadas durante o sono profundo, mantendo os contadores de inicializações e de despertares por temporizador e botão.

4.3(10)
16
Raspberry Pi Pico: variação de brilho do LED com PWM (efeito de respiração)
#160Iniciante

Raspberry Pi Pico: variação de brilho do LED com PWM (efeito de respiração)

O LED não só pisca — ele respira! Com o PWM (modulação por largura de pulso) do Raspberry Pi Pico, o brilho do LED aumenta e diminui lentamente, como a luz de um MacBook em repouso. Aprenda a usar a classe machine.PWM, duty_u16() (brilho de 16 bits, de 0–65535), frequência PWM (freq(1000) — 1.000 ciclos de ligar/desligar por segundo, sem cintilação visível), laços com passos usando range() (crescentes e decrescentes) e saída semelhante à analógica em um pino digital.

0.0(0)
4