Use this forum to chat about hardware specific topics for the ESP8266 (peripherals, memory, clocks, JTAG, programming)

User avatar
By Randomized ESP
#90465 Hello,
I keep running into an issue using the NodeMCU 1.0 (ESP-12E), where the serial monitor outputs:
cause: 4 wdt reset
I am using 4 ultrasoundsensors, one in each corner next to a linear actuator, to determine the height of a desk, while it is being raised or lowered. If the actuators are out of sync, the fastest one should be stopped, until they are in sync again.

The same code doesn't crash on an arduino nano, it just doesn't fully work, as it doesn't have 6 Interrupt Pins.
What could be causing this issue?


Error:

Code: Select all--- Serial monitor started ---

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

wdt reset
load 0x4010f000, len 1392, room 16
tail 0
chksum 0xd0
csum 0xd0
v3d128e5c
~ld


Code:
Code: Select all// Hardware: NodeMCU 1.0(12-E), 2x L298N, 4x HC-SR04 ultrasoundsensors, 4x linear actuator, 2x buttons
#define tINT 4            // Total number of sensors
#define soundSpeed 343.0  // Speed of sound in m/s
///// PINS //////////////////
#define RELAY1    10  // S3; RELAY1&2 for changing motor_direction. RELAY1 = HIGH && RELAY2 == LOW => raise
#define RELAY2    9   // S2; RELAY1 = LOW && RELAY2 = HIGH => lower
#define pwmFrontLeft  1       // TX
#define pwmBackRight  3       // RX
#define pwmBackLeft  15      // D8
#define pwmFrontRight  13      // D7
#define sensorFrontLeft  4  //D2
#define sensorBackLeft   2 //D4
#define sensorFrontRight  14 //D5
#define sensorBackRight  12 //D6
#define buttonUp  5     //D1 Switch 1
#define buttonDown  16  //D0 Switch 2
#define triggerPin 0  // D3; Pin number for the common trigger

volatile unsigned long travelTime[tINT];  // Place to store traveltime of the pusle
volatile unsigned long startTime[tINT];   // Place to store ping times (interrupt)
volatile bool new_measurement = false;
volatile int motor_direction = 0; // 0 = still, 1 = down, 2 = up, 3 = request pause
float distance[tINT];                     // Calculated distances in cm

int lowest = 0; // height of lowest motor
int highest = 0; // height of highest motor

void ICACHE_RAM_ATTR handleSensorFrontRight();
void ICACHE_RAM_ATTR handleSensorFrontLeft();
void ICACHE_RAM_ATTR handleSensorBackRight();
void ICACHE_RAM_ATTR handleSensorBackLeft();
void ICACHE_RAM_ATTR handleButtonUp();
void ICACHE_RAM_ATTR handleButtonDown();
void ICACHE_RAM_ATTR commonSensorHandler(bool pinState, int nIRQ);
void ICACHE_RAM_ATTR handleButtonDownRising();
void ICACHE_RAM_ATTR handleButtonDownFalling();
void ICACHE_RAM_ATTR handleButtonUpRising();
void ICACHE_RAM_ATTR handleButtonUpFalling();

// PWM on arduino is 8-bit (0-253), on NodeMCU 10-bit(0-1023). Min 140-150, or stalling occurs.
int pwmFrontLeftUp = 245;
int pwmBackRightUp = 170;
int pwmBackLeftUp = 255;
int pwmFrontRightUp = 164;

// min pwm 130, they tend to stall with less
int pwmFrontLeftDown = 1023;
int pwmBackRightDown = 130;
int pwmBackLeftDown = 244;
int pwmFrontRightDown = 130;
/****************************************************************
      SETUP
****************************************************************/
void setup()
{
  Serial.begin(115200);
  while (!Serial) {
    ; // Wait for Serial
  }
  Serial.println("--- Serial monitor started ---");
  pinMode(triggerPin, OUTPUT);   // common triggerpin
  pinMode(RELAY1, OUTPUT);
  pinMode(RELAY2, OUTPUT);
  pinMode(pwmFrontLeft, OUTPUT);
  pinMode(pwmBackRight, OUTPUT);
  pinMode(pwmBackLeft, OUTPUT);
  pinMode(pwmFrontRight, OUTPUT);
  // Interrupt PINs
  pinMode(sensorFrontLeft, INPUT);
  pinMode(sensorBackLeft, INPUT);
  pinMode(sensorFrontRight, INPUT);
  pinMode(sensorBackRight, INPUT);
  pinMode(buttonDown, INPUT_PULLUP); 
  pinMode(buttonUp, INPUT_PULLUP);   

  attachInterrupt(digitalPinToInterrupt(buttonUp), handleButtonUp, CHANGE);
  attachInterrupt(digitalPinToInterrupt(buttonDown), handleButtonDown, CHANGE);
  attachInterrupt(digitalPinToInterrupt(sensorFrontLeft), handleSensorFrontLeft, CHANGE );
  attachInterrupt(digitalPinToInterrupt(sensorBackLeft), handleSensorBackLeft, CHANGE );
  attachInterrupt(digitalPinToInterrupt(sensorFrontRight), handleSensorFrontRight, CHANGE );
  attachInterrupt(digitalPinToInterrupt(sensorBackRight), handleSensorBackRight, CHANGE );
}
/****************************************************************
      LOOP
****************************************************************/
void loop()
{
  if (motor_direction == 1) // turn backwards
  {
    Serial.println("backwards");
    doMeasurement();
    // set standard pwm values, then check if some of the motors should wait for slower motors (PWM=0)
    int curPWM[4] = {pwmFrontLeftDown, pwmBackRightDown, pwmBackLeftDown, pwmFrontRightDown};
    delayMicroseconds(10); // wait some time for measurement to complete
    // look for highest motor, i.e. the slowest one
    highest = 0; // assume slowest motor is index 0, then whether that's true
    for (int i = 1; i < 5; i++)
    { if (distance[i] > distance[highest])
      {
        highest = i;
      }
    }
    // if distance between two motors is above 1cm, pause the faster one
    for (int i = 0; i < 5; i++)
    { if (distance[highest] - distance[i] > 1)
      {
        curPWM[i] = 0;
      }
    } // Set PWM speed
    analogWrite(pwmFrontLeft, curPWM[0]);
    analogWrite(pwmBackRight, curPWM[1]);
    analogWrite(pwmBackLeft, curPWM[2]);
    analogWrite(pwmFrontRight, curPWM[3]);
    digitalWrite(RELAY1, LOW); // Turn relays to diff. signals to turn on all motors.
    digitalWrite(RELAY2, HIGH);
  }
  else if (motor_direction == 2) // raise
  {
    Serial.println("raise");
    doMeasurement();
    int curPWM[4] = {pwmFrontLeftUp, pwmBackRightUp, pwmBackLeftUp, pwmFrontRightUp};
    delayMicroseconds(10);
    // Find lowest distance, i.e. slowest motor
    lowest = 0;
    for (int i = 1; i < 5; i++)
    { if (distance[i] < distance[lowest])
      {
        lowest = i;
      }
    }
    // Pause motors that are more than 1cm ahead of the slowest one
    for (int i = 0; i < 5; i++)
    { if (distance[i] - distance[lowest] > 1)
      {
        curPWM[i] = 0;
      }
    }
    analogWrite(pwmFrontLeft, curPWM[0]);
    analogWrite(pwmBackRight, curPWM[1]);
    analogWrite(pwmBackLeft, curPWM[2]);
    analogWrite(pwmFrontRight, curPWM[3]);
    digitalWrite(RELAY1, HIGH);
    digitalWrite(RELAY2, LOW);
  }
  else if (motor_direction == 3)
  { // time to pause the motors
    Serial.println("stopping");
    analogWrite(pwmFrontLeft, 0);
    analogWrite(pwmBackRight, 0);
    analogWrite(pwmBackLeft, 0);
    analogWrite(pwmFrontRight, 0);
    digitalWrite(RELAY2, LOW);
    digitalWrite(RELAY1, LOW);
    motor_direction = 0; // reset motor_direction
  }
  else {}
  delayMicroseconds(50); // short break
}
/****************************************************************
      Retrieve measurement and set next trigger
****************************************************************/
void doMeasurement()
{ // Send the trigger to calculate distances
  digitalWrite(triggerPin, HIGH);    // HIGH pulse for at least 10µs
  delayMicroseconds(10);
  digitalWrite(triggerPin, LOW);     // Set LOW again
  // Wait a bit then calculate distances
  delayMicroseconds(20);
  // First read will be 0 (no distance  calculated yet)
  // Read results. Pause interrupts to avoid corruption (having traveltime overwritten by interrupts while reading from it)
  noInterrupts();
  for (int i = 0; i < tINT; i++)
  {
    distance[i] = travelTime[i] / 2.0 * (float)soundSpeed / 10000.0;   // in cm
  }
  interrupts();
  // Initiate next trigger
  // digitalWrite(triggerPin, LOW);  // rest of loop already takes > 2µs
  // delayMicroseconds(2);

}
/****************************************************************
      INTERRUPT handling
****************************************************************/
void handleSensorFrontRight(void)
{
  bool pinRead = digitalRead(sensorFrontRight);
  commonSensorHandler(pinRead, 0);
}
void handleSensorFrontLeft(void)
{
  bool pinRead = digitalRead(sensorFrontLeft);
  commonSensorHandler(pinRead, 1);
}
void handleSensorBackLeft(void)
{
  bool pinRead = digitalRead(sensorBackLeft);
  commonSensorHandler(pinRead, 2);
}
void handleSensorBackRight(void)
{
  bool pinRead = digitalRead(sensorBackRight);
  commonSensorHandler(pinRead, 3);
}
// Common function for interrupts
void commonSensorHandler(bool pinState, int nIRQ)
{
  unsigned long currentTime = micros();  // Get current time (in µs)
  if (pinState)
  {
    startTime[nIRQ] = currentTime;     // If pin state has changed to HIGH -> remember start time (in µs)
  }
  else
  {
    travelTime[nIRQ] = currentTime - startTime[nIRQ]; // If pin state has changed to LOW -> calculate time passed (in µs)
  }
}

void handleButtonUp() { // When switch 1 is pressed
  Serial.println("Button up pressed");
  bool risingEdge = digitalRead(buttonUp);
  if (risingEdge) {
    handleButtonUpRising(); // signal goes LOW->HIGH, when button is released
  }
  else {
    handleButtonUpFalling(); // Signal goes HIGH->LOW, when button is pressed
  }
}
void  handleButtonUpFalling() { // raise
  motor_direction = 2;
}
void  handleButtonUpRising() { // pause
  motor_direction = 3;
}
void handleButtonDown() { // When switch 2 is pressed
  bool risingEdge = digitalRead(buttonDown);
  if (risingEdge) {
    handleButtonDownRising();
  }
  else {
    handleButtonDownFalling();
  }
}
void  handleButtonDownFalling() { // lower
  motor_direction = 1;
}
void  handleButtonDownRising() { // Beim loslassen pausieren anfragen
  motor_direction = 3;
}
// END
User avatar
By Randomized ESP
#90482 Apparently, it's a no go to use the S2 pin as an Output.
Ended up testing every single PIN for it's Input and Output capability:
Outputs(12): 16,15,14,13,12,10,5,4,3,2,1,0
Inputs(9): 14,13,12,10,5,4,3,2,0
crash-inducing(5): 11,9,8,7,6
Utilizing every single pin, there's a total of 12 digital pins plus one analog pin, which could be just enough for this project. Some of the pins have multiple functions and using them leads to other issues, such as no communication via USB, but that's another story.

I've already ordered mcp23017 I/O extenders, though it will take some weeks to arrive. Until then, the abovementioned pins will have to serve.
User avatar
By eriksl
#90795 WDT reset = "watchdog timer reset". It kicks in when user code is running too long without yielding control to the system. So I'd focus on that. Don't run (possible) long loops and other lengthy operations without yielding control.