Skip to main content
Visitor II
September 28, 2026
Question

VL53L9CX on STEVAL-VL53L9: firmware faults on the first frame (ERROR_CODE 0x0F00, REF_ARRAY_ERROR, ref_amplitude = 0) — reproduced with 2 sensor

  • September 28, 2026
  • 2 replies
  • 22 views

Summary

On my STEVAL-VL53L9, the VL53L9CX boots, installs FW patch 0.17, reaches STREAMING with all error bits clear — and then faults on the very first frame. The reference SPAD array reports zero amplitude on every channel, as if the VCSELs never fire.

I have reproduced this with two different VL53L9CX parts and three independent host implementations, each of which is known to work on another STEVAL-VL53L9. I have run out of things I can change from software, and I would like to understand what the firmware error codes mean.

Setup

  • Board: STEVAL-VL53L9, VL53L9CX hand-soldered by me
  • Host: ESP32 (DevKit v1) through the J2 header, plain I2C at 400 kHz, address 0x29
  • Clock: on-board 12 MHz Y1 (R25 removed, R24 fitted, R23 removed); EXT_CLOCK = 12000000
  • J3 (Host IO) = 3.3 V; VDDA_CFG = 2.8 V, VDDIO_CFG = 1.8 V
  • XSHUT driven by the host, SYNC_IN tied high, INTR not used (FRAME_READY polled)
  • FW patch 0.17 (9,865 bytes, byte-identical to vl53l9_patch.h in STSW-IMG053 / 53L9A1 BSP)

Symptom

Default profile (54x42, SHORT context, ULTRA_LOW power, MANUAL sync, 10 ms exposure):

 

power on / load patch / BOOT / version check 0.17 OK

configure OK

START_STREAM fsm = 0x03 STREAMING, ERROR_STATUS = 0x00

TRIGGER_NEXT_FRAME -> FRAME_READY never set

fsm = 0x02 STANDBY

ERROR_CODE = 0x0F00

ERROR_STATUS = 0x80 (FW_ERROR only)

LDD_STATUS[0..4] = 00 00 14 00 00

frame_counter = 0, temperature = 35, ldd_temperature = 0

ref LONG/SHORT amplitude = 0 on both channels

START_STREAM again (no XSHUT cycle) -> accepted, next frame faults with

ERROR_CODE = 0x0008, ERROR_STATUS = 0x80, LDD_STATUS all 0

With CSI-2 output and the ESP32-P4 project's profile (binning 2, AUTONOMOUS, 10 ms period, 4 ms exposure, DSS off), the fault happens within 100 ms of START_STREAM, and this time the reference-array check is reported explicitly:

 

fsm = 0x02 STANDBY ERROR_CODE = 0x0F00 ERROR_STATUS = 0xC0 (FW_ERROR | REF_ARRAY_ERROR)

LDD_STATUS[0..4] = 00 00 14 00 00 frame_counter = 1

ref_amplitude = 0 on all channels

I_LIMIT, VHV_UNDERVOLTAGE, VHV_OVERVOLTAGE, SPAD_SUPPLY_OVERLOAD, PLL_LOCK and SOF_OUTSIDE_BLANKING are never set.

What I have ruled out

Host software — three independent implementations, same result:

  1. A port of the ST core driver (vl53l9.c, core 1.0.0) with my own ESP32 platform layer.
  2. The same port configured to reproduce kamibukuro5656/VL53L9CX_ESP32-P4_USB_ROS2 exactly (works on a STEVAL-VL53L9 over MIPI CSI-2): same call order, same profile, same CSI-2 settings.
  3. A from-scratch C++ port of VanBruce/vl53l9cx-python (works on a STEVAL-VL53L9 + Raspberry Pi 5 over plain I2C): same boot timing, same default profile, same frame-read procedure.

All three follow the I2C rules I know of: index write and data read as separate transactions with a STOP in between (no repeated START), no address-only probe transactions. A 4 KB write/read-back to 0x1800 matches exactly, the model ID reads 0x53334C39, and the patch version check passes.

The sensor: I replaced the VL53L9CX with a second, previously unused part. The calibration data returned by vl53l9_get_calib_data() differs between the two (1839 vs 1806 non-zero bytes), so they are genuinely different dies. Both fail the same way.

The board:

  • R23 / R24 / R25 visually checked: removed / fitted / removed
  • J3 on 3.3 V, SYNC_IN measured at 3.3 V
  • P3V3 (VBAT_LDD / VBAT_RX supply) measured at C6/C7: 3.3 V, and it stays at 3.3 V at the moment the frame is triggered
  • AVDD 2.8 V, IOVDD 1.8 V, DVDD 1.2 V all good
  • Clock: COMMAND_SWITCH_TO_FAST_CLOCK succeeds and a 2332-byte burst read works on the PLL clock; I also re-soldered the oscillator — no change
  • No brownout or reset on either side at any point

Configuration variations that did not change the outcome: SHORT / LONG context; binning 2 / 8 / 12; exposure 1 / 4 / 10 ms; REGULAR / ULTRA_LOW power; MANUAL / AUTONOMOUS sync; I3C / CSI-2 output; DSS on / off.

Questions

  1. What do ERROR_CODE 0x0F00, 0x0903 and 0x0008 mean? (0x0903 appears with CSI-2 output and binning 12; 0x0008 after restarting the stream without an XSHUT cycle.) This is the one piece of information I cannot get anywhere else.
  2. What does LDD_STATUS[2] = 0x14 indicate? It is set whenever the firmware gets as far as configuring the laser driver.
  3. What would an open VBAT_LDD (B1) joint look like? I hand-soldered both parts and cannot inspect the LGA joints. Would an open B1 produce exactly this signature (FW_ERROR / REF_ARRAY_ERROR, no I_LIMIT or VHV bits, ref_amplitude = 0, ldd_temperature = 0), or would the laser driver report it differently?
  4. Is CAL_TARGET_LD (0x049C) = 0x0000 expected after boot? The driver never writes it; I assume it comes from OTP.
  5. Is there any initialization step that enables the laser driver and is not exposed through the public driver API?

Appendix: possible issues in the ST core driver (vl53l9.c, core 1.0.0)

Found while porting. Separate from the question above, but may be worth a look.

  1. _init_default_config() ends with vl53l9_read32(..., CAB_DIST_SCALE, &data) right after setting data = 0x01000800; it should be vl53l9_write32. On my device the register read back 0x00000000 before I changed it.
  2. vl53l9_get_status() reads the five LDD status bytes into status->laser_driver (index 0) every time; it should be &status->laser_driver[i].
  3. Several enum locals (FSM state, sync mode, context, DSS mode) are filled with vl53l9_read8(). This works with 1-byte enums (-fshort-enums, the ARM EABI default) but leaves three bytes of stack garbage with 4-byte enums.
  4. vl53l9_set_hw_config() masks the status-line data type with 0x2F; the field is GENMASK(5, 0), so bit 4 is dropped.
  5. _wait_for_state() and _write_cmd() report a timeout when the expected state or command completion arrives on the last polling iteration.

Thanks in advance for any pointers.

2 replies

MeganeG
ST Technical Moderator
September 29, 2026

Hello ​@76EHwan,

You can find all the error codes information in the UM3683 Programming guide user manual (link).

No, there is no initialization required for the laser driver; everything is handled by the firmware in the device.

It is possible that your devices are not properly powered. Could you probe the three power supplies and let us know the values? 

Thanks in advance, Megane

In order to give better visibility on the answered topics, please click on 'Best answer' on the reply which solved your issue or answered your question.
Visitor
September 29, 2026

I am seeing what appears to be the same issue on my STEVAL-VL53L9 setup, so I wanted to add my findings in case they help identify the root cause.

My setup is:

  • STEVAL-VL53L9 / VL53L9CX

  • Raspberry Pi 4

  • Connected through the Raspberry Pi camera/FFC interface

  • I2C communication through /dev/i2c-10

  • VL53L9CX detected at address 0x29

  • XSHUT held high

  • The setup was previously functional and I was successfully receiving ranging/depth data.

Initially, I was using a lower-resolution configuration with binning 4 (24x20 zones), and the sensor was successfully streaming ranging data.

I then started experimenting with increasing the amount of data/resolution and attempted to configure the sensor for binning 2 (54x42 zones). The driver itself accepts the configuration:

vl53l9_set_binning(..., 2) = 0
vl53l9_set_frame_period(...) = 0

and the sensor remains in STANDBY (FSM = 0x02) after configuration.

However, after these tests the sensor stopped producing frames.

The important part is that I then returned completely to my original working source code and configuration (binning 4 / 24x20), but the original code no longer produces ranging frames either.

I verified that the original tracked source files were unchanged using Git, so this is not simply a case of accidentally modifying the previously working driver.

After a cold power cycle, I now typically see the following behavior:

  1. VL53L9CX is detected normally at 0x29.

  2. The first initialization attempt can return VL53L9_ERROR_TIMEOUT (-5).

  3. After that attempt, the device is actually in STANDBY (FSM = 0x02).

  4. A second run reaches the ranging loop, but FRAME_READY never becomes asserted and no depth frame is received.

Reading the device status after this gives:

FSM           : 0x02
COMMAND_ERROR : 0x00
FRAME_READY : 0x00

Firmware error : 0x0F00

VHV overvoltage : 0
VHV undervoltage : 0
SPAD supply overload : 0
HV boost limit : 0
SOF outside blanking : 0
PLL lock error : 0
Reference array error : 1
Internal FW error : 1

Laser drivers:
0x00 0x00 0x00 0x00 0x00

So this looks very similar to the 0x0F00 + REF_ARRAY_ERROR + FW_ERROR behavior reported in this thread.

What is particularly interesting in my case is that this exact STEVAL-VL53L9 board and Raspberry Pi/FFC setup was successfully ranging before I experimented with the higher-resolution configuration. After the issue appeared, going back to the previously working software/configuration did not recover the sensor, even after power cycling.

I would therefore also be very interested to know what exactly firmware error 0x0F00 represents and whether this condition can leave the device in some persistent fault/calibration state that requires a specific reset/recovery procedure.

Is there a recommended procedure to fully reset/recover the VL53L9CX from this condition?

Also, could changing the binning/resolution from 24x20 to 54x42, or starting the sensor with an inconsistent output configuration, trigger this type of reference-array/internal firmware fault?

I can provide additional register dumps or run specific diagnostic tests on the Raspberry Pi if that would help.