En aquest cas s'ha dissenyat un sonòmetre per a un estudi musical. El microcontrolador treballa com a client i envia periòdicament el valor de so mesurat i el seu espectre freqüencial a un full de càlcul de Google, si es supera un determinat llindar, fent servir un script. A més, canvia el color del LED. Un applet d'IFTTT envia un missatge de Telegram la informació que es va penjant al full de càlcul. Addicionalment, hi ha una aplicació per a mòbils que permet consultar la informació. El programa del microcontrolador és el següent:
#include "arduinoFFT.h" #include <WiFiNINA.h> #include <SPI.h> #include <Adafruit_NeoPixel.h> #define SAMPLES 512 // tiene que ser una potencia de 2 #define SAMPLING_FREQUENCY 10000 // Hz, menos de 10000 por el ADC, la mitad es la freq max hasta la cual te muestra #define MICpin A6 #define LED 1 #define maxl 50 // brillo maximo del LED #define maxp 30 // valor maximo para que se envien los datos #define server_len 50 #define pag_len 400
arduinoFFT FFT = arduinoFFT();
Adafruit_NeoPixel cadena = Adafruit_NeoPixel(3, 1, NEO_GRB + NEO_KHZ800);
WiFiSSLClient client;
const char idXarxa[] = "xarxa"; // Nombre del punto de acceso
const char contrasenya[] = "contrasenya"; // Contraseña de connexión
const String server0 = F("script.google.com");
String pagina0 = "";
const String pagina_base = F("/macros/s/AKfycbyVI9uKFTJHpBlcF_DQOl_v0LS0H_lWxzG7owK5CsUfXqh9tg/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;
String data;
String peticio = ""; // Aquí guardaremos una línea de la petición del cliente
String peticioAux = ""; // y la petición anterior
int maxim, minim, suma, y, x;
const int vhz[] PROGMEM = {10, 25, 31, 40, 50, 63, 80, 100, 125, 160, 200, 250, 315, 400, 500, 630, 800,
1000, 1250, 1600, 2000, 2500, 3150, 4000, 4990}; // 25 es el numero de muestras que cogemos para enviar a sheets
float lecCorr, mitjana, rms; // en vhz se ponen los valores 10 y 4990 donde deberian ir 20 y 5000 por errores que da el arduino
unsigned int sampling_period_us;
unsigned long microseconds;
double vReal[SAMPLES];
double vImag[SAMPLES];
int status = WL_IDLE_STATUS;
void setup() {
cadena.begin();
cadena.show();
Serial.begin(115200);
pinMode(LED, OUTPUT);
sampling_period_us = round(1000000*(1.0/SAMPLING_FREQUENCY));
while (status != WL_CONNECTED) {
status = WiFi.begin(idXarxa, contrasenya);
}
redir = false;
}
void loop() {
maxim = 0;
minim = 1024;
suma = 0;
// calcul de x
for(int i=0; i<SAMPLES; i++) {
microseconds = micros();
vReal[i] = analogRead(MICpin);
vImag[i] = 0;
suma = suma + vReal[i];
if(vReal[i] > maxim){
maxim = vReal[i];
}
if(vReal[i] < minim){
minim = vReal[i];
}
}
mitjana = float(suma) / SAMPLES;
rms = 0;
for (int i = 0; i < SAMPLES; i++){
lecCorr = float(vReal[i]) - mitjana;
rms = rms + lecCorr * lecCorr;
}
rms = sqrt(rms /SAMPLES);
x=rms*maxl/512;
if (x>maxl){
x=maxl;
}
if (x<0){
x=0;
}
byte x2=byte(x);
cadena.setPixelColor(0,x2,maxl-x2,0);//RGB
cadena.show();
while (client.available()) {
char c = client.read();
if (c == '\n') {
peticioAux = peticio;
peticio = "";
completa = true;
} else {
peticio += c;
}
if (completa){
if (peticioAux.startsWith(F("HTTP/1.1 200"))){
pendent = true;
}
if (peticioAux.startsWith(F("HTTP/1.1 302"))){
redir = true;
}
if (redir && (peticioAux.startsWith(F("Location:")))){
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;
}
}
if (pendent) {
pendent = false;
}
if(x2>maxp){ // valor a partir del cual consideramos que es mucho ruido
FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
double peak = FFT.MajorPeak(vReal, SAMPLES, SAMPLING_FREQUENCY);
if (ara || ((millis() - darreraConnexio > periodeConnexio))) {
data = "?X=";
data += x;
for (byte i = 0; i < 25 ; i++){
data += "&v" + String(vhz[i]) + "hz=";
data += 20*log10(vReal[round((vhz[i]*SAMPLES)/(SAMPLING_FREQUENCY/2))])-40;
// para que no de valores tan altos
}
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)) {
client.print(F("GET "));
client.print(F(pagina));
client.println(F(" HTTP/1.1"));
client.print(F("Host: "));
client.println(F(server));
client.println(F("Connection: close"));
client.println();
darreraConnexio = millis();
Serial.println(data);
} else {
Serial.println(F("connection failed"));
}
}
}
}
L'script és el següent:
var IdFull = "1LtKt6XSbV9L8QWDtxrme7PXWWVo0Lfu1ODrdPpouYWI";
var numFull = 0;
function doGet(e) {
var resultat = '';
var camps = new Array(26);
var X = e.parameter.X;
var v10hz = e.parameter.v10hz;
var v25hz = e.parameter.v25hz;
var v31hz = e.parameter.v31hz;
var v40hz = e.parameter.v40hz;
var v50hz = e.parameter.v50hz;
var v63hz = e.parameter.v63hz;
var v80hz = e.parameter.v80hz;
var v100hz = e.parameter.v100hz;
var v125hz = e.parameter.v125hz;
var v160hz = e.parameter.v160hz;
var v200hz = e.parameter.v200hz;
var v250hz = e.parameter.v250hz;
var v315hz = e.parameter.v3150hz;
var v400hz = e.parameter.v400hz;
var v500hz = e.parameter.v500hz;
var v630hz = e.parameter.v630hz;
var v800hz = e.parameter.v800hz;
var v1000hz = e.parameter.v1000hz;
var v1250hz = e.parameter.v1250hz;
var v1600hz = e.parameter.v1600hz;
var v2000hz = e.parameter.v2000hz;
var v2500hz = e.parameter.v2500hz;
var v3150hz = e.parameter.v3150hz;
var v4000hz = e.parameter.v4000hz;
var v4990hz = e.parameter.v4990hz;
if (X == undefined){
resultat = 'Falten paràmetres';
} else {
camps[0]="";
camps[1] = X;
camps[2] = v10hz; // Va als 20 Hz
camps[3] = v25hz;
camps[4] = v31hz;
camps[5] = v40hz;
camps[6] = v50hz;
camps[7] = v63hz;
camps[8] = v80hz;
camps[9] = v100hz;
camps[10] = v125hz;
camps[11] = v160hz;
camps[12] = v200hz;
camps[13] = v250hz;
camps[14] = v315hz;
camps[15] = v400hz;
camps[16] = v500hz;
camps[17] = v630hz;
camps[18] = v800hz;
camps[19] = v1000hz;
camps[20] = v1250hz;
camps[21] = v1600hz;
camps[22] = v2000hz;
camps[23] = v2500hz;
camps[24] = v3150hz;
camps[25] = v4000hz;
camps[26] = v4990hz; // Va als 5000 Hz
var valu = new Array(1);
valu[0] = camps;
var sh = SpreadsheetApp.openById(IdFull);
var sheet = sh.getSheets();
var range = sheet[numFull].getLastRow();
if (range > 1){
var rangeVal = sheet[numFull].getRange(2, 1, 1, 27);
rangeVal.setValues(valu);
} else {
sheet[numFull].appendRow(camps);
}
for(j = 1;j < 27;j++){
camps[j] = "";
}
camps[0] =X;
sheet[numFull].appendRow(camps);
resultat = 'Valors guardats';
}
return ContentService.createTextOutput(resultat);
}
L'aplicació feta amb App Inventor té el següent disseny de pantalla:

El programa és el següent:



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