bambuddy/spoolbuddy/scripts/scale_diag.py
Keybored eb3450b1d0
Spoolbuddy init and diagnostic improvements (#814)
Spoolbuddy init and diagnostic improvements (#814)
2026-03-26 16:16:09 +01:00

228 lines
7.1 KiB
Python

#!/usr/bin/env python3
"""NAU7802 Scale Diagnostic.
I2C address: 0x2A
Bus: /dev/i2c-1 (GPIO2/GPIO3 on RPi)
"""
import os
import sys
import time
import smbus2
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "daemon")))
from nau7802 import NAU7802
def _env_int(name: str, default: int) -> int:
value = os.environ.get(name)
if value is None or value == "":
return default
try:
return int(value)
except ValueError:
return default
I2C_BUS = _env_int("SPOOLBUDDY_I2C_BUS", 1)
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
def main():
print("=" * 60)
print("NAU7802 Scale Diagnostic")
print("=" * 60)
print(f"Configured bus: {I2C_BUS}, address: 0x{NAU7802_ADDR:02X}")
# Probe both common I2C buses and show where devices are actually visible.
found_by_bus: dict[int, list[int]] = {}
for bus_num in (0, 1):
found_by_bus[bus_num] = []
try:
with smbus2.SMBus(bus_num) as probe_bus:
for addr in range(0x03, 0x78):
try:
probe_bus.read_byte(addr)
found_by_bus[bus_num].append(addr)
except OSError:
continue
except FileNotFoundError:
continue
except PermissionError:
continue
for bus_num, addrs in found_by_bus.items():
if addrs:
pretty = " ".join(f"0x{a:02X}" for a in addrs)
print(f"Bus {bus_num} devices: {pretty}")
else:
print(f"Bus {bus_num} devices: (none)")
if NAU7802_ADDR not in found_by_bus.get(I2C_BUS, []):
for alt in (1, 0):
if alt != I2C_BUS and NAU7802_ADDR in found_by_bus.get(alt, []):
print(f"\nHint: NAU7802 (0x{NAU7802_ADDR:02X}) appears on bus {alt}, not configured bus {I2C_BUS}.")
print(f"Try: SPOOLBUDDY_I2C_BUS={alt} .../scale_diag.py")
break
scale = NAU7802()
try:
print("[1] Initializing...")
scale.init()
# Print key interpreted config values
revision = scale.read_reg(REG_REVISION)
revision_id = revision & 0x0F
print(f" Revision ID: {revision_id}")
# PU_CTRL bit 7: AVDD source select
pu_ctrl = scale.read_reg(REG_PU_CTRL)
avdds = (pu_ctrl >> 7) & 0x1
avdds_str = "Internal LDO" if avdds == 1 else "AVDD pin input"
print(f" AVDD source: {avdds_str}")
ctrl1 = scale.read_reg(REG_CTRL1)
vldo = (ctrl1 >> 3) & 0b111
vldo_map = {
0b111: "2.4V",
0b110: "2.7V",
0b101: "3.0V",
0b100: "3.3V",
0b011: "3.6V",
0b010: "3.9V",
0b001: "4.2V",
0b000: "4.5V",
}
vldo_str = vldo_map.get(vldo, f"Unknown ({vldo})")
gain = ctrl1 & 0b111
gain_map = {
0b000: "1x",
0b001: "2x",
0b010: "4x",
0b011: "8x",
0b100: "16x",
0b101: "32x",
0b110: "64x",
0b111: "128x",
}
gain_str = gain_map.get(gain, f"Unknown ({gain})")
print(f" LDO setting (VLDO): {vldo_str}")
print(f" Gain setting: {gain_str}")
ctrl2 = scale.read_reg(REG_CTRL2)
sps = (ctrl2 >> 4) & 0b111
sps_map = {
0b000: "10 SPS",
0b001: "20 SPS",
0b010: "40 SPS",
0b011: "80 SPS",
0b100: "320 SPS",
}
sps_str = sps_map.get(sps, f"Unknown ({sps})")
print(f" Sample rate: {sps_str}")
adc = scale.read_reg(REG_ADC)
chopper = (adc >> 4) & 0b11
chopper_str = {0b00: "Enabled", 0b01: "Enabled", 0b10: "Enabled", 0b11: "Disabled"}.get(
chopper, f"Unknown ({chopper})"
)
print(f" ADC chopper: {chopper_str}")
pga = scale.read_reg(REG_PGA)
low_esr = (pga >> 6) & 0x1
low_esr_str = "Enabled" if low_esr == 0 else "Disabled"
print(f" PGA low-ESR caps: {low_esr_str}")
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(" 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}")
is_known_error = False
if isinstance(e, OSError):
if e.errno == 16: # Device or resource busy
is_known_error = True
print("\nI2C DEVICE BUSY (Errno 16): Another process is using the I2C bus.")
print("This typically means the SpoolBuddy daemon is already reading the scale.")
print("\nTo run this diagnostic, stop the daemon first:")
print(" sudo systemctl stop bambuddy")
print(" # Run diagnostic")
print(" .../scale_diag.py")
print(" # Restart daemon when done:")
print(" sudo systemctl start bambuddy")
elif e.errno == 121:
is_known_error = True
print("\nI2C NACK (Errno 121): the device did not acknowledge reads at 0x2A.")
print("Check:")
print(" - NAU7802 SDA/SCL are on the configured bus pins")
print(" - 3.3V and GND are correct and stable")
print(" - Sensor address is really 0x2A")
print(" - No loose wire or swapped SDA/SCL")
else:
print(f"\nI2C Error (Errno {e.errno}): {e}")
# Only print full traceback for unexpected errors
if not is_known_error:
import traceback
traceback.print_exc()
sys.exit(1)
finally:
scale.close()
if __name__ == "__main__":
main()