odelot / aws-mqtt-websockets

Implementation of a middleware to use AWS MQTT service through websockets, aiming the ESP8266 plataform
GNU Lesser General Public License v3.0
231 stars 67 forks source link

WDT Reset on Pubsubclient Connect #42

Open benwinston opened 6 years ago

benwinston commented 6 years ago

Hi All,

I am trying to connect to AWS IoT / use MQTT running one of the examples from this project on a NodeMCU. While the sketch runs, an error occurs and the serial monitor shows a WDT Soft Reset.

From using the ESPExceptionDecoder, there appears to be an issue with AWSWebSocketClient. I get the following trace from the Decoder:

0x40207c8d: AWSWebSocketClient::available() at /Users/ben/Documents/Arduino/libraries/AWSWebSocketClient/AWSWebSocketClient.cpp line 475 0x40207820: PubSubClient::connect(char const, char const, char const, char const, unsigned char, unsigned char, char const) at /Users/ben/Documents/Arduino/libraries/PubSubClient/src/PubSubClient.cpp line 180 0x402078a4: PubSubClient::connect(char const) at /Users/ben/Documents/Arduino/libraries/PubSubClient/src/PubSubClient.cpp line 106 0x402023ae: generateClientID() at /Users/ben/Documents/Arduino/odelot_pubsubclient_test070818/odelot_pubsubclient_test070818.ino line 57 0x40202480: connect() at /Users/ben/Documents/Arduino/odelot_pubsubclient_test070818/odelot_pubsubclient_test070818.ino line 97 0x402025fc: setup() at /Users/ben/Documents/Arduino/odelot_pubsubclient_test070818/odelot_pubsubclient_test070818.ino line 147 0x40209d20: loop_wrapper() at /Users/ben/Library/Arduino15/packages/esp8266/hardware/esp8266/2.4.1/cores/esp8266/core_esp8266_main.cpp line 118

The error occurred on line this line from my sketch: client.connect(clientID)

And the line that caused the error in AWSWebSocketClient.cpp is the call to "getsize" in the available() method:

//return with there is bytes to consume from the circular buffer (used by mqtt layer)
int AWSWebSocketClient::available(){
  //force websocket to handle it messages
  if (_connected == false)
      return false;
  return bb.getSize ();
}

The code for my sketch follows:

#include <Arduino.h>
#include <Stream.h>

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>

//AWS
#include "sha256.h"
#include "Utils.h"

//WEBSockets
#include <Hash.h>
#include <WebSocketsClient.h>

//MQTT PUBSUBCLIENT LIB 
#include <PubSubClient.h>

//AWS MQTT Websocket
#include "Client.h"
#include "AWSWebSocketClient.h"
#include "CircularByteBuffer.h"

extern "C" {
  #include "user_interface.h"
}

//AWS IOT config, change these:
//Redacted for GitHub post
char wifi_ssid[]       = "";
char wifi_password[]   = "";
char aws_endpoint[]    = "";
char aws_key[]         = "";
char aws_secret[]      = "";
char aws_region[]      = "";
const char* aws_topic  = "";
int port = 443;

//MQTT config
const int maxMQTTpackageSize = 512;
const int maxMQTTMessageHandlers = 1;

ESP8266WiFiMulti WiFiMulti;
AWSWebSocketClient awsWSclient(100);

PubSubClient client(awsWSclient);

//# of connections
long connection = 0;

//generate random mqtt clientID
char* generateClientID () {
  char* cID = new char[23]();
  for (int i=0; i<22; i+=1)
    cID[i]=(char)random(1, 256);
  return cID;
}

//count messages arrived
int arrivedcount = 0;

//callback to handle mqtt messages
void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}

//connects to websocket layer and mqtt layer
bool connect () {

    if (client.connected()) {    
        client.disconnect ();
    }  
    //delay is not necessary... it just help us to get a "trustful" heap space value
    delay (1000);
    Serial.print (millis ());
    Serial.print (" - conn: ");
    Serial.print (++connection);
    Serial.print (" - (");
    Serial.print (ESP.getFreeHeap ());
    Serial.println (")");

    //creating random client id
    char* clientID = generateClientID ();
    client.setServer(aws_endpoint, port);
    if (client.connect(clientID)) {
      Serial.println("connected");     
      return true;
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      return false;
    }
    Serial.println("Afterclientconnectedcheck");
}

//subscribe to a mqtt topic
void subscribe () {
    client.setCallback(callback);
    client.subscribe(aws_topic);
   //subscript to a topic
    Serial.println("MQTT subscribed");
}

//send a message to a mqtt topic
void sendmessage () {
    //send a message   
    char buf[100];
    strcpy(buf, "{\"state\":{\"reported\":{\"on\": false}, \"desired\":{\"on\": false}}}");   
    int rc = client.publish(aws_topic, buf); 
}

void setup() {
    wifi_set_sleep_type(NONE_SLEEP_T);
    Serial.begin (115200);
//    delay (2000);
    Serial.setDebugOutput(1);

    //fill with ssid and wifi password
    WiFiMulti.addAP(wifi_ssid, wifi_password);
    Serial.println ("connecting to wifi");
    while(WiFiMulti.run() != WL_CONNECTED) {
        delay(100);
        Serial.print (".");
    }
    Serial.println ("\nconnected");
//Ben Winston 07.09.18 DELAY BEFORE AWS client invocation
  delay(100);
  Serial.print("Initializing AWS\n");

    //fill AWS parameters    
    awsWSclient.setAWSRegion(aws_region);
    awsWSclient.setAWSDomain(aws_endpoint);
    awsWSclient.setAWSKeyID(aws_key);
    awsWSclient.setAWSSecretKey(aws_secret);
    awsWSclient.setUseSSL(true);
  Serial.print("\nAWS Set up--------------");
    if (connect ()){
      subscribe ();
      sendmessage ();
    }

}

void loop() {
  //keep the mqtt up and running
  if (awsWSclient.connected ()) {    
    Serial.println("invoking client loop");
      client.loop ();
  } else {
    //handle reconnection
    if (connect ()){
      subscribe ();      
    }
  }
}

Any suggestions are appreciated.

srinivastinku commented 5 years ago

I face the same issue, exactly same line. @benwinston Have you found any solution to this problem?

benwinston commented 5 years ago

@srinivastinku No I was unable to resolve.

srinivastinku commented 5 years ago

@benwinston Okay thank you. @odelot Can you please help here.

odelot commented 5 years ago

hi guys, are you using it with arduinoWebSockets v.2.1.0, arduino/esp sdk 2.4.1 and pubsubclient version v2.6?

are you having problem with the example? or have you customize the code already?

This line creates a circular buffer with 1000 bytes that will be used between the websocket layer and the mqtt layer. Maybe try to decrease this number? AWSWebSocketClient awsWSclient(1000);

I've tested with Wemos flash size 4M (1M SPIFFS). I do not have a NodeMCU to test if it is a problem with the board. The example is working here.

On Thu, Apr 11, 2019 at 3:01 AM srinivastinku notifications@github.com wrote:

@benwinston https://github.com/benwinston Okay thank you. @odelot https://github.com/odelot Can you please help here.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/odelot/aws-mqtt-websockets/issues/42#issuecomment-481977151, or mute the thread https://github.com/notifications/unsubscribe-auth/AE34DoiE-l_TUvcuLFJ7OwRyBV9q6i1kks5vfs-pgaJpZM4VKqUk .

asinkxcoswt commented 3 years ago

Hi, I have stuck with the same problem and finally can make it work now. My code is based on this sketch https://github.com/HarringayMakerSpace/awsiot/blob/master/Esp8266AWSIoTExample/Esp8266AWSIoTExample.ino

which is a bit different from the OP's, but maybe it might shares some characteristics, so I think it might be useful to share my discovery.

I found 3 pitfalls that can cause the problem.

  1. You cannot call the pubsubClient.connect(clientId) inside the setup() phase, doing so will cause the error WDT reset as in the OP. If you look at the example sketch, it has the function pubSubCheckConnect() which is called inside loop() to handle all the connection. I don't know the reason but I believe it has some.
void pubSubCheckConnect() {
  if ( ! pubSubClient.connected()) {
    Serial.print("PubSubClient connecting to: "); Serial.print(awsEndpoint);
    while ( ! pubSubClient.connected()) {
      Serial.print(".");
      pubSubClient.connect("ESPthing");
    }
    Serial.println(" connected");
    pubSubClient.subscribe("inTopic");
  }
  pubSubClient.loop();
}
  1. You cannot declare WiFiClient and all the certificate variables inside a function, you have to make it global. For example

This does not work.

PubSubClient* pubSubClient;
void handleMessageReceived(char* topic, byte* payload, unsigned int len);

void setup() {
   WiFiClientSecure wiFiClient;
   const char* awsIotEndpoint = ...
   const String certificatePemCrt =...
   const String privatePemKey =...
   const String caPemCrt =...
   const unsigned int awsIotPort =...

    uint8_t binaryCert[certificatePemCrt.length() * 3 / 4];
    int len = b64decode(certificatePemCrt, binaryCert);
    wiFiClient.setCertificate(binaryCert, len);

    uint8_t binaryPrivate[privatePemKey.length() * 3 / 4];
    len = b64decode(privatePemKey, binaryPrivate);
    wiFiClient.setPrivateKey(binaryPrivate, len);

    uint8_t binaryCA[caPemCrt.length() * 3 / 4];
    len = b64decode(caPemCrt, binaryCA);
    wiFiClient.setCACert(binaryCA, len);

   pubSubClient = new PubSubClient(awsIotEndpoint, 8883, handleMessageReceived, wiFiClient);
}

void loop() {
   pubSubCheckConnect();
}

But this works.

PubSubClient* pubSubClient;
void handleMessageReceived(char* topic, byte* payload, unsigned int len);
WiFiClientSecure wiFiClient;
const char* awsIotEndpoint = ...
const String certificatePemCrt =...
const String privatePemKey =...
const String caPemCrt =...
const unsigned int awsIotPort =...

void setup() {
    uint8_t binaryCert[certificatePemCrt.length() * 3 / 4];
    int len = b64decode(certificatePemCrt, binaryCert);
    wiFiClient.setCertificate(binaryCert, len);

    uint8_t binaryPrivate[privatePemKey.length() * 3 / 4];
    len = b64decode(privatePemKey, binaryPrivate);
    wiFiClient.setPrivateKey(binaryPrivate, len);

    uint8_t binaryCA[caPemCrt.length() * 3 / 4];
    len = b64decode(caPemCrt, binaryCA);
    wiFiClient.setCACert(binaryCA, len);

     pubSubClient = new PubSubClient(awsIotEndpoint, 8883, handleMessageReceived, wiFiClient);
}

void loop() {
   pubSubCheckConnect();
}
  1. Becareful about NTP sync logic. The example sketch has the following method to handle NTP stuff which works fine.

    void setCurrentTime() {
    configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov");
    
    Serial.print("Waiting for NTP time sync: ");
    time_t now = time(nullptr);
    while (now < 8 * 3600 * 2) {
    delay(500);
    Serial.print(".");
    now = time(nullptr);
    }
    Serial.println("");
    struct tm timeinfo;
    gmtime_r(&now, &timeinfo);
    Serial.print("Current time: "); Serial.print(asctime(&timeinfo));
    }

I do not fully understand it but I found that using other NTP solutions such as what described in this tutorial does not work with AWS IOT.

Final Suggestion

If you cannot make it work yet, I suggest that you start over with the example sketch that works. Try changing it a little bit by bit and test if it works before going further.

Hope this helps anyone who stuck with this problem.