Como vimos no nosso ultimo post, podemos fazer um ECG em casa com apenas alguns componentes eletrônicos simples. Montar um ECG do zero pode ser um pouco complicado para alguém sem nenhuma experiencia com componentes eletrônicos, por isso existem alguns módulos já montados e prontos para fazer medições com menos ruído quando comparado com os montados totalmente em casa, já que os componentes já estão soldados, o que evita mau contato nas conexões, além de alguns módulos já apresentarem filtros para eliminar ruídos do sinal medido.
Caracteristicas do módulo
Neste post, vamos utilizar o módulo AD8232, o qual foi propriamente feito para aferirmos medidas de ECG. Apesar de ter uma amplificação fixa de 100 vezes, ao contrário do nosso ECG feito em casa, onde podemos amplificar o sinal medido até milhares de vezes facilmente, esse módulo facilita nossa vida de diversas formas. De uma forma pura, quando medimos a atividade elétrica do coração, podemos enfrentar vários tipos de interferências, tanto por movimento, quanto por artigos eletrônicos ou rádio. Por isso, o AD8232 vem integrado com vários tipos de filtros de sinal. Além disso, a própria placa do módulo contém um plug do tipo P2 para fazermos a conexão com os eletrodos, dispensando o uso de cabos jacaré, por exemplo. O módulo também contém um LED especial, que “pisca” conforme os batimentos cardíacos, assim podemos saber se o módulo está funcionando corretamente independente da comunicação com o Arduino. Veja a seguir algumas características desse módulo:
- Tensão entre 2V e 3.5V
- Baixo consumo de corrente (170 µA)
- Filtro passa alta ajustável de 2 polos
- Pino Shutdown (usado para economizar energia)
- Saída analogia de fácil leitura por microcontroladores.
- Temperatura de funcionamento entre -40 e 85 graus Celsius.
- Dimensões de: 3.5 cm x 3 cm.
O módulo possui dois barramentos de pinos, um com 3 pinos para ligar os eletrodos, para o caso de não querermos usar o conector P2, e outro com 6 pinos, para alimentação e comunicação com o Arduino, conforme a tabela abaixo:
Nome do pino | Função do pino |
3.3v | Alimentação positiva |
GND | Alimentação negativa (terra) |
SDN (Shutdown) | Quando colocado em “Low”, o modulo entra em modo “Low power”. |
LA | Sinal do eletrodo 1 a ser posicionado no braço esquerdo ( Left Arm) |
RA | Sinal do eletrodo 2 a ser posicionado no braço direito ( Right Arm) |
RL | Sinal do eletrodo 3 a ser posicionado na perna esquerda ( Right Leg) |
OUTPUT | Saída analógica das leituras “filtradas” e amplificadas dos eletrodos |
LO+ | Utilizado como um detector de eletrodos. Caso o eletrodo LA esteja conectado (na porta +IN do CI) é enviado um sinal LOW, caso esteja desconectado é enviado um sinal HIGH |
LO- | Utilizado como um detector de eletrodos. Caso o eletrodo RA esteja conectado (na porta +IN do CI) é enviado um sinal LOW, caso esteja desconectado é enviado um sinal HIGH |
Nota: Este produto NÃO é um dispositivo médico e não se destina a ser usado como tal, nem para diagnosticar ou tratar quaisquer condições.
lOCALIZAÇÃO DOS ELETRODOS
Como sabemos, o ECG é baseado nos sinais elétricos captados de eletrodos posicionados pelo corpo. No entanto, esses eletrodos devem ser posicionados em determinados lugares para que tenhamos uma melhor captação de sinais.
O módulo AD8232, em seu kit, contém um cabo P2 com 3 plugs para eletrodos, e cada um desses deve ser posicionado de acordo com a figura a seguir:
- Verde: Perna direita
- Vermelho: Braço direito
- Amarelo: Braço esquerdo
Circuito com arduino
A ligação com o Arduino é bem simples. Basta alimentar o módulo usando os pinos GND e 3,3V e ligar a saída do módulo diretametne na porta A0, para fazermos as medições do sinal já amplificado e filtrado. Caso seja desejável, podemos ligar os pinos de detecção de eletrodos soltos em qualquer porta digital, para monitorar essa condição. No circuito abaixo, usamos para isso as entradas digitais 4 e 5. Se quiser, é possível ainda acionar o pino de baixo consumo de energia usando qualquer outra porta digital (ligação não mostrada no circuito abaixo).
Programação
O exemplo a seguir é bem simples, e realiza a medição do sinal e envio do sinal medido para a porta serial, que pode posteriormente ser visto com o Plotter Serial da IDE do Arduino. Esse exemplo também pode ser encontrado nos links a seguir: [ver código] [baixar código].
// Teste de leitura de sinais de ECG usando o módulo AD8232.
//
// Criado por: Lucas Jácome Cabral, 2023
/////////////////////////////////////////////////////////////////
// esta função só roda uma vez, no início
void setup() {
// inicializa a comunicação serial:
Serial.begin(115200);
// Define como entrada os pinos de detecção de eletrodo solto:
pinMode(4, INPUT);
pinMode(5, INPUT);
}
/////////////////////////////////////////////////////////////////
// esta função se repete indefinidamente
void loop() {
// Se algum eletrodo está solto, imprime '!' pela serial
if((digitalRead(4) == 1)||(digitalRead(5) == 1)){
Serial.println('!');
}
else{
//Leitura do pino Analógico e envio pela porta serial
Serial.println(analogRead(A0));
}
// aguarda 2ms, o que equivale a aprox. 500 medições por segundo
delay(2);
}
Depois de feita as conexões e o upload do código no Arduino uno, abra o “Plotter serial“(Crtl + Shift + L) dentro da IDE Arduino e já será mostrada a onda detectada pelo módulo ECG, como pode ser visto na figura abaixo.
Assim como acontece com o ECG feito em casa, ao ligarmos o nosso circuto no computador, estamos sujeitos ao ruído da rede de alimentação da nossa casa, que funciona a 60Hz. Assim, seu sinal, utilizando esse código, pode sair como o mostrado a seguir:
Pode não parecer, mas na imagem acima temos o sinal de ECG. O problema é que, somado a ele, temos também uma senoide em 60Hz, que é o ruído causado pela nossa rede de alimentação (tomadas de casa).
Um modo simples de eliminar esse ruído é fazer uma média do sinal a cada 1/60 segundo. Assim, temos um ponto (uma média) para cada ciclo completo do ruído, fazendo com que ele desapareça. Um programa que faz isso pode ser visto a seguir [ver código] [baixar código].
/*
* Exemplo de coleta de dados de ECG usando o módulo AD8232.
* O sinal coletado é coletado a 1800Hz, e passa por um filtro
* digital simples (média de 30 pontos) para eliminar o ruído
* da rede elétrica em 60Hz. Assim, a frequência final do sinal
* apresentado na porta serial é de 1800Hz/30 = 60Hz.
* O LED interno da placa Arduino é aceso quando o módulo AD8232
* detecta um eletrodo solto.
*
* Criado por: Erick Dario León Bueno de Camargo, 2023
*/
// Frequencia de amostragem desejada, em Hz:
long frequencia = 1800;
unsigned long atraso_us = (1000000.0/frequencia);
int medida;
unsigned long tempo_atual, tempo_anterior;
int contador;
/////////////////////////////////////////////////////////////////
// Esta função só roda uma vez, no início
void setup() {
// initialize the serial communication:
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(4, INPUT);
pinMode(5, INPUT);
tempo_anterior = micros();
medida = 0;
contador = 0;
}
/////////////////////////////////////////////////////////////////
// Esta função se repete indefinidamente
void loop() {
tempo_atual = micros();
if((digitalRead(4) == 1)||(digitalRead(5) == 1)){
digitalWrite(LED_BUILTIN, HIGH);
}
else{
if (digitalRead(LED_BUILTIN) == 1){ // se está aceso, apaga
digitalWrite(LED_BUILTIN, LOW);
}
// o abs() evita problemas com overflow da função micros()
if (abs(tempo_atual - tempo_anterior) > atraso_us) {
tempo_anterior = tempo_atual;
medida = medida + analogRead(A0); // soma a medida atual
contador = contador + 1;
}
if (contador >= 30){
Serial.println(medida/30);
medida = 0;
contador = 0;
}
}
}
Esse programa faz a medida do sinal a 1800Hz, ou seja, 1800 pontos por segundo, e, a cada 30 pontos, calcula e envia a média desses pontos pela serial. Assim, temos no final 1800/30 = 60 pontos por segundo, como pode ser visto na imagem a seguir:
Essa abordagem é bem simples, e funciona muito bem para elimiar ruído em 60Hz, mas em compensação o final fica distorcido, pois são eliminadas do sinal as componentes de alta frequência (os picos pontudos do sinal são atenuados), e o sinal é apresentado em 60Hz.
Para quem quer apenas ver a curva, esse sinal está ótimo! Mas, se você deseja eliminar apenas o ruído em 60Hz, e ainda mostrar o sinal em frequências maiores, é possível implementar digitalmente um filtro notch. Um filtro notch é um filtro que rejeita o sinal em uma determinada frequência, ou faixa de frequências, e deixa passar o sinal nas demais frequências fora dessa faixa. Um filtro notch bastante simples e antigo, criado especificamente para filtrar ruídos de rede de sinais de ECG na década de 80, foi implementado no código a seguir [ver código] [baixar código].
/*
* Exemplo de coleta de dados de ECG usando o módulo AD8232.
* O sinal coletado passa por um filtro notch digital para eliminar
* o ruído da rede elétrica em 60Hz.
* O LED interno da placa Arduino é aceso quando o módulo AD8232
* detecta um eletrodo solto.
*
* Filtro notch baseado em:
* CHOY, T. T. C.; LEUNG, P. M. Real time microprocessor-based
* 50 Hz notch filter for ECG. Journal of biomedical engineering,
* v. 10, n. 3, p. 285-288, 1988.
*
* Opções do filtro variam com a frequência de aquisição (Fs):
* Para Fs = 120Hz, usar "N_PONTOS 2" e "N 1"
* Para Fs = 240Hz, usar "N_PONTOS 3" e "N 2"
* Para Fs = 360Hz, usar "N_PONTOS 4" e "N 3"
* "a" deve estar entre 0 e 1:
* - "a" próximo de 0 distorce mais o sinal de ECG, mas é
* menos suscetível a variações nas frequências do ruído
* e de amostragem;
* - "a" próximo de 1 distorce menos o sinal de ECG, mas é
* mais suscetível a variações nas frequências do ruído
* e de amostragem.
*
* Criado por: Erick Dario León Bueno de Camargo, 2023
*/
// Frequencia de amostragem desejada, em Hz:
long frequencia = 120;
// Constantes necessárias para o filtro notch
#define N_PONTOS 2
#define N 1
#define a 0.05
unsigned long atraso_us = (1000000.0/frequencia);
int X[N_PONTOS]; // vetor com as medidas atuais
int Y[N_PONTOS]; // vetor com as medidas filtradas
unsigned long tempo_atual, tempo_anterior;
int contador;
/////////////////////////////////////////////////////////////////
// Esta função só roda uma vez, no início
void setup() {
// initialize the serial communication:
Serial.begin(115200);
pinMode(4, INPUT);
pinMode(5, INPUT);
pinMode(LED_BUILTIN, OUTPUT);
tempo_anterior = micros();
contador = 0;
}
/////////////////////////////////////////////////////////////////
// Esta função se repete indefinidamente
void loop() {
tempo_atual = micros();
if((digitalRead(4) == 1)||(digitalRead(5) == 1)){
digitalWrite(LED_BUILTIN, HIGH);
}
else{
if (digitalRead(LED_BUILTIN) == 1){ // se está aceso, apaga
digitalWrite(LED_BUILTIN, LOW);
}
// o abs() evita problemas com overflow da função micros()
if (abs(tempo_atual - tempo_anterior) > atraso_us) {
tempo_anterior = tempo_atual;
X[contador] = analogRead(A0); // realiza medida atual
int ponto_anterior = (contador+N_PONTOS-N)%N_PONTOS;
Y[contador] = ((X[contador]+X[ponto_anterior])/2) + a*(((X[contador]+X[ponto_anterior])/2) - Y[ponto_anterior]);
Serial.println(Y[contador]);
contador = contador + 1;
if (contador >= N_PONTOS){
contador = 0;
}
}
}
}
Para os mais curiosos, a descrição desse filtro pode se vista no artigo “CHOY, T. T. C.; LEUNG, P. M. Real time microprocessor-based 50 Hz notch filter for ECG. Journal of biomedical engineering, v. 10, n. 3, p. 285-288, 1988“.
O problema de filtros notch é que, como são projetados para eliminar uma frequência específica, se a frequência do ruído ou a frequência de amostragem apresentarem qualquer variação, o filtro acaba não eliminando totalmente o ruído. Esse filtro possui um parâmetro “a”, que deve ser escolhido entre 0 e 1, e ajusta o “peso” do filtro. Um valor de “a” próximo de 0 distorce mais o sinal de ECG, mas é menos suscetível a variações nas frequências do ruído e de amostragem, ao passo que um valor de “a” próximo de 1 distorce menos o sinal de ECG, mas é mais suscetível a variações nas frequências do ruído e de amostragem. A seguir são apresentados dois exemplos da curva de ECG coletados com esse filtro:
Apenas como curiosidade, é possível alterar o último exemplo para que as medidas do ADC sejam disparadas por um timer do Arduino. Isso faz com que a frequência de aquisição seja mais estável, já que é controlada internamente pelo microcontrolador. Um exemplo dessa abordagem é visto a seguir [ver código] [baixar código]:
/*
* Exemplo de coleta de dados de ECG usando o módulo AD8232, e
* configurando o disparo do ADC por timer para controlar a
* frequência de aquisição.
* O sinal coletado passa por um filtro notch digital para eliminar
* o ruído da rede elétrica em 60Hz.
* O LED interno da placa Arduino é aceso quando o módulo AD8232
* detecta um eletrodo solto.
*
* Filtro notch baseado em:
* CHOY, T. T. C.; LEUNG, P. M. Real time microprocessor-based
* 50 Hz notch filter for ECG. Journal of biomedical engineering,
* v. 10, n. 3, p. 285-288, 1988.
*
* Opções do filtro variam com a frequência de aquisição (Fs):
* Para Fs = 120Hz, usar "N_PONTOS 2" e "N 1"
* Para Fs = 240Hz, usar "N_PONTOS 3" e "N 2"
* Para Fs = 360Hz, usar "N_PONTOS 4" e "N 3"
* "a" deve estar entre 0 e 1:
* - "a" próximo de 0 distorce mais o sinal de ECG, mas é
* menos suscetível a variações nas frequências do ruído
* e de amostragem;
* - "a" próximo de 1 distorce menos o sinal de ECG, mas é
* mais suscetível a variações nas frequências do ruído
* e de amostragem.
*
* O timer deve ser ajustado de acordo com a frequência de
* aquisição desejada:
* - Fs = 120Hz, usar "OCR1A = 16666;" e "OCR1B = 16666;"
* - Fs = 240Hz, usar "OCR1A = 8332;" e "OCR1B = 8332;"
* - Fs = 360Hz, usar "OCR1A = 5554;" e "OCR1B = 5554;"
*
* Criado por: Erick Dario León Bueno de Camargo, 2023
*/
// Constantes necessárias para o filtro notch
#define N_PONTOS 2
#define N 1
#define a 0.95
int X[N_PONTOS]; // vetor com as medidas atuais
int Y[N_PONTOS]; // vetor com as medidas filtradas
int contador;
bool medida_disponivel = false;
/////////////////////////////////////////////////////////////////
// Esta interrupção roda assim que o ADC termina uma medida:
ISR (ADC_vect){
X[contador] = ADC;
medida_disponivel = true;
}
EMPTY_INTERRUPT (TIMER1_COMPB_vect); // limpa a configuração do timer
/////////////////////////////////////////////////////////////////
// Esta função só roda uma vez, no início
void setup() {
// initialize the serial communication:
Serial.begin(115200);
pinMode(4, INPUT);
pinMode(5, INPUT);
pinMode(LED_BUILTIN, OUTPUT);
contador = 0;
// configuração do Timer 1
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
TCCR1B = bit (CS11) | bit (WGM12); // CTC, prescaler = 8
TIMSK1 = bit (OCIE1B); // habilita interrupção por comparação
//OCR1A = 5554; // 16Mz/8/(5554+1) = 360,036Hz
//OCR1B = 5554; // 16Mz/8/(5554+1) = 360,036Hz
//OCR1A = 8332; // 16Mz/8/(8332+1) = 240,0096Hz
//OCR1B = 8332; // 16Mz/8/(8332+1) = 240,0096Hz
OCR1A = 16666; // 16Mz/8/(16666+1) = 119,9976Hz
OCR1B = 16666; // 16Mz/8/(16666+1) = 119,9976Hz
// configuração do ADC
ADCSRA = bit (ADEN) | bit (ADIE) | bit (ADIF); // liga o ADC e interrupção (ADC_vect)
ADCSRA |= bit (ADPS2); // Prescaler de 16
ADMUX = bit (REFS0) | (0 & 7); // leitura do canal 0 (A0)
ADCSRB = bit (ADTS0) | bit (ADTS2); // ADC dispara com TIMER1_COMPB_vect
ADCSRA |= bit (ADATE); // liga o disparo automático
}
/////////////////////////////////////////////////////////////////
// Esta função se repete indefinidamente
void loop() {
if((digitalRead(4) == 1)||(digitalRead(5) == 1)){
digitalWrite(LED_BUILTIN, HIGH);
}
else{
digitalWrite(LED_BUILTIN, LOW);
}
if(medida_disponivel){
int ponto_anterior = (contador+N_PONTOS-N)%N_PONTOS;
Y[contador] = ((X[contador]+X[ponto_anterior])/2) + a*(((X[contador]+X[ponto_anterior])/2) - Y[ponto_anterior]);
Serial.println(Y[contador]);
contador = contador + 1;
if (contador >= N_PONTOS){
contador = 0;
}
medida_disponivel = false;
}
}
Texto por: Lucas Jácome Cabral e Erick León
Revisão: Erick León