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 fa sonar el brunzidor de la placa per advertir l'usuari.
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 controlar que el ritme cardíac estigui en un interval de valors adequat respecte l'activitat física que s'està produint en el moment.
El programa del microcontrolador és el següent:
#include <SPI.h> // Carreguem la biblioteca SPI #include <WiFiNINA.h> // Carreguem la biblioteca WiFiNINA #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 LED 6 // pota on hem connectat el LED #define server_len 50 #define pag_len 400
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/^^fycbwpxbSdN0rB9volSR4crEo_Rw03SyTh4pGXF2nVJYYEV8pWL72KsyHNrfp_2bI06alU/exec";
char server[server_len];
char pagina[pag_len];
unsigned char Pulsacions = 0; // Pulsacions per minut
unsigned long darreraConnexio = 0;
const unsigned long periodeConnexio = 15000UL;
bool pendent, completa, redir;
bool ara = false;
int pols;
int vec[5] = {-1,-1,-1,-1,-1};
int i = 0;
int suma = 0;
int mitjanarit;
int contador = 0;
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;
WiFiSSLClient client;
void setup() { // Inicialització
pinMode(3, OUTPUT);
Serial.begin(9600); // 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);
}
Serial.print("Connectat a ");
Serial.println(WiFi.SSID());
Serial.print("Estat de la connexió: ");
Serial.println(WiFi.status());
Serial.print("Adreç 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(){
Pulsacions = llegir();
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ç
// 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;
Serial.println(peticioAux);
}
if(!ara) {
if (i <= 4) {
vec[i] = Pulsacions;
i = i + 1;
} else {
i = 0;
vec[i] = Pulsacions;
i = i + 1;
}
Serial.println(vec[0]);
Serial.println(vec[1]);
Serial.println(vec[2]);
Serial.println(vec[3]);
Serial.println(vec[4]);
suma = 0;
for (int j=0;j<5;j++) {
if (vec[j] > -1) {
suma += vec[j];
}
}
Serial.print("Suma: ");
Serial.println(suma);
contador = 0;
for (int j=0;j<5;j++) {
if (vec[j] > -1) {
contador += 1;
}
}
mitjanarit = suma/contador;
Serial.print("Mitjana: ");
Serial.println(mitjanarit);
}
// Quan toca, tornem a fer una petició
if (ara || ((millis() - darreraConnexio > periodeConnexio))) {
if(!ara) {
data = "?";
data += "p=";
data += mitjanarit;
}
if (redir){
redir = false;
} else {
pagina0 = pagina_base + data;
server0.toCharArray(server, server_len);
pagina0.toCharArray(pagina, pag_len);
}
client.stop();
if(!ara) {
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);
if (mitjanarit < 40 or mitjanarit > 200) {
tone(3, 440, 1500);
delay(200);
tone(3, 262, 1500);
delay(200);
tone(3, 440, 1500);
delay(200);
tone(3, 262, 1500);
}
} else {
Serial.println("connection failed");
}
}
ara = false;
}
}
// Compta els polsos en 20 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) <= 15000){ // Farem mesures durant 15 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 * 4;
return Polsos;
}
El programa de l'script és el següent:
var IdFull = "^^Y98aIhaVZnBv9OdYbdk6hIySP8WJmQYfYOZmsbqrTg"; // 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
var numCols = 1; // Nombre de columnes que hem de llegir
// Script per interactuar amb el full de càlcul
// Funció que s'executa quan hi ha una ordre get
// La nostra funció tindrà dos paràmetres:
// p Valor de les polsacions
function doGet(e) {
var resultat = '';
var camps = new Array(2); // Valors per guardar a la taula
// Assignem els paràmetres a variables
var Accio = e.parameter.acc;
var Pols = e.parameter.p;
if (Accio == undefined){
if ((Pols == undefined)){
resultat = 'Falten paràmetres';
} else {
camps[0] = Pols;
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';
}
}
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é dues pantalles. La disposició d'elements a la primera pantalla és:

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



La disposició d'elements a la segona pantalla és:

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


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