Multi Ultrasonic with Port Expander ,External Interrupt

I'm using NodeMCU, PCF8574 ,Hc-sr04.

I only can run when i<1 in void loop(), when I try 2 or more it gonna error like this.

Soft WDT reset

>>>stack>>>

ctx: cont
sp: 3ffffdd0 end: 3fffffc0 offset: 01b0
3fffff80:  40201936 000003e8 000003e8 40202b04  
3fffff90:  feefeffe 00000000 3ffee2fc 3ffee488  
3fffffa0:  3fffdad0 00000000 3ffee458 4020340c  
3fffffb0:  feefeffe feefeffe 3ffe850c 4010149d  
<<<stack<<<

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


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

wdt reset

Code

#include <Wire.h> // use the Wire library

#define PCF8574_ADDR (0x20)
#define MAX_DISTANCE_IN_MM       (4000)   // max. valid value for distance
#define DURATION_TO_DISTANCE(x)  ((17*(x))/100) // usec -> mm.

//P0-P3 = Trig,P4-P7 = Echo
#define ECHOPIN    D5 //(External Interrupt 0)


volatile uint32_t tH, tL, pulse_width = 0;
uint16_t dist_mm;       // distance in mm.
char sbuf[32];          // used for sprintf()

void setup() {

   Wire.begin();
   Wire.setClock(400000L);// for 400kHz
   writeOutput(0xF0);
   attachInterrupt(ECHOPIN , eint_isr, CHANGE ); // D6 pin (EINT0)
   Serial.begin( 115200 );
   delay(1000);
}

void writeOutput( byte value ) { // write one byte
  Wire.beginTransmission( PCF8574_ADDR );
  Wire.write( value ); 
  Wire.endTransmission();
}

byte readInput( void ) { // read one byte
  byte data = 0xff;
  Wire.requestFrom( PCF8574_ADDR, 1 ); 
  
  delayMicroseconds(4);
  if ( Wire.available() ) {
     data = Wire.read();
  }
  return data;
}

uint16_t read_ultrasonic_sensor( int pin ) {

  writeOutput( 0xF0 | (1 << pin) );
  writeOutput( 0xF0 );
  pulse_width = 0;
  while ( pulse_width == 0 ) {}  // wait until pulse_width > 0
  return DURATION_TO_DISTANCE( pulse_width ); 
}

void loop() {
  for (int i=0; i < 1; i++ ) {
    dist_mm = read_ultrasonic_sensor( i );
    if ( dist_mm > MAX_DISTANCE_IN_MM ) {
      Serial.println( "Out of range." );
    } else {
      sprintf( sbuf, "Sensor %d, %d.%1d cm", i, (dist_mm/10), (dist_mm%10) );
      Serial.println( sbuf );
    }
    delay(50);
  }
  delay( 250 );
}

volatile uint32_t timestamp;

void eint_isr() { // ISR for Ext. Interrupt
  timestamp = micros();  // read the timestamp (in microseconds)
  if ( digitalRead( ECHOPIN ) == HIGH ) { 
    tH = timestamp;
  } else { // LOW
    tL = timestamp;
    pulse_width = (tL-tH);
  }
}

I'm using NodeMCU PCF8574 Hc-sr04.

Some punctuation in this sentence would be useful. Links would be, too.

A schematic certainly wouldn't be a waste of effort.

The NodeMCU has the watchdog timer enabled by default. It will reset the timer after each return from loop. When you increase the loop function's execution time by setting the loop bound to 2 (now you have 100ms delay in your loop instead of 50ms), the watchdog will trigger, because it wasn't reset in time.

You can either remove the for loop and let the loop function be your for loop, or reset the watchdog manually inside the for loop.

Option 1:

uint8_t loopCounter;

void loop() {
    if(loopCounter < 1)
    {
        dist_mm = read_ultrasonic_sensor( i );
        if ( dist_mm > MAX_DISTANCE_IN_MM ) {
            Serial.println( "Out of range." );
            } else {
            sprintf( sbuf, "Sensor %d, %d.%1d cm", i, (dist_mm/10), (dist_mm%10) );
            Serial.println( sbuf );
        }
        delay(50);
        loopCounter++;
    }
    else
    {
        delay( 250 );
        loopCounter = 0;
    }
}

Option 2:

void loop() {
    for (int i=0; i < 1; i++ ) {
        dist_mm = read_ultrasonic_sensor( i );
        if ( dist_mm > MAX_DISTANCE_IN_MM ) {
            Serial.println( "Out of range." );
            } else {
            sprintf( sbuf, "Sensor %d, %d.%1d cm", i, (dist_mm/10), (dist_mm%10) );
            Serial.println( sbuf );
        }
        delay(50);
        
        // reset the watchdog
        ESP.wdtFeed();
    }
    delay( 250 );
}

LightuC:
The NodeMCU has the watchdog timer enabled by default. It will reset the timer after each return from loop. When you increase the loop function's execution time by setting the loop bound to 2 (now you have 100ms delay in your loop instead of 50ms), the watchdog will trigger, because it wasn't reset in time.

You can either remove the for loop and let the loop function be your for loop, or reset the watchdog manually inside the for loop.

I'm still got the same error when I put second ultrasonic.

What change(s) did you make? When you change your code, post the new version so that we can keep up.

groundFungus:
What change(s) did you make? When you change your code, post the new version so that we can keep up.

I try to deal with Watchdog by add-in setup() and loop().

#include <Wire.h> // use the Wire library

// connect address pins: A0=0 (GND), A1=0 (GND), A2=0 (GND)
//#define ADDR_BITS (0B000) // A0=0,A1=0,A2=0
//#define I2C_SLAVE_ADDR ((0B0111000) | ADDR_BITS) // 7-bit address

#define PCF8574_ADDR (0x20)
#define MAX_DISTANCE_IN_MM       (4000)   // max. valid value for distance
#define DURATION_TO_DISTANCE(x)  ((17*(x))/100) // usec -> mm.


#define ECHOPIN    D5 //(External Interrupt 0)


volatile uint32_t tH, tL, pulse_width = 0;
uint16_t dist_mm;       // distance in mm.
char sbuf[32];          // used for sprintf()
void setup() {

  ESP.wdtDisable();
  ESP.wdtEnable(WDTO_8S); //WDTO_8S → https://github.com/esp8266/Arduino/blob/master/cores/esp8266/Esp.h

  Wire.begin();
  Wire.setClock(400000L);// for 400kHz
  writeOutput(0xF0);
  attachInterrupt(ECHOPIN , eint_isr, CHANGE ); 
  Serial.begin( 115200 );
  delay(1000);
}

void writeOutput( byte value ) { // write one byte
  Wire.beginTransmission( PCF8574_ADDR );
  Wire.write( value );
  Wire.endTransmission();
}

byte readInput( void ) { // read one byte
  byte data = 0xff;
  Wire.requestFrom( PCF8574_ADDR, 1 );

  delayMicroseconds(4);
  if ( Wire.available() ) {
    data = Wire.read();
  }
  return data;
}

uint16_t read_ultrasonic_sensor( int pin ) {

  writeOutput( 0xF0 | (1 << pin) );
  writeOutput( 0xF0 );
  pulse_width = 0;
  while ( pulse_width == 0 ) {}  // wait until pulse_width > 0
  return DURATION_TO_DISTANCE( pulse_width );
}

void loop() {
  for (int i = 0; i < 1; i++ ) {
    dist_mm = read_ultrasonic_sensor( i );
    if ( dist_mm > MAX_DISTANCE_IN_MM ) {
      Serial.println( "Out of range." );
    } else {
      sprintf( sbuf, "Sensor %d, %d.%1d cm", i, (dist_mm / 10), (dist_mm % 10) );
      Serial.println( sbuf );
    }
    delay(5);

    ESP.wdtDisable();
    ESP.wdtEnable(WDTO_8S);
    // reset the watchdog
    ESP.wdtFeed();
  }
  delay( 250 );
  ESP.wdtFeed();
}



volatile uint32_t timestamp;

void eint_isr() { // ISR for Ext. Interrupt
  timestamp = micros();  // read the timestamp (in microseconds)
  if ( digitalRead( ECHOPIN ) == HIGH ) {
    tH = timestamp;
  } else { // LOW
    tL = timestamp;
    pulse_width = (tL - tH);
  }
}

I decode error using EspExceptionDecoder.

Decoding stack results
0x40201936: eventTask at /Users/MolomitZ/Library/Arduino15/packages/esp8266/hardware/esp8266/2.5.0/cores/esp8266/core_esp8266_si2c.c line 519
0x4020340c: loop_task(ETSEvent*) at /Users/MolomitZ/Library/Arduino15/packages/esp8266/hardware/esp8266/2.5.0/cores/esp8266/core_esp8266_main.cpp line 133
0x4010149d: realloc at /Users/MolomitZ/Library/Arduino15/packages/esp8266/hardware/esp8266/2.5.0/cores/esp8266/umm_malloc/umm_malloc.c line 1628
  while ( pulse_width == 0 ) {}  // wait until pulse_width > 0

I expect that this will hang your program if there is no echo (too close or too far). A hung program is a sure way to trigger a watchdog timeout.