ESP01 expansion using MCP23017 on breadboard only works if touched

I have following schematic that shows that I am trying to two GPIOs of EPS01 to multiple using MCP23017.
Unfortunately I have this issue some sort of magical behaviour, that only when I bring my finger close to MCP23017 it seems to let the current flow through and move my motors.

2nd issue is I don't want them to be running at the same time as the due to limitation of the power that I have it from the solar panel stored in single 18650 Li-ion battery.

3rd problem : the motors are supposed to run in CW and CCW alternatively. I don't get them to run both direction, both the motors keep on running in same direction whatever they started with.

IMPORTANT: I was able to just work with single motor/multiple motors using only ESP01 but only thing it skips due to lack of power if multiple stepper motors are used, and I was able to work with only ESP01 and 4988A / DRV8255 with 28BYJ-48.

Here is the schematic:

Here are the excerpts from my main.cpp


#define MOVETO 2000 // position to which to move
#define SPEED 200 // initial constant speed
#define MAXSPEED 300 // constant speed
#define ACCEL 50
#define MCP23017_ADDRESS 0x20
#define enablePin 1 //TX Pin
#define CtrlPin 3 //Rx Pin
#define motorInterfaceType 1
#define NUM_MOTORS 2 // # of motors to control
Adafruit_MCP23017 mcp;

long int lowPosition ;
long int highPosition;
int step_pin[2] = {4, 22};
int dir_pin[2] = {5, 21};
AccelStepper motor1 = AccelStepper(motorInterfaceType, step_pin[0], dir_pin[0]);
AccelStepper motor2 = AccelStepper(motorInterfaceType, step_pin[1], dir_pin[1]);
AccelStepper steppers[NUM_MOTORS] = {motor1, motor2};


void setup() {

  highPosition = MOVETO;
  lowPosition = 0.0;

  mcp.begin(MCP23017_ADDRESS);
  Wire.begin(0,2);
  pinMode(enablePin,FUNCTION_3);
  pinMode(enablePin, OUTPUT);

  // set motor pins as output
  for (int i = 0; i < 2; i++) {
    mcp.pinMode(step_pin[i], OUTPUT);
    mcp.pinMode(dir_pin[i], OUTPUT)
    steppers[i].disableOutputs();
    }
     digitalWrite(enablePin,HIGH);
}

> Blockquote

void loop(){
  delay(5000);
  digitalWrite(enablePin,LOW);
  for ( int i = 1; i<2;i++){
    **CW(steppers[i]);**
    delayMicroseconds(500);
    steppers[i].enableOutputs();
    while ( (myclient.status.indexOf("OFF") !=0) && (steppers[i].distanceToGo() != 0))
    {
      steppers[i].run();
      delayMicroseconds(2000);
      //delay(500);
    }
    steppers[i].disableOutputs();
    delay(4000);
  }
  
  digitalWrite(enablePin,HIGH);
  delay(5000);
  
  digitalWrite(enablePin,LOW);
  for ( int i = 1; i<2;i++){
    **CCW(steppers[i]);**
    delayMicroseconds(500);
    steppers[i].enableOutputs();
    while ( (myclient.status.indexOf("OFF") !=0) && (steppers[i].distanceToGo() != 0))
    {
      steppers[i].run();
      delayMicroseconds(2000);
      //delay(500);
    }
    steppers[i].disableOutputs();
    delay(4000);
  }
  digitalWrite (enablePin, HIGH);

}

Well this behavior is not unknown I am sure every professional engineer has come across this at least once in his / her career.

Normally it comes from loose connections, but it is not restricted to this. It can also be due to the captivate effects of your body. It is related to the "it only works when I connect my oscilloscope to it.

They can be very difficult to trace. It can be anything from a faulty component to bad design, or a fault in some seemingly unrelated part of the circuit.

Make sure that you have all the address lines connected, your circuit diagram shows the 23017 address lines are floating. Make sure your decoupling capacitors are all ceramic. And make sure you actually connect the reset pin to something and not let it float. Connect it to Vdd through a 10K resistor, to allow the internal auto reset circuit to work.

The MCP23017 address lines must be biased actively. You can't just leave them unconnected. See page 11 of the MCP23017 datasheet.

A0 11 15 Hardware address pin. Must be externally biased.
A1 12 16 Hardware address pin. Must be externally biased.
A2 13 17 Hardware address pin. Must be externally biased.

HI @Grumpy_Mike

Sorry my schematic diagram I missed couple of lines.. infact I have connected reset pin of 23017 to 3.3v by 3R3.

Please forgive my novice diagram this is my first of kind schematic and I don't know much about the electronics. Whatever I did was based on my last one month of excursion in this field.

Here is the new Schematic diagram that I have.

and actual photo of the breadboard connections.

IMPORTANT point is : Actually I don't even need touch directly to MCP, even if I am 1cm close to it motor gets alive.

Greatly appreciate any help here.

Jag

Hi @groundFungus

Thanks for the reply.

Yes it was my schematic mistake I correct it and uploaded one already.

Also attached here is the actual photo of the breadboard connections.

Thanks a ton,

watersoup

hi,
I am slapping my forehead here...

I corrected the bias issue by connecting into ground. is that ok ?

Thanks
watersoup

Not exactly know what you mean by "bias issue", I am also not sure what is connected to ground. Can you please post your schematic again indicating what you connected to ground by using a unique colour.

You are doing very well for such a little time.
You might want to check out my web site for some fundamentals.

http://www.thebox.myzen.co.uk/Tutorial/Inputs.html
and
http://www.thebox.myzen.co.uk/Tutorial/De-coupling.html

Yes, connecting the address inputs to ground is a way to correctly bias them as is required by the data sheet.

Corrected Schematic


I made adjustment to the circuit using couple of ceramic capacitors to stablize, but still the same issue, Its working like oracle's fortune ball...lol.

I bring my finger closer to it starts moving all the motors, irrespective of the underlying conditions.

HEre is my code that I uploaded it to ESP01.

#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_MCP23017.h>
#include <AccelStepper.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncWebServer.h>
#include "wifiparams.h"

#define MOVETO 2000 // position to which to move
#define SPEED 200 // initial constant speed
#define MAXSPEED 300 // constant speed
#define ACCEL 50
#define MCP23017_ADDRESS 0x20
#define enablePin 1 //TX Pin
#define CtrlPin 3 //Rx Pin
#define motorInterfaceType 1
#define NUM_MOTORS 2 // # of motors to control

char  help_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>  
<html lang="en">
  <head>
  <meta charset="utf-8" >
  <title>ESP-01 WiFi stepper controller</title>
  </head>
  <body>
    <h2>ESP-01 WiFi stepper controller</h2>
      <p>Control stepper: 
      <a href="/CCW" target="_self" >CCW</a>
      <a href="/CW" target="_self">CW</a>
      <a href="/OFF" target="_self">OFF</a></p>
      <p>current status : MYSTATUS  </p>
  </body>
</html>
)rawliteral";

const int STEPS_PER_REV = 2048;
Adafruit_MCP23017 mcp;
long int lowPosition ;
long int highPosition;

// int step_pin[2] = {4, 22};
// int dir_pin[2] = {5,21};
int step_pin[2] = {11, 1};
int dir_pin[2] = {12,0};

// int motor3_PIN[2] = {7, 22};

// DEFINE ARRAY OF STEPPER MOTORS
AccelStepper motor1 = AccelStepper(motorInterfaceType, step_pin[0], dir_pin[0]);
AccelStepper motor2 = AccelStepper(motorInterfaceType, step_pin[1], dir_pin[1]);

AccelStepper steppers[NUM_MOTORS] = {motor1, motor2};
AsyncWebServer server(80);

AccelStepper *ActiveMotor;
int ActiveNum;

class ClientObj{
  public:
    AsyncClient client;
    String status="";
  
    void setStatus(String newstatus){
      status = newstatus;
    }

    void print(String linetobeprinted){
        Serial.println( linetobeprinted);
    }
};

ClientObj myclient;
String status = "";

void printHelp(AsyncWebServerRequest *Request){

  AsyncClient *aclient = Request->client();
  String htmlstr = String(help_html);
  String ctrlpin_input = String(analogRead(CtrlPin));
  String ctrlpin_char = String(ctrlpin_input);
  String pos;
  String distancetoGo;
  for(int i=0; i<NUM_MOTORS; i++){
    pos = pos + "Blind " + String(i) + ": " + String(steppers[i].currentPosition()) + "\n";
    distancetoGo = distancetoGo + String(i) + ": " + String(steppers[i].distanceToGo()) + "\n";
  }
  htmlstr.replace("MYSTATUS ", myclient.status + '@' + aclient->remoteIP().toString().c_str() +
           "  Pin:" + ctrlpin_char + " LowPoint:" + lowPosition + " HighPoint:" + highPosition + 
            "\n DISTANCE TO GO: \n" + distancetoGo + "\nPOSITION :\n" + pos );
  
  Request->send(200, "text/html", (const String) htmlstr);
}

// Initialize WiFi
void initWiFi() {
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password); //--> Connect to your WiFi router
    //----------------------------------------Wait for connection
    delay(5000);
    while (WiFi.status() != WL_CONNECTED)
    {
      Serial.print(".");
      delay(1000);
    }
    Serial.println("");
    Serial.println("WiFi connected");
}

void rotate(AccelStepper stepper){
  
  stepper.enableOutputs();
  while ( (myclient.status.indexOf("OFF") !=0) && (stepper.distanceToGo() != 0))
  {
    stepper.run();
    delayMicroseconds(2000);
    //delay(500);
  } 
  if (stepper.distanceToGo() == 0){
    stepper.disableOutputs();
	  delayMicroseconds(500);
  }
  stepper.disableOutputs();
}

void CCW( AsyncWebServerRequest *Request){
//   AsyncClient *aclient = Request->client();
//   Serial.printf("client ip : %s\n", aclient->remoteIP().toString().c_str());
  status = myclient.status;
  // Serial.println("CCW()");

  // Match the request
  if (status.indexOf("CCW") !=0 )
  {
    myclient.status="CCW";
    for(int i=0;i<NUM_MOTORS;i++){
      steppers[i].enableOutputs();
      if (steppers[i].isRunning()) {
        steppers[i].stop();
        delayMicroseconds(500);
      }
      steppers[i].setSpeed(-SPEED);
      steppers[i].moveTo(lowPosition);
      delayMicroseconds(500);
    }
  }
  printHelp(Request);
}

void CW(AsyncWebServerRequest *Request){

    // AsyncClient *aclient = Request->client();
    // Serial.printf("client ip : %s\n", aclient->remoteIP().toString().c_str());
    status = myclient.status;
    // Serial.println("CW()");
  
    if (status.indexOf("CW") !=0 ){
      myclient.status="CW";
      for ( int i =0; i < NUM_MOTORS;i++){
        steppers[i].enableOutputs();
        if (steppers[i].isRunning()) {
          steppers[i].stop();
          delayMicroseconds(500);
        }
        steppers[i].setSpeed(SPEED);
        steppers[i].moveTo(highPosition);
        delayMicroseconds(500);
      }
    }
    printHelp(Request);
}

void setLow(AsyncWebServerRequest *Request){

  // AsyncClient *aclient = Request->client();
  // Serial.printf("client ip : %s\n", aclient->remoteIP().toString().c_str());
  status = myclient.status;
  // Serial.println("CW()");

  if (status.indexOf("OFF")  == 0 ){
    ActiveMotor->enableOutputs();
    long int currpos = ActiveMotor->currentPosition();
    for (int i =0; i< NUM_MOTORS; i++){
      steppers[i].enableOutputs();
      lowPosition = ( 0 < currpos ) ? currpos : 0;
      steppers[i].disableOutputs();
    }
  }
  printHelp(Request);
}

void setHigh(AsyncWebServerRequest *Request){

  // AsyncClient *aclient = Request->client();
  // Serial.printf("client ip : %s\n", aclient->remoteIP().toString().c_str());
  status = myclient.status;
  // Serial.println("CW()");

  if (status.indexOf("OFF") == 0 ){
    ActiveMotor->enableOutputs();
    long int currpos = ActiveMotor->currentPosition();
    for(int i=0; i<NUM_MOTORS; i++){
      steppers[i].enableOutputs();
      highPosition = (currpos < MOVETO )? currpos: MOVETO;
      steppers[i].disableOutputs();
    }
  }
  printHelp(Request);
}

void setOFF(){
  status = myclient.status;
  // Serial.println("LED is OFF()");
  if (status.indexOf("OFF") <0 ){
    myclient.status="OFF";
    for ( int i = 0; i<NUM_MOTORS; i++){
          steppers[i].stop();
            delayMicroseconds(500);
          steppers[i].disableOutputs();
        }
    digitalWrite(enablePin,HIGH);
  }
}

void OFF(AsyncWebServerRequest *Request){
  // AsyncClient * aclient = Request->client();
  // Serial.printf("client ip : %s\n", aclient->remoteIP().toString().c_str());
  setOFF();
  printHelp(Request);
}
  
void notFound(AsyncWebServerRequest *request) {
    request->send(404, "text/plain", "Not found");
    printHelp(request);
}

void setuprequest(){

  server.on("/OFF", HTTP_GET, OFF);
  server.on("/CCW", HTTP_GET, CCW);
  server.on("/CW" , HTTP_GET, CW);
  server.on("/setLow", HTTP_GET, setLow);
  server.on("/setHigh", HTTP_GET, setHigh);
  server.onNotFound(notFound);
  // Web Server Root URL
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    printHelp(request);
  });
}


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

  delay(1000);
  myclient.status=String("OFF");
  highPosition = MOVETO;
  lowPosition = 0.0;

  initWiFi();

  // define handlers for the request;
  setuprequest();
  Serial.flush();
  Serial.end();
  // start the server
  server.begin();

  Wire.begin(0,2);
  // mcp.begin(MCP23017_ADDRESS);
  mcp.begin();


  pinMode(enablePin,FUNCTION_3);
  pinMode(enablePin, OUTPUT);

  // set motor pins as output
  for (int i = 0; i < NUM_MOTORS; i++) {
    mcp.pinMode(step_pin[i], OUTPUT);
    // mcp.digitalWrite(step_pin[i], LOW);
    mcp.pinMode(dir_pin[i], OUTPUT);
    // mcp.digitalWrite(dir_pin[i], LOW);
    steppers[i].enableOutputs();
    steppers[i].setAcceleration(ACCEL);
    steppers[i].setSpeed(SPEED);
    steppers[i].setMaxSpeed(MAXSPEED);
    }

  setOFF();
  // for now disengage everything
  ActiveNum=0;
  
}

void loop(){

  ActiveMotor = &(steppers[ActiveNum]);
  if ((myclient.status.indexOf("OFF") != 0) && (ActiveNum < NUM_MOTORS))
  {
    if ( (ActiveMotor->distanceToGo() != 0))
    {
      ActiveMotor->run();
      delayMicroseconds(1000);
      // delay(500);
    }
    else if (ActiveMotor->distanceToGo() == 0)
    {
      ActiveMotor->disableOutputs();
      delayMicroseconds(500);
      ActiveNum = ActiveNum+1;
    }
  } else if (ActiveNum == NUM_MOTORS){
    setOFF();
    // reset the ActiveMotor index
    ActiveNum = 0;
  }
}

Your ESP is a 3V3 device but you are connecting the I2C pull-up resistors to 5V, so that is wrong. Pull them up to the 3V3 line that is the ESP's power supply.
, also make them 1K8.

Thanks @Grumpy_Mike , finally after couple of busy days I am getting back to this, I will try your suggestion, couldn't catch what you said exactly? I am not at all using 5V supply anywhere, its either 3.3 V of 12V ( for VMOT). Are you referring me to pull-up resisters that I am applying ?

Greatly appreciate you help,
Watersoup

Why have you got the the enable line on the stepping motors bused half way between 3V3 and ground?

Hi
@Grumpy_Mike : the enable is my default way of enabling and disabling the motor Drivers ( to save energy and keep them from heating up). I am not sure what you meant by "half way between 3v3 and ground " Enable is directly connect to TX/RX of ESP01 based on the Tasmota design given here: https://github.com/arendst/Tasmota/discussions/11731

Only difference is I adopted this to enable using TX and use GPIOs to STEP and DIR of driver. I tested this design without MCP and it worked perfectly.

I feel that I have issues with my code, what I get in serial monitor is following:

ets Jan 8 2013,rst cause:4, boot mode:(1,6)

wdt reset

Thanks
Watersoup

Here is my code:


#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_MCP23017.h>
#include <AccelStepper.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncWebServer.h>
#include "wifiparams.h"

#define SDA 0
#define SDL 2  
#define MOVETO 2000 // position to which to move
#define SPEED 200 // initial constant speed
#define MAXSPEED 300 // constant speed
#define ACCEL 50
#define MCP23017_ADDRESS 0x20
#define enablePin 1 //TX Pin
#define CtrlPin 3 //Rx Pin
#define motorInterfaceType 1
#define NUM_MOTORS 2 // # of motors to control

char  help_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>  
<html lang="en">
  <head>
  <meta charset="utf-8" >
  <title>ESP-01 WiFi stepper controller</title>
  </head>
  <body>
    <h2>ESP-01 WiFi stepper controller</h2>
      <p>Control stepper: 
      <a href="/CCW" target="_self" >CCW</a>
      <a href="/CW" target="_self">CW</a>
      <a href="/OFF" target="_self">OFF</a></p>
      <p>current status : MYSTATUS  </p>
  </body>
</html>
)rawliteral";

const int STEPS_PER_REV = 2048;
Adafruit_MCP23017 mcp;
long int lowPosition ;
long int highPosition;

// int step_pin[2] = {4, 22};
// int dir_pin[2] = {5,21};
int step_pin[2] = {11, 1};
int dir_pin[2] = {12,0};

// int motor3_PIN[2] = {7, 22};

// DEFINE ARRAY OF STEPPER MOTORS
AccelStepper motor1 = AccelStepper(motorInterfaceType, step_pin[0], dir_pin[0]);
AccelStepper motor2 = AccelStepper(motorInterfaceType, step_pin[1], dir_pin[1]);

AccelStepper steppers[NUM_MOTORS] = {motor1, motor2};
AsyncWebServer server(80);

AccelStepper *ActiveMotor;
int ActiveNum;

class ClientObj{
  public:
    AsyncClient client;
    String status="";
  
    void setStatus(String newstatus){
      status = newstatus;
    }

    void print(String linetobeprinted){
        Serial.println( linetobeprinted);
    }
};

ClientObj myclient;
String status = "";



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

  delay(1000);
  myclient.status=String("OFF");
  highPosition = MOVETO;
  lowPosition = 0.0;

  initWiFi();

  // define handlers for the request;
  setuprequest();

  Serial.println("\nI2C Scanner");
  Serial.print("SDA: ");Serial.println(SDA);
  Serial.print("SCL: ");Serial.println(SCL);

  // start the server
  Serial.flush();
  Serial.end();

  Wire.begin(SCL,SDA);
  delay(1000);
  mcp.begin(MCP23017_ADDRESS);
  // mcp.begin();

  server.begin();

  pinMode(enablePin,FUNCTION_3);
  pinMode(enablePin, OUTPUT);

  // set motor pins as output
  for (int i = 0; i < NUM_MOTORS; i++) {
    mcp.pinMode(step_pin[i], OUTPUT);
    // mcp.digitalWrite(step_pin[i], LOW);
    mcp.pinMode(dir_pin[i], OUTPUT);
    // mcp.digitalWrite(dir_pin[i], LOW);
    steppers[i].enableOutputs();
    steppers[i].setAcceleration(ACCEL);
    steppers[i].setSpeed(SPEED);
    steppers[i].setMaxSpeed(MAXSPEED);
    }

  setOFF();
  // for now disengage everything
  ActiveNum=0;

  delay(10000);
}

void loop(){

  ActiveMotor = &(steppers[ActiveNum]);
  if ((myclient.status.indexOf("OFF") != 0) && (ActiveNum < NUM_MOTORS))
  {
    if ( (ActiveMotor->distanceToGo() != 0))
    {
      ActiveMotor->run();
      delayMicroseconds(1000);
      // delay(500);
    }
    else if (ActiveMotor->distanceToGo() == 0)
    {
      ActiveMotor->disableOutputs();
      delayMicroseconds(500);
      ActiveNum = ActiveNum+1;
    }
  } else if (ActiveNum == NUM_MOTORS){
    setOFF();
    // reset the ActiveMotor index
    ActiveNum = 0;
  }
}

NOTE: I removed most of the functions which have already been tested in the simpler setup.

Thanks,
Watersoup

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.