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);
}

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