mobizt / ESP_SSLClient

The upgradable Secure Layer Networking (SSL/TLS) TCP Client for Arduino devices that support external networking interfaces e.g., WiFiClient, EthernetClient, and GSMClient.
MIT License
18 stars 2 forks source link

Flush write buffer error on first message sent, stack overflow on second message sent #17

Open RaymondReddingt0n opened 1 week ago

RaymondReddingt0n commented 1 week ago

Hello, I am working on a personal project which involves setting up a connection to an AWS database and sending and receiving data through HTTPS requests. However, I have encountered an issue when trying to set up the GET method. Both work the first time but then the 2nd time they run it encounters a stack overflow error. Below is the code as well as additional details. I am using an Arduino UNO WiFi R4.

void fetchDeviceData() {
  WiFiSSLClient ssl_client;

  // Define GET url
  const char* serverName = "xd6v8fwy40.execute-api.eu-north-1.amazonaws.com";
  const char* resourcePath = "/prod?thermostat_id=";

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\nStarting connection to server...");

    if (ssl_client.connect(serverName, 443)) {
      Serial.println("Connected to server");

      String connectionUrl = "GET /prod/thermostat?thermostat_id=" + arduino_id + " HTTP/1.1";

      // Send HTTP request
      ssl_client.println(connectionUrl);
      ssl_client.println("Host: xd6v8fwy40.execute-api.eu-north-1.amazonaws.com");
      ssl_client.println("Connection: close");
      ssl_client.println();

      // Wait for the server to respond
      String responseBody = "";
      bool bodyStarted = false;

      // Wait for the server to respond
      while (ssl_client.connected() || ssl_client.available()) {
        String line = ssl_client.readStringUntil('\n');
        if (!bodyStarted) {
          // Detect the end of headers and start of the body
          if (line == "\r" || line == "") {
            bodyStarted = true;
            continue;
          }
        }
        if (bodyStarted) {
          responseBody += line;
        }
      }

      Serial.println("Response:");
      Serial.println(responseBody);

      // Parse JSON
      StaticJsonDocument<1024> doc;
      DeserializationError error = deserializeJson(doc, responseBody);

      if (error) {
        Serial.print("JSON deserialization failed: ");
        Serial.println(error.c_str());
        return;
      }

      compressorOn = doc["compressor_status"];
      defrostOn = doc["defrosting_status"];
      fansOn = doc["fans_status"];
      manualMode = doc["manualMode"];
      delay_time = doc["delay_time"];
      safeDelta = doc["safe_delta"];
      tempD_lim_inf = doc["temp_defrosting_lower"];
      tempD_lim_sup = doc["temp_defrosting_upper"];
      tempT = doc["temp_t"];

      ssl_client.stop();  // Close the connection
    } else {
      Serial.println("Connection to server failed");
    }
  } else {
    Serial.println("WiFi not connected");
  }
}

void sendDeviceData() {
  // Connection to AWS
  WiFiClient wifi;
  ESP_SSLClient net;

  timeClient.update();

  // Get the current epoch time in UTC
  unsigned long utcTime = timeClient.getEpochTime();

  // Print the UTC time in Unix format
  Serial.print("Current UTC time in Unix format: ");
  Serial.println(utcTime);

  net.setX509Time(utcTime);

  // Configure WiFiClientSecure to use the AWS certificates we generated
  net.setCACert(AWS_CERT_CA);
  net.setCertificate(AWS_CERT_CRT);
  net.setPrivateKey(AWS_CERT_PRIVATE);

  net.setBufferSizes(1024 /* rx */, 512 /* tx */);

  net.setDebugLevel(0);
  net.setClient(&wifi);

  // Connect to the MQTT broker on the AWS endpoint we defined earlier
  client.begin(AWS_IOT_ENDPOINT, 8883, net);
  client.setKeepAlive(delay_time / 1000 + 2);
  client.onMessage(messageReceived);

  // Try to connect to AWS and count how many times we retried.
  int retries = 0;
  Serial.println("Connecting to AWS IOT...");

  Serial.println(client.connected());

  while (!client.connect(DEVICE_NAME)) {
    Serial.print(".");
    delay(10000);
  }

  Serial.println("\nconnected!");
  Serial.println(client.connected());

  // If we land here, we have successfully connected to AWS!
  // And we can subscribe to topics and send messages.

  timeClient.update();

  // Get the current epoch time in UTC
  utcTime = timeClient.getEpochTime();

  // Convert epoch time to readable date and time
  int currentYear = 1970;
  unsigned long secondsInYear = 31536000;
  while (utcTime >= secondsInYear) {
    utcTime -= secondsInYear;
    currentYear++;
    if (currentYear % 4 == 0) {
      secondsInYear = 31622400; // Leap year
    } else {
      secondsInYear = 31536000;
    }
  }

  // Extract month, day, hour, minute, and second
  int month = 1;
  unsigned long secondsInMonth[] = {2678400, 2419200, 2678400, 2592000, 2678400, 2592000, 2678400, 2678400, 2592000, 2678400, 2592000, 2678400};
  if (currentYear % 4 == 0) {
    secondsInMonth[1] = 2505600; // February in a leap year
  }
  while (utcTime >= secondsInMonth[month - 1]) {
    utcTime -= secondsInMonth[month - 1];
    month++;
  }
  int day = utcTime / 86400 + 1;
  int hour = (utcTime % 86400) / 3600;
  int minute = (utcTime % 3600) / 60;
  int second = utcTime % 60;

  // Format timestamp as "DD/MM/YYYY HH:MM:SS"
  char timestamp[20];
  sprintf(timestamp, "%02d/%02d/%04d %02d:%02d:%02d", day, month, currentYear, hour, minute, second);

  // Print the UTC time in Unix format
  Serial.print("Current UTC time in Unix format: ");
  Serial.println(utcTime);

  Serial.println("Sending device data to AWS...");

  // Create a JSON object to hold all the data
  DynamicJsonDocument dataDoc(1024); // Adjust size as needed
  dataDoc["thermostat_id"] = arduino_id;
  dataDoc["timestamp"] = timestamp;
  dataDoc["temp_a"] = String(tempA);
  dataDoc["temp_d"] = String(tempD);
  dataDoc["temp_f"] = String(tempF);
  dataDoc["temp_r"] = String(tempR);
  dataDoc["compressor_status"] = compressorOn;
  dataDoc["fans_status"] = fansOn;
  dataDoc["defrosting_status"] = defrostOn;
  dataDoc["temp_setpoint"] = String(tempS);
  dataDoc["temp_defrosting_lower"] = String(tempD_lim_inf);
  dataDoc["temp_defrosting_upper"] = String(tempD_lim_sup);
  dataDoc["safe_delta"] = String(safeDelta);
  dataDoc["delay_time"] = String(delay_time);
  dataDoc["message"] = message;
  dataDoc["alarm"] = alarm;
  dataDoc["manualMode"] = manualMode;
  dataDoc["temp_t"] = tempT;

  // Serialize JSON object to string
  String jsonData;
  serializeJson(dataDoc, jsonData);

  // Publish temperature data to AWS IoT
  if (client.publish(AWS_IOT_TOPIC_1, jsonData)) {
    Serial.println("Logs data published to AWS IoT.");
  } else {
    Serial.println("Failed to publish logs data.");
  }

  Serial.println("Json temp logs: ");
  Serial.println(jsonData);

  client.disconnect();
  net.stop();
}

In these two functions, the first one is to handle a GET request and the second one is to establish an MQTT connection to AWS and send data to it. They are called in order one after the other on the void loop() infinitely. The loop order is supposed to happen as follows:

However, in reality, only the first three work.

And then on the next sendDeviceData(), on the following line, I get the following error:

while (!client.connect(DEVICE_NAME)) {

17:07:37.010 -> Firmware name: "C:\Users\myname~1\AppData\Local\Temp\arduino_build_443729/aws_database.ino", compiled on: Aug 30 2024
17:07:37.151 -> Fault on interrupt or bare metal(no OS) environment
17:07:37.151 -> Error: Main stack(20007800) was overflow
17:07:37.151 -> ===== Thread stack information =====
17:07:37.293 ->   addr: 20007b00    data: 51c2bcaf
17:07:37.293 ->   addr: 20007b04    data: 0d895147
17:07:37.293 ->   addr: 20007b08    data: f64f630a
[...]
17:07:47.756 -> ====================================
17:07:47.896 -> Usage fault is caused by attempts to switch to an invalid state (e.g., ARM)
17:07:47.896 -> Show more call stack info by run: addr2line -e "C:\Users\myname~1\AppData\Local\Temp\arduino_build_443729/aws_database.ino".elf -a -f 0001633e 000160ec 0001b834 00027ef6 0001ba38 0001bde2 000240f6 0001bf96 0000a3d8 00022e78 00023c66 00005af8 0001c6ba 00006e54 0000717e 0000753a

I believe it might be due to the fact I'm using two different libraries, one for the get and the other for the send, which might be interfering with each other. These are the libraries I'm using for this portion of the code:

#include "WiFiSSLClient.h"
#include <MQTTClient.h>
#include <ESP_SSLClient.h>

I'm also using these libraries:

#include <WiFiS3.h>
#include <ArduinoJson.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <NTPClient.h>
#include "certs.h"
#include <ArduinoHttpClient.h>

I've added setDebugLevel(4) to my code and this is the full log:

For the connection to AWS:

15:32:02.437 -> > INFO.mConnectBasicClient: Basic client connected!
15:32:02.437 -> > INFO.mConnectSSL: Start connection.
15:32:02.437 -> > WARN.mConnectSSL: Connection *will* fail, no authentication method is setup.
15:32:02.623 -> > INFO.mConnectSSL: Wait for SSL handshake.
15:32:02.623 -> > INFO.mUpdateEngine: State RECVREC
15:32:02.669 -> > INFO.mUpdateEngine: State RECVREC
15:32:02.810 -> > INFO.mRunUntil: SSL state changed.
15:32:02.810 -> > INFO.mRunUntil: State RECVREC
15:32:02.810 -> > INFO.mRunUntil: Expected bytes count: 5
15:32:04.490 -> > INFO.mUpdateEngine: State RECVREC
15:32:10.064 -> > INFO.mUpdateEngine: State RECVREC
15:32:10.250 -> > INFO.mUpdateEngine: State SENDAPP
15:32:10.389 -> > INFO.mRunUntil: SSL state changed.
15:32:10.389 -> > INFO.mRunUntil: State SENDAPP
15:32:10.389 -> > INFO.mConnectSSL: Connection successful!
15:32:10.389 -> Arduino_1> INFO.mRunUntil: SSL state changed.
15:32:10.529 -> > INFO.mRunUntil: State SENDAPP
15:32:10.529 -> > INFO.mRunUntil: Expected bytes count: 5
15:32:10.622 -> > INFO.mUpdateEngine: State RECVREC
15:32:10.669 -> > INFO.mUpdateEngine: State SENDAPP
15:32:10.854 -> > INFO.mUpdateEngine: State RECVAPP

To send a Json to AWS:

15:32:11.088 -> Sending device data to AWS...
15:32:11.135 -> 0⸮INFO.mUpdateEngine: State SENDAPP
15:32:11.276 -> > INFO.mRunUntil: SSL state changed.
15:32:11.276 -> > INFO.mRunUntil: State SENDAPP
15:32:11.276 -> > INFO.mRunUntil: Expected bytes count: 5
15:32:11.413 -> {"thermostat_id":"thisisatestaswell","timestamp":"02/09/2024 15:32:11","temp_a":"23.81","temp_d":"23.62","temp_f":"23.56","temp_r":"23.81","compressor_status":true,"fans_status":false,"defrosting_status":false,"temp_setpoint":"30.00","temp_defrosting_lower":"-5.00","temp_defrosting_upper":"0.00","safe_delta":"5.00","delay_time":"10000.00","message":"Initializing device...","alarm":false,"manualMode":false,"temp_t":2}> INFO.mRunUntil: SSL state changed.
15:32:11.829 -> > INFO.mRunUntil: State SENDAPP
15:32:11.829 -> > INFO.mRunUntil: Expected bytes count: 5
15:32:11.829 -> Logs data published to AWS IoT.
15:32:11.970 -> Json temp logs: 
15:32:11.970 -> {"thermostat_id":"thisisatestaswell","timestamp":"02/09/2024 15:32:11","temp_a":"23.81","temp_d":"23.62","temp_f":"23.56","temp_r":"23.81","compressor_status":true,"fans_status":false,"defrosting_status":false,"temp_setpoint":"30.00","temp_defrosting_lower":"-5.00","temp_defrosting_upper":"0.00","safe_delta":"5.00","delay_time":"10000.00","message":"Initializing device...","alarm":false,"manualMode":false,"temp_t":2}
15:32:12.342 -> ⸮INFO.mRunUntil: SSL state changed.
15:32:12.482 -> > INFO.mRunUntil: State SENDAPP
15:32:12.482 -> > INFO.mRunUntil: Expected bytes count: 5
15:32:12.623 -> > INFO.mRunUntil: SSL state changed.
15:32:12.623 -> > INFO.mRunUntil: State SENDAPP
15:32:12.623 -> > INFO.mRunUntil: Expected bytes count: 5
15:32:12.669 -> > INFO.mUpdateEngine: State RECVREC
15:32:12.763 -> > INFO.mUpdateEngine: State SENDAPP
15:32:12.901 -> > INFO.mUpdateEngine: State SENDREC
15:32:13.040 -> > INFO.mUpdateEngine: State Connection close
15:32:13.040 -> > WARN.mRunUntil: Terminating because the ssl engine closed.
15:32:13.040 -> > ERROR.flush: Could not flush write buffer!

The flush error message happens after I try to disconnect the connection (client.disconnect()).

Any help would be greatly appreciated! Thank you.

RaymondReddingt0n commented 1 week ago

Please help, the error is persisting, I believe it's the error flushing the write buffer that is causing my program to crash.