ruvnet/RuView · error · CSIParseError

Invalid ESP32 CSI data format

Error message

Invalid ESP32 CSI data format

What it means

After utf-8 decoding, ESP32CSIParser requires the payload to start with the literal 'CSI_DATA:' prefix; anything else raises CSIParseError('Invalid ESP32 CSI data format'). This parser understands only that exact CSV text protocol, so the error means the bytes on the wire are not a text CSI frame — logs, boot output, binary frames, or garbage.

Source

Thrown at archive/v1/src/hardware/csi_extractor.py:74

    def parse(self, raw_data: bytes) -> CSIData:
        """Parse ESP32 CSI data format.
        
        Args:
            raw_data: Raw bytes from ESP32
            
        Returns:
            Parsed CSI data
            
        Raises:
            CSIParseError: If data format is invalid
        """
        if not raw_data:
            raise CSIParseError("Empty data received")
        
        try:
            data_str = raw_data.decode('utf-8')
            if not data_str.startswith('CSI_DATA:'):
                raise CSIParseError("Invalid ESP32 CSI data format")
            
            # Parse ESP32 format: CSI_DATA:timestamp,antennas,subcarriers,freq,bw,snr,[amp],[phase]
            parts = data_str[9:].split(',')  # Remove 'CSI_DATA:' prefix
            
            timestamp_ms = int(parts[0])
            num_antennas = int(parts[1])
            num_subcarriers = int(parts[2])
            frequency_mhz = float(parts[3])
            bandwidth_mhz = float(parts[4])
            snr = float(parts[5])
            
            # Convert to proper units
            frequency = frequency_mhz * 1e6  # MHz to Hz
            bandwidth = bandwidth_mhz * 1e6  # MHz to Hz
            
            # Parse amplitude and phase arrays from the remaining CSV fields.
            # Expected format after the header fields: comma-separated float values
            # representing interleaved amplitude and phase per antenna per subcarrier.

View on GitHub (pinned to 4685618388)

Solutions

  1. Filter before parsing: only pass bytes whose stripped value starts with b'CSI_DATA:' (see validationCode)
  2. If the firmware emits ADR-018 binary frames, construct CSIExtractor with parser_format='binary' so ESP32BinaryParser is selected
  3. Flash a firmware build that outputs the CSI_DATA CSV line format per docs/hardware-setup.md

Example fix

# before
for chunk in stream:
    data = parser.parse(chunk)  # raises on every log/boot line

# after
for line in stream:
    line = line.strip()
    if not line.startswith(b'CSI_DATA:'):
        continue  # skip console noise
    data = parser.parse(line)
Defensive patterns

Strategy: validation

Validate before calling

line = raw.strip()
if not line.startswith(b'CSI_DATA:'):
    return None  # console/log noise or wrong protocol
return parser.parse(line)

Type guard

def is_esp32_text_csi(raw: bytes) -> bool:
    return bool(raw) and raw.strip().startswith(b'CSI_DATA:')

Try / catch

try:
    data = parser.parse(raw)
except CSIParseError as e:
    logger.debug('non-CSI line dropped: %r -> %s', raw[:40], e)

Prevention

When it happens

Trigger: Feeding ESP32 boot/log lines ('rst:0x1 ...', 'I (320) wifi:...') printed on the same UART; sending an ADR-018 binary frame while config parser_format is not 'binary'; leading whitespace/CR before the prefix; firmware that prints a different CSV header.

Common situations: Firmware console logs interleaved with CSI output on one serial line; forgetting parser_format='binary' when the node streams ADR-018 frames; chunked reads that begin mid-line so the prefix is missed.

Related errors


AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16). Data as JSON: /api/errors/8d045f6635e24134. Report an issue: GitHub.