POST HTTPS ESP32 T-Call A7670E v1.0

Bonjour,
J'utilise un Lilygo ESP32 T-Call A7670E v1.0 pour un projet de microcapteur 3G/4G.
J'essaie d'envoyer un paquet de données de 15 lignes à mon API avec une requête POST HTTPS. Normalement la bibliotheque TinyGsm est sensé geré la securité automatiquement avec le port 443. Mais lors de l'envoi de mon lot de donnees jai en retour "400 The plain HTTP request was sent to HTTPS port".
J'utilise TinyGsmClient actuellement, je sais que je dois utiliser TinyGsmClientSecure mais cela m'empeche de televerser mon code. Serait il possible d'avoir un peu d'aide svp?`

Si vous demandez à passer en HTTP et que l'autre côté attend du HTTPS, c'est normal que ça plante.

Avez vous regardé leur exemple en HTTPS ?

Bonjour
jai suivi l'exemple et je l'ai meme essayer mais mon probleme cest que lors de la compilation jai une lié a TinyGsmClientSecure qui n'est pas connu de la bibliotheque.

C:\Users\Groupe TN\Documents\Arduino\Code_de_base_THPH_NO2_GPS_UTC_RTC_SD_OPTIMISED\Code_de_base_THPH_NO2_GPS_UTC_RTC_SD_LTE_HTTPS_AT\Code_de_base_THPH_NO2_GPS_UTC_RTC_SD_LTE_HTTPS_AT.ino:87:1: error: 'TinyGsmClientSecure' does not name a type; did you mean 'TinyGsmClient'?
87 | TinyGsmClientSecure clientHttps(modem);
| ^~~~~~~~~~~~~~~~~~~
| TinyGsmClient
C:\Users\Groupe TN\Documents\Arduino\Code_de_base_THPH_NO2_GPS_UTC_RTC_SD_OPTIMISED\Code_de_base_THPH_NO2_GPS_UTC_RTC_SD_LTE_HTTPS_AT\Code_de_base_THPH_NO2_GPS_UTC_RTC_SD_LTE_HTTPS_AT.ino:88:36: error: 'clientHttps' was not declared in this scope
88 | HttpClient httpClient = HttpClient(clientHttps, xano_server, xano_port);
| ^~~~~~~~~~~
Plusieurs bibliothèque trouvées pour "SD.h"
Utilisé: C:\Users\Groupe TN\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.2.1\libraries\SD
Non utilisé: C:\Users\Groupe TN\Documents\Arduino\libraries\SD
exit status 1

Compilation error: 'TinyGsmClientSecure' does not name a type; did you mean 'TinyGsmClient'?

//*************************
//* Définition du modem   *
//*************************
#define TINY_GSM_MODEM_A7670
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
#define TINY_GSM_USE_SSL true // Important pour que la bibliothèque active les fonctions SSL

//*********************************************
//* Inclusions des bibliothèques (en premier) *
//*********************************************
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
#include <Adafruit_AHTX0.h>
#include <RTClib.h>
#include <TinyGsmClient.h>
#include <ArduinoHttpClient.h>
#include <ArduinoJson.h>

//*********************************************
//* Définition des broches du modem A7670E      *
//*********************************************
#define MODEM_BAUDRATE          (115200)
#define MODEM_DTR_PIN           (14)
#define MODEM_TX_PIN            (26)
#define MODEM_RX_PIN            (25)
#define BOARD_PWRKEY_PIN        (4)
#define BOARD_LED_PIN           (12)
#define MODEM_RING_PIN          (13)
#define MODEM_RESET_PIN         (27)
#define MODEM_RESET_LEVEL       LOW
#define SerialAT                Serial2

//******************************************
//* Définition des broches du capteur NO2  *
//******************************************
#define NO2_TX                  (33)
#define NO2_RX                  (32)

// --- Broches I2C pour l'ESP32 ---
#define I2C_SDA_PIN             (21)
#define I2C_SCL_PIN             (22)

//******************************************
//* Définition des broches du module SD    *
//******************************************
#define SD_CS_PIN               (15)
#define SD_MOSI                 (23)
#define SD_MISO                 (19)
#define SD_SCK                  (18)

// --- Paramètres de connexion ---
const char apn[] = "orangeweb";
const char gprsUser[] = "";
const char gprsPass[] = "";

const char xano_server[] = "x8ki-letl-twmt.n7.xano.io";
const char xano_path[]   = "/api:kmfhir9E/mesure/";
const int  xano_port     = 443;

// --- Identifiants ---
String modem_imei = "";
const char* sensor_serial = "HBNO24G/AAA00001";

// --- Variables de calibration et de données ---
float temperatureOffsetBMP = -7.1;
float pressureOffset = -17.0;
float temperatureOffsetAHT = -5.6;
float humidityOffset = 2.70;
float altitudeOffset = 6.0;
float latitude = 0.0, longitude = 0.0, gps_altitude = 0.0, speed = 0.0;
int satellites = 0;

//******************************************
//* Déclaration des objets                 *
//******************************************
Adafruit_BMP280 bmp;
Adafruit_AHTX0 aht;
RTC_DS3231 rtc;
HardwareSerial SerialNO2(1);

TinyGsm modem(SerialAT);
// Utilisation du client sécurisé pour les connexions HTTPS
TinyGsmClientSecure clientHttps(modem);
HttpClient httpClient = HttpClient(clientHttps, xano_server, xano_port);

// --- Variables pour la nouvelle logique de temps ---
unsigned long lastLoopTime = 0;
int minuteCounter = 0;
const int BATCH_SIZE = 15;
const char* DATA_FILENAME = "/data_minute.csv";

//******************************************
//* Définitions des Fonctions              *
//******************************************

/**
 * @brief Affiche les informations de connexion réseau (Opérateur, Signal, IP).
 */
void displayNetworkInfo() {
  Serial.println("--- Informations Réseau ---");
  String operatorName = modem.getOperator();
  Serial.print("Opérateur: "); Serial.println(operatorName);
  int signalQuality = modem.getSignalQuality();
  Serial.print("Qualité du signal (RSSI): "); Serial.print(signalQuality); Serial.println(" dBm");
  String localIP = modem.getLocalIP();
  Serial.print("Adresse IP: "); Serial.println(localIP);
  Serial.println("---------------------------");
}

/**
 * @brief Vérifie la somme de contrôle (checksum) d'un paquet de données.
 */
bool verifyChecksum(byte* data, int length) {
  if (length < 2) return false;
  byte sum = 0;
  for (int i = 1; i < length - 1; i++) {
    sum += data[i];
  }
  sum = 255 - sum + 1;
  return sum == data[length - 1];
}

/**
 * @brief Synchronise l'horloge RTC avec l'heure du réseau cellulaire.
 */
void syncTimeWithNetwork() {
  Serial.println("Synchronisation de l'heure avec le réseau...");
  int year, month, day, hour, minute, second;
  float networkTimezone;
  if (modem.getNetworkTime(&year, &month, &day, &hour, &minute, &second, &networkTimezone)) {
    DateTime localTime(year, month, day, hour, minute, second);
    rtc.adjust(localTime);
    char rtcTimestamp[20];
    sprintf(rtcTimestamp, "%04d-%02d-%02d %02d:%02d:%02d",
            localTime.year(), localTime.month(), localTime.day(),
            localTime.hour(), localTime.minute(), localTime.second());
    Serial.print("Heure réseau reçue et RTC ajusté à : "); Serial.println(rtcTimestamp);
  } else {
    Serial.println("Impossible de récupérer l'heure du réseau.");
  }
}

/**
 * @brief Tente d'obtenir les coordonnées GPS du modem.
 */
bool getGPSLocation(float &lat, float &lon, float &alt, float &spd, int &sats) {
  Serial.println("Tentative de localisation GPS...");
  if (!modem.enableGPS()) {
    Serial.println("ERREUR: La commande modem.enableGPS() a échoué !");
    return false;
  }
  Serial.println("GPS activé.");
  const unsigned long GPS_FIX_TIMEOUT_MS = 100000; // 100 secondes au total
  unsigned long startGPSTime = millis();
  bool fixObtained = false;
  uint8_t gpsStatus = 0; 
  while (millis() - startGPSTime < GPS_FIX_TIMEOUT_MS) {
    if (modem.getGPS(&gpsStatus, &lat, &lon, &spd, &alt, &sats) && gpsStatus > 0) {
      Serial.printf("Fix GPS obtenu en %.1f secondes.\n", (float)(millis() - startGPSTime) / 1000.0);
      fixObtained = true;
      break;
    }
    delay(2000); 
  }
  if (!fixObtained) {
    Serial.println("Échec de la localisation GPS après le délai imparti.");
    lat = 0.0; lon = 0.0; alt = 0.0; spd = 0.0; sats = 0;
  }
  modem.disableGPS();
  Serial.println("GPS désactivé.");
  return fixObtained;
}

/**
 * @brief Convertit une concentration de NO2 de ppm en µg/m³.
 */
float convertPpmToUgm3(float ppm, float temperature, float pressure) {
    const float M_NO2 = 46.0055;
    const float R = 8.31446;
    if (temperature < -273.15) return -1.0;
    float tempKelvin = temperature + 273.15;
    float pressurePascals = pressure * 100.0;
    return (ppm * M_NO2 * pressurePascals) / (R * tempKelvin);
}

/**
 * @brief Lit les dernières lignes du fichier de données, les formate en JSON et les envoie.
 */
void readAndSendBatchData() {
    File dataFile = SD.open(DATA_FILENAME, FILE_READ);
    if (!dataFile) {
        Serial.println("Impossible d'ouvrir le fichier de données pour l'envoi.");
        return;
    }

    String lines[BATCH_SIZE];
    int lineCount = 0;
    while (dataFile.available()) {
        String line = dataFile.readStringUntil('\n');
        line.trim();
        if (line.length() > 0 && line.indexOf("Timestamp") == -1) {
            lines[lineCount % BATCH_SIZE] = line;
            lineCount++;
        }
    }
    dataFile.close();

    if (lineCount == 0) {
        Serial.println("Aucune donnée à envoyer.");
        return;
    }

    bool gps_fixed = getGPSLocation(latitude, longitude, gps_altitude, speed, satellites);

    DynamicJsonDocument doc(12288); // Augmenté pour la nouvelle charge utile
    JsonArray jsonArray = doc.to<JsonArray>();

    int linesToSend = min(lineCount, BATCH_SIZE);
    for (int i = 0; i < linesToSend; i++) {
        String currentLine = lines[(lineCount - linesToSend + i) % BATCH_SIZE];
        JsonObject obj = jsonArray.createNestedObject();

        char csvBuffer[250];
        currentLine.toCharArray(csvBuffer, 250);
        char* token = strtok(csvBuffer, ",");
        int col = 0;
        
        char* values[16] = {0};
        while(token != NULL && col < 16) {
            values[col++] = token;
            token = strtok(NULL, ",");
        }

        obj["timestamp_text"] = values[0];
        obj["temp_ext_c"] = atof(values[1]);
        obj["pressure_hpa"] = atof(values[2]);
        obj["altitude_m"] = atof(values[3]);
        obj["temp_int_c"] = atof(values[4]);
        obj["humidity_percent"] = atof(values[5]);
        obj["no2_ppm"] = atof(values[6]);
        obj["no2_ppb"] = atof(values[7]);
        obj["no2_ugm3"] = atof(values[8]);
        
        obj["latitude"] = latitude;
        obj["longitude"] = longitude;
        obj["gps_altitude_m"] = gps_altitude;
        obj["gps_speed_kmh"] = speed;
        obj["gps_satellites"] = satellites;

        obj["imei"] = values[14];
        obj["sensor_serial"] = values[15];
    }

    String jsonPayload;
    serializeJson(doc, jsonPayload);
    
    Serial.println("\n[HTTP] Envoi du lot de données à Xano...");
    Serial.println(jsonPayload);

    httpClient.post(xano_path);
    httpClient.sendHeader("Content-Type", "application/json");
    httpClient.sendHeader("Content-Length", jsonPayload.length());
    httpClient.sendHeader("Connection", "close");
    httpClient.beginBody();
    httpClient.print(jsonPayload);
    httpClient.endRequest();

    int statusCode = httpClient.responseStatusCode();
    String response = httpClient.responseBody();
    Serial.print("[HTTP] Statut de la réponse: "); Serial.println(statusCode);
    Serial.print("[HTTP] Corps de la réponse: "); Serial.println(response);

    if (statusCode >= 200 && statusCode < 300) {
        Serial.println("[HTTP] Lot de données envoyé avec succès !");
        DateTime now = rtc.now();
        char archiveName[30];
        sprintf(archiveName, "/archive_%04d%02d%02d_%02d%02d.csv", now.year(), now.month(), now.day(), now.hour(), now.minute());
        if (SD.rename(DATA_FILENAME, archiveName)) {
            Serial.printf("Fichier de données archivé sous: %s\n", archiveName);
        } else {
            Serial.println("ERREUR: Impossible de renommer le fichier de données. Il sera réutilisé.");
        }
    } else {
        Serial.println("[HTTP] Échec de l'envoi du lot. Les données seront renvoyées au prochain cycle.");
    }
}

/**
 * @brief Écrit les données collectées sur la carte SD dans un fichier CSV.
 */
void writeDataToSD(const char* filename, const DateTime& now, float tempBMP, float pressure, float altitude, float tempAHT, float humidity,
                   float no2_ppm, float no2_ppb, float no2_ugm3, float lat, float lon, float gps_alt, float gps_spd, int gps_sats,
                   const char* imei, const char* serial) {
  File dataFile = SD.open(filename, FILE_APPEND);
  if (dataFile) {
    if (dataFile.size() == 0) {
      dataFile.println("Timestamp,TempBMP_C,Pressure_hPa,Altitude_m,TempAHT_C,Humidity_%,NO2_ppm,NO2_ppb,NO2_ugm3,Latitude,Longitude,GPS_Altitude_m,GPS_Speed_kmh,GPS_Satellites,IMEI,SensorSerial");
    }
    char timestampStr[20];
    sprintf(timestampStr, "%04d-%02d-%02d %02d:%02d:%02d", now.year(), now.month(), now.day(), now.hour(), now.minute(), now.second());
    dataFile.printf("%s,%.2f,%.2f,%.2f,%.2f,%.2f,%.3f,%.1f,%.2f,%.6f,%.6f,%.2f,%.2f,%d,%s,%s\n",
                    timestampStr, tempBMP, pressure, altitude, tempAHT, humidity,
                    no2_ppm, no2_ppb, no2_ugm3, lat, lon, gps_alt, gps_spd, gps_sats,
                    imei, serial);
    dataFile.close();
    Serial.printf("Données enregistrées sur %s\n", filename);
  } else {
    Serial.printf("Erreur à l'ouverture du fichier SD: %s\n", filename);
  }
}

//******************************************
//* Fonction SETUP - Initialisation        *
//******************************************
void setup() {
  Serial.begin(115200);
  Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
  delay(3000);
  Serial.println("\n--- Démarrage HBNO24G/AAA00001 ---");

  Serial.println("Initialisation des capteurs...");
  if (!bmp.begin(0x77)) { Serial.println("ERREUR: Capteur BMP280 introuvable."); while (true) delay(2000); }
  if (!aht.begin()) { Serial.println("ERREUR: Capteur AHT20 introuvable."); while (true) delay(2000); }
  SerialNO2.begin(9600, SERIAL_8N1, NO2_RX, NO2_TX);
  Serial.println("Capteurs initialisés.");
  
  Serial.println("Initialisation du modem...");
  pinMode(BOARD_PWRKEY_PIN, OUTPUT);
  digitalWrite(BOARD_PWRKEY_PIN, LOW); delay(100);
  digitalWrite(BOARD_PWRKEY_PIN, HIGH); delay(2000);
  digitalWrite(BOARD_PWRKEY_PIN, LOW); delay(1000);

  SerialAT.begin(MODEM_BAUDRATE, SERIAL_8N1, MODEM_RX_PIN, MODEM_TX_PIN);
  Serial.println("Attente de la réponse du modem...");
  if (!modem.init()) { Serial.println("ÉCHEC de l'initialisation du modem ! Redémarrage..."); delay(1000); ESP.restart(); }
  Serial.println("Modem initialisé.");

  modem_imei = modem.getIMEI();
  Serial.print("IMEI du modem: ");
  Serial.println(modem_imei);

  Serial.println("Attente du réseau cellulaire...");
  if (!modem.waitForNetwork(120000L)) { Serial.println("ÉCHEC! Redémarrage..."); delay(1000); ESP.restart(); }
  Serial.println("Connecté au réseau.");

  Serial.printf("Connexion à l'APN : %s...\n", apn);
  if (!modem.gprsConnect(apn, gprsUser, gprsPass)) { Serial.println("ÉCHEC! Redémarrage..."); delay(1000); ESP.restart(); }
  Serial.println("Connecté au GPRS.");
  
  displayNetworkInfo();

  Serial.println("Initialisation du module RTC...");
  if (!rtc.begin()) { Serial.println("ERREUR: Module RTC introuvable!"); while (true) delay(1000); }
  if (rtc.lostPower()) { Serial.println("RTC a perdu l'heure, synchro avec le réseau..."); }
  syncTimeWithNetwork();
  
  Serial.print("Initialisation de la carte SD...");
  SPI.begin(SD_SCK, SD_MISO, SD_MOSI);
  if (!SD.begin(SD_CS_PIN)) { Serial.println("ÉCHEC!"); while (true) delay(2000); }
  Serial.println(" OK.");

  Serial.println("\n--- Configuration terminée. Démarrage du cycle de mesure. ---\n");
}

//******************************************
//* Fonction LOOP - Cycle principal        *
//******************************************
void loop() {
  lastLoopTime = millis();
  Serial.println("\n------ Cycle de mesure (1 minute) ------");
  minuteCounter++;

  DateTime now = rtc.now();
  float temperatureBMP = bmp.readTemperature() + temperatureOffsetBMP;
  float pressure = (bmp.readPressure() / 100.0F) + pressureOffset;
  float altitude = bmp.readAltitude(1017.00);
  sensors_event_t humidityEvent, tempEvent;
  aht.getEvent(&humidityEvent, &tempEvent);
  float temperatureAHT = tempEvent.temperature + temperatureOffsetAHT;
  float humidityAdjusted = humidityEvent.relative_humidity + humidityOffset;
  
  float no2ConcentrationPPM = -1.0, no2ConcentrationPPB = -1.0, no2ConcentrationUGM3 = -1.0;
  
  while (SerialNO2.available()) { SerialNO2.read(); }
  byte readCommand[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
  SerialNO2.write(readCommand, sizeof(readCommand));

  unsigned long no2_read_timeout = millis() + 5000;
  int bytesRead = 0;
  byte no2Data[9];
  while (millis() < no2_read_timeout && bytesRead < 9) {
    if (SerialNO2.available()) {
      no2Data[bytesRead++] = SerialNO2.read();
    }
  }

  if (bytesRead == 9 && verifyChecksum(no2Data, 9)) {
    int rawValuePPB = (no2Data[2] * 256) + no2Data[3];
    no2ConcentrationPPB = (float)rawValuePPB;
    no2ConcentrationPPM = no2ConcentrationPPB / 1000.0;
    no2ConcentrationUGM3 = convertPpmToUgm3(no2ConcentrationPPM, temperatureBMP, pressure);
  } else {
    Serial.printf("Erreur de lecture NO2 (octets lus: %d)\n", bytesRead);
  }

  writeDataToSD(DATA_FILENAME, now, temperatureBMP, pressure, altitude, temperatureAHT, humidityAdjusted,
                no2ConcentrationPPM, no2ConcentrationPPB, no2ConcentrationUGM3, latitude, longitude, gps_altitude, speed, satellites,
                modem_imei.c_str(), sensor_serial);

  if (minuteCounter >= BATCH_SIZE) {
    Serial.println("\n--- Cycle de 15 minutes atteint. Préparation de l'envoi du lot. ---");
    minuteCounter = 0;

    if (!modem.isGprsConnected()) {
        Serial.println("Connexion GPRS perdue, tentative de reconnexion...");
        if (modem.gprsConnect(apn, gprsUser, gprsPass)) {
          Serial.println("Reconnexion GPRS réussie.");
          displayNetworkInfo(); 
          syncTimeWithNetwork(); 
        } else {
           Serial.println("Échec de la reconnexion. Les données seront envoyées au prochain cycle.");
           goto end_loop;
        }
    }
    readAndSendBatchData();
  } else {
    Serial.printf("Cycle %d/%d. Données enregistrées. Prochain envoi dans %d minute(s).\n", minuteCounter, BATCH_SIZE, BATCH_SIZE - minuteCounter);
  }

end_loop:
  Serial.println("------ Fin du cycle. En attente de la prochaine minute. ------");
  unsigned long loopDuration = millis() - lastLoopTime;
  if (loopDuration < 60000) {
    delay(60000 - loopDuration);
  }
}

Quelquun peut il me dire si mon code a une erreur?

dans ce cas il se peut que votre module ne soit pas supporté par leur bibliothèque

** SSL/TLS (HTTPS)*

    • Supported on:*
  •  * SIM800, SIM7000, A7672X, u-Blox, XBee *cellular*, ESP8266, Sequans Monarch and Quectel BG95 and BG96*
    
  •  * Note: **only some device models or firmware revisions have this feature** (SIM8xx R14.18, A7, etc.)*
    
    • Not yet supported on:*
  •  * SIM 5360/5320/7100, SIM 7500/7600/7800*
    
    • Not possible on:*
  •  * SIM900, A6/A7, Neoway M590, XBee *WiFi**
    

dans ce cas il faudra vous même gérer les commandes.

Bonjour
Merci pour ces information. J'ai donc géré moi meme avec les commandes AT qui m'ont quand meme permis de pas mal avancer.
Je rencontre maintenant un probleme lié a la liaison SSL et je vais donc ouvrir un autre sujet pour en parler.

Je vous remercie