Funzione asincrona

CIao,
ho fatto la mia prima animazione con la matrice di led usando il led matrix editor all'apposito sito.
Una busta che scorre da sinistra a destra e che indica l'invio di un messaggio. Funziona alla grande.
Il codice è banale:


#include "Arduino_LED_Matrix.h"
#include "send_message.h"

ArduinoLEDMatrix matrix;

void setup() {
  Serial.begin(115200);
  matrix.begin();
}

void loop(){
  int size = sizeof(send_message) / sizeof(send_message[0]);
  for (int i = 0 ; i<size ; i++){
    matrix.loadFrame(send_message[i]);
    delay(50);
  }
  delay(2000);
}

Però mi piacerebbe che durante l'animazione Arduino non si dedicasse completamente ad essa passando al codice successivo solo al termine dell'animazione ma la eseguisse in modo asincrono, in background insomma.
Infondo è solo una forma grafica con cui comunicare all'operatore che sta di fronte che qualcosa è successo: non mi piace che si fermino le altre azioni.

E' possibile qualcosa del genere, oppure no?

Certo ... in casi così devi dimenticare delay() ed devi studiarti come si usa la funzione millis(), prima QUI, poi QUI e QUI e QUI e tutti gli articoli che sono in QUESTA pagina ... vedrai che poi ti sarà tutto più chiaro :wink:

Guglielmo

1 Like

Conosco la tecnica del confronto del timestamp per effettuare il contenuto dell' IF che lo confronta con l'ultimo valore salvato ed esegue il codice solo quando è passato un tempo superiore alla soglia impostata.
Mi scuso se il codice qui sopra ha tratto in inganno non mostrandolo ma il problema sta proprio nel codice contenuto nell'IF.
Va bene anche con lo stratagemma del millis() riesco ad evitare che venga eseguito ad ogni ciclo ma quando viene eseguito il contenuto, nel mio caso un animazione con la matrice di LED, Arduino esegue solo quella.

Nell'esempio sopra anche mettendolo dentro ad un test come questo

if(currentMillis - previousMillis > interval) {
   [...]
}

vengono comunque mostrati 20 frames da 50 milliscondi, ossia Arduino si occpua a tempo pieno dell'animazione per 1 intero secondo.

La mia domanda era a qualcosa tipo l'esecuzione di una funzione in un thread separato.
Ossia che, una volta lanciata, Arduino vada avanti nell'esecuzione del codice senza attendere il suo completamento.
Ora dovrei essermi spiegato più chiaramente.

Di quale Arduino si sta parlando?

Non ci sono i thread permessi da un SO multitasking. Il modo più semplice per il caso specifico, che non è la soluzione generale, è continuare a richiamare la funzione di animazione dal programma principale, in tutti i punti dova sta li ad attendere qualcosa.

1 Like

Giusto. Non l'ho specificato. Scusa.
Arduino Uno R4 Wifi. Quello con la matrice a LED 12x8 in cui eseguo l'animazione di cui sopra.

E' una MCU single core, quindi quello che puoi fare è usare un task manager che sia compatibile con il Renesas RA4M1.

So che ci sono alcune librerie, ma non so se hanno già esteso la compatibilità all'Arduino Uno R4.

Anche se secondo me strutturando per bene il software non è necessario. L'importante è evitare di scrivere del codice bloccante.

1 Like

Tra le librerie a corredo della UNO R4 c'è il porting della libreria FreeRTOS™ per Arduino, solo che ... l'uso di un sistema operativo realtime (come è FreeRTOS™) non è cosa che si improvvisa e richiede parecchio studio e comprensione dei meccanismi che ci sono dietro ... :roll_eyes:

Guglielmo

Ma anche no...
Facci vedere come hai scritto il "nuovo" codice usando millis() così ti diciamo meglio.

Devi usare millis() anche per i 20 frame! Ogni 50ms ne chiami uno, togliendo il for.
Devo abituarti a ragionare nel dominio del tempo: non devi fare un'animazione di 20 frame in un secondo, ma devi visualizzare un frame ogni volta che trascorrono 50ms.

Certo, posto tutto il codice dello sketch?

Due parole sul cosa sto cercando di fare e a che punto sono.

Il tema è quello di misurare la temperatura del luogo dove metterò l'arduino (sta arrivando il BME280) E fin qui niente di speciale.
Voglio fare data logger su file di testo, un file per giorno (stanno arrivando la slitta SPI per la SD e l'RTC DS3231).
L'idea è di misurare la temperatura ogni 20 minuti (poi decido se questo intervallo di tempo fa per me) , archiviarla su file e mandare Arduino in sleep per far risparmiare la pila : non mi arriva la corrente dove metterò l'arduino.

Inoltre ogni volta che la temperatura scende sotto ad una certa soglia accodare un messaggio HTML in coda che mi avvisa che è scesa e quando è scesa.

Non appena è disponibile una connessione di rete WIFI tra le 5 possibili (non la faccio andare 24 h su 24 e la prima potrebbe attivarsi dopo giorni) fare l'upload via FTP di tutti file archiviati fino a ieri (quello di oggi lo sto ancora scrivendo) e scaricare la coda dei messaggi via chat.

Ora, senza i componenti che mi stanno arrivando ho potuto solo gestire i messaggi via chat, e funziona!

Genero un messaggio fittizio ogni 10 secondi giusto per riempire la coda (a regime sarà la temperatura del sensore a inserire un messaggio in coda) e la scarico quando è disponibile il WIFI.

Visto che il WIFI va a singhiozzo non posso mettere WiFi.begin() nel metodo setup() poichè mi serve fare il check sullo stato nel loop() e nel caso non sia connessa provare a riconnettersi.

Visti i tempi non è così essenziale ottimizzare il codice perchè una lettura ogni 20 minuti posso anche permettermi di attendere 1 secondo l'animazione della matrice a led (per altro utile se vedo direttamente l'arduino) ma mi piacerebbe comunque capire. Se avete voglia e tempo per aiutarmi a farlo.

Ora posto il codice e mi espongo un pò a critiche ma sono sicuro che saranno costruttive.


#include <WiFiS3.h>
#include "WiFiSSLClient.h"
#include "arduino_secrets.h"
#include <cppQueue.h>
#include "RTC.h"
#include "send_message.h"
#include "coda.h"
#include "Arduino_LED_Matrix.h"

#define	IMPLEMENTATION	FIFO

char Telegram_BOT[50]  = TELEGRAM_BOT;
char Telegram_chatID[15] = TELEGRAM_CHATID;

bool connected  = false; 
ArduinoLEDMatrix matrix;

cppQueue	q_telegram(100, 24, IMPLEMENTATION);	// Instantiate queue -- 24 mesaggi da 100 caratteri ?

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  // while (!Serial) {; }  
  matrix.begin();

  //Telegram_BOT = TELEGRAM_BOT;
  //Telegram_chatID = TELEGRAM_CHATID; 

  // set RTC clock
  RTC.begin();
  RTCTime startTime(5, Month::DECEMBER, 2023, 15, 53, 00, DayOfWeek::TUESDAY, SaveLight::SAVING_TIME_ACTIVE);
  RTC.setTime(startTime);
  
  Serial.println("Arduino uno R4 Wifi - Telegram message on channel");
  q_telegram.push("Arduino uno R4 Wifi - Telegram message on channel");

}

void loop() {

  RTCTime currentTime;
  RTC.getTime(currentTime);

  // Se l'evento è verificato accoda il messaggio Telegram
  if (currentTime.getSeconds() % 10 ==0 ){  
    char messaggioHTML[100]  ;
    int h = currentTime.getHour();
    int m = currentTime.getMinutes();
    int s = currentTime.getSeconds();
    sprintf(messaggioHTML, "Ecco il messaggio delle ore %d:%d:%d ", h,m,s);
    Serial.print(" messaggio composto : ") ;Serial.println(messaggioHTML);
    q_telegram.push(&messaggioHTML);
  }

  if (currentTime.getSeconds() % 2 ==0){
    // Stampa ora ogni 5 secondi 
    Serial.print(currentTime.getHour());Serial.print(":");Serial.print(currentTime.getMinutes());Serial.print(":");Serial.print(currentTime.getSeconds());Serial.println("");    
    Serial.print("Stato WIFI : ");  Serial.println(WiFi.status());
  }

  if (q_telegram.getCount()>0 && currentTime.getSeconds() % 10 ==0){
    Serial.print("La coda TELEGRAM contiene "); Serial.print(q_telegram.getCount());  Serial.println(" messaggi"); 
  }



  // Svuolta la coda di messaggi Telegram
  unsigned int t1 = millis(); 
  while(!q_telegram.isEmpty() && millis() - t1 < 1000 ){
    if (WiFi.status()!=WL_CONNECTED ){
      matrix.loadFrame(coda[q_telegram.getCount()]);
      connected = connetti();
    }
    if (WiFi.status()==WL_CONNECTED ){
      char mg[100];
      q_telegram.pop(&mg);
      bool res = Telegram_Send(mg);   // Uncomment for PRODUCTION , commente for TEST
      //bool res = Telegram_Send_Simulate(mg);  // Uncomment for TEST , commente for PRODUCTION
      if(res){
        anim_send();
      }    
      // Se il messaggio non è stato spedito inseriscilo nuovmaente in coda.
      // Questo serve quando ci sono problemi con l'invio
      if (!res && !q_telegram.isFull()){
        q_telegram.push(&mg);
        Serial.print("il messaggio '"); Serial.print(mg);  Serial.println("' stato rimesso in coda perchè non consegnabile");
      }
    }
  }

}

void anim_send(){
  int size = sizeof(send_message) / sizeof(send_message[0]);
  for (int i = 0 ; i<size ; i++){
    matrix.loadFrame(send_message[i]);
    delay(35);
  }
}

bool connetti(){
  //bool connected = false;
  // Connessione alla rete
  if (!connected){
    char ssid[] = SECRET_SSID1;
    char pass[] = SECRET_PASS1;
    connected = connetti_rete(ssid,pass);
  }  
  if (!connected){
    char ssid[] = SECRET_SSID2;
    char pass[] = SECRET_PASS2;
    connected = connetti_rete(ssid,pass);
  }  
  if (!connected){
    char ssid[] = SECRET_SSID3;
    char pass[] = SECRET_PASS3;
    connected = connetti_rete(ssid,pass);
  }   
  if (!connected){
    char ssid[] = SECRET_SSID4;
    char pass[] = SECRET_PASS4;
    connected = connetti_rete(ssid,pass);
  }   
  if (!connected){
    char ssid[] = SECRET_SSID5;
    char pass[] = SECRET_PASS5;
    connected = connetti_rete(ssid,pass);
  }   
  return connected;
}

bool Telegram_Send_Simulate(char MessaggioHTML[]){
  bool res = true;
  Serial.println("*** MESSAGGIO TELEGRAM SIMULATO ");
  Serial.print("    "); Serial.println(MessaggioHTML); 
  return res;
}


bool Telegram_Send(char MessaggioHTML[]){
  bool res = false;
  WiFiSSLClient  client;
  if(!client.connect("api.telegram.org",443)){    
    Serial.println("Non connesso");
  }else{
    Serial.println("Mando messaggio...");
    client.println("GET /" + (String)Telegram_BOT + "/sendMessage?chat_id=" +  (String)Telegram_chatID + "&parse_mode=html&text="+ MessaggioHTML +" HTTP/1.1");
    client.println("Host: api.telegram.org");
    client.println("Connection: close");
    client.println("");
    
    unsigned int t1 = millis();
    bool timeout = false;
    while(client.available()==0 && !timeout){
      if(millis() - t1 > 5000){
        Serial.println("Non inviato");
        Serial.println("Timeout");        
        timeout= true;
      }
    }
    if (client.available()){
      res = true;
      Serial.println("Inviato");
    }
    client.stop();    
  }
  return res;
}

bool connetti_rete(char NetName[], char NetPwd[]){
  bool c = false;
  unsigned int SOGLIA_MS_RICERCA_RETE = 200;

  Serial.print("Try to connect to '"); Serial.print(NetName); Serial.println("' ...");
  WiFi.begin(NetName,NetPwd);
  
  unsigned int t1 = millis();  
  while (WiFi.status()!=WL_CONNECTED && millis() - t1 < SOGLIA_MS_RICERCA_RETE ){
    delay(50);
    Serial.print(".");
  }

  // Se è passato troppo tempo smetti di cercare questa rete
  if(millis() - t1 > SOGLIA_MS_RICERCA_RETE && WiFi.status()!=WL_CONNECTED ){
    Serial.println("Esco per troppo tempo passato");
    return false;
  }
  
  if (WiFi.status()==WL_CONNECTED){
    c  = true;
    //Serial.print("Stato : "); Serial.println(WiFi.status());
    //Serial.print("Connected to nework '");  Serial.print(NetName); Serial.println("'");
    //Serial.print("IP : "); Serial.println(WiFi.localIP());

    char messaggioHTML[100];
    messaggioHTML[0] = '\0';
    strcat(messaggioHTML,"Connesso sulla rete : ");
    strcat(messaggioHTML,NetName);
    q_telegram.push(&messaggioHTML);    
  }
  Serial.println("");
  return c;
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.