Files
bambuddy/spoolbuddy/scripts/scale_diag.py
T
maziggy 2b1fe03159 Fix SpoolBuddy scale first reading and extract hardware drivers
The NAU7802 ADC returns a stale max-scale value (0x7FFFFF) on its
  first conversion after power-up, polluting the moving average and
  making the initial weight report wildly inaccurate. Flush the first
  reading during init().

  Also extract both hardware drivers out of diagnostic scripts into
  proper daemon modules:
  - NAU7802 scale driver: scripts/scale_diag.py -> daemon/nau7802.py
  - PN5180 NFC driver: scripts/read_tag.py -> daemon/pn5180.py

  The production daemon was importing driver classes from test scripts
  since the original SpoolBuddy commit. Diagnostic scripts now import
  from the driver modules. Removed the sys.path hack from main.py.
2026-03-25 11:57:28 +01:00

134 lines
4.4 KiB
Python

#!/usr/bin/env python3
"""NAU7802 Scale Diagnostic.
Standalone diagnostic script — the NAU7802 driver lives in
spoolbuddy/daemon/nau7802.py and is imported from there.
"""
import sys
import time
from pathlib import Path
import smbus2
# Add daemon package to sys.path so we can import the driver
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from daemon.nau7802 import I2C_BUS, NAU7802, NAU7802_ADDR
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(" Initialized OK")
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}")
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()