vshymanskyy / TinyGSM

A small Arduino library for GSM modules, that just works
GNU Lesser General Public License v3.0
1.91k stars 713 forks source link

Multiple clients with TINY_GSM_MODEM_SIM7000SSL fails on http connection for file download #567

Open Ollebolle opened 2 years ago

Ollebolle commented 2 years ago

[x] I have read the Troubleshooting section of the ReadMe

What type of issues is this?

[ ] Request to support a new module

[ ] Bug or problem compiling the library [x] Bug or issue with library functionality (ie, sending data over TCP/IP) [ ] Question or request for help

What are you working with?

Modem: SIM7000 Main processor board: TTGO ESP32 TinyGSM version: 0.11.4 Code:

// Set serial for debug console (to the Serial Monitor, default speed 115200)
#define SerialMon Serial

// Set serial for AT commands (to the module)
// Use Hardware Serial on Mega, Leonardo, Micro
#define SerialAT Serial1
#define TINY_GSM_MODEM_SIM7000SSL
#define TINY_GSM_RX_BUFFER 512 // Set RX buffer to 1Kb
#define SerialAT Serial1

// See all AT commands, if wanted
#define DUMP_AT_COMMANDS

// set GSM PIN, if any
#define GSM_PIN ""

// Your GPRS credentials, if any
const char apn[] = ""; //SET TO YOUR APN
const char gprsUser[] = "";
const char gprsPass[] = "";

#include <TinyGsmClient.h>
#include <SPI.h>
#include <SD.h>
#include <Ticker.h>
#include <PubSubClient.h>
#include <Preferences.h>
#include "GSMFOTAUpdate.h" 

#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger);
#else
TinyGsm modem(SerialAT);
#endif

#define uS_TO_S_FACTOR 1000000ULL // Conversion factor for micro seconds to seconds
#define TIME_TO_SLEEP 60          // Time ESP32 will go to sleep (in seconds)

#define UART_BAUD 9600
#define PIN_DTR 25
#define PIN_TX 27
#define PIN_RX 26
#define PWR_PIN 4

#define SD_MISO 2
#define SD_MOSI 15
#define SD_SCLK 14
#define SD_CS 13
#define LED_PIN 12
#define PIN_BAT 35 /* read battery voltage, Disabled when using USB - ONLY INPUT PIN */

#define SerialMon Serial
// MQTT details
const char *broker = "";

TinyGsmClientSecure client(modem, 0);
TinyGsmClient clientOta(modem, 1);
PubSubClient mqtt(client);

// OTA STUFF
const int port = 80;                                          // Non https. For HTTPS 443. As of today, HTTPS doesn't work.
// const char otaUrl[] = "/testbin.ino.bin"; // Host => bucket-name.s3.region.amazonaws.com
const char otaServer[]   = "*.amazonaws.com";  
long contentLength;                                       // How many bytes of data the .bin is
bool isValidContentType = false; 

boolean mqttConnect()
{
  SerialMon.print("Connecting to ");
  SerialMon.print(broker);

  // Connect to MQTT Broker
  boolean status = mqtt.connect(id.c_str(), id.c_str(), pwd.c_str());

  if (status == false)
  {
    SerialMon.println(" fail");
    return false;
  }
  SerialMon.println(" success");
  mqtt.publish(topicConnection, hubId);
  return mqtt.connected();
}

void enableGPS(void)
{
  Serial.println("Turning on GPS power");
  modem.sendAT("+SGPIO=0,4,1,1");
  delay(2000);
  Serial.println("Enabling GPS");
  modem.enableGPS();
}

void disableGPS(void)
{
  modem.sendAT("+SGPIO=0,4,1,0");
  modem.disableGPS();
}

void modemPowerOn()
{
  pinMode(PWR_PIN, OUTPUT);
  digitalWrite(PWR_PIN, LOW);
  delay(1000); //Datasheet Ton mintues = 1S
  digitalWrite(PWR_PIN, HIGH);
}

void modemPowerOff()
{
  pinMode(PWR_PIN, OUTPUT);
  digitalWrite(PWR_PIN, LOW);
  delay(1500); //Datasheet Ton mintues = 1.2S
  digitalWrite(PWR_PIN, HIGH);
}

void modemRestart()
{
  modemPowerOff();
  delay(1000);
  modemPowerOn();
}

void init()
{
  modemRestart();

  SerialAT.begin(UART_BAUD, SERIAL_8N1, PIN_RX, PIN_TX);

  pinMode(LED_PIN, OUTPUT);

  delay(10000);

  String res;

  Serial.println("========INIT========");

  if (!modem.init())
  {
    modemRestart();
    delay(2000);
    Serial.println("Failed to restart modem, attempting to continue without restarting");
    return;
  }

  Serial.println("========SIMCOMATI======");
  modem.sendAT("+SIMCOMATI");
  modem.waitResponse(1000L, res);
  res.replace(GSM_NL "OK" GSM_NL, "");
  Serial.println(res);
  res = "";
  Serial.println("=======================");

  Serial.println("=====Preferred mode selection=====");
  modem.sendAT("+CNMP?");
  if (modem.waitResponse(1000L, res) == 1)
  {
    res.replace(GSM_NL "OK" GSM_NL, "");
    Serial.println(res);
  }
  res = "";
  Serial.println("=======================");

  Serial.println("=====Preferred selection between CAT-M and NB-IoT=====");
  modem.sendAT("+CMNB?");
  if (modem.waitResponse(1000L, res) == 1)
  {
    res.replace(GSM_NL "OK" GSM_NL, "");
    Serial.println(res);
  }
  res = "";
  Serial.println("=======================");

  String name = modem.getModemName();
  Serial.println("Modem Name: " + name);

  String modemInfo = modem.getModemInfo();
  Serial.println("Modem Info: " + modemInfo);

  // Unlock your SIM card with a PIN if needed
  if (GSM_PIN && modem.getSimStatus() != 3)
  {
    modem.simUnlock(GSM_PIN);
  }

  modem.gprsConnect(apn, gprsUser, gprsPass);

  /*
        2 Automatic
        13 GSM only
        38 LTE only
        51 GSM and LTE only
    * * * */
  bool response;

  do
  {
    response = modem.setNetworkMode(38);
    delay(500);
  } while (!response);

  /*
        1 CAT-M
        2 NB-Iot
        3 CAT-M and NB-IoT
    * * */
  do
  {
    response = modem.setPreferredMode(3);
    delay(500);
  } while (!response);

  digitalWrite(LED_PIN, HIGH);

  Serial.println();
  Serial.println("Device is connected .");
  Serial.println();

  // Serial.println("=====Inquiring UE system information=====");
  // modem.sendAT("+CPSI?");
  // if (modem.waitResponse(1000L, res) == 1) {
  //     res.replace(GSM_NL "OK" GSM_NL, "");
  //     Serial.println(res);
  // }

  SerialMon.print("Waiting for network...");
  if (!modem.waitForNetwork())
  {
    SerialMon.println(" fail");
    delay(10000);
    return;
  }
  SerialMon.println(" success");

  // MQTT Broker setup
  mqtt.setServer(broker, 8883);
  mqtt.setKeepAlive(90);
  mqtt.setCallback(mqttCallback);
  enableGPS();
}

// used to extract header value from headers for ota update
String getHeaderValue(String header, String headerName) {
  return header.substring(strlen(headerName.c_str()));
}

void GSMOTA() {
  Serial.println("\n♦ ♦ UPDATE AVALIBLE ♦ ♦\n");

  Serial.println("Connecting to: " + String(otaServer)); 

  while (!clientOta.connect(otaServer, port)) {
    Serial.print("*");
  }

  Serial.print("Connected to OTA server: ");
  Serial.println(clientOta.connect(otaServer, port));

  Serial.println("Fetching bin file at: " + String(otaUrl));    // tells where its going to get the .bin and the name its looking for

  // Get the contents of the bin file
  clientOta.print(String("GET ") + otaUrl + " HTTP/1.1\r\n" +
                  "Host: " + String(otaServer) + "\r\n" +
                  "Cache-Control: no-cache\r\n" +
                  "Connection: keep-alive\r\n\r\n");

  unsigned long timeout = millis();
  while (clientOta.available() == 0) {
    if (millis() - timeout > 120000) {
      Serial.println("clientOta Timeout !");
      clientOta.stop();
      return;
    }
  }
  // Once the response is available start reading reply
  while (clientOta.available()) {

    // read line till /n
    String line = clientOta.readStringUntil('\n');
    // remove space, to check if the line is end of headers
    line.trim();

    // if the the line is empty, this is end of headers
    // break the while and feed the remaining clientOta to the Update.writeStream();
    if (!line.length()) {
      // headers ended
      break; // and get the OTA update started
    }

    // Check if the HTTP Response is 200 if not break and Exit Update
    if (line.startsWith("HTTP/1.1")) {
      if (line.indexOf("200") < 0) {
        Serial.println("Got a non 200 status code from otaServer. Exiting OTA Update.");
        break;
      }
    }

    // extracting headers starting with content length
    if (line.startsWith("Content-Length: ")) {
      contentLength = atol((getHeaderValue(line, "Content-Length: ")).c_str());
      Serial.println("Got " + String(contentLength) + " bytes from otaServer");
    }

    // Next, the content type
    if (line.startsWith("Content-Type: ")) {
      String contentType = getHeaderValue(line, "Content-Type: ");
      Serial.println("Got " + contentType + " payload.");
      if (contentType == "application/octet-stream") {
        isValidContentType = true;
      }
    }
  }

  // Check what is the contentLength and if content type is `application/octet-stream`
  Serial.println("contentLength : " + String(contentLength) + ", isValidContentType : " + String(isValidContentType));

  // check contentLength and content type
  if (contentLength && isValidContentType) {
    // Check if there is enough to OTA Update
    bool canBegin = Update.begin(contentLength);

    // If yes, begin
    if (canBegin) {
      Serial.println("Begin GSMFOTA update. This may take 2 - 25 minutes to complete depending on the connection and file size. Patience!!!");
      Serial.println("Firmware updating...");
      size_t written = Update.writeStream(clientOta);

      if (written == contentLength) {
        Serial.println("Written : " + String(written) + " successfully");
      } else {
        Serial.println("Written only : " + String(written) + "/" + String(contentLength));
      }

      if (Update.end()) {
        Serial.println("OTA done!");
        if (Update.isFinished()) {
          Serial.println("Update successfully completed. Rebooting to activate.");
          ESP.restart();
        } else {
          Serial.println("Update not finished... Something went wrong!");
          ESP.restart();
        }
      } else {
        Serial.println("Error Occurred! \nError: " + String(Update.getError()) + " " + String(Update.errorString()) );
        Serial.println("Will try to update again at a different time.\n");
        clientOta.flush();
        ESP.restart();
      }
    } else {
      // not enough space to begin OTA, check .bin size
      Serial.println("Not enough space to begin OTA\n");
      clientOta.flush();
      ESP.restart();
    }
  } else {
    Serial.println("There was no content in the response\n");
    clientOta.flush();
    ESP.restart();
  }
}

void setup()
{
  // Set console baud rate
  SerialMon.begin(115200);

  delay(500);

  // Set LED OFF
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, HIGH);
  pinMode(PIN_BAT, INPUT);

  init();
  delay(5000);
  GSMOTA();
}

void loop()
{

}

Scenario, steps to reproduce

I'm trying to run OTA through http and not https. My MQTT connection via SSL works perfectly fine, but using a separate TinyGsmClient for the OTA connection fails while attempting to connect to the server. However, if I instead define TINY_GSM_MODEM_SIM7000 the get request for the binary succeeds without issue, but then I will have no way of having MQTT connection with SSL...

Expected result

I'm expecting both TinyGsmClientSecure and TinyGsmClient to work together.

Actual result

clientOta fails to even connect to the server and clientOta.connect(otaServer, port) returns 0

Debug and AT command log


♦ ♦ UPDATE AVALIBLE ♦ ♦

Connecting to: *.amazonaws.com

OK
AT+CACLOSE=1

ERROR
AT+CACID=1

OK
AT+CASSLCFG=1,ssl,0

OK
AT+CAOPEN=1,"*.amazonaws.com",80
*AT+CACLOSE=1
AT+CACID=1
SRGDamia1 commented 2 years ago

I'm combing through months of old issues. Were you able to resolve this?

gotnull commented 1 year ago

Anyone get an update on this?