How to connect multiple multiplexer(CD74HC4067 16-Channel/PCA9548A 8-channel) to ESP32S NodeMCU to control 32 TOF sensors (VL53LDK)

I want to connect ESP32S to 32 sensors without using up as many pins. I did some research and I know I need some kind of multiplexer to handle this. I tried a few different multiplexers but I am not getting consistent results not all sensors are working together, some work some do not work, individually all sensors are working.

Also I am not sure if I should keep my ESP32S,Multiplexer,Sensors all on 3.3V or 5V.
My ESP32S is power via USB port on laptop and I have a 5V 5A PSU that can be used if required.

Eventually I want to scale this project to have 64 sensors that control 64 addressable LED strips (WS2812B) 5V. Control number of LEDs that light up based on distance.
1 I2C Bus 1 16-Channel 4 Sensor.pdf (98.1 KB)
1 I2C Bus 2 8-Channel xshut 4 Sensor.pdf (99.9 KB)
2 I2C Bus 2 8-Channel 4 Sensor.pdf (96.1 KB)

links to all hardware used in this project

ESP32S
https://roboticsdna.in/product/node-mcu-esp8266-32/?src=google&kwd=&adgroup={adgroup}&device=c&campaign={campaign}&adgroup={adgroup}&keyword=&matchtype=&gad_source=1&gclid=Cj0KCQiAy8K8BhCZARIsAKJ8sfRf8qDVVBG2OHaIAAXndgAS1dWa2NBtPDF0Ie7KQ9B4e97F2NCrUo4aAthYEALw_wcB

Multiplexer 8-channel
https://roboticsdna.in/product/cjmcu-tca9548a-i2c-8-channel-multiple-extensions-development-board/

Multiplexer 16-channel

Distance sensor
https://roboticsdna.in/product/vl53l0x-laser-ranging-sensor-time-of-flight-tof/

@Wawa @Grumpy_Mike @kmin
Any guidance will be extremely helpful, Thank you.

Can you post code, an annotated schematic, and a drawing of what you’re trying to create ?

1 Like

Hi, @ank_15

What is your project?

How far apart will the sensors be?

Thanks.. Tom.. :smiley: :+1: :coffee: :australia:

The VL53LDK is an I2C device; use an I2C multiplexer (e.g. TCA9548A I2C Multiplexer : ID 2717 : Adafruit Industries, Unique & fun DIY electronics and kits).

Please check attached PDFs, I am trying all 3 approaches none is working for me !
I was thinking let me decide on the hardware part then I can post code for relevant circuit.

Can you please post your code and a schematic for each of your attampts.

If we don't know what you have tried, we cannot really help you.

Thanks.. Tom.. :smiley: :+1: :coffee: :australia:

1 Like

Interactive panel which lights up based on distance data from sensors.
Closer the person more Leds will light up, further the person less Leds will light up.

Aprox size of Panel
3.3Mts (10ft) length
1.2Mts (4ft) height

Space between sensors aprox 25-35mm

Since Esp32 has 3.3V operating voltage, correct supply voltage for multiplexer and sensors is 3.3V, not 5V.

If you connect one PCA9548A to pins 21/22 and few sensors to multiplexer, can you find them with TCA9548 I2CScanner sketch?

Ok, I will go with what @sterretje has suggested and go with 2 8-channel multiplexor also will update circuit as @kmin has suggested and get back to you after testing following this guide

Thank you for quick response.

@kmin @sterretje @TomGeorge

#include <Wire.h>

#define MULTIPLEXER_ADDR 0x71  // Default I2C address of TCA9548A

void selectMultiplexerChannel(uint8_t channel) {
  if (channel > 7) {
    Serial.println("Error: Invalid channel number! Must be between 0 and 7.");
    return;
  }

  Wire.beginTransmission(MULTIPLEXER_ADDR);
  Wire.write(1 << channel);  // Activate the specific channel
  uint8_t error = Wire.endTransmission();
  if (error != 0) {
    Serial.print("Error selecting channel ");
    Serial.print(channel);
    Serial.println(" on the multiplexer!");
  } else {
    Serial.print("Multiplexer channel ");
    Serial.print(channel);
    Serial.println(" selected successfully.");
  }
}

void setup() {
  Wire.begin(21, 22);  // Initialize IΒ²C with SDA on GPIO21, SCL on GPIO22
  Serial.begin(115200);
  while (!Serial);  // Wait for the Serial Monitor to connect
  delay(1000);

  Serial.println("\nI2C Scanner with Multiplexer ready!");

  // Test communication with the multiplexer
  Wire.beginTransmission(MULTIPLEXER_ADDR);
  if (Wire.endTransmission() != 0) {
    Serial.println("Error: Multiplexer not detected at address 0x70!");
    while (1);  // Stop here if the multiplexer isn't detected
  } else {
    Serial.println("Multiplexer detected at address 0x70.");
  }
}

void loop() {
  Serial.println("Scanning for I2C devices on multiplexer...");

  // Iterate through all multiplexer channels (0–7)
  for (uint8_t channel = 0; channel < 8; channel++) {
    selectMultiplexerChannel(channel);  // Select the current channel
    Serial.print("Scanning on TCA9548A Channel #");
    Serial.println(channel);

    uint8_t count = 0;

    // Scan for devices on the selected channel
    for (uint8_t address = 1; address < 127; address++) {
      if (address == MULTIPLEXER_ADDR) continue;  // Skip the multiplexer address itself

      Wire.beginTransmission(address);
      if (Wire.endTransmission() == 0) {  // If the device acknowledges
        Serial.print("Found I2C device at address: 0x");
        if (address < 16) Serial.print("0");  // Add leading zero for addresses < 0x10
        Serial.print(address, HEX);
        Serial.println();
        count++;
      }
    }

    if (count == 0) {
      Serial.println("No I2C devices found on this channel.\n");
    } else {
      Serial.print("Total I2C devices found on channel ");
      Serial.print(channel);
      Serial.print(": ");
      Serial.println(count);
    }
  }

  Serial.println("\nScan complete. Waiting before next scan...\n");
  delay(50000);  // Wait before scanning again
}

1 I2C Bus 1 8-Channel 2 Sensor.pdf (97.1 KB)

Serial Monitor:

I2C Scanner with Multiplexer ready!
Multiplexer detected at address 0x70.
Scanning for I2C devices on multiplexer...
Multiplexer channel 0 selected successfully.
Scanning on TCA9548A Channel #0
Found I2C device at address: 0x29
Total I2C devices found on channel 0: 1
Multiplexer channel 1 selected successfully.
Scanning on TCA9548A Channel #1
No I2C devices found on this channel.

Multiplexer channel 2 selected successfully.
Scanning on TCA9548A Channel #2
No I2C devices found on this channel.

Multiplexer channel 3 selected successfully.
Scanning on TCA9548A Channel #3
No I2C devices found on this channel.

Multiplexer channel 4 selected successfully.
Scanning on TCA9548A Channel #4
No I2C devices found on this channel.

Multiplexer channel 5 selected successfully.
Scanning on TCA9548A Channel #5
No I2C devices found on this channel.

Multiplexer channel 6 selected successfully.
Scanning on TCA9548A Channel #6
No I2C devices found on this channel.

Multiplexer channel 7 selected successfully.
Scanning on TCA9548A Channel #7
No I2C devices found on this channel.

Scan complete. Waiting before next scan...

#include <Wire.h>

void setup() {
  Wire.begin(21, 22);  // Initialize IΒ²C with SDA on GPIO21, SCL on GPIO22 (default ESP32 pins)
  Serial.begin(115200);
  while (!Serial);  // Wait for the Serial Monitor to connect (optional)
  Serial.println("\nI2C Scanner ready!");
}

void loop() {
  Serial.println("Scanning for I2C devices...");
  uint8_t count = 0;

  for (uint8_t address = 1; address < 127; address++) {
    Wire.beginTransmission(address);
    if (Wire.endTransmission() == 0) {  // Check if a device acknowledges the address
      Serial.print("Found I2C device at address: 0x");
      if (address < 16) Serial.print("0");  // Add leading zero for addresses < 0x10
      Serial.print(address, HEX);
      Serial.println();
      count++;
    }
  }

  if (count == 0) {
    Serial.println("No I2C devices found.\n");
  } else {
    Serial.print("Total I2C devices found: ");
    Serial.println(count);
  }

  delay(5000);  // Wait 5 seconds before scanning again
}

And if I run Abv code on same circuit I get these results in serial Monitor:

Scanning for I2C devices...

Found I2C device at address: 0x29
Found I2C device at address: 0x71

Total I2C devices found: 2

try with adafruit scanner sketch on your post#10 link.

/**
 * TCA9548 I2CScanner.ino -- I2C bus scanner for Arduino
 *
 * Based on https://playground.arduino.cc/Main/I2cScanner/
 *
 */

#include "Wire.h"

#define TCAADDR 0x71

void tcaselect(uint8_t i) {
  if (i > 7) return;
 
  Wire.beginTransmission(TCAADDR);
  Wire.write(1 << i);
  Wire.endTransmission();  
}


// standard Arduino setup()
void setup()
{
    while (!Serial);
    delay(1000);

    Wire.begin();
    
    Serial.begin(115200);
    Serial.println("\nTCAScanner ready!");
    
    for (uint8_t t=0; t<8; t++) {
      tcaselect(t);
      Serial.print("TCA Port #"); Serial.println(t);

      for (uint8_t addr = 0; addr<=127; addr++) {
        if (addr == TCAADDR) continue;

        Wire.beginTransmission(addr);
        if (!Wire.endTransmission()) {
          Serial.print("Found I2C 0x");  Serial.println(addr,HEX);
        }
      }
    }
    Serial.println("\ndone");
}

void loop() 
{
}

@kmin
No output in serial Monitor

I did some research and was able to reach till here.

#include <Wire.h>

#define TCAADDR1 0x71  // Address of first TCA9548A
#define TCAADDR2 0x70  // Address of second TCA9548A

void tcaselect(uint8_t mux_addr, uint8_t channel) {
  if (channel > 7) return;  // Ensure the channel is valid

  Wire.beginTransmission(mux_addr);  // Select the multiplexer
  Wire.write(1 << channel);          // Select the channel
  Wire.endTransmission();
}

void scanMux(uint8_t mux_addr) {
  for (uint8_t channel = 0; channel < 8; channel++) {
    tcaselect(mux_addr, channel);  // Select the channel on the given multiplexer
    Serial.print("\nScanning Multiplexer at 0x");
    Serial.print(mux_addr, HEX);
    Serial.print(", Channel ");
    Serial.println(channel);

    uint8_t count = 0;  // Count the devices found on this channel
    for (uint8_t addr = 1; addr < 127; addr++) {
      if (addr == TCAADDR1 || addr == TCAADDR2) continue;  // Skip multiplexer addresses

      Wire.beginTransmission(addr);
      if (Wire.endTransmission() == 0) {  // If a device acknowledges the address
        Serial.print("Found device at address: 0x");
        if (addr < 16) Serial.print("0");  // Add leading zero for addresses < 0x10
        Serial.println(addr, HEX);
        count++;
      }
    }

    if (count == 0) {
      Serial.println("No devices found on this channel.");
    } else {
      Serial.print("Total devices found on channel ");
      Serial.print(channel);
      Serial.print(": ");
      Serial.println(count);
    }
  }
}

void setup() {
  Serial.begin(115200);
  while (!Serial);
  delay(1000);

  Wire.begin(21, 22);  // Initialize I2C (ESP32 default pins: SDA = 21, SCL = 22)
  Serial.println("\nTCA9548A Dual Multiplexer Scanner");

  // Ensure both multiplexers are detected
  Wire.beginTransmission(TCAADDR1);
  if (Wire.endTransmission() != 0) {
    Serial.println("Error: TCA9548A at 0x71 not detected!");
  } else {
    Serial.println("TCA9548A at 0x71 detected successfully.");
  }

  Wire.beginTransmission(TCAADDR2);
  if (Wire.endTransmission() != 0) {
    Serial.println("Error: TCA9548A at 0x70 not detected!");
  } else {
    Serial.println("TCA9548A at 0x70 detected successfully.");
  }

  // Scan each multiplexer
  scanMux(TCAADDR1);  // Scan the first multiplexer
  scanMux(TCAADDR2);  // Scan the second multiplexer
}

void loop() {
  // Nothing to do in the loop
}

Serial Monitor:

TCA9548A at 0x71 detected successfully.
TCA9548A at 0x70 detected successfully.

Scanning Multiplexer at 0x71, Channel 0
Found device at address: 0x29
Total devices found on channel 0: 1

Scanning Multiplexer at 0x71, Channel 1
Found device at address: 0x29
Total devices found on channel 1: 1

Scanning Multiplexer at 0x71, Channel 2
No devices found on this channel.

Scanning Multiplexer at 0x71, Channel 3
No devices found on this channel.

Scanning Multiplexer at 0x71, Channel 4
No devices found on this channel.

Scanning Multiplexer at 0x71, Channel 5
No devices found on this channel.

Scanning Multiplexer at 0x71, Channel 6
No devices found on this channel.

Scanning Multiplexer at 0x71, Channel 7
No devices found on this channel.

Scanning Multiplexer at 0x70, Channel 0
Found device at address: 0x29
Total devices found on channel 0: 1

Scanning Multiplexer at 0x70, Channel 1
Found device at address: 0x29
Total devices found on channel 1: 1

Scanning Multiplexer at 0x70, Channel 2
No devices found on this channel.

Scanning Multiplexer at 0x70, Channel 3
No devices found on this channel.

Scanning Multiplexer at 0x70, Channel 4
No devices found on this channel.

Scanning Multiplexer at 0x70, Channel 5
No devices found on this channel.

Scanning Multiplexer at 0x70, Channel 6
No devices found on this channel.

Scanning Multiplexer at 0x70, Channel 7
No devices found on this channel.

This code correctly detects 2 MUX & 2 Sensors attached to each MUX correctly.

I have used TCA9548A MUX, used 3.3 V, pulled up SDA SCL lines to 5V.

Still sometimes it does not detect MUX, sometimes detects ghost devices, what can I do to make it more reliable. I pulled up using 1K resistors as I had only those available, and I doubt this might be the cause...

Next I will try to get readings from all these sensors, then add full 16 sensors and get readings from them. I am not sure exactly what resistor to use as pull up thought.

Last step will be to add LED strip.

I can't imagine the logic for that, except if you really want to damage something.

I was wondering the same but I read it somewhere...
I have updated circuit to pull up on 3.3V

Still occasionally ghost devices are detected what can I do to avoid this?

Your sensor module might already have pullups onboard (have a look) and even if they don't, I would go with 4.7k or 10k pullups. You could also try pullups on unused channels. And leave them disabled on your code.

@kmin @sterretje @TomGeorge

I tried this setup
6 strips of 60 LEDs on WS2812b ( Powered by PSU 5V 5A)
6 VL53lox Laser ToF sensors via MUX TCA9548A ( powered by 3.3V of ESP32 )
ESP pins used for signaling 13, 12, 14, 27, 25, 32

This is working fine. All LEDs are lite up and as we move hand closer to sensor more LED turn off.

code

#include <Wire.h>                 // I2C communication library
#include <Adafruit_VL53L0X.h>      // VL53L0X distance sensor library
#include <Adafruit_NeoPixel.h>     // NeoPixel LED library

// Define multiplexer address
#define MUX_ADDR 0x70  

// LED strip settings
#define NUM_LEDS 60    // Number of LEDs per strip
#define BRIGHTNESS 30  // Brightness level (0-255)
#define LED_STEP 5     // Number of LEDs to update per cycle (higher = faster updates)  

// Define sensor and LED configurations
const uint8_t sensorChannels[] = {2, 3, 4, 5, 6, 7}; // MUX channels for each sensor
const uint8_t ledPins[] = {13, 12, 14, 27, 25, 32};  // Corresponding LED strip pins
const uint8_t numSensors = sizeof(sensorChannels) / sizeof(sensorChannels[0]); // Total number of sensors

// Sensor and LED objects
Adafruit_VL53L0X sensor = Adafruit_VL53L0X();  // Single VL53L0X sensor instance
Adafruit_NeoPixel strips[numSensors] = {      // Array of NeoPixel strips for each sensor
    Adafruit_NeoPixel(NUM_LEDS, 13, NEO_GRB + NEO_KHZ800),
    Adafruit_NeoPixel(NUM_LEDS, 12, NEO_GRB + NEO_KHZ800),
    Adafruit_NeoPixel(NUM_LEDS, 14, NEO_GRB + NEO_KHZ800),
    Adafruit_NeoPixel(NUM_LEDS, 27, NEO_GRB + NEO_KHZ800),
    Adafruit_NeoPixel(NUM_LEDS, 25, NEO_GRB + NEO_KHZ800),
    Adafruit_NeoPixel(NUM_LEDS, 32, NEO_GRB + NEO_KHZ800)
};

// Variables to store previous sensor readings and LED states
int previousDistances[numSensors] = {0};    // Store previous distance values
int previousLedCounts[numSensors] = {0};   // Store previous LED count for smoother updates

// Function to select a specific sensor channel on the multiplexer
void selectMuxChannel(uint8_t channel) {
    if (channel > 7) return;  // Ensure the channel is within the valid range
    Wire.beginTransmission(MUX_ADDR);  // Begin communication with MUX
    Wire.write(1 << channel);  // Select the desired channel
    Wire.endTransmission();    // End transmission
}

void setup() {
    Serial.begin(115200);  // Initialize serial monitor
    Wire.begin(21, 22);    // Start I2C communication (SDA: 21, SCL: 22)

    Serial.println("Initializing VL53L0X sensors...");
    
    // Initialize each sensor one by one through the multiplexer
    for (uint8_t i = 0; i < numSensors; i++) {
        selectMuxChannel(sensorChannels[i]); // Switch to current sensor channel
        delay(50);  // Short delay to stabilize sensor
        
        // Try initializing the sensor and print the status
        if (sensor.begin()) {
            Serial.print("Sensor initialized on channel ");
            Serial.println(sensorChannels[i]);
        } else {
            Serial.print("Failed to initialize sensor on channel ");
            Serial.println(sensorChannels[i]);
        }
        
        // Reset previous distance and LED count for smooth startup
        previousDistances[i] = 0;
        previousLedCounts[i] = 0;
    }

    // Initialize each LED strip
    for (uint8_t i = 0; i < numSensors; i++) {
        strips[i].begin();           // Start the NeoPixel strip
        strips[i].setBrightness(BRIGHTNESS); // Set LED brightness
        strips[i].show();            // Ensure LEDs are turned off initially
    }
}

void loop() {
    // Read and process each sensor
    for (uint8_t i = 0; i < numSensors; i++) {
        selectMuxChannel(sensorChannels[i]); // Select the sensor
        VL53L0X_RangingMeasurementData_t measure; // Structure to store sensor data
        sensor.rangingTest(&measure, false); // Get distance measurement

        // Process the sensor reading
        int distanceCm;
        if (measure.RangeStatus != 4) { 
            distanceCm = constrain((measure.RangeMilliMeter / 10 / 2) * 2, 10, 60);
        } else {
            distanceCm = 100;  // Out of range β†’ Set a high value to turn all LEDs on
        }

        // Filter out minor fluctuations to make LED changes smoother
        if (abs(distanceCm - previousDistances[i]) > 2) {
            previousDistances[i] = distanceCm;
        } else {
            distanceCm = previousDistances[i];
        }

        // Print the distance value for debugging
        Serial.print(distanceCm);
        Serial.print("\t");

        // Update LED strip based on distance with a gradient color
        setLedCountMaxSpeed(i, distanceCm);
    }
    Serial.println(); // New line in serial output
}

// πŸš€ FUNCTION: Set LED Count with Multi-Color Gradient πŸš€
void setLedCountMaxSpeed(uint8_t stripIndex, int distance) {
    int targetLedCount;

    if (distance >= 60 || distance >= 100) {  
        // πŸš€ When no obstacle (out of range), turn on all LEDs
        targetLedCount = NUM_LEDS;
    } else {  
        // πŸ” Reverse Mapping: More distance = More LEDs
        targetLedCount = map(distance, 10, 60, 2, NUM_LEDS);
    }

    bool updated = false; // Track if we updated any LED

    if (targetLedCount != previousLedCounts[stripIndex]) {
        int step = LED_STEP; // Number of LEDs to update per cycle

        if (targetLedCount > previousLedCounts[stripIndex]) {
            for (int i = 0; i < step && previousLedCounts[stripIndex] < targetLedCount; i++) {
                int ledIndex = previousLedCounts[stripIndex];
                uint32_t color = getGradientColor(ledIndex);
                strips[stripIndex].setPixelColor(ledIndex, color);
                previousLedCounts[stripIndex]++;
                updated = true;
            }
        } else if (targetLedCount < previousLedCounts[stripIndex]) {
            for (int i = 0; i < step && previousLedCounts[stripIndex] > targetLedCount; i++) {
                previousLedCounts[stripIndex]--;
                strips[stripIndex].setPixelColor(previousLedCounts[stripIndex], strips[stripIndex].Color(0, 0, 0));
                updated = true;
            }
        }

        if (updated) {
            strips[stripIndex].show(); // Apply updates only if needed
        }
    }
}

// 🎨 FUNCTION: Generate 4-Color Gradient (Green β†’ Yellow β†’ Red β†’ Deep Red) 🎨
uint32_t getGradientColor(int ledIndex) {
    float position = (float)ledIndex / (NUM_LEDS - 1); // Normalize position (0 to 1)
    int red, green, blue;

    if (position <= 0.25) {  
        // 🟩 Green β†’ Yellow
        red = map(position * 100, 0, 25, 0, 255);
        green = 255;
        blue = 0;
    } 
    else if (position <= 0.66) {  
        // 🟨 Yellow β†’ Bright Red
        red = 255;
        green = map(position * 100, 25, 66, 255, 50);
        blue = 0;
    } 
    else {  
        // πŸ”΄ Bright Red β†’ Deep Red
        red = 255;
        green = map(position * 100, 66, 100, 50, 0);
        blue = 0;
    }

    return strips[0].Color(red, green, blue);
}

Now I want to expand this to 12 Strips which I was able to do successfully using 5V 5A PSU attached video

Video

The problem is when I replace a 5V 5A PSU with a 5V 20A PSU, all LEDS start blinking randomly and they are lit in random colors.

The reason I am replacing PSU is because I think 5A 5V PSU is stretched to limit when powering these 12 strips as some instability can be seen in 1 strip in video but that has settled down automatically now.

As far as I can say 20A supply will provide only as many amps as is demanded from it so 5A is able to handle it then 20A should be able to handle it comfortable. The funny thing is if I connect a different brand 5A 5V PSU even then the setup becomes unstable!

working 5A 5V PSU output voltage 5.5 to 5.7 5V/5A Maxicom Mini SMPS Metal Power Supply

not working 5A 5V output voltage 5.0 to 5.1
METEK-05V/05A-INDUSTRIAL DRIVER, 25W, Voltage: 24 V at best price in Mumbai

not working 20A 5V output voltage 4.8 to 4.9 5V 20A SMPS Power Supply at β‚Ή 350/piece | Switch Mode Power Supply in Jalandhar | ID: 2853915916388

Each LED strip is around 1.2 mts long

I would guess you are getting voltage drop on you LED strings. Measure the voltage at the LEDs and see what you get.