Direct Drive 4 Wire negative direction

113 views
Skip to first unread message

Wltr

unread,
Sep 11, 2024, 5:16:05 PM9/11/24
to accelstepper
i"m direct driving a 4 wire servo directly from an arduino. Only positive move or moveto commands work if a negative number is put in it still physically moves positive. Its wired correct because it works with arduino stepper library

Geoff Smith

unread,
Sep 11, 2024, 7:39:35 PM9/11/24
to accels...@googlegroups.com

Post your full code here and someone will respond with assistance.

--
You received this message because you are subscribed to the Google Groups "accelstepper" group.
To unsubscribe from this group and stop receiving emails from it, send an email to accelstepper...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/accelstepper/7bb75a1f-f3a7-42d9-b41c-4225efecee9an%40googlegroups.com.

 

Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted

Brian Baidal

unread,
Jul 8, 2025, 4:08:19 PMJul 8
to accelstepper
I'm having the same issue as the OP: when pairing a couple of 28BYJ-48 stepper motors to an ESP32-C3 Super Mini board using ULN-2003 driver boards (blue) they just go in one direction, even when I set an absolute position lower than the current position.

I also had to set a negative speed when trying to go backwards because otherwise the current position keeps increasing forever instead of decreasing until eventually reaching the target position, and that doesn't have any actual effect of the physical direction (the motor spins in the same direction regardless of the speed being positive or negative).

Testing them with Arduino's Stepper library and ESPHome's Stepper component they work fine in both directions.

Here's the full code in case that helps:

#include <AccelStepper.h>
#include <esp_sleep.h>
#include <PubSubClient.h>
#include <WiFi.h>


#define CONNECTION_RETRY_DELAY 500 // In milliseconds
#define COVER_UPDATE_DELAY 1000 // In milliseconds
#define LOOP_DELAY 0
#define MQTT_BROKER "192.168.50.4"
#define MQTT_CLIENT_ID "persiana-dormitorio"
#define MQTT_PORT 1883
#define MQTT_TOPIC_COMMAND "persiana-dormitorio/command"
#define MQTT_TOPIC_POSITION "persiana-dormitorio/position"
#define MQTT_TOPIC_STATE "persiana-dormitorio/state"
#define MQTT_KEEP_ALIVE 60 // In seconds
#define MQTT_PASSWORD "***REDACTED***"
#define MQTT_UPDATE_DELAY 100
#define MQTT_USERNAME "***REDACTED***"
#define STEPPER_1_PIN_A 7 // Blue
#define STEPPER_1_PIN_B 6 // Green
#define STEPPER_1_PIN_C 5 // Yellow
#define STEPPER_1_PIN_D 1 // Black
#define STEPPER_2_PIN_A 4 // Black
#define STEPPER_2_PIN_B 3 // Yellow
#define STEPPER_2_PIN_C 20 // Green
#define STEPPER_2_PIN_D 10 // Blue
#define STEPPER_MAX_POSITION 14336 // 7 revs * 2048 steps
#define STEPPER_SPEED 256 // In steps/s
#define WAKEUP_PIN 2
#define WIFI_GATEWAY "192.168.50.1"
#define WIFI_PASSWORD "***REDACTED***"
#define WIFI_SSID "***REDACTED***"
#define WIFI_STATIC_IP "192.168.50.191"
#define WIFI_SUBNET "255.255.255.0"

#define COVER_ACTION_CLOSING -1
#define COVER_ACTION_OPENING 1
#define COVER_COMMAND_CLOSE "close"
#define COVER_COMMAND_OPEN "open"
#define COVER_COMMAND_STOP "stop"
#define COVER_COMMAND_TOGGLE "toggle"
#define COVER_STATE_CLOSED "closed"
#define COVER_STATE_CLOSING "closing"
#define COVER_STATE_OPEN "open"
#define COVER_STATE_OPENING "opening"

RTC_DATA_ATTR int lastStepperPosition = 0;
RTC_DATA_ATTR int lastCoverAction = COVER_ACTION_CLOSING;

int currentTime = 0;
int lastCoverUpdateTime = 0;
int lastMqttUpdateTime = 0;
int lastDebug = 0; // TODO: Remove this

IPAddress mqttBroker(MQTT_BROKER);
IPAddress wifiStaticIp(WIFI_STATIC_IP);
IPAddress wifiGateway(WIFI_GATEWAY);
IPAddress wifiSubnet(WIFI_SUBNET);

const int numSteppers = 2;
bool isStepperEnabled[numSteppers] { false, false };
AccelStepper steppers[numSteppers] {
  {AccelStepper::FULL4WIRE, STEPPER_1_PIN_A, STEPPER_1_PIN_B, STEPPER_1_PIN_C, STEPPER_1_PIN_D, false},
  {AccelStepper::FULL4WIRE, STEPPER_2_PIN_A, STEPPER_2_PIN_B, STEPPER_2_PIN_C, STEPPER_2_PIN_D, false}
};

WiFiClient wifi;
PubSubClient mqtt(wifi);

void publishCoverStatus() {
  String coverState;
  int coverPosition = ceil((float)steppers[0].currentPosition() * (float)100 / (float)STEPPER_MAX_POSITION);

  mqtt.publish(MQTT_TOPIC_POSITION, String(coverPosition).c_str(), true);

  if (steppers[0].distanceToGo() > 0) {
    coverState = COVER_STATE_OPENING;
  } else if (steppers[0].distanceToGo() > 0) {
    coverState = COVER_STATE_CLOSING;
  } else if (coverPosition == 0) {
    coverState = COVER_STATE_CLOSED;
  } else {
    coverState = COVER_STATE_OPEN;
  }

  mqtt.publish(MQTT_TOPIC_STATE, String(coverState).c_str(), true);

  lastCoverUpdateTime = millis();
}

void mqttCallback(char* topic, byte* message, unsigned int length) {
  if (!length) {
    return;
  }

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

  Serial.print("Received MQTT message '");
  Serial.print(payload);
  Serial.print("' on topic ");
  Serial.println(topic);

  if (String(topic) == MQTT_TOPIC_COMMAND) {
    mqtt.publish(MQTT_TOPIC_COMMAND, NULL, true);

    if (payload == COVER_COMMAND_OPEN) moveCoverTo(100);
    else if (payload == COVER_COMMAND_CLOSE) moveCoverTo(0);
    else if (payload == COVER_COMMAND_TOGGLE) toggleCover();
    else if (payload == COVER_COMMAND_STOP) stopCover();
    else if (isDigit(payload[0])) moveCoverTo(payload.toInt());
  }
}

void moveCoverTo(int targetPercent) {
  if (targetPercent > 100) {
    targetPercent = 100;
  } else if (targetPercent < 0) {
    targetPercent = 0;
  }

  Serial.print("Moving cover to position: ");
  Serial.println(targetPercent);

  int targetPosition = STEPPER_MAX_POSITION * targetPercent / 100;

  if (targetPosition > steppers[0].currentPosition()) {
    lastCoverAction = COVER_ACTION_OPENING;
  } else if (targetPosition < steppers[0].currentPosition()) {
    lastCoverAction = COVER_ACTION_CLOSING;
  }

  for (int i = 0; i < sizeof(steppers) / sizeof(steppers[0]); i++) {
    steppers[i].moveTo(targetPosition);

    if (targetPosition >= steppers[i].currentPosition()) {
      steppers[i].setSpeed(STEPPER_SPEED);
    } else {
      steppers[i].setSpeed(-STEPPER_SPEED);
    }
  }
}

void stopCover() {
  Serial.println("Stopping cover");

  for (int i = 0; i < sizeof(steppers) / sizeof(steppers[0]); i++) {
    steppers[i].moveTo(steppers[i].currentPosition());
  }
}

void toggleCover() {
  Serial.println("Toggling cover");

  if (steppers[0].distanceToGo() != 0) {
    stopCover();
  } else if (lastCoverAction == COVER_ACTION_CLOSING) {
    moveCoverTo(100);
  } else {
    moveCoverTo(0);
  }
}

void updateSteppers() {
  for (int i = 0; i < sizeof(steppers) / sizeof(steppers[0]); i++) {
    if (steppers[i].distanceToGo() != 0) {
      if (!isStepperEnabled[i]) {
        steppers[i].enableOutputs();
        isStepperEnabled[i] = true;
      }

      steppers[i].runSpeed();
    } else {
      steppers[i].disableOutputs();
      isStepperEnabled[i] = false;

      if (i == 0 && steppers[i].currentPosition() != lastStepperPosition) {
        lastStepperPosition = steppers[i].currentPosition();

        publishCoverStatus();
      }
    }
  }
}

void setupSteppers() {
  for (int i = 0; i < sizeof(steppers) / sizeof(steppers[0]); i++) {
    steppers[i].setCurrentPosition(lastStepperPosition);
    steppers[i].setMaxSpeed(STEPPER_SPEED);
  }
}

void setupWifi() {
  WiFi.config(wifiStaticIp, wifiGateway, wifiGateway, wifiSubnet);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  Serial.print("Connecting to WiFi ");

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

  Serial.println();
  Serial.println("WiFi connected successfully");
}

void setupMqtt() {
  Serial.print("Connecting to MQTT ");

  mqtt.setServer(mqttBroker, MQTT_PORT);
  mqtt.setKeepAlive(MQTT_KEEP_ALIVE);
  mqtt.setCallback(mqttCallback);

  while (!mqtt.connected()) {
    if (!mqtt.connect(MQTT_CLIENT_ID, MQTT_USERNAME, MQTT_PASSWORD)) {
      Serial.print(".");
      delay(CONNECTION_RETRY_DELAY);
    }
  }

  Serial.println();
  Serial.println("MQTT connected successfully");

  mqtt.subscribe(MQTT_TOPIC_COMMAND);
}

void setup() {
  Serial.begin(115200);

  analogReadResolution(12);
  pinMode(WAKEUP_PIN, INPUT_PULLUP);

  setupSteppers();
  setupWifi();
  setupMqtt();

  if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_EXT0) {
    toggleCover();
  }
}

void loop() {
  if (!mqtt.connected()) {
    setupMqtt();
  }

  currentTime = millis();

  if (lastMqttUpdateTime == 0 || currentTime > lastMqttUpdateTime + MQTT_UPDATE_DELAY) {
    mqtt.loop();
  }

  updateSteppers();

  // TODO: Remove this
  if (currentTime > lastDebug + 500) {
    Serial.print("Current position: ");
    Serial.print(steppers[0].currentPosition());
    Serial.print(" | Target position: ");
    Serial.print(steppers[0].targetPosition());
    Serial.print(" | Distance to go: ");
    Serial.print(steppers[0].distanceToGo());
    Serial.print(" | Speed: ");
    Serial.print(steppers[0].speed());
    Serial.print(" | Last action: ");
    Serial.print(lastCoverAction);
    Serial.print(" | Last position: ");
    Serial.println(lastStepperPosition);

    lastDebug = currentTime;
  }

  if (lastCoverUpdateTime == 0 || currentTime > lastCoverUpdateTime + COVER_UPDATE_DELAY) {
    publishCoverStatus();
  }

  if (LOOP_DELAY > 0) {
    delay(LOOP_DELAY);
  }
}

gjgsm...@gmail.com

unread,
Jul 8, 2025, 6:23:58 PMJul 8
to accelstepper
Looks like it's all there. See three dots  "..." to expand all text.
I can see 9 functions plus setup() and loop(), correct?

gjgsm...@gmail.com

unread,
Jul 8, 2025, 6:45:57 PMJul 8
to accelstepper
I can have a closer look at your code in a few days but a comment for now...

Motor direction is determined by the sign on the  STEPPER_SPEED variable, '+ and -'. The call to runSpeed() is looking for the last setSpeed() call.
So, you initialise setSpeed() with a 'positive' number, this also sets the direction and unless you give it another number prior to calling runSpeed the motor will turn in this direction.

#define STEPPER_SPEED 256 // In steps/s

Suggestion for now:
Try calling  setSpeed() each time in your updateSteppers() function.

There are some other suggestions I could make and time permitting I will reply again later if needed.
Good luck
Geoff


Brian Baidal

unread,
Jul 10, 2025, 5:21:22 PMJul 10
to accelstepper
Actually setting a positive or negative speed for each specific movement is the first idea that came to my mind. I implemented that in the moveCoverTo function, in these lines of code:


if (targetPosition >= steppers[i].currentPosition()) {
  steppers[i].setSpeed(STEPPER_SPEED);
} else {
  steppers[i].setSpeed(-STEPPER_SPEED);
}

But that didn't work at all: as I mention, it fixed the calculations performed by the library, but the physical movement of the motor was still broken (rotating in the same direction regardless of positive or negative speed).

Anyway thanks for jumping in and nevermind: since I couldn't really use this library I replaced it with another one made with this specific stepper motor in mind (TinyStepper_28BYJ_48) and it worked just fine out of the box.

PS: Sorry for the repeated messages, but Google Groups kept deleting them as soon as I posted each one of them, and turns out they all appeared hours later. I've removed all of them except for the last one.
Reply all
Reply to author
Forward
0 new messages