4

I'm trying to build my own motorised roller blinds based off this project. It uses the Stepper_28BYJ_48 library.

The problem I'm having is that the motor is either very slow and jittery, going forward 1 step at a time with a slight pause, or it overheats, i'm assuming, and restarts the device (Node MCU) with this motor setup.

I'm especially confused as I can load the motor-on-roller-blind-ws onto my setup and it will turn the motor smoothly each time.

I'm implementing the same code r.e. the motor functionality from what i can see.

I initiate the motor up top:

Stepper_28BYJ_48 small_stepper(D1, D3, D2, D4); //Initiate stepper driver

then in loop I call the step method with the appropriate direction.

Full code is here:

#include <Stepper_28BYJ_48.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoOTA.h>
#include "My_Helper.h"
#include "ConfigHelper.h"

//ALL CONFIG CHANGES ARE LOCATED IN My_Helper.h//
//ALL CONFIG CHANGES ARE LOCATED IN My_Helper.h//
//ALL CONFIG CHANGES ARE LOCATED IN My_Helper.h//
//ALL CONFIG CHANGES ARE LOCATED IN My_Helper.h//
//ALL CONFIG CHANGES ARE LOCATED IN My_Helper.h//
//ALL CONFIG CHANGES ARE LOCATED IN My_Helper.h//
//ALL CONFIG CHANGES ARE LOCATED IN My_Helper.h//
//ALL CONFIG CHANGES ARE LOCATED IN My_Helper.h//
//ALL CONFIG CHANGES ARE LOCATED IN My_Helper.h//

ConfigHelper helper = ConfigHelper();

WiFiClient espClient;
PubSubClient client(espClient);
Stepper_28BYJ_48 small_stepper(D1, D3, D2, D4); //Initiate stepper driver
JsonObject jsonConfig;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  stopPowerToCoils();

  if(!SPIFFS.begin()){
      Serial.println("An Error has occurred while mounting SPIFFS");
      client.publish(coverDebugTopic, "Critical Error!", false);
      return;
  }

  if(helper.loadconfig()){
  
    jsonConfig = helper.getconfig();

    currentPosition = jsonConfig["current"];
    minPosition = jsonConfig["min"];
    maxPosition = jsonConfig["max"];

  } else {
    client.publish(coverDebugTopic, "No config found, using default configuration", false);
  }
  
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
  client.setBufferSize(1024);
  
  if (connectClient()) {
    //Send cover entity details to home assistant on initial connection
    //for auto discovery

    DynamicJsonDocument mqttDevConfig(225);
    mqttDevConfig["name"] = mqttCoverDeviceClientId;
    mqttDevConfig["mf"] = manufacturer;
    mqttDevConfig["mdl"] = model;
    mqttDevConfig["sw"] = softwareVersion;
    mqttDevConfig["ids"][0] = mqttCoverDeviceClientId;
    mqttDevConfig["ids"][1] = mqttResetDeviceClientId;
    mqttDevConfig["ids"][2] = mqttMinDeviceClientId;
    mqttDevConfig["ids"][3] = mqttMaxDeviceClientId;
    
    DynamicJsonDocument mqttCoverConfig(540);
    mqttCoverConfig["name"] = mqttCoverDeviceName;
    mqttCoverConfig["dev_cla"] = mqttCoverDeviceClass;
    mqttCoverConfig["stat_t"] = coverStateTopic;
    mqttCoverConfig["cmd_t"] = coverCommandTopic;
    mqttCoverConfig["opt"] = false;
    mqttCoverConfig["ret"] = true;
    mqttCoverConfig["avty_t"] = coverAvailabilityTopic;
    mqttCoverConfig["uniq_id"] = mqttCoverDeviceClientId;
    mqttCoverConfig["dev"] = mqttDevConfig;

    char coverJson[540];
    serializeJsonPretty(mqttCoverConfig, coverJson);
    client.publish(coverConfigTopic, coverJson, false);
    
    DynamicJsonDocument mqttResetConfig(505);
    mqttResetConfig["name"] = mqttResetDeviceName;
    mqttResetConfig["ic"] = "mdi:lock-reset";
    mqttResetConfig["cmd_t"] = resetCommandTopic;
    mqttResetConfig["stat_t"] = resetStateTopic;
    mqttResetConfig["avty_t"] = coverAvailabilityTopic;
    mqttResetConfig["uniq_id"] = mqttResetDeviceClientId;
    mqttResetConfig["dev"] = mqttDevConfig;

    char resetJson[505];
    serializeJsonPretty(mqttResetConfig, resetJson);
    client.publish(resetConfigTopic, resetJson, false);

    if(minPosition == -1){
      DynamicJsonDocument mqttMinConfig(515);
      mqttMinConfig["name"] = mqttMinDeviceName;
      mqttMinConfig["ic"] = "mdi:blinds-open";
      mqttMinConfig["cmd_t"] = minCommandTopic;
      mqttMinConfig["stat_t"] = minStateTopic;
      mqttMinConfig["avty_t"] = coverAvailabilityTopic;
      mqttMinConfig["uniq_id"] = mqttMinDeviceClientId;
      mqttMinConfig["dev"] = mqttDevConfig;
  
      char minJson[515];
      serializeJsonPretty(mqttMinConfig, minJson);
      client.publish(minConfigTopic, minJson, false);
    } else {
      client.publish(minConfigTopic, "", false);
    }
    

    if(maxPosition == -1){
      DynamicJsonDocument mqttMaxConfig(515);
      mqttMaxConfig["name"] = mqttMaxDeviceName;
      mqttMaxConfig["ic"] = "mdi:blinds";
      mqttMaxConfig["cmd_t"] = maxCommandTopic;
      mqttMaxConfig["stat_t"] = maxStateTopic;
      mqttMaxConfig["avty_t"] = coverAvailabilityTopic;
      mqttMaxConfig["uniq_id"] = mqttMaxDeviceClientId;
      mqttMaxConfig["dev"] = mqttDevConfig;
  
      char maxJson[515];
      serializeJsonPretty(mqttMaxConfig, maxJson);
      client.publish(maxConfigTopic, maxJson, false);
    } else {
      client.publish(maxConfigTopic, "", false);
    }

  }

  //Setup OTA
  {
    ArduinoOTA.setHostname((mqttCoverDeviceClientId + "-" + String(ESP.getChipId())).c_str());

    ArduinoOTA.onStart([]() {
      Serial.println("Start");
    });
    ArduinoOTA.onEnd([]() {
      Serial.println("\nEnd");
    });
    ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
      Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
    });
    ArduinoOTA.onError([](ota_error_t error) {
      Serial.printf("Error[%u]: ", error);
      if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
      else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
      else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
      else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
      else if (error == OTA_END_ERROR) Serial.println("End Failed");
    });
    ArduinoOTA.begin();
  }
}

void loop() {
  //OTA client code
  ArduinoOTA.handle();

  //while connected we send the current door status
  //and trigger relay if we need to
  if (client.connected()) {
    client.loop();

    //only activate motor if we 
    if(motorDirection == OPEN || motorDirection == CLOSE){
      int stepsBetweenMinMax = maxPosition - minPosition;

      if (motorDirection == OPEN) {
        if((minPosition != -1 && currentPosition == minPosition) ||
            stepsBetweenMinMax == 0){
          stopAndPublishState(motorDirection);
        } else {
          small_stepper.step(-1);
          currentPosition = currentPosition - 1;
        }
        
      } else if (motorDirection == CLOSE) {
        if((maxPosition != -1 && currentPosition == maxPosition) ||
            stepsBetweenMinMax == 0){
          stopAndPublishState(motorDirection);
        } else {
          small_stepper.step(1);
          currentPosition = currentPosition + 1;
        }
      }

      Serial.print("Current Position: ");
      Serial.println(currentPosition);
     
        
      DynamicJsonDocument doc(50);
      JsonObject currJson = doc.to<JsonObject>();
      currJson["min"] = minPosition;
      currJson["max"] = maxPosition;
      currJson["current"] = currentPosition;
      publishDebugJson(currJson);
      if(helper.saveconfig(currJson)){
        //client.publish(maxConfigTopic, "", false);
      } else {
        motorDirection = STOP;
        stopPowerToCoils();
      }
    }
    
  } else {
    connectClient();
  }
}

void publishDebugJson(JsonObject json){
  char mqttJson[50];
  serializeJsonPretty(json, mqttJson);
  client.publish(coverDebugTopic, mqttJson, false);
}

void stopAndPublishState(int finishedState){
  stopPowerToCoils();
  motorDirection = STOP;
  const char* endState = opened;
  if(finishedState == CLOSE){
    endState = closed;
  }
  client.publish(coverStateTopic, endState, true);
}

void setup_wifi() {

  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  //Set WiFi mode so we don't create an access point.
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

boolean connectClient() {
  // Loop until we're connected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Check connection
    if (client.connect(mqttCoverDeviceClientId.c_str(), mqtt_user, mqtt_password, coverAvailabilityTopic, 0, true, payloadNotAvailable)) {
      // Make an announcement when connected
      Serial.println("connected");
      client.publish(coverAvailabilityTopic, payloadAvailable, true);

      client.subscribe(coverCommandTopic);
      client.subscribe(coverAvailabilityTopic);
      client.subscribe(resetCommandTopic);
      client.subscribe(minCommandTopic);
      client.subscribe(maxCommandTopic);

      Serial.println("Subscribed to: ");
      Serial.println(coverCommandTopic);
      Serial.println(coverAvailabilityTopic);
      Serial.println(resetCommandTopic);
      Serial.println(minCommandTopic);
      Serial.println(maxCommandTopic);
      return true;
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
      return false;
    }
  }
  return true;
}

void callback(char* topic, byte* message, unsigned int length) {

  String messageStr;

  for (int i = 0; i < length; i++) {
    messageStr += (char)message[i];
  }

  if (String(topic) == coverCommandTopic) {
    Serial.print("Home Assistant Command: ");
    Serial.println(messageStr);

    if (messageStr == payloadStop) {
      stopPowerToCoils();
      motorDirection = STOP;
    }

    if (messageStr == payloadOpen) {
      motorDirection = OPEN;
    }

    if (messageStr == payloadClose) {
      motorDirection = CLOSE;
    }
  }

  if (String(topic) == resetCommandTopic) {
    Serial.print("Home Assistant Config Reset Blinds: ");
    Serial.println(messageStr);

    if(messageStr == "ON"){
      helper.deletefile();
      ESP.reset();
    } 
  }

  if(String(topic) == minCommandTopic){
    Serial.print("Home Assistant Config Set Blinds Min: ");
    Serial.println(messageStr);

    if(messageStr == "ON"){
      minPosition = currentPosition;
      DynamicJsonDocument minDoc(50);
      JsonObject minJson = minDoc.to<JsonObject>();
      minJson["min"] = minPosition;
      minJson["max"] = maxPosition;
      minJson["current"] = currentPosition;
      client.publish(coverDebugTopic, "Min Position Set", false);
      publishDebugJson(minJson);
      if(helper.saveconfig(minJson)){
        client.publish(minConfigTopic, "", false);
        client.publish(coverStateTopic, opened, false);
      }
    }
  }

  if(String(topic) == maxCommandTopic){
    Serial.print("Home Assistant Config Set Blinds Max: ");
    Serial.println(messageStr);

    if(messageStr == "ON"){
      maxPosition = currentPosition;
      DynamicJsonDocument maxDoc(50);
      JsonObject maxJson = maxDoc.to<JsonObject>();
      maxJson["min"] = minPosition;
      maxJson["max"] = maxPosition;
      maxJson["current"] = currentPosition;
      client.publish(coverDebugTopic, "Max Position Set", false);
      publishDebugJson(maxJson);
      if(helper.saveconfig(maxJson)){
        client.publish(maxConfigTopic, "", false);
        client.publish(coverStateTopic, closed, false);
      }
    }
  }
}

void stopPowerToCoils() {
  digitalWrite(D1, LOW);
  digitalWrite(D2, LOW);
  digitalWrite(D3, LOW);
  digitalWrite(D4, LOW);
}
2
  • 1
    My guess would be that you are doing way too many things in the loop, which would explain the delay between steps. Add (local) traces with precise timestamps to see how much time it's spending in the various parts of the loop (and how often the loop is run) to confirm.
    – jcaron
    Oct 7 '20 at 12:06
  • @jcaronThat is exactly what it was! thank you. If you care to put it as an answer i'll glady accept :)
    – jampez77
    Oct 7 '20 at 13:01
4

The loop functions does quite a lot of things, some of them probably a bit lengthy. So it takes a while to execute and get to the next call of loop and the next step.

Add (local) traces with precise timestamps to see how much time it's spending in the various parts of the loop (and how often the loop is run) to confirm and isolate the bits that are problematic.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.