Internet de les coses amb ESP32 i ESP8266

Exemples Referència Plaques   Recursos CITCEA
Projectes Programació Perifèrics   Inici

Sensor de ritme cardíac

Programa del grup 3

En aquest cas s'ha dissenyat un mesurador que es comunica amb un script de Google, el qual guarda les dades en un full de càlcul. Si les pulsacions surten fora del rang considerat normal es canvia el color del NeoPixel i sona el brunzidor.

El programa del microcontrolador és el següent:

// Biblioteques
#include <Adafruit_NeoPixel.h> // Carregar biblioteca NeoPixel
#include <SPI.h>
#include <WiFiNINA.h>
// Valors predefinits
#define LEDPIN 6 // Pota on hi ha el LED de la placa
#define SENSOR A2 // Pota on hi ha el sensor
#define ValComp 6 // Nombre de repeticions per eliminar soroll
#define server_len 50
#define pag_len 400
// Objectes
Adafruit_NeoPixel cadena = Adafruit_NeoPixel(1, 1, NEO_GRB + NEO_KHZ800); //(Quantitat LEDs, Pota on es connecten, Tipus NeoPixels)
WiFiSSLClient client;
// Variables i constants
unsigned char Pulsacions = 0; // Pulsacions per minut
const char idXarxa[] = "xarxa-wifi"; // Nom del punt d'accés
const char contrasenya[] = "contrasenya-wifi"; // Contrasenya de connexió
const String server0 = "script.google.com";
String pagina0 = "";
const String pagina_base = "/macros/s/^^fycbz2fTbh7suCc9md1mo-66TfZUyzqbXy3clO_l3EwVE5W6szaOWR6liFWfJA3qiEj6TR/exec?";
char server[server_len];
char pagina[pag_len];
unsigned long darreraConnexio = 0;
const unsigned long periodeConnexio = 10000UL;
bool pendent, completa, redir;
bool ara = false;
float puls;
String data;
String peticio = ""; // Aquí guardarem una línia de la petició del client
String peticioAux = ""; // i la petició anterior (també ho farem servir de reserva)
int status = WL_IDLE_STATUS;
int llPolsos[4]={-1,-1,-1,-1};
int i =0;
int bo=1;
int mit=0;
// Setup
void setup(){
  pinMode(LEDPIN, OUTPUT);
  digitalWrite(LEDPIN, LOW);
  Serial.begin(9600);
  pinMode(3, OUTPUT);
  //A partir d'aquí setup full de càlcul
  while (!Serial) {
    ; // Esperem que l'usuari obri el monitor sèrie
  }
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("No s'ha trobat el dispositiu Wi-Fi");
    while (true); // Bloquegem el programa
  }
  String versio = WiFi.firmwareVersion();
  if (versio < "1.0.0") {
    Serial.println("Convindria actualitzar el firmware");
  }
  while (status != WL_CONNECTED) {
    Serial.print("Connectant a la xarxa ");
    Serial.println(idXarxa);
    status = WiFi.begin(idXarxa, contrasenya);
    delay(10000); // Ho tornarem a intentar passats 10 s
  }
  Serial.print("Connectat a ");
  Serial.println(WiFi.SSID());
  Serial.print("Estat de la connexió: ");
  Serial.println(WiFi.status());
  Serial.print("Adreça IP del dispositiu: ");
  Serial.println(WiFi.localIP());
  Serial.print("Intensitat del senyal: ");
  Serial.print(WiFi.RSSI());
  Serial.println(" dBm");
  Serial.println();
  redir = false;
  //Fins aquí
  cadena.begin(); // Inicialitza els NeoPixels
  cadena.show();
}
// Loop
void loop(){
  Pulsacions = llegir();
  if (i==4){
    i=0;
  }
  if (llPolsos[3] != -1){
    mit=((llPolsos[0]+llPolsos[1]+llPolsos[2]+llPolsos[3])/10);
    if (Pulsacions < (Pulsacions+mit) and Pulsacions > (Pulsacions - mit) and Pulsacions >0){
      bo=0;
    }
  }
  llPolsos[i]=Pulsacions;
  i=i+1;
  if(bo==0){
    delay(1000); // Espera 1 segon
    if (Pulsacions > 100 || Pulsacions < 50){ // Nombre de pulsacions frontera
      cadena.setPixelColor(0, 25, 0, 0); // Pixel vermell
      cadena.show(); // Actualitza
      tone(3, 440, 1000); // La durant 1 s
      delay(3000); // Espera 3 segons
      cadena.setPixelColor(0, 0, 0, 0); // Pixel apagat
      cadena.show(); // Actualitza
    } else {
      cadena.setPixelColor(0, 0, 25, 0); // Pixel verd
      cadena.show(); // Actualitza
      delay(3000); // Espera 3 segons
      cadena.setPixelColor(0, 0, 0, 0); // Pixel apagat
      cadena.show(); // Actualitza
    }
  }
  // El bucle principal té tres parts:
  // 1. Gestió dels caràcters que arriben
  // 2. Tractament de les dades rebudes
  // 3. Nova petició quan ha passat el temps
  while (client.available()) {
    // Gestió dels caràcters que arriben
    // Aquest bucle va guardant els caràcters rebuts
    // i espera al moment en que arriba un salt de línia
    char c = client.read(); // Rebem caràcters del servidor
    if (c == '\n') { // Mirem si és un salt de línia
      peticioAux = peticio; // Guardem la petició anterior
      peticio = ""; // Ens preparem per a la línia següent
      completa = true; // Preparat per escriure-ho
    } else {
      peticio += c; // Afegim el caràcter rebut
    } // Quan ha arribat un salt de línia, hem de mirar què ha arribat
    if (completa){ // Ha arribat una línia completa
      if (peticioAux.startsWith("HTTP/1.1 200")){ // Resposta bona
        pendent = true;
      }
	  if (peticioAux.startsWith("HTTP/1.1 302")){ // Redireccionament
        redir = true;
      }
	  if (redir && (peticioAux.startsWith("Location:"))){
        // Si hi ha redireccionament, hem de buscar l'adreça
        // i extreure'n el servidor i la pàgina
        String adre = peticioAux.substring(peticioAux.indexOf("//") +2);
        String server1 = adre.substring(0, adre.indexOf(".com") +4);
        String pagina1 = adre.substring(adre.indexOf(".com") +4);
        server1.toCharArray(server, 50);
        pagina1.toCharArray(pagina, 400);
        ara = true;
      }
      completa = false;
    }
  }
  // Hi ha una resposta per processar
  // Però ignorarem les dades que ens envia l'script
  if (pendent) {
    pendent = false;
  }
  // Quan toca, tornem a fer una petició
  if (ara || ((millis() - darreraConnexio > periodeConnexio))) {
    if(!ara){
      Serial.print(Pulsacions);
      Serial.println(" pulsacions per minut");
      data = "p=";
      data += Pulsacions;
    }
    if (redir){
      redir = false;
    } else {
      pagina0 = pagina_base + data;
      server0.toCharArray(server, server_len);
      pagina0.toCharArray(pagina, pag_len);
    }
    client.stop();
    if (client.connect(server, 443)) {
      if (bo==0){
        bo=1;
        Serial.println("S'ha fet la connexió al servidor");
        client.print("GET ");
        client.print(pagina);
        client.println(" HTTP/1.1");
        client.print("Host: ");
        client.println(server);
        client.println("Connection: close");
        client.println();
        // Guardem quan hem fet la connexió
        darreraConnexio = millis();
        if (!ara) {
          Serial.print("Enviat: ");
          Serial.println(data);
        }
      }
    } else {
      Serial.println("connection failed");
    }
    ara = false;
  }
}
// Funcions
// 1) Compta els polsos en 15 s i retorna les pulsacions per minut
unsigned char llegir(){
  unsigned int Max = 0; // Valor màxim del senyal en un període
  unsigned int Min = 4096; // Valor mínim del senyal en un període
  unsigned int Centre = 0; // Valor mig del senyal en un període
  unsigned char Polsos = 0;
  boolean Pols = 1; // Posem a 1 perquè primer hem de trobar una vall
  unsigned long TempsLec = millis(); // Guarda l'instant de la lectura anterior
  unsigned long Temps = millis(); // Guarda l'instant d'inici del procés
  while((millis() - Temps) <= 10000){ // Farem mesures durant 10 s
    // a partir del moment en el que trobem el centre
    if((millis() - TempsLec) >= 2){ // Fa una lectura cada 2 ms
      TempsLec = millis(); // Guardem el nou instant
      unsigned int Lectura = analogRead(SENSOR); // Valor llegit a l'entrada analògica
      if(Centre == 0){ // Estem a la primera fase, buscant el centre
        if(Lectura > Max){
          Max = Lectura;
        }
        if(Lectura <Min){
          Min = Lectura;
        }
        if((millis() - Temps) > 2000){
          Centre = (Max + Min) / 2; // Ja tenim el valor del centre de l'ona
          Temps = millis(); // Torna a iniciar el temps
        }
      } else {
        if(Pols){ // Si busquem la vall
          if(Lectura < (Centre + Min)/2){
            Pols = 0; // Hem trobat la vall
            digitalWrite(LEDPIN, LOW); // Apaga el LED
          }
        } else {
          if(Lectura > (Centre + Max)/2){
            Pols = 1; // Hem trobat el pic
            digitalWrite(LEDPIN, HIGH); // Encén el LED
            Polsos++; // Afegim un pols
          }
        }
      }
    }
  }
  Polsos = Polsos * 6; //Ha de ser igual a 60000 si multipliquem amb el temps de mesura
  return Polsos;
}

El programa de l'script és el següent:

var IdFull = "^^vA9ZPvYbbcv6D-iP7TRuA08HApN8I0c3Sw0bqgLUlM"; //Identificador del full de càlcul
// S'aconsegueix picant a Comparteix i triant Opcions avançades
var numFull = 0; // Número del full amb el que hem de treballar
// Script per interactuar amb el full de càlcul
// Funció que s'executa quan hi ha una ordre get
// La nostra funció tindrà un paràmetre:
// p/s pulsacions/s
//Envia un correu si passa de 200 p/m
function doGet(e) {
  var resultat = '';
  var camps = new Array(1); // Valors per guardar a la taula
  // Assignem els paràmetres a variables
  var Pulseg = e.parameter.p;
  var ara = new Date();
  var Hora = ara.getHours();
  var Minut = ara.getMinutes();
  var horaActual = Hora + "." + Minut + " h";
  var destinatari = "ainacivi@gmail.com"; // Pot ser qualsevol adreça de correu vàlida
  var titol = "Pulsacions anòmales";
  var text = "Bon dia,"
  text = text + "\n\n"; // Dos salts de línia
  text1 = text + "Avui a les " + horaActual + " la Paula ha tingut pulsacions molt elevades.";
  text1 = text1 + " Pots consultar-les totes al següent full de càlcul: https://tuit.cat/zcild";
  text2 = text + "Avui a les " + horaActual + " la Paula ha tingut pulsacions molt baixes.";
  text2 = text2 + " Pots consultar-les totes al següent full de càlcul: https://tuit.cat/zcild";
  if (Pulseg > 100){
    MailApp.sendEmail(destinatari, titol, text1);
  }
  if (Pulseg < 50){
    MailApp.sendEmail(destinatari, titol, text2);
  }
  if (Pulseg == undefined) {
    resultat = 'Falten paràmetres';
  } else {
    camps[0] = Pulseg;
    var sh = SpreadsheetApp.openById(IdFull);
    var sheet = sh.getSheets();
    sheet[numFull].appendRow(camps); // Afegeix una fila amb la llista de dades en format matriu
    resultat = 'Valors guardats';
  }
  return ContentService.createTextOutput(resultat);
}

 

 

Llicència de Creative Commons
Aquesta obra d'Oriol Boix està llicenciada sota una llicència no importada Reconeixement-NoComercial-SenseObraDerivada 3.0.