It was supposed to be simple. Three SCD41 CO₂ sensors, one TCA9548A multiplexer, one ESP32-S3. Turn it on, scan the bus, see three devices. Instead I saw: ❌ NO DEVICES FOUND.
Day 1: It Is the Wiring
I checked every wire three times. SDA to GPIO 4. SCL to GPIO 5. 3.3V to VCC. GND to GND. I even color-coded them so I would not get confused. The I²C scanner still showed nothing.
I added 4.7kΩ pull-up resistors because the internet said to. Still nothing. I removed them because maybe the ESP32 internal pull-ups were enough. Still nothing. I put them back because I was desperate. Still nothing.
I went to bed angry.
Day 2: It Is the Address
The SCD41 default address is 0x62. I checked the datasheet. I checked my code. I even printed the address in hex to make sure I was not going crazy. The address was right.
I tried swapping sensors. Maybe one was dead? I tried each sensor directly on the bus without the multiplexer. They all worked individually. The sensors were fine. The ESP32 was fine. The mux was the problem.
I went to bed frustrated.
Day 3: The Multiplexer
I sat down with the TCA9548A datasheet and read it line by line. The control register expects a channel mask: 0x01 for channel 0, 0x02 for channel 1, 0x04 for channel 2.
My code said: Wire.write(1 << ch);
That should work. 1 << 0 = 0x01. 1 << 1 = 0x02. 1 << 2 = 0x04. Perfect, right?
Except... I was calling tcaSelect(ch) with ch = 0, 1, 2 for my three sensors. But somewhere in my init code, I had a copy-paste error where I was passing the wrong variable. One sensor init was calling tcaSelect(0) but then talking to the library as if it was on a different channel.
The mux was switching to channel 0, but my SCD41 library call was trying to read from the default bus without re-selecting the channel afterward. The channel got reset between the mux select and the sensor read.
I fixed it by making tcaSelect() part of every single read and write. No assumptions. The sensor read function now looks like:
tcaSelect(0);
err = scd0.getDataReadyStatus(ready);
if (err == NO_ERROR && ready) {
err = scd0.readMeasurement(co2, t, rh);
}
At 1:14 AM, the Serial Monitor printed:
[OK] Ch0 Cabin running
[OK] Ch1 Purified running
[OK] Ch2 CO2 Tank running
I yelled. My mom knocked on my door to make sure I was okay.
What I Learned
- Never assume the bus state. Always re-select the mux channel before every transaction.
- Sleep matters. Day 1 and Day 2 I was exhausted and chasing wrong theories. Day 3 I was rested and found it in an hour.
- The Serial Monitor is your best friend. Add debug prints. Lots of them. You can remove them later.