[RTL8722/Muon] I2C requestFrom() corrupts every 3rd received byte

Summary

On Particle Muon (RTL8722DM / msom), when reading 30 bytes from an I2C device behind a PCA9546A mux, Wire.requestFrom(addr, 30) returns 30 but every 3rd byte (indices 2, 5, 8, 11, …) is wrong. The 1st and 2nd byte of each 3-byte group look correct. This points to a HAL or DMA stride/alignment bug in the I2C RX path, not application or bus wiring.

Environment

  • Device: Particle Muon (RTL8722DM), platform msom
  • Device OS version: 6.3.4
  • Setup: Sensirion SPS30 (0x69) on I2C behind NXP PCA9546A (0x70) on channel 0. Same bus: 50 kHz, endTransmission(true), 500 µs delay after mux select.
  • Repro: Minimal app (no SPS30 library): set pointer 0x0300, delay 100 ms, mux select + 500 µs, requestFrom(0x69, 30), then 30× Wire.read().

Expected behavior

SPS30 returns 10 × (2 data bytes + 1 CRC byte) = 30 bytes. Sensirion CRC-8 (poly 0x31) over each 2-byte pair should match the 3rd byte. On other platforms (e.g. generic esp32/arduino) the same sequence yields all CRCs OK.

Actual behavior

  • requestFrom(0x69, 30) returns 30 (no truncation).
  • Raw 30 bytes are identical on every read (not random noise).
  • All 10 CRCs fail. The 3rd byte of each triplet (the CRC byte) is wrong; the first two bytes of each triplet look plausible (e.g. 01 01, 00 00, 13 3C, …).

Example raw frame (repeated every read):

01 01 00  00 00 00  00 00 00  00 00 40  13 3C E0  26 00 9C  75 00 23  00 00 00  00 22 00  00 00 F0
  • Word 0: data 01 01, expected CRC 0x44, got 0x00
  • Word 1: data 00 00, expected CRC 0x81, got 0x00
  • Word 2: data 00 00, expected CRC 0x81, got 0x00
  • Word 3: data 00 00, expected CRC 0x81, got 0x40
  • Words 4–9: data bytes look reasonable; CRC bytes still do not match computed CRC.

So indices 2, 5, 8, 11, … (every 3rd byte) are corrupted or replaced; indices 0,1, 3,4, 6,7, … look correct. This is consistent with a HAL/DMA copy or buffer layout bug that affects every 3rd byte (e.g. 32-bit vs 24-bit alignment or wrong stride).

Notes

  • Muon HAL I2C buffer size (256 bytes) is not the cause; 30 < 256 and the issue is value corruption, not length.
  • Mux timing (500 µs after select) and 50 kHz clock are applied; flushing Wire RX and 100 ms delay between set-pointer and read do not fix the corruption.
  • Workaround attempt: Reading in 10 separate requestFrom(addr, 3) transactions (one Sensirion word per transaction) still yields the same corrupted frame and all CRCs BAD. So the bug affects even 3-byte reads, not only longer ones.
  • Pull-ups: 2.2 kΩ added on mux downstream (SDA and SCL to 3.3 V). Raw frame changed slightly (e.g. different leading byte) but all 10 CRCs still BAD. Confirms corruption is HAL, not bus level.

Request

Please check the RTL8722 I2C HAL (and any DMA or buffer handling used for requestFrom / read path) for a stride or alignment bug that could corrupt every 3rd received byte. If possible, suggest a workaround or HAL patch for Muon.

Minimal repro code

Can be provided on request (Particle app: set pointer 0x0300, requestFrom(0x69, 30), 30× read, Sensirion CRC check, no external library).

Hi @ntableman

I think this is a big clue to what is going on. For the i2c mux you need pull-ups on both sides of the chip as in this diagram from the data sheet. Is this how you have it wired?

1 Like

The Muon has 3.3K pull-ups on SDA and SCL to 3V3 on the MCU to HAT connector side. I double-checked that because that was one of the first things I thought of.

1 Like

Hi @ntableman

Another good thing to check is what else is on that i2c bus? Both on the Muon side and on the other side of the mux. If you can temporarily remove those other connections that might help to debug this.

For i2c I also like to ask what the wiring is like for the data and clock, but also for power and ground to the slaves. We have had people run i2c over many meters of wire for various reasons, which can be done, but you must be careful about how much noise margin you have to get a reliable system.

Let me try to make sure I answer all the great and helpful questions-

1 this is muon/m1e setup and I am using the stemma qt/qwiic connectors. Behavior is the same on both.

2 wires are all about 1.5 “

3 the mux has resistors on the input side

4 I added them on the output side

5 speeds are 100 as is the norm (I think)

6 I has no other devices on the i2c bus other than the ones built in to the m1e/muon and the entire reason for this is that seemingly i2c 2 is only half exposed and an internal device is on 69 as is my sps30…

One thing that I do not have is the reset wired because it is not covered by the 4 wires or the qwiic cables. I didn’t see that connection as needed in the past. Is it here - or was I lucky in the past!

Let me know if this helps! If I missed any questions let me know…I’ll get the answers asap. I appreciate the help.

I assume you've connected the SPS30 to 5V but the pull-ups to 3V3. That is the correct way.

Also have you seen this ominous note at Sparkfun? Which values are you reading?

Sensirion has written drivers for both the UART protocol and I2C. Unfortunately we've found the I2C is limited to only mass concentrations (not number concentrations) using the Arduino platform. So if you plan to use this sensor with an Arduino, use the UART interface. Both interfaces are described in their datasheet.

I didn't dig into it enough to determine why it doesn't work on Arduino, but it's worth looking into because it could also affect Particle.

If you do end up needing to switch to UART, the SC16IS752 works well on Particle devices using the SC16IS7xxRK library. That's the dual UART serial version and works over both I2C and SPI to the MCU.

1 Like

Yes the SPS30 is on 5v, and I am aware of the notes on this device and have used them for years on other controllers and systems, I have 5 here running on esp32's right now under Arduino.

This 3rd byte issue is happening on Sen5x and Sen6x air sensors as well, I guess I'll try a few SC16IS752's and see what I can get working.

I also determined that the reset has a resister on built in, so it should not be involved here.

I’m seeing the same I2C corruption with SEN55 (Sen5x) on Muon. A few details that might help narrow it down:

What I see (not only every 3rd byte):

  • Single requestFrom(24) for the SEN55 measurement block. We get 24 bytes back, but the payload is wrong.

  • Byte 3 is often 0xD2 (SEN55 I2C write address), i.e. address leaking into the data stream.

  • Other data bytes are wrong too (e.g. PM2.5, temperature, VOC index), so it’s not limited to every-3rd-byte or CRC-only.

  • Data-ready poll often times out even when the sensor has new data, which could point to the same HAL/read-path issue.

What we’ve already tried:

  • Wire.setClock(100000) and Wire.setClock(50000) after Wire.begin() — corruption persists.

  • SEN55 is behind a PCA9546A mux (channel 0); we use endTransmission(true) and 500 µs delay after mux select. Same I2C bus as other sensors (BME280, SHT30, etc.) that read correctly with shorter transactions.

Diagnostics we can share:

  • We log the raw 24-byte read (hex) and a small set of flags (bytes received, data-ready result, exit reason). Happy to paste example payloads and describe the exact read sequence (mux select → delay → read command → requestFrom) if that would help.

Ask:

  • Is there a known workaround (e.g. smaller requestFrom chunks, or a different HAL branch) that we should try on Muon for Sensirion Sen5x/Sen6x?

  • Can we get this prioritized for a HAL fix or documented workaround?