Sinric/Websockets keeps getting stuck. Smarthome help

Hey guys,

I am using a D1 mini as Sinric Host to control my smarthome.
Garage doors (433Mhz) Switches (433MHz) and Windows Shutters (Stepper Motors connected to DRV8825).

This is my biggest project so far and the thing i am most proud of.
But with implementing a second DRV to control the left window shutter independently, my project got buggy.

I spent at least 10 hours trying to debug the code while pulling out my hair, changed many parts back and forth but it refuses to work.
Without external help i am not capable of finishing it.
But i must to be able to live peacefully.

The program (huge, but much to be ignored bc. repetitive) keeps getting stuck.

Let's say i start the // 7.40 Nap AN with google home. The window shutters close according to code and open 7 hours and 40 minutes later, but then The ESP does not respond to new requests anymore. I have to reset it to get it back to work again.

Since today i got the additional bug that the DRV "Oben" at Pins

const int resetSleepOben = D6;
const int StepOben = D7;
const int DirOben = D8;

which is controlling the left window shutter "rollolinks" is still working, but the DRV "Unten at Pins

const int resetSleepUnten = D2;
const int StepUnten = D3;
const int DirUnten = D4;

does not move anymore.

I am sure the problem is in how it is handeling the Websocket code because i just copypastaed it.

Here is the code in pieces with a small descriptions below.
Below those also the full code.

#include <AccelStepper.h>
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266Ping.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsClient.h>
#include <ArduinoJson.h>
#include <StreamString.h>
#include <RCSwitch.h>

boolean napmode = false;
boolean rollolinks = true;
unsigned long napende;


const int ledPin = 2;
const int resetSleepUnten = D2;
const int StepUnten = D3;
const int DirUnten = D4;
const int resetSleepOben = D6;
const int StepOben = D7;
const int DirOben = D8;
const int transmitterPin = D5;


IPAddress ip (192, 168, 178, 83);


RCSwitch mySwitch = RCSwitch();
AccelStepper stepperUnten = AccelStepper(1, StepUnten, DirUnten); //STEP, DIR
AccelStepper stepperOben = AccelStepper(1, StepOben, DirOben); //STEP, DIR

ESP8266WiFiMulti WiFiMulti;
WebSocketsClient webSocket;
WiFiClient client;

#define MyApiKey "XXX"
#define MySSID "It hurts when IP"
#define MyWifiPassword "XXX"

#define HEARTBEAT_INTERVAL 300000 // 5 Minutes 

uint64_t heartbeatTimestamp = 0;
boolean isConnected = false;

Importing all libraries. rollolinks=true makes the left windows shutter move with the rest. "Unten" Corresponds to the DRV that has 2 window shutters, "Oben" to the DRV for the left window shutter.

void turnOn(String deviceId) {
  if (deviceId == "5c3cbebdbd3a297e44c2b4ad") // eins
  {  
    senden_pollin("000000000000010101010001", "eins");
  } 
  else if (deviceId == "5c2ba92f796c411478dfc24b") // zwei
  { 
    senden_pollin("000000000001000101010001", "zwei");
  }
  else if (deviceId == "5c3cc0c3bd3a297e44c2b4b0") // drei
  { 
    senden_pollin("000000000001010001010001", "drei");
  }
  else if (deviceId == "5c2ba940796c411478dfc24f") // vier
  { 
    senden_pollin("000000000001010100010001", "vier");
  }
  else if (deviceId == "5c2ba940796c411478dfc24f") // fünf
  { 
    senden_pollin("010000000000010101010001", "fünf");
  }

First turn on strings for Power plug sockets. all working.

//      ROLLOS    ANFANG

  else if (deviceId == "5e9398923fe9eb7fe2524f5a") // rollolinks  AN
  {
  rollolinks = true;
  blinken(200,1);
  }
  
  else if (deviceId == "5e5f82534fcc8d0db3409269") // rollos AN
  {
    rollosBewegen(47000);
  }
  else if (deviceId == "5e66578ecafbba4ea4f42cbc") // Rolloreset jetzt AN
  {
    stepperUnten.setCurrentPosition(47000);
    if (rollolinks) {
      stepperOben.setCurrentPosition(47000);
    }
    rollosBewegen(0);
  }
  else if (deviceId == "5e85581784b8e724dc0f2d04") // 0.20 Nap AN
  {
    napmode = true;
    napende = (millis()+1200000-8000);
    rollosBewegen(47000);
  }  
  else if (deviceId == "5e85583984b8e724dc0f2d1b") // 1.40 Nap AN
  {
    napmode = true;
    napende = (millis()+6000000-8000);
    rollosBewegen(47000);
  }
  else if (deviceId == "5e8557cb84b8e724dc0f2cd3") // 6.10 Nap AN
  {
    napmode = true;
    napende = (millis()+22200000-8000);
    rollosBewegen(47000);
  }
  else if (deviceId == "5e85580b84b8e724dc0f2cfa") // 7.40 Nap AN
  {
    napmode = true;
    napende = (millis()+27600000-8000);
    rollosBewegen(47000);
  }
  else if (deviceId == "5e86275fce60582b5f88bf98") // 9.10 Nap AN
  {
    napmode = true;
    napende = (millis()+33000000-8000);
    rollosBewegen(47000);
  }

//      ROLLOS    ENDE

Turn on strings for the window shutters.
"Rolloreset" opens the rollos in case the ESP gets restarted while they are closed.
The "Nap" codes close the shutters and create a timestamp in the future for them to be opened again. This is handled in the loop.

  else if (deviceId == "5c3cbc9bbd3a297e44c2b4a2") // rechner
  {
    senden_pollin("010101010100010101010001", "rechner");
  }
  else if (deviceId == "5c3cbc4fbd3a297e44c2b4a0") // system
  {
    senden_pollin("000000000000010101010001", "eins");
    delay(1600);
    senden_pollin("010101010100010101010001", "rechner");
  }

  
  else if (deviceId == "5c44701ae20cf81e2fc7d2c9") // tor
  {
    senden_hof("11110011001111101", "gartentor");
  }
  else if (deviceId == "5c447037e20cf81e2fc7d2cb") // hoflicht
  {
    senden_hof("11110011001111000", "hoflicht");
  }
  else if (deviceId == "5c4473c7e20cf81e2fc7d2d0") // omator
  {
    senden_hof("11110011001111100", "omator");
  }
  else if (deviceId == "5c4473dde20cf81e2fc7d2d2") // werkstatttor
  {
    senden_hof("11110011001111001", "werkstatttor");
  }
  else if (deviceId == "5c44d09ef7e36f2beb4ee743") // hoflicht blinken
  {
    senden_hof("11110011001111000", "hoflicht");
    delay(2400);
    senden_hof("11110011001111000", "hoflicht");
    delay(2400);
    senden_hof("11110011001111000", "hoflicht");
    delay(1200);
    senden_hof("11110011001111000", "hoflicht");
    delay(1200);
    senden_hof("11110011001111000", "hoflicht");
    delay(1200);
    senden_hof("11110011001111000", "hoflicht");
    delay(1200);
    senden_hof("11110011001111000", "hoflicht");
    delay(400);
    senden_hof("11110011001111000", "hoflicht");
    delay(400);
    senden_hof("11110011001111000", "hoflicht");
    delay(400);
    senden_hof("11110011001111000", "hoflicht");
    delay(400);
    senden_hof("11110011001111000", "hoflicht");
    delay(400);
    senden_hof("11110011001111000", "hoflicht");
    delay(400);
    senden_hof("11110011001111000", "hoflicht");
    delay(400);
    senden_hof("11110011001111000", "hoflicht");
    delay(400);
  }  
  else if (deviceId == "5c3f5f57fa93fa46cefa20ec") // verlassen routine tor
  {
    senden_pollin("010101010100010101010001", "rechner");
    Serial.print("Pinge PC \n");
    if (Ping.ping(ip) == false) {
      delay(4000);
      senden_pollin("010101010100010101010001", "rechner");
      }
    delay(500);
    senden_hof("11110011001111000", "hoflicht");
    delay(1000);
    senden_hof("11110011001111101", "gartentor");
    for (int i=0; i <= 30; i++){
      
      if (Ping.ping(ip) == false) {
        Serial.print("keine Antwort \n");
        i = 30;
      }
      else {
        Serial.print("Antwort \n");
        blinken(50,1);
        if (i == 4){
          senden_pollin("010101010100010101010001", "rechner");
          blinken(50,4);
        }
      }

    }
    
    breathe();
    senden_pollin("000000000000010101010100", "eins");
  }
  
  else if (deviceId == "5c44d10ef7e36f2beb4ee749") // verlassen routine kein tor
  {
    senden_pollin("010101010100010101010001", "rechner");
    Serial.print("Pinge PC \n");
    IPAddress ip (192, 168, 178, 83);
    if (Ping.ping(ip) == false) {
      delay(4000);
      senden_pollin("010101010100010101010001", "rechner");
      }
    delay(500);
    senden_hof("11110011001111000", "hoflicht");
    for (int i=0; i <= 30; i++){
      
      if (Ping.ping(ip) == false) {
        Serial.print("keine Antwort \n");
        i = 30;
      }
      else {
        Serial.print("Antwort \n");
        blinken(50,1);
        if (i == 4){
          senden_pollin("010101010100010101010001", "rechner");
          blinken(50,4);
        }
      }

    }
    
    breathe();
    senden_pollin("000000000000010101010100", "eins");
  }

  else
  {
    Serial.print("Turn on for unknown device id: ");
    Serial.println(deviceId);    
  }     
}

Some other devices. All fine.

The code was too big for one message. Wow :slight_smile:

void turnOff(String deviceId) {
  if (deviceId == "5c3cbebdbd3a297e44c2b4ad") // eins
  {
    senden_pollin("000000000000010101010100", "eins");
  }
  else if (deviceId == "5c2ba92f796c411478dfc24b") // zwei
  {
    senden_pollin("000000000001000101010100", "zwei");
  }
  else if (deviceId == "5c3cc0c3bd3a297e44c2b4b0") // drei
  {
    senden_pollin("000000000001010001010100", "drei");
  }
  else if (deviceId == "5c2ba940796c411478dfc24f") // vier
  {
    senden_pollin("000000000001010100010100", "vier");
  }
  else if (deviceId == "5c2ba940796c411478dfc24f") // fünf
  {
    senden_pollin("010000000000010101010100", "fünf");
  }
  
  
  else if (deviceId == "5e5f82534fcc8d0db3409269") // rollos AUS
  {
    rollosBewegen(0);
  }
    
  else if (deviceId == "5e9398923fe9eb7fe2524f5a") // rollolinks  AUS
  {
  rollolinks = false;
  blinken(200,1);
  }
    
  else if (deviceId == "5e66578ecafbba4ea4f42cbc") // Rolloreset jetzt AUS
  {
    stepperUnten.setCurrentPosition(0);
    if (rollolinks){
      stepperOben.setCurrentPosition(0);
    }
    rollosBewegen(47000);
  }


  

  
  else {
    Serial.print("Turn off for unknown device id: ");
    Serial.println(deviceId);    
  }     
}

Same for turn off strings. Some removed to not exceed message character limit.

void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
  switch(type) {
    case WStype_DISCONNECTED:
      isConnected = false;    
      Serial.printf("[WSc] Webservice disconnected from sinric.com!\n");
      break;
    case WStype_CONNECTED: {
      isConnected = true;
      Serial.printf("[WSc] Service connected to sinric.com at url: %s\n", payload);
      Serial.printf("Waiting for commands from sinric.com ...\n");        
      }
      break;
    case WStype_TEXT: {
        Serial.printf("[WSc] get text: %s\n", payload);
        // Example payloads

        // For Switch or Light device types
        // {"deviceId": xxxx, "action": "setPowerState", value: "ON"} // https://developer.amazon.com/docs/device-apis/alexa-powercontroller.html

        // For Light device type
        // Look at the light example in github
#if ARDUINOJSON_VERSION_MAJOR == 5
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject((char*)payload);
#endif
#if ARDUINOJSON_VERSION_MAJOR == 6        
        DynamicJsonDocument json(1024);
        deserializeJson(json, (char*) payload);      
#endif        
        String deviceId = json ["deviceId"];     
        String action = json ["action"];
        
        if(action == "setPowerState") { // Switch or Light
            String value = json ["value"];
            if(value == "ON") {
                turnOn(deviceId);
            } else {
                turnOff(deviceId);
            }
        }
        else if(action == "action.devices.commands.OnOff") { // Switch 
            String value = json ["value"]["on"];
            Serial.println(value); 
            
            if(value == "true") {
                turnOn(deviceId);
            } else {
                turnOff(deviceId);
            }
        }
        else if (action == "SetTargetTemperature") {
            String deviceId = json ["deviceId"];     
            String action = json ["action"];
            String value = json ["value"];
        }
        else if (action == "test") {
            Serial.println("[WSc] received test command from sinric.com");
        }
      }
      break;
    case WStype_BIN:
      Serial.printf("[WSc] get binary length: %u\n", length);
      break;
  }
}

Web socket code i do not understand. Might be buggy

void setup() {
  Serial.begin(115200);
  stepperUnten.setMaxSpeed(1600.0);  
  stepperUnten.setAcceleration(4500);
  stepperOben.setMaxSpeed(1600.0);  
  stepperOben.setAcceleration(4500);
  stepperOben.setPinsInverted(true);

  pinMode(ledPin, OUTPUT);
  pinMode(resetSleepUnten, OUTPUT);
  pinMode(StepUnten, OUTPUT);
  pinMode(DirUnten, OUTPUT);
  pinMode(DirOben, OUTPUT);
  pinMode(resetSleepOben, OUTPUT);
  pinMode(StepOben, OUTPUT);
  pinMode(transmitterPin, OUTPUT);
  
  RCSwicthSetup();
  
  WiFiMulti.addAP(MySSID, MyWifiPassword);
  Serial.println();
  Serial.print("Connecting to Wifi: ");
  Serial.println(MySSID);  

  // Waiting for Wifi connect
  while(WiFiMulti.run() != WL_CONNECTED) {
    blinken(250, 1);
    Serial.print(".");
  }
  if(WiFiMulti.run() == WL_CONNECTED) {
    Serial.println("");
    Serial.print("WiFi connected. ");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
    breathe();
  }

  // server address, port and URL
  webSocket.begin("iot.sinric.com", 80, "/");

  // event handler
  webSocket.onEvent(webSocketEvent);
  webSocket.setAuthorization("apikey", MyApiKey);
  
  // try again every 5000ms if connection has failed
  webSocket.setReconnectInterval(5000);   // If you see 'class WebSocketsClient' has no member named 'setReconnectInterval' error update arduinoWebSockets
}

Pretty much self-explanatory

void loop() {
  webSocket.loop();
  
  if (stepperUnten.distanceToGo() == 0){
    digitalWrite(resetSleepUnten,LOW);
    digitalWrite(ledPin,HIGH);
  }
  else {
    stepperUnten.run();
    digitalWrite(ledPin,LOW);
  }
  if (stepperOben.distanceToGo() == 0){
    digitalWrite(resetSleepOben,LOW);
    digitalWrite(ledPin,HIGH);
  }
  else {
    stepperOben.run();
    digitalWrite(ledPin,LOW);
  }
  

  
  if (napmode == true){
    if (millis() >= napende){
      napmode = false;
      napende = 0;
      rollosBewegen(0);
    }
  }
  
  if(isConnected) {
      uint64_t now = millis();
      
      // Send heartbeat in order to avoid disconnections during ISP resetting IPs over night. Thanks @MacSass
      if((now - heartbeatTimestamp) > HEARTBEAT_INTERVAL) {
          heartbeatTimestamp = now;
          webSocket.sendTXT("H");          
      }
  }
}

loop that handles the DRV movements and looks if the end of the nap is reached. Might be buggy

void RCSwicthSetup() {
 mySwitch.enableTransmit(transmitterPin);

 // set protocol (default is 1, will work for most outlets)
 mySwitch.setProtocol(1);

 // Optional set pulse length.
mySwitch.setPulseLength(300);
 
}

void breathe() {
  for (int i=1;i<1200;i++){
    delayMicroseconds(1200-i);
    digitalWrite(ledPin, LOW);
    delayMicroseconds(i);
    digitalWrite(ledPin, HIGH);
    delay(0);
  }
  for (int i=1;i<800;i++){
    delayMicroseconds(i);
    digitalWrite(ledPin, LOW);
    delayMicroseconds(800-i);
    digitalWrite(ledPin, HIGH);
    delay(0);
  }
}
void blinken(int puls, int anzahl) {
  for (int j=1;j<=anzahl;j++){
  digitalWrite(ledPin, LOW);
  delay(puls);
  digitalWrite(ledPin, HIGH);
  delay(puls);
  }
}


void senden_hof(const char* code, String device){
  Serial.print("Schalte ");
  Serial.print(device);
  mySwitch.setPulseLength(570);
  mySwitch.send(code);
  blinken(200, 1);
}

void senden_pollin(const char* code, String device){
  Serial.print("Schalte ");
  Serial.print(device);
  mySwitch.setPulseLength(300);
  mySwitch.send(code);
  blinken(200, 1);
}

void rollosBewegen(unsigned int zielposition){
  blinken(200,1);
  digitalWrite(resetSleepUnten,HIGH);
  stepperUnten.moveTo(zielposition);
  if (rollolinks) {
    digitalWrite(resetSleepOben,HIGH);
    stepperOben.moveTo(zielposition);
  }
}

Rest of code. "rollosBewegen" always moves the DRV "Unten" and the DRV "Oben" only if rollolinks is enabled.

I attached the full code if someone wants to look at it.
If anything is unclear, i will describe anything as detailed as i can.

Any answer is hugely appreciated.

Rollos_Sinric.ino (15.3 KB)

I'd guess that you have a pin conflict. Do your basic functions work ok if you get rid of the wifi stuff?

I'm pretty much sure this is no pin conflict.

Sometimes the shutters are working. I just woke up, tried to open the shutters and all of them (also DRV unten) worked. Afterwards i tried to open them again and only the left (DRV oben) worked, the right shutters stuck again.