OTA con ESP32C3 non funziona nel mio programma

Salve a tutti,
vi è mai capitato che OTA non funzioni ?
Allora se lo provo da solo non ci sono problemi, funziona benissimo, ma se lo inserisco nel mio codice non c'è verso di farlo funzionare.
Vi posto il codice OTA compreso:

#include "Credentials.h"
#include <WiFi.h>
#include <ESPmDNS.h>
#include <MySQL_Generic.h>
#include <WiFiClient.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include <ArduinoOTA.h>

#define RX_PIN 8
#define TX_PIN 9
#define LED_RED   3
#define LED_GREEN 4
#define LED_WHITE 19

#define MYSQL_DEBUG_PORT Serial
#define _MYSQL_LOGLEVEL_      0

IPAddress server(10,20,1,21);                   // Server database (this is my setup you use your database server IP)
uint16_t server_port = 3306;                    // is the 3306 standard port (idem)

WiFiUDP ntpUDP;                                 // Define NTP Client to get time
NTPClient timeClient(ntpUDP);

// Replace with your network credentials
const char* host = "Meteo";
const char* ssid = "MyHome";                   // Input your wifi network name
const char* pass = "Mypassword";             // Input your wifi password

const char   default_database[] = "meteo";      // my setup
const char   default_table[]    = "meteo";      // my setup

const  char* ntpServer = "it.pool.ntp.org";     // my setup
const  long  gmtOffset_sec = 0;                 // my setup
const  int   daylightOffset_sec = 3600;         // my setup

String Latitude  = "41.353333";                 // my setup
String Longitude = "19.456667";                 // my setup
String elevation = "5.0";                       // my setup
String field1,field2,field3,field4,field5,field6,field7,field8,field9,field10,Insert_Data,Data,postStr;

// Thingspeak Write API
const char* server1 = "api.thingspeak.com";
const char* api_key = "123456789012121";       // API write key

char created_at[28];

// Base query
String INSERT_SQL = "INSERT INTO meteo.meteo (created_at,field1,field2,field3,field4,field5,field6,field7,field8,field9,field10,latitude,longitude,elevation) VALUES (";

MySQL_Connection conn((Client *)&client);
MySQL_Query *query_mem;


/////////////////////////////////////////// SETUP
void setup() {
  
  Serial.begin(115200);
  delay(1500);
  while (!Serial && millis() < 2000);           // wait for serial port to connect
  if (Serial) {
      Serial.println("Serial OK");
  }
    // Begin WiFi section
  MYSQL_DISPLAY1("Connecting to", ssid);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, pass);
  
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }

//*********************************************************************** OTA WEB START
  ArduinoOTA
    .onStart([]() {
      String type;
      if (ArduinoOTA.getCommand() == U_FLASH)
        type = "sketch";
      else // U_SPIFFS
        type = "filesystem";

      // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
      Serial.println("Start updating " + type);
    })
    .onEnd([]() {
      Serial.println("\nEnd");
    })
    .onProgress([](unsigned int progress, unsigned int total) {
      Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
    })
    .onError([](ota_error_t error) {
      Serial.printf("Error[%u]: ", error);
      if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
      else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
      else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
      else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
      else if (error == OTA_END_ERROR) Serial.println("End Failed");
    });

  ArduinoOTA.begin();
//*********************************************************************** OTA WEB END

  Serial1.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN);  // Serial port to HC12
  delay(1500);
  while (!Serial1 && millis() < 2000);          // wait for serial port to connect
  if (Serial1) {
      Serial.println("Serial 1 OK");
      Serial1.flush();
  }

  pinMode(LED_GREEN, OUTPUT);                   // Init OFF
  digitalWrite(LED_GREEN, LOW);                 // INIT OFF
  pinMode(LED_RED, OUTPUT);                     // Init OFF
  digitalWrite(LED_RED, LOW);                   // INIT OFF
  pinMode(LED_WHITE, OUTPUT);                   // Init OFF
  digitalWrite(LED_WHITE, LOW);                 // INIT OFF

  // Init and get the time
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);

  MYSQL_DISPLAY1("\nStarting Basic_Insert_ESP on", ARDUINO_BOARD);
  MYSQL_DISPLAY(MYSQL_MARIADB_GENERIC_VERSION);

  // print out info about the connection:
  MYSQL_DISPLAY1("Connected to network. My IP address is:", WiFi.localIP());
  MYSQL_DISPLAY3("Connecting to SQL Server @", server, ", Port =", server_port);
  MYSQL_DISPLAY5("User =", user, ", PW =", password, ", DB =", default_database);

  // For ThingSpeak
  if(!MDNS.begin("METEO")) {
     Serial.println("Error starting mDNS");
     return;
  }
  MDNS.addService("Meteo", "tcp", 80);

}

/////////////////////////////////////////// Delay function
void millisDelay( long int delayTime){
  long int start_time = millis();
  while ( millis() - start_time < delayTime) ;
}

/////////////////////////////////////////// Build Localtime field
void printLocalTime(){ 
  
  timeClient.update();
  struct tm timeinfo;
  if ( !getLocalTime(&timeinfo )) {
    Serial.println("Failed to obtain time");
    return;
  }
  strftime( created_at, 28, "'%Y-%m-%dT%H:%M:%S+00:00'", &timeinfo ); //+ or - then 00:00 indicate delta time from UCT

}

/////////////////////////////////////////// Insert Record
void runInsert() {
  
  // Initiate the query class instance
  MySQL_Query query_mem = MySQL_Query(&conn);

  if (conn.connected())  {
     if ( !query_mem.execute(Insert_Data.c_str()) )  {
         MYSQL_DISPLAY("Insert error");
     } else {
         MYSQL_DISPLAY("Meteo Data Inserted...");
     }
  } else {
      MYSQL_DISPLAY("Disconnected from Server. Can't insert.");
  }

}

/////////////////////////////////////////// SEND TO THINGSPEAK
void Send_data() {

  // Send data to ThingSpeak
  WiFiClient client;
  if (client.connect(server1,80)) {
     Serial.println("Connect to ThingSpeak - OK");
     Serial.println("");
     String postStr = "";
     postStr+="GET /update?api_key=";
     postStr+=api_key;
     postStr+=Data.substring(0,Data.length() - 1);
     postStr+=" HTTP/1.1\r\nHost: a.c.d\r\nConnection: close\r\n\r\n";
     postStr+="";
     client.print(postStr);
     //Serial.println(postStr);
  }
  //Serial.println(postStr);
  while(client.available()) {
     String line = client.readStringUntil('r');
     Serial.print(line);
  }

}

/////////////////////////////////////////// LOOP
void loop() {

  ArduinoOTA.handle();

  // Send data to ThingSpeak
  if (Serial1.available() > 0) {                 // If HC-12 has data
      Data = Serial1.readStringUntil('\n');
      // Check that the data are for ThingSpeak
      if (Data.substring(5,11) == "field1") {
          //Serial.println(Data.substring(5,11));
          digitalWrite(LED_GREEN, HIGH);         // INIT ON
          Send_data();
         // millisDelay(500);
          Serial.println("Data in ThingSpeak inserted...");
          Serial.println("");
      } else {
        // Send data to internal database
        digitalWrite(LED_RED, HIGH);             // INIT ON
        printLocalTime();                        // Print Time
        Insert_Data = INSERT_SQL;                // query construction
        Insert_Data += created_at;
        Insert_Data += ",";
        Insert_Data += Data;
        Insert_Data += ",'";
        Insert_Data += Latitude;
        Insert_Data += "','";
        Insert_Data += Longitude;
        Insert_Data += "','";
        Insert_Data += elevation;
        Insert_Data += "')";

        MYSQL_DISPLAY("Connecting to database...");
        if (conn.connectNonBlocking(server, server_port, user, password) != RESULT_FAIL) {
           // millisDelay(500);
            runInsert();
            conn.close();                        // close the connection
        } else {
            MYSQL_DISPLAY("\nConnect failed. Trying again on next iteration.");
        }
      }
      Serial1.flush();
  }
  digitalWrite(LED_GREEN, LOW);                  // LED OFF
  digitalWrite(LED_RED, LOW);                    // LED OFF

}
//END

Io ritengo di averlo inseirto nel modo corretto, ma forse mi sbaglio...

Io ho sempre trovato il meccanismo di OTA implementato nell'IDE estremamente capriccioso...
Non so se con il nuovo IDE le cose sono migliorate anche se mi verrebbe da dire di no.

Se vuoi qualcosa di remoto che funziona a colpo sicuro, probabilmente ti conviene usare la libreria inclusa nel core HTTPUpdateServer con cui puoi caricare il nuovo firmware compilato usando un browser.
E' vero che rispetto al metodo diretto con l'IDE c'è un passaggio in più (compilazione del firmware nella cartella dello sketch), ma in questo modo io non ho mai fallito un aggiornamento da remoto.
Inoltre non hai bisogno del PC con cui hai sviluppato, ma ti basta un computer qualsiasi o anche uno smartphone.

2 Likes

Ciao cotestatnt,
grazie per avermi risposto, si ho provato la versione WEB, ma in cambio ho una marea di errori con il mio setup.
Mentre se uso la libreria da sola, cosi come l'OTA standard, funziona bene...
Non è sicuramente una problema di board, mi immagino e credo sia dovuto ad un problema delle librerie che utilizzo o di come il programma funziona, forse limita in qualche modo il funzionamento della libreria OTA. Il problema è che non ne vengo fuori, nel senso che non riesco a capire dove si nasconde il problema. Adesso provo ad escludere alcune routine e vedo se riesco a trovare il colpevole.

Ho scoperto è if (Serial1.available() > 0) se lo tolgo appare la porta di rete, se lo lascio sparisce.
Il problema è che devo per forza sondare la seriale per fare in modo che tutto funzioni :frowning_face:
Forse impiega troppo tempo o risorse.
Ci sono modi alternativi per capire se il buffer della seriale ha dei dati dentro senza rallentare troppo il programma ?

Guarda che il metodo available() è banale e velocissimo ...

uint32_t uartAvailable(uart_t* uart)
{

    if(uart == NULL) {
        return 0;
    }

    UART_MUTEX_LOCK();
    size_t available;
    uart_get_buffered_data_len(uart->num, &available);
    if (uart->has_peek) available++;
    UART_MUTEX_UNLOCK();
    return available;
}

... recupera dei valori da alcune strutture relative alla "uart" e fa una somma ... impossibile che "impieghi troppo tempo o risorse" ... il problema deve essere altrove :roll_eyes:

Guglielmo

Per curiosità ho provato a replicare la tua situazione.
Per rendere la cosa ancor più "drammatica", ho messo fisicamente in loop TX e RX di Serial1 con baud rate di 500000bps, facendo rimpallare continuamente un carattere senza alcun delay (in pratica la UART è sempre impegnata).

Ho fatto una decina di upload (cambiando delle stupidaggini tra un update ed il successivo) tutti conclusi con successo.
Potrebbe essere qualcosa legato alla tua rete e/o al PC che usi?
Hai provato a disattivare eventuali firewall?

Ciao Guglielmo,
scusami per il ritardo, sono appena rientrato, allora faccio subito delle prove per vedere se la tua soluzione mi funziona.
Poi ti faccio sapere.

Emmm ... quale soluzione? Io ti ho solo mostrato i sorgenti del metodo available() per dimostrarti che la colpa non può essere imputata ad esso ... :roll_eyes:

Hai invece visto il successivo post di cotestatnt? Perché sembrerebbe che il problema sia altrove.

Guglielmo

Ciao contestatnt,
ti ringrazio per il tuo test ed i consigli, ma quello che mi dici mi lascia un pelino perplesso.
Allora la mia rete ha un server firewall pfsense subito dopo il router che non ha alcun filtro a parte il mac delle schede in uso, che si occupa della sicurezza della rete, tutti gli accessi esterni da e verso internet avvengono solo via vpn.
Tutti i miei PC in rete locale hanno il firewall disattivato i server sono impostati in pass-through sia per i protocolli TCP che UDP, in rete è tutto aperto, per una mia comodità.
L'unica cosa è che gli access point interni utilizzano il mac delle schede che li utilizzano, mentre quelli esterni passano dal firewall.
Io sono in una sottorete di test che non parla con nessuno e che utilizzo per le prove ed è sempre aperta, dove c'è un NAS che mi da alcuni servizi per i programmi ed il repository, il PC per arduino e l'access point, tanto ci sono solo i miei pasticci.
Per questo ritengo che sia abbastanza difficile che sia un problema dovuto alla sicurezza.
Non ti nascondo che firewall e networking sono stati per anni la mia attività principale.
Potrebbe invece essere un problema di configurazione dell'ESP ??
Magari ho sbagliato a configurarlo...
Comunque indagherò anche su quello che mi hai appena consigliato, si sa mai... sai con l'età metto in preventivo che posso anche sbagliarmi e non è difficile.

Ma l'ultimissima che per me è una cosa strana, è che ascoltando i giusti consigli che ho ricevuto in precedenza, ho sostituito la variabile INSERT_DATA con Insert_Data dal momento che non è una costante, ho voluto fare le cose per benino.
Bene, fatto questo e ricompilato il programma non inseriva più i record nel database e mi dava errore. Ho ricontrollato tutto più di un centinaio di volte, alla fine esausto ho rimesso la variabile INSERT_DATA tutto maiuscolo e tutto ha ripreso a funzionare !!
Boh !!
Ha dimenticavo, proverò anche a cambiare PC, però devo dirti che sto usando IDE2.2.1 mentre con la precedente versione questo problema non è mai accaduto.

Scusami Guglielmo, ho letto velocemente ma senza entrare nel dettaglio, mi premeva di più ringraziarvi per la vostra sollecita risposta decisamente gradita.
Poi sono passato a rispondere a contestatnt, visto che non c'era del codice in mezzo.
E avrei preso tempo per analizzare meglio quello che mi avevi postato tu.
Di fatto mi hanno appena chiamato e mi tocca farmarmi ancora.
Spero stanotte di avere un minuto di tranquillità per rivedere i vostri consigli.
Grazie ancora ci risentiamo presto...

Io SCONSIGLIO sempre l'utilizzo della versione 2.x dell'IDE che considero poco più che una beta (... ancora piena di problemi e carente di funzionalità).

Dai retta, installa l'ultima versione "legacy" dell'IDE, la 1.8.19 che è la più stabile ed affidabile e fai le prove con quella.

Guglielmo

1 Like

Rieccomi Gugliemo,
Orca che mi dici !!
Sinceramente pensavo che fosse abbastanza stabile qest'ultima versione.
L'ho installata da poco...
Non vorrei perdermi la manualità che sto apprendendo in questo momento con la versione 2.
Domandona, voi sapere se possono coesistere le due versioni ?
Grazie

Io comunque le mie prove le ho fatte con la versione 2.1.etc e si può coesistere con la versione 1.8.etc senza alcun problema.

1 Like

Caspita, con la versione 1.8.19 funziona !!
contestatnt, ma tu che PC hai ? è molto prestazionale, il mio, almeno questo che sto usando è un vecchio PC 4 core che funziona da anni senza mai un problema è un I5-4670 a 3.4Ghz con 16Mb di RAM.


Comunque ho ricontrollato tutto quanto questa notte ed il pc riceve e trasmette senza problemi in rete sia con il TCP che UDP, senza alcuna restrizione, antivirus e firewall disattivati.
Il python installato è l'ultima versione.
Guglielmo il tuo è stato un valido consiglio, quello di installare la vecchia versione, grazie, mi sono tolto un peso.

Ma adesso 'contestatnt' devo capire perchè il tuo PC funziona con l'IDE 2 ed il mio no !!
Grrr
Al momento ho installato entrambe le versioni che funzionano bene almeno sembra.

Per questo dicevo che è "capriccioso"... io trovo sia inaccettabile che sullo stesso PC in un modo funzioni e nell'altro no.

Il PC con cui ho fatto le prove in oggetto è un semplice i5 con Windows 10, ma non penso proprio che questo c'entri qualcosa.
ArduinoOTA è una libreria che di base usa un client UDP e questo è un altro punto a sfavore secondo me, anche se almeno hanno aggiunto una verifica di integrità dei dati usando un hash MD5.
Un semplice stream UDP, qualsiasi CPU uscita negli ultimi 15/20 anni è in grado di gestirlo.

1 Like

Comunque se vuoi provare il metodo che fa uso della libreria di update integrata nel core (che ovviamente ho provato con il tuo codice :wink:) potresti aggiungere un task distinto cosi da non "infastidire" il resto del codice (codice preso paro paro dall'esempio incluso, ma incapsulato in un task FreeRTOS):


#include <WebServer.h>
#include <Update.h>

WebServer update_server(80);
const char* serverIndex = "<form method='POST' action='/update' enctype='multipart/form-data'><input type='file' name='update'><input type='submit' value='Update'></form>";

void ota_task(void* arg) {
  Serial.println("Start OTA update server");
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    vTaskDelay(100/portTICK_PERIOD_MS);
    Serial.print(".");
  }
  update_server.on("/", HTTP_GET, []() {
    update_server.sendHeader("Connection", "close");
    update_server.send(200, "text/html", serverIndex);
  });

  update_server.on("/update", HTTP_POST, []() {
    update_server.sendHeader("Connection", "close");
    update_server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
    ESP.restart();
  }, []() {
    HTTPUpload& upload = update_server.upload();
    if (upload.status == UPLOAD_FILE_START) {
      Serial.setDebugOutput(true);
      Serial.printf("Update: %s\n", upload.filename.c_str());
      if (!Update.begin()) { //start with max available size
        Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_WRITE) {
      if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
        Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_END) {
      if (Update.end(true)) { //true to set the size to the current progress
        Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
      } else {
        Update.printError(Serial);
      }
      Serial.setDebugOutput(false);
    } else {
      Serial.printf("Update Failed Unexpectedly (likely broken connection): status=%d\n", upload.status);
    }
  });
  update_server.begin();
  Serial.println("Ready! Open http://meteo.local in your browser\n");

  for (;;) {
    update_server.handleClient();
    vTaskDelay(5/portTICK_PERIOD_MS);
  }
  vTaskDelete(NULL);
}

E poi nel setup() fai partire il task con l'istruzione

  // Start OTA task
  xTaskCreate(ota_task, "ota_task", 8196, NULL, 5, NULL);

Il risultato è questo:

image

1 Like

Immaginavo ... :roll_eyes: ... inoltre con la vecchia versione puoi usare tutti i "Tools" che con la nuova, magari sbaglio, ma non mi risulta che puoi ancora installare/usare ...

Guglielmo

1 Like

Ciao contestatnt,

si capisco che sia capriccioso, ma se la versione non è ancora matura come afferma Guglielmo, ci può stare, il software è una bestia difficile, anche con linguaggi semplici ci sono sempre dei "buchi", purtroppo anche uno bravo commette errori che sono molto più difficili da scoprire...

Ti ringrazio per il codice che hai postato, l'ho gia provato, ma in cambio ricevo una marea di errori, credo che faccia a cazzotti con le librerie che sto utilizzando.

Poi scusa la mia ignoranza ma come faccio da inserirlo nel mio codice senza che mi dia problemi ?

Io nasco dal fortran HP poi l'assembler 8000 (che ricordo con difficoltà) ed il basic (in varie versioni), si ho provato altri linguaggi, ma la mia esperienza di programmazione finisce con il clipper e visual object. Dopo, il vuoto per secoli...

Nel senso che mi sono dato ai sistemi operativi ed al networking, per cui mi mancano dei tasselli che immagino siano importanti o meglio essenziali per capire il tuo post.

Sono andato a leggermi che vuol dire FreeRTOS ed in tutta sincerità ho capito poco, sarò anche stanco in questo momento, ma dedicherò un pò di tempo per cercare comprendere meglio quello che mi hai indicato, come alternativa e soprattutto come fare per implementarlo nel mio programma.

Intanto Vi ringrazio di cuore per tutti i consigli ed indicazioni che mi avete fornito, senza i consigli di tutti quanti, anche passati, non sarei stato in grado di mettere giù una sola riga.

Io so che esistono delle funzioni o mi immagino che esistono, ma ogni volta devo andare a leggermi o cercare come funziona, quindi per me è impegnativo scrivere del codice.

Anzi sono a chiedervi un vostro parere su come l'ho scritto, (voglio dire il codice che ho postato) so che è soggettivo, e che forse ha anche poco senso chiederlo, ma per me è rassicurante avere un parere di chi programma quotidianamente, ci tengo molto almeno per capire se sono sulla buona strada o no, e magari sapere cosa devo migliorare se possibile.

Io cerco di essere piuttosto semplice nella stesura del codice, questo lo faccio soprattutto per non trovarmi in difficoltà in seguito, intendo avere la capacità di poterlo capire anche dopo mesi e più passa il tempo più diventa difficile, la mia "suca" (alias testa in Milanese) è sempre meno vivace...

Un salutone a tutti quanti.
PS.
Scusami contestatnt, per la domanda, ma hai avuto problemi con il trasportatore ??
Il nomignolo é simpatico e mi incuriosisce.

Grazie Gugliemo per il tuo consiglio.
Sei stato risolutivo alla grande, mi stavo demotivando mica da ridere!!

E' un VERO sistema operativo real-time ... ESP32 è basato su tale OS; ESP32 ha due core ... in uno girano i vari servizi di sistema, nell'altro le tue applicazioni ... per questo è molto più stabile dei vecchi ESP8266 mono-core dove, se facevi un loop stretto, piantavi tutto e scattava il WDT hardware :wink:

FreeRTOS è molto potente e, ove occorre, si riescono a fare belle applicationi real-time, multi task, interrupt driven, ma ... NON è un prodotto banale ed usarlo bene richiede un discreto studio.

Per cominciare ad usarlo in ambiente Arduino su ESP32, posso consigliarti QUESTO libro :wink:

In generale invece, trovi la documentazione su FreeRTOS QUI (è il loro sito dove c'è di tutto e di più).

Guglielmo

P.S.: Giusto per info, quando "Futura Elettronica" ricomincerà i corsi in presenza (attualmente fa solo quelli on-line :confused:) ... troverai me come "istruttore" per quelli relativi a FreeRTOS :grin:

P.P.S.: mi accorgo solo ora che parli ESP32C ... che è single-core e quindi occorre più attenzione ...

1 Like