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 2

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.

També es va crear una aplicació de telèfon mòbil que permet veure l'última dada obtinguda i el gràfic de tots els valors de pulsacions, així com els valors màxim i mínim fins al moment.

El programa del microcontrolador és el següent:

#include <SPI.h>
#include  <WiFiNINA.h>
#include <Adafruit_NeoPixel.h>
#define LEDPIN 6  // Pota on hi ha el LED de la placa
#define SENSOR A2  // Pota on hi ha el sensor
#define  ValComp 6
#define server_len 50
#define pag_len 400
#define P_max 200
#define P_min 40
const char idXarxa[] = "xarxa-wifi";  // Nom del punt d'accés
const char contrasenya[] = "contrasenya-wifi""; //Contrassenya
unsigned char Pulsacions = 0;
Adafruit_NeoPixel cadena = Adafruit_NeoPixel(3, 1, NEO_GRB + NEO_KHZ800);
boolean color = false;
const String server0 = "script.google.com";
String pagina0 = "";
const String pagina_base = "/macros/s/^^fycby91BLlyp5Rk2UxpP1nvnRtliZrtnLbNeA02H3WIYHeoz4lnS9JimeEVosvzRbOM6DM/exec?" ;
char server[server_len];
char pagina[pag_len];
unsigned long darreraConnexio = 0;
bool pendent, completa, redir;
bool ara = false;
String data;
String peticio = "";
String peticioAux = "";
int status = WL_IDLE_STATUS;
WiFiSSLClient client;
void setup() {  // Inicialització
  Serial.begin(9600); // Monitor sèrie
  pinMode(LEDPIN, OUTPUT);
  digitalWrite(LEDPIN, LOW);
  cadena.begin();  // Inicialitza els NeoPixels        
  cadena.show();
  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(5000); // Ho tornarem a intentar passats 5 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;
}
void loop() {
  while (client.available()) {
    char c = client.read(); // Rebem caràcters del servidor
    Serial.print(c);
    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
    }
    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ó
  Pulsacions = llegir();
  if (Pulsacions>P_max || Pulsacions<P_min){
    color = true;
  }else{
    color = false; 
  }
  data = "t=";
  data += Pulsacions;     
  if (redir){
    redir = false;
  }else {
    pagina0 = pagina_base + data;
    server0.toCharArray(server, server_len);
    pagina0.toCharArray(pagina, pag_len);
  }
  ara = false;
  client.stop();
  if (client.connect(server, 443)) {
    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();
    Serial.print("Enviat: ");
    Serial.println(data);
  } else {
    Serial.println("connection failed");
  }
}
unsigned char llegir(){
  unsigned int Max = 0;    
  unsigned int Min = 4096;
  unsigned int Centre = 0;
  unsigned char Polsos = 0;
  boolean Pols = 1;
  unsigned long TempsLec = millis();
  unsigned long Temps = millis();
  while((millis() - Temps) <= 15000){
    if((millis() - TempsLec) >= 2){  
      TempsLec = millis();
      unsigned int Lectura = analogRead(SENSOR);
      if(Centre == 0){  
        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
          if((Max-Min)>300||(Max-Min)<50){ //Si el max i min estan molt allunyats
            Serial.print("Max = ");
            Serial.print(Max);
            Serial.print("  Centre = ");
            Serial.print(Centre);
            Serial.print("  Min = ");
            Serial.println(Min);
            Serial.println("Tornant a calcular centre");  
            cadena.setPixelColor(0, 0, 0, 250);  // NeoPixel blau
            cadena.show();
            Centre = 0;
            delay(3000)
            Temps = millis(); //tornem a calcular el centre
            Min = 4096;
            Max = 0;
          }else{
            Serial.println("Centre trobat");
            Serial.print("Max = ");
            Serial.print(Max);
            Serial.print("  Centre = ");
            Serial.print(Centre);
            Serial.print("  Min = ");
            Serial.println(Min);
          }
        }
      } else {
        if(Pols){  // Si busquem la vall
          if(Lectura < (Centre + Min)/2){
            Pols = 0;  // Hem trobat la vall
            digitalWrite(LEDPIN, LOW);  // Apaga el LED
            cadena.setPixelColor(0, 0, 0, 0);  // Apaga el NeoPixel
            cadena.show();
          }
        } else {
          if(Lectura > (Centre + Max)/2){
            Pols = 1;  // Hem trobat el pic
            digitalWrite(LEDPIN, HIGH);  // Encén el LED
            if(color){    //Si les pulsacions estan fora del intèrval acceptat
              cadena.setPixelColor(0, 200, 0, 0);  // Encén NeoPixel vermell
              cadena.show();            
            }else{
              cadena.setPixelColor(0, 0, 200, 0);  // Encén NeoPixel verd
              cadena.show();
            }
            Polsos++;  // Afegim un pols
          }
        }
      }
    }
  }
  Polsos = Polsos * 4;
  return Polsos;
}

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

var IdFull = "^^vwT764z-Xc7VV-Z7Vi6mAYbYnt15r_N6mBb4VzAyUA"; // Identificador del full de càlcul 
var numFull = 0;  // Número del full amb el que hem de treballar
var numCols = 2;  // Nombre de columnes que hem de llegir
// Funció que s'executa quan hi ha una ordre get
// t    Valor de les pulsacions
function doGet(e) {
  var resultat = '';
  var camps = new Array(1);  // Valors per guardar a la taula
  var Accio = e.parameter.acc;
  // Assignem els paràmetres a variables
  var Pols =  e.parameter.t;
  if (Accio == undefined){  
    if (Pols == undefined){
      resultat = 'Falten paràmetres';
    } else {
      var sh = SpreadsheetApp.openById(IdFull);
      var sheet = sh.getSheets();
      if (sheet[numFull].getLastRow()>=181){
        sheet[numFull].deleteRows(2,180);
      }
      camps[0] = Pols;
      sheet[numFull].appendRow(camps);
      resultat = 'Valors guardats';
    }
  }
  if (Accio == "read"){
    var sh = SpreadsheetApp.openById(IdFull);
    var sheet = sh.getSheets();
    // Llegim les dades del full seleccionat
    var full = sheet[numFull].getDataRange().getValues();
    range = sheet[numFull].getLastRow();  // Índex de la darrera filera (+1 pels títols)
    if (range > 1){    // Si és 1 només hi ha els títols
      var filera = full[range -1];    // Agafem la darrera filera
      var resultat = '';
      for(var j = 0;j < numCols;j++){ 
        if (j > 0){
          resultat = resultat + '/';  // La barra separarà els valors
        }
        resultat = resultat + filera[j];
      }
    }
  }
  return ContentService.createTextOutput(resultat); 
}

L'aplicació feta amb App Inventor té una pantalla. La disposició d'elements és:

Pantalla

El programa d'aquesta pantalla és el següent:

Programa

 

 

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