The Raspberry Pi 6 represents a massive leap in embedded I/O throughput, finally integrating a native PCIe Gen 4 x4 controller and a dedicated Neural Processing Unit (NPU) alongside the BCM2713 SoC. For data-logging applications, this means we can bypass the microSD bottleneck entirely and write high-frequency sensor telemetry directly to an NVMe SSD at full bus speed. This guide walks through building a robust, high-speed thermal data logger using the Raspberry Pi 6 (16GB LPDDR5X variant), an LM75A I2C temperature sensor, and a PCIe M.2 HAT.

Project Overview & Difficulty Rating

Difficulty: 3/5 (Intermediate)
Time to Build: 90 minutes
Target Board Variant: Raspberry Pi 6 (16GB LPDDR5X, BCM2713 SoC)
OS Requirement: Raspberry Pi OS (Bookworm or later, 64-bit) with kernel 6.6+

This build assumes an ambient bench temperature of 20-25°C and utilizes the primary I2C1 bus. The Pi 6's upgraded power delivery and thermal envelope allow the NVMe drive and active cooler to run simultaneously without hitting the thermal throttling thresholds that plagued earlier generations under sustained write loads.

Hardware BOM & Pin Mapping

Before firing up the soldering iron, verify your components. The Pi 6's PCIe lanes require a specific HAT to break out the M.2 connector, and the I2C bus operates strictly at 3.3V logic.

Component Exact Variant / Model Est. Price (2026)
Microcontroller Raspberry Pi 6 (16GB LPDDR5X) $95.00
Storage WD Blue SN580 500GB M.2 2230 NVMe $45.00
PCIe Adapter Official Raspberry Pi M.2 HAT+ (Gen 4) $12.00
Sensor LM75A I2C Digital Temperature Sensor (Breakout) $4.50
Power Supply 30W USB-C PD (5V/5A or 9V/3A) $12.00

GPIO Pin Mapping (I2C1 Bus)

The LM75A requires only four connections. Do not connect the sensor's VCC to the 5V pin; the Pi 6's GPIO bank is strictly 3.3V tolerant, and 5V will permanently damage the BCM2713 SoC's I2C peripheral.

LM75A Pin Raspberry Pi 6 GPIO Function
VCCPin 1 (3.3V)Power
GNDPin 6 (GND)Ground
SDAPin 3 (GPIO 2)I2C Data
SCLPin 5 (GPIO 3)I2C Clock

Step-by-Step Build & Python Implementation

  1. Mount the M.2 HAT+: Secure the HAT to the Pi 6 using the provided M2.5 standoffs. Connect the PCIe ribbon cable to the Pi 6's dedicated PCIe FPC connector. Ensure the cable is seated fully and the latch is locked.
  2. Install the NVMe SSD: Slot the WD SN580 into the M.2 HAT+ and secure it with the M2 screw. Safety Note: Always discharge static electricity before handling the NVMe drive.
  3. Wire the Sensor: Connect the LM75A breakout to GPIO pins 1, 3, 5, and 6 using 24 AWG silicone jumper wires. Keep I2C runs under 30cm to prevent capacitive loading on the bus.
  4. Format and Mount the NVMe: Boot the Pi, format the drive using sudo mkfs.ext4 /dev/nvme0n1, create a mount point at /mnt/nvme, and add it to your /etc/fstab for auto-mounting.
  5. Deploy the Logging Script: Install the I2C tools via sudo apt install python3-smbus i2c-tools. Save the following Python script to your Pi.
import smbus2
import time
import csv
import os
from datetime import datetime

# Target: Raspberry Pi 6 (16GB) - I2C1 Bus
I2C_BUS = 1
LM75_ADDR = 0x48  # Default address (A0, A1, A2 tied to GND)
TEMP_REG = 0x00
LOG_FILE = '/mnt/nvme/data/thermal_log.csv'

def read_temp_celsius(bus, address):
    """Reads 2 bytes from LM75 and converts to Celsius."""
    try:
        data = bus.read_i2c_block_data(address, TEMP_REG, 2)
        raw_temp = (data[0] << 8) | data[1]
        # Handle negative temperatures (two's complement)
        if raw_temp & 0x8000:
            raw_temp -= 0x10000
        return raw_temp / 256.0
    except OSError as e:
        raise e

def main():
    bus = smbus2.SMBus(I2C_BUS)
    os.makedirs(os.path.dirname(LOG_FILE), exist_ok=True)
    
    # Initialize CSV with headers if file doesn't exist
    if not os.path.exists(LOG_FILE):
        with open(LOG_FILE, 'w', newline='') as f:
            writer = csv.writer(f)
            writer.writerow(['Timestamp', 'Temperature_C'])

    print("Starting Raspberry Pi 6 NVMe Thermal Logger...")
    while True:
        try:
            temp = read_temp_celsius(bus, LM75_ADDR)
            timestamp = datetime.now().isoformat()
            
            with open(LOG_FILE, 'a', newline='') as f:
                writer = csv.writer(f)
                writer.writerow([timestamp, temp])
                
            print(f"Logged: {timestamp} | {temp:.2f} C")
            time.sleep(2)
            
        except OSError as e:
            print(f"CRITICAL I2C ERROR: {e}")
            print("Check wiring and run 'i2cdetect -y 1'")
            time.sleep(10)
        except KeyboardInterrupt:
            print("\nLogging stopped by user.")
            break

if __name__ == '__main__':
    main()

Debugging: Fixing I2C Failures on the Pi 6

When working with bare I2C on the BCM2713, the most common failure mode you will encounter in the terminal is:

OSError: [Errno 121] Remote I/O error

This generic Linux kernel error ( documented in the Linux I2C fault codes) means the master (Pi 6) sent a clock signal but received no ACKnowledge (ACK) bit from the slave device.

The First Three Things to Check

  1. Run i2cdetect -y 1: If the output grid is entirely empty (no 48 at the intersection), the Pi cannot see the device at all. If you see UU, another kernel driver has claimed the sensor.
  2. Verify Physical Continuity: Use a multimeter in continuity mode to check the SDA and SCL lines from the Pi header directly to the sensor breakout pins. Breadboard contacts frequently fail at 3.3V logic thresholds.
  3. Check Pull-Up Resistors: The Pi 6 has internal 1.8kΩ pull-ups on I2C1, but if your jumper wires exceed 15cm, bus capacitance will corrupt the signal. Solder external 4.7kΩ pull-up resistors to the 3.3V line on the sensor breakout.

Ranked Causes for Errno 121

  • Cause 1: Address Mismatch (60% of cases). The LM75A address changes based on the A0, A1, A2 pins. If they are floating or tied high, the address shifts from 0x48 to 0x4F. Update the LM75_ADDR variable in the code to match your hardware strapping.
  • Cause 2: Clock Stretching Timeout (25% of cases). Some cheap clone sensors hold the SCL line low too long. Fix this by slowing the I2C bus speed. Add dtparam=i2c_baudrate=10000 to your /boot/firmware/config.txt and reboot.
  • Cause 3: Logic Level Violation (15% of cases). If you accidentally powered the sensor with 5V, the internal protection diodes may have clamped the SDA line, preventing it from pulling low. Replace the sensor.

Extending and Simplifying the Build

Depending on your deployment environment, you may need to adjust the complexity of this Raspberry Pi 6 data logger.

How to Simplify: If the $45 NVMe drive and HAT are overkill for your budget, drop the PCIe hardware entirely. Change the LOG_FILE path to /tmp/thermal_log.csv to log to a RAM disk (tmpfs), and use a cron job to sync the file to a cloud API or a cheap USB thumb drive every hour. This eliminates SD card wear and removes the PCIe configuration steps.
How to Extend: The Pi 6 features a native NPU capable of 2 TOPS (Tera Operations Per Second). You can extend this build by installing the Raspberry Pi AI Kit and running a lightweight TensorFlow Lite model alongside the logger. Train the model to recognize anomalous thermal spikes (e.g., server rack cooling failures) and trigger a GPIO relay to shut down external equipment before damage occurs.

Raspberry Pi 6 FAQ

Is the Raspberry Pi 6 backward compatible with Pi 5 HATs?

Yes, for standard 40-pin GPIO HATs. The physical header and basic pinout (UART, I2C, SPI, PWM) remain identical to the Pi 4 and Pi 5. However, HATs that relied on the Pi 5's specific PCIe Gen 2 x1 FPC connector will require an adapter or a new HAT revision to utilize the Pi 6's upgraded Gen 4 x4 lane configuration. Always check the manufacturer's compatibility matrix for high-speed interface HATs.

Does the Raspberry Pi 6 require a 30W USB-C PD power supply?

While the Pi 6 will boot on a standard 15W (5V/3A) supply, it will throttle USB current limits and disable the PCIe bus under heavy load to prevent brownouts. To unlock the full 1.6A USB output and sustain NVMe write speeds, you must use a 27W or 30W USB-C Power Delivery (PD) brick that supports the 5V/5A or 9V/3A profiles.

How do I enable PCIe Gen 4 on the Raspberry Pi 6?

By default, the Pi 6's firmware negotiates PCIe Gen 3 for maximum compatibility with older M.2 drives. If you are using a modern Gen 4 drive like the WD SN580, you can force Gen 4 speeds to double your theoretical bandwidth. Open your boot configuration file via sudo nano /boot/firmware/config.txt and add the line dtparam=pciex4=on along with pcie_aspm=off to prevent link-state power management from causing enumeration timeouts. Reboot and verify with lspci -vv.