#!/usr/bin/env python3 """NAU7802 Scale Diagnostic — ported from SpoolBuddy Rust firmware. I2C address: 0x2A Bus: /dev/i2c-0 (GPIO0/GPIO1 on RPi) """ import struct import sys import time import smbus2 I2C_BUS = 0 NAU7802_ADDR = 0x2A # Register addresses REG_PU_CTRL = 0x00 REG_CTRL1 = 0x01 REG_CTRL2 = 0x02 REG_ADCO_B2 = 0x12 # ADC output MSB REG_ADCO_B1 = 0x13 REG_ADCO_B0 = 0x14 # ADC output LSB REG_ADC = 0x15 REG_PGA = 0x1B REG_PWR_CTRL = 0x1C REG_REVISION = 0x1F # PU_CTRL bits PU_RR = 0x01 # Register reset PU_PUD = 0x02 # Power up digital PU_PUA = 0x04 # Power up analog PU_PUR = 0x08 # Power up ready (read-only) PU_CS = 0x10 # Cycle start PU_CR = 0x20 # Cycle ready (read-only) PU_OSCS = 0x40 # Oscillator select PU_AVDDS = 0x80 # AVDD source select class NAU7802: def __init__(self, bus=I2C_BUS, addr=NAU7802_ADDR): self._bus = smbus2.SMBus(bus) self._addr = addr def close(self): self._bus.close() def read_reg(self, reg: int) -> int: return self._bus.read_byte_data(self._addr, reg) def write_reg(self, reg: int, val: int): self._bus.write_byte_data(self._addr, reg, val & 0xFF) def init(self): """Initialize NAU7802 — matches Rust firmware init sequence.""" revision = self.read_reg(REG_REVISION) print(f" Revision: 0x{revision:02X}") # Reset self.write_reg(REG_PU_CTRL, PU_RR) time.sleep(0.010) self.write_reg(REG_PU_CTRL, 0x00) # Power up digital + analog self.write_reg(REG_PU_CTRL, PU_PUD | PU_PUA) # Wait for power-up ready for _ in range(100): status = self.read_reg(REG_PU_CTRL) if status & PU_PUR: print(" Power-up ready") break time.sleep(0.001) else: raise TimeoutError("NAU7802 power-up timeout") # Sample rate: 10 SPS (bits 6:4 of CTRL2 = 0b000) ctrl2 = self.read_reg(REG_CTRL2) self.write_reg(REG_CTRL2, (ctrl2 & 0x8F) | (0 << 4)) print(" Sample rate: 10 SPS") # Gain: 128x (bits 2:0 of CTRL1 = 0b111) ctrl1 = self.read_reg(REG_CTRL1) self.write_reg(REG_CTRL1, (ctrl1 & 0xF8) | 7) print(" Gain: 128x") # LDO: 3.3V (bits 5:3 of CTRL1 = 0b100) ctrl1 = self.read_reg(REG_CTRL1) self.write_reg(REG_CTRL1, (ctrl1 & 0xC7) | (0b100 << 3)) # Enable internal LDO (bit 7 of CTRL1) ctrl1 = self.read_reg(REG_CTRL1) self.write_reg(REG_CTRL1, ctrl1 | 0x80) print(" LDO: 3.3V (internal)") # Start conversion cycle pu_ctrl = self.read_reg(REG_PU_CTRL) self.write_reg(REG_PU_CTRL, pu_ctrl | PU_CS) print(" Conversion started") def data_ready(self) -> bool: return bool(self.read_reg(REG_PU_CTRL) & PU_CR) def read_raw(self) -> int: """Read 24-bit signed ADC value.""" b2 = self.read_reg(REG_ADCO_B2) b1 = self.read_reg(REG_ADCO_B1) b0 = self.read_reg(REG_ADCO_B0) raw = (b2 << 16) | (b1 << 8) | b0 # Sign extend 24-bit to 32-bit if raw & 0x800000: raw |= 0xFF000000 raw = struct.unpack("i", struct.pack("I", raw))[0] return raw def main(): print("=" * 60) print("NAU7802 Scale Diagnostic") print("=" * 60) scale = NAU7802() try: print("[1] Initializing...") scale.init() print("[2] Waiting for first reading...") for _ in range(200): if scale.data_ready(): break time.sleep(0.010) else: print(" Timeout waiting for data ready") sys.exit(1) print("[3] Reading 10 samples (10 SPS = ~1 second)...") readings = [] for i in range(10): # Wait for data ready for _ in range(200): if scale.data_ready(): break time.sleep(0.010) raw = scale.read_raw() readings.append(raw) print(f" Sample {i + 1:2d}: {raw:>10d}") avg = sum(readings) / len(readings) spread = max(readings) - min(readings) print(f"\n Average: {avg:>10.0f}") print(f" Min: {min(readings):>10d}") print(f" Max: {max(readings):>10d}") print(f" Spread: {spread:>10d}") print("\n" + "=" * 60) print("Diagnostic complete!") print("=" * 60) except Exception as e: print(f"\nERROR: {e}") import traceback traceback.print_exc() sys.exit(1) finally: scale.close() if __name__ == "__main__": main()