Collegamento SPI tra Esp8266 e sensore fulmini AS3935

Ciao a tutti,
ho realizzato un piccolo codice per rilevare fulmini utilizzando il sensore AS3935 ed Arduino tramite comunicazione SPI, prendendo spunto da esempi trovati in rete.
Il tutto funziona correttamente.

Volendo trasmettere i dati via wifi, ho deciso di trasferire il tutto da Arduino a una scheda Esp8266 12-E.

Non riesco però a capire come e dove sbaglio a collegare e a utilizzare la comunicazione SPI nella scheda esp8266.

Intanto riporto il codice funzionante che utilizzo con Arduino:

#include <SPI.h>
#include <AS3935.h>

void printAS3935Registers();          //rileva e stampa i parametri del sensore (rumore di fondo, spike rejection, soglia di controllo)

byte SPItransfer(byte sendByte);      //funzione che si occupa del traferimento dei dati via SPI
int valore_calibrazione;

void AS3935Irq();                     //funzione che verifica se ci sono disturbi o fulmini e causa l'interrupt

AS3935 AS3935(SPItransfer,3,2);       //primo parametro: funzione per il trasferimento dei dati via SPI     
                                      //secondo parametro: pin CS collegato ad Arduino
                                      //terzo parametro:pin IRQ collegato ad Arduino
void setup()
{
  Serial.begin(9600);
  SPI.begin();
  SPI.setDataMode(SPI_MODE1);
  SPI.setClockDivider(SPI_CLOCK_DIV16);
  SPI.setBitOrder(MSBFIRST);
  AS3935.reset();                             //ripristina tutti i valori di registro interni ai valori predefiniti
  delay(10);
  AS3935.setOutdoors();                       //setta il sensore per il funzionamento all'esterno
  AS3935.registerWrite(AS3935_NF_LEV,2);      //scrive 2 nel Noise Level register 
  
  //se il sensore non riesce a stare nel range di tolleranza la calibrazione non andrà a buon fine e restituirà false
  if(!AS3935.calibrate()) {
    Serial.println("La calibrazione non è andata a buon fine, controllare il cablaggio");
  }
  
  valore_calibrazione=AS3935.registerRead(AS3935_TUN_CAP);  //il range è compreso tra 0 e 15
  Serial.print("La calibrazione rilevata è (range concesso tra 0 e 15): ");
  Serial.println(valore_calibrazione);

  AS3935.enableDisturbers();      //turn on indication of distrubers, once you have AS3935 all tuned, you can turn those off with disableDisturbers()
  
  printAS3935Registers();         //rileva e stampa i parametri del sensore (rumore di fondo, spike rejection, soglia di controllo)
  
  //usare interrupt significa che non serve controllare lo stato del pin continuamente, lo fa il chip da solo
  attachInterrupt(0,AS3935Irq,RISING);
}

void loop()
{ 

}

void printAS3935Registers()
{
  int noiseFloor = AS3935.getNoiseFloor();
  int spikeRejection = AS3935.getSpikeRejection();
  int watchdogThreshold = AS3935.getWatchdogThreshold();
  Serial.print("Il rumore di fondo è: ");
  Serial.println(noiseFloor,DEC);
  Serial.print("Lo spike rejection è: ");
  Serial.println(spikeRejection,DEC);
  Serial.print("La soglia di controllo è: ");
  Serial.println(watchdogThreshold,DEC);  
}

byte SPItransfer(byte sendByte)
{
  return SPI.transfer(sendByte);
}

void AS3935Irq()
{
    //verifica cosa ha causato l'interrupt
    //appena leggiamo l'interrupt, il pin IRQ diventa LOW
    int irqSource = AS3935.interruptSource();   //restituisce bit 0: livello rumore troppo alto
                                                //            bit 2: rilevato disturbo
                                                //            bit 3: rilevato fulmine
    
    if (irqSource & 0b0001){
      Serial.println("Livello rumore troppo alto");
    }
    if (irqSource & 0b0100){
      Serial.println("Disturbo rilevato");     
    }  
    if (irqSource & 0b1000){
      int strokeDistance = AS3935.lightningDistanceKm();    //determina la distanza del fulmine in km
      
      if (strokeDistance < 41 && strokeDistance > 0)        //40 km è la distanza di rilevamento massima
      {
        Serial.print("Lightning detected ");
        Serial.print(strokeDistance,DEC);
        Serial.println(" kilometers away.");
      }
   }
}

Chiedo se qualcuno sa aiutarmi su come devo collegare il sensore a una scheda Esp8266 e come devo variare il codice.

Grazie mille a chi vorrà aiutarmi

Mmmm ... se il modulo che hai funziona con Arduino è un modulino progettato per lavorare con segnali a 5V ... i livelli invece di ESP8266 sono tutti a 3.3V ... hai verificato con il produttore se il modulo è in grado di lavorare con tali livelli di tensione?

Comunque, i pin del ESP8266 che si possono usare sono:

... così puoi verificare che almeno i pin usati per il collegamento siano giusti.

Guglielmo

Innanzitutto ringrazio per avermi risposto e chiarito qualche passaggio.
Confermo che sto usando i 3 pin corretti D5, D6 e D7 per il collegamento.
In merito alla tensione, il modulo va alimentato a 5V, infatti lo alimento a 5V tramite un alimentatore per breadboard. Va bene questo o è comunque errato utilizzare una scheda esp8266 che lavora a 3V?
Inoltre avrei un chiarimento in merito al codice che ho utilizzato per Arduino in modalità Spi: nella parte di codice attachInterrupt(0,AS3935Irq,RISING); si fa riferimento al pin 0, che però non ho utilizzato. Vorrei provare a mettere una foto del collegamento hardware ma non capisco come si fa qui nel forum.

Questo il collegamento, funzionante, utilizzato con la scheda Arduino

Un conto è l'alimentazione (la scheda potrebbe avere un regolatore a bordo) ed un conto sono i livelli dei segnali che arrivano sui pin del sensore (il bus SPI degli Arduino classici, AVR, lavora a 5V) ...
... dacci un link esatto al modulo che hai acquistato e vediamo se danno delle specifiche tecniche.

Guglielmo

P.S.: Ti faccio un esempio, i classici moduli bluetooth HC-05 si alimentano a 5V, ma i segnali TX ed RX devono essere a 3.3V pena il possibile danneggiamento ...

Chiaro, pensavo che fosse sufficiente alimentare il sensore a 5V e poi si potesse collegare a qualsiasi scheda dotata di comunicazione Spi.

Comunque il sensore l'ho acquistato qui:
https://futuranet.it/prodotto/breakout-sensore-di-fulmini-montato/

Nel caso quindi fosse proprio questa la causa del problema, come posso risolvere la mancanza di WiFi di Arduino Uno? Che scheda mi consigliate o come mi consigliate di fare?

... il pdf di documentazione scaricabile non da molte informazioni, ma una cosa la dice ... che è alimentabile sia a 3.3 che a 5V, quindi, teoricamente, se i pin utilizzati sono quelli corretti, alimentandolo a 3.3V dal ESP8266 dovrebbe funzionare ... :roll_eyes:

Guglielmo

P.S.: per il momento prova a non usare interrupt ...

Ok è già qualcosa in più di quello che sapevo!
Per non utilizzare interrupt, quale pin devo monitorare e come per capire quando il segnale cambia?
Non ho mai avuto a che fare con interrupt e mi sono limitato a semplificare un codice esempio per adattarlo alle mie esigenze.

Non so, tocca studiarsi il datasheet e vedere se c'è un sistema ... quello che è certo è che se lo alimenti a 5V poi il bus si aspetta segnali di quel livello, quindi, se lo colleghi a ESP, alimentalo a 3.3V.

Guglielmo

Mmm ... mi sa che l'interrupt non è evitabile ... :roll_eyes:

Sono andato a guardare un vecchio programma che ho scritto molti anni fa (2017) in cui usavo quel sensore ... intanto lo usavo su bus I2C e non SPI, ma, si, usavo l'interrupt ...

Guglielmo

Tramite I2C potrebbe cambiare qualcosa?
Come cambiano i collegamenti ed eventualmente il mio codice?
La cosa che più mi lascia perplesso è che con arduino funziona, quindi facendo gli stessi collegamenti è variando i due pin diversi mi aspettavo funzionasse senza difficoltà anche sulla scheda esp8266.

... e perché mai? Sono due MCU totalmente diverse, una a 8 bit una a 32 bit, una con una tecnologia, una con un'altra ... non è assolutammete detto che una cosa che funziona su un Arduino classico funzioni su altre MCU.

Comunque se lo facevi funzionare con SPI, prosegui come facevi, verifica solo che i pin che usi siano utilizzabili su ESP (secondo la tabella che ti ho messo) ed alimentalo a 3.3V ... poi si vede.

Guglielmo

Una cosa piuttosto, tu sai che su ESP i pin vanno indicati con il numero di GPIO o con il prefisso Dx?

Ovvero, se su Arduino classico identifichi, ad esempio il pin D2 solo con 2, su ESP DEVI o mettere D2 oppure indicare 4, ovvero il GPIO 4 ... mi raccomando, altrimenti non funzionerà mai.

Guglielmo

Grazie mille Guglielmo, stasera provo come da tue indicazioni, posterò sia la foto dei collegamenti che il nuovo codice modificato per la scheda esp8266 in modo che possiamo capire quale potrebbe essere il problema.

Ho provato a seguire i consigli che mi son stati dati: ho quindi collegato il sensore alla scheda esp8266, compresa l'alimentazione a 3V, e ho cambiato i pin nel codice.
Riporto quindi di seguito le foto dei collegamenti e il nuovo codice.

#include <SPI.h>
#include <AS3935.h>

void printAS3935Registers();          //rileva e stampa i parametri del sensore (rumore di fondo, spike rejection, soglia di controllo)

byte SPItransfer(byte sendByte);      //funzione che si occupa del traferimento dei dati via SPI
int valore_calibrazione;

void ICACHE_RAM_ATTR AS3935Irq();                     //funzione che verifica se ci sono disturbi o fulmini e causa l'interrupt
                                                      //ICACHE_RAM_ATTR serve per caricare nella ram altrimenti con esp non funzionano gli interrupt
                                                      
AS3935 AS3935(SPItransfer,D4,D3);     //primo parametro: funzione per il trasferimento dei dati via SPI     
                                      //secondo parametro: pin CS collegato ad Arduino
                                      //terzo parametro:pin IRQ collegato ad Arduino
void setup()
{
  Serial.begin(9600);
  SPI.begin();
  SPI.setDataMode(SPI_MODE1);
  SPI.setClockDivider(SPI_CLOCK_DIV16);
  SPI.setBitOrder(MSBFIRST);
  AS3935.reset();                             //ripristina tutti i valori di registro interni ai valori predefiniti
  delay(10);
  AS3935.setOutdoors();                       //setta il sensore per il funzionamento all'esterno
  AS3935.registerWrite(AS3935_NF_LEV,2);      //scrive 2 nel Noise Level register 
  
  //se il sensore non riesce a stare nel range di tolleranza la calibrazione non andrà a buon fine e restituirà false
  if(!AS3935.calibrate()) {
    Serial.println("La calibrazione non è andata a buon fine, controllare il cablaggio");
  }
  
  valore_calibrazione=AS3935.registerRead(AS3935_TUN_CAP);  //il range è compreso tra 0 e 15
  Serial.print("La calibrazione rilevata è (range concesso tra 0 e 15): ");
  Serial.println(valore_calibrazione);

  AS3935.enableDisturbers();      //turn on indication of distrubers, once you have AS3935 all tuned, you can turn those off with disableDisturbers()
  
  printAS3935Registers();         //rileva e stampa i parametri del sensore (rumore di fondo, spike rejection, soglia di controllo)
  
  //usare interrupt significa che non serve controllare lo stato del pin continuamente, lo fa il chip da solo
  attachInterrupt(0,AS3935Irq,RISING);
}

void loop()
{ 

}

void printAS3935Registers()
{
  int noiseFloor = AS3935.getNoiseFloor();
  int spikeRejection = AS3935.getSpikeRejection();
  int watchdogThreshold = AS3935.getWatchdogThreshold();
  Serial.print("Il rumore di fondo è: ");
  Serial.println(noiseFloor,DEC);
  Serial.print("Lo spike rejection è: ");
  Serial.println(spikeRejection,DEC);
  Serial.print("La soglia di controllo è: ");
  Serial.println(watchdogThreshold,DEC);  
}

byte SPItransfer(byte sendByte)
{
  return SPI.transfer(sendByte);
}

void AS3935Irq()
{
    //verifica cosa ha causato l'interrupt
    //appena leggiamo l'interrupt, il pin IRQ diventa LOW
    int irqSource = AS3935.interruptSource();   //restituisce bit 0: livello rumore troppo alto
                                                //            bit 2: rilevato disturbo
                                                //            bit 3: rilevato fulmine
    
    if (irqSource & 0b0001){
      Serial.println("Livello rumore troppo alto");
    }
    if (irqSource & 0b0100){
      Serial.println("Disturbo rilevato");     
    }  
    if (irqSource & 0b1000){
      int strokeDistance = AS3935.lightningDistanceKm();    //determina la distanza del fulmine in km
      
      if (strokeDistance < 41 && strokeDistance > 0)        //40 km è la distanza di rilevamento massima
      {
        Serial.print("Lightning detected ");
        Serial.print(strokeDistance,DEC);
        Serial.println(" kilometers away.");
      }
   }
}

 


Il sensore sembra rilevare qualcosa, ma la calibrazione non mi sembra andare a buon fine, mi da un valore 15.
Riporto infatti di seguito quanto viene visualizzato nel serial monitor:

19:16:15.243 -> La calibrazione rilevata è (range concesso tra 0 e 15): 15
19:16:16.985 -> Il rumore di fondo è: 2
19:16:17.032 -> Lo spike rejection è: 2
19:16:17.079 -> La soglia di controllo è: 2
19:16:19.511 -> Livello rumore troppo alto
19:18:48.194 -> Lightning detected 8 kilometers away.
19:18:54.032 -> Disturbo rilevato
19:18:54.348 -> Lightning detected 8 kilometers away.
19:18:59.293 -> Disturbo rilevato
19:19:01.992 -> Lightning detected 8 kilometers away.
19:19:04.387 -> Disturbo rilevato
19:19:37.542 -> Disturbo rilevato
19:19:40.289 -> Disturbo rilevato
19:19:41.947 -> Lightning detected 8 kilometers away.
19:19:43.995 -> Disturbo rilevato
19:19:44.820 -> Disturbo rilevato
19:19:45.522 -> Disturbo rilevato
19:19:46.225 -> Disturbo rilevato
19:19:46.873 -> Lightning detected 8 kilometers away.

Quesi sensori sono molto sensibili hai disturbi, ricordo che dovetti lavorare parecchio con i vari parametri che si possonio configurare (vd. datasheet) per avere qualche cosa di "utilizzabile" praticamente.

Guglielmo

Come faccio a capire però che il collegamento e il codice sono corretti?

In particolare vorrei chiedere se è corretto scrivere 0 nella seguente parte di codice:

attachInterrupt(0,AS3935Irq,RISING)

Hai la possibilità di dirmi come hai settato tu al tempo i parametri così faccio delle prove?

Sicuramente NO, è sempre molto sconsigliato mettere direttamente il numero di un pin nella attachInterrupt(), come è ampiamente spiegato nel reference (che andrebbe studiato).

Quale pin hai usato per l'interrupt? D3? allora dovresti scrivere, come dice il reference:

attachInterrupt(digitalPinToInterrupt(D3), AS3935Irq, RISING);

... sperando che la cosa sia valida anche per ESP (mai provato) ... :roll_eyes:

Guglielmo

Ricordavo che c'erano anche altre particolarità e che la ISR andasse dichiarata in altro modo ...
... guardati QUESTO tutorial ...

ISRs need to have ICACHE_RAM_ATTR before the function definition to run the interrupt code in RAM.

... quindi la tua ISR deve essere dichiarata:

ICACHE_RAM_ATTR void AS3935Irq() {
   ...
   ...
}

Guglielmo

Buonasera Guglielmo,
ho provveduto a seguire i consigli e sembra che ora il tutto funzioni, almeno per quel che ne capisco dai primi test.
Ho voluto proseguire col progetto, e volevo far si che quando viene rilevato un fulmine la funzione AS3935Irq richiamata dall'interrupt non si limitasse a scrivere nel serial monitor ma che inviasse i dati ad un host per salvarli col metodo get in un database.

Il codice che utilizzo, e che ho provveduto ad inserire nella funzione, se lo utilizzo al di fuori della funzione AS3935Irq funziona correttamente.
Se invece lo metto all'interno della funzione, la connessione non va mai a buon fine, come mai?

Di seguito il codice che ho inserito:

//collegamento all'host
      WiFiClientSecure client;
      client.setInsecure();       //non si utilizzano certificati di sicurezza

      //verifica se il collegamento non è andato a buon fine
      if (!client.connect(host, port)) {
        delay(5000);
        return;
      }

      Serial.println("VERIFICA OK");

      //se la connessione all'host è avvenuta 
      if (client.connected()) {

        String var="distanza_fulmine=" + String(distanza_fulmine);
        String url = "GET /aggiungi_fulmini.php?" + var + " HTTP/1.1";

        Serial.println(url);

        //invio url all'host per il salvataggio dei dati sul database mysql
        client.println(url);
        client.println("Host: www.xyz.it");
        client.println("Access-Control-Allow-Origin: *");
        client.println("Connection: close");
        client.println();
        client.stop();
      }

      }

client.connect(host, port) non va mai a buon fine e quindi la connessione non avviene.