scale_diag.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. #!/usr/bin/env python3
  2. """NAU7802 Scale Diagnostic.
  3. Standalone diagnostic script — the NAU7802 driver lives in
  4. spoolbuddy/daemon/nau7802.py and is imported from there.
  5. """
  6. import sys
  7. import time
  8. from pathlib import Path
  9. import smbus2
  10. # Add daemon package to sys.path so we can import the driver
  11. sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
  12. from daemon.nau7802 import I2C_BUS, NAU7802, NAU7802_ADDR
  13. def main():
  14. print("=" * 60)
  15. print("NAU7802 Scale Diagnostic")
  16. print("=" * 60)
  17. print(f"Configured bus: {I2C_BUS}, address: 0x{NAU7802_ADDR:02X}")
  18. # Probe both common I2C buses and show where devices are actually visible.
  19. found_by_bus: dict[int, list[int]] = {}
  20. for bus_num in (0, 1):
  21. found_by_bus[bus_num] = []
  22. try:
  23. with smbus2.SMBus(bus_num) as probe_bus:
  24. for addr in range(0x03, 0x78):
  25. try:
  26. probe_bus.read_byte(addr)
  27. found_by_bus[bus_num].append(addr)
  28. except OSError:
  29. continue
  30. except FileNotFoundError:
  31. continue
  32. except PermissionError:
  33. continue
  34. for bus_num, addrs in found_by_bus.items():
  35. if addrs:
  36. pretty = " ".join(f"0x{a:02X}" for a in addrs)
  37. print(f"Bus {bus_num} devices: {pretty}")
  38. else:
  39. print(f"Bus {bus_num} devices: (none)")
  40. if NAU7802_ADDR not in found_by_bus.get(I2C_BUS, []):
  41. for alt in (1, 0):
  42. if alt != I2C_BUS and NAU7802_ADDR in found_by_bus.get(alt, []):
  43. print(f"\nHint: NAU7802 (0x{NAU7802_ADDR:02X}) appears on bus {alt}, not configured bus {I2C_BUS}.")
  44. print(f"Try: SPOOLBUDDY_I2C_BUS={alt} .../scale_diag.py")
  45. break
  46. scale = NAU7802()
  47. try:
  48. print("[1] Initializing...")
  49. scale.init()
  50. print(" Initialized OK")
  51. print("[2] Waiting for first reading...")
  52. for _ in range(200):
  53. if scale.data_ready():
  54. break
  55. time.sleep(0.010)
  56. else:
  57. print(" Timeout waiting for data ready")
  58. sys.exit(1)
  59. print("[3] Reading 10 samples (10 SPS = ~1 second)...")
  60. readings = []
  61. for i in range(10):
  62. # Wait for data ready
  63. for _ in range(200):
  64. if scale.data_ready():
  65. break
  66. time.sleep(0.010)
  67. raw = scale.read_raw()
  68. readings.append(raw)
  69. print(f" Sample {i + 1:2d}: {raw:>10d}")
  70. avg = sum(readings) / len(readings)
  71. spread = max(readings) - min(readings)
  72. print(f"\n Average: {avg:>10.0f}")
  73. print(f" Min: {min(readings):>10d}")
  74. print(f" Max: {max(readings):>10d}")
  75. print(f" Spread: {spread:>10d}")
  76. print("\n" + "=" * 60)
  77. print("Diagnostic complete!")
  78. print("=" * 60)
  79. except Exception as e:
  80. print(f"\nERROR: {e}")
  81. is_known_error = False
  82. if isinstance(e, OSError):
  83. if e.errno == 16: # Device or resource busy
  84. is_known_error = True
  85. print("\nI2C DEVICE BUSY (Errno 16): Another process is using the I2C bus.")
  86. print("This typically means the SpoolBuddy daemon is already reading the scale.")
  87. print("\nTo run this diagnostic, stop the daemon first:")
  88. print(" sudo systemctl stop bambuddy")
  89. print(" # Run diagnostic")
  90. print(" .../scale_diag.py")
  91. print(" # Restart daemon when done:")
  92. print(" sudo systemctl start bambuddy")
  93. elif e.errno == 121:
  94. is_known_error = True
  95. print("\nI2C NACK (Errno 121): the device did not acknowledge reads at 0x2A.")
  96. print("Check:")
  97. print(" - NAU7802 SDA/SCL are on the configured bus pins")
  98. print(" - 3.3V and GND are correct and stable")
  99. print(" - Sensor address is really 0x2A")
  100. print(" - No loose wire or swapped SDA/SCL")
  101. else:
  102. print(f"\nI2C Error (Errno {e.errno}): {e}")
  103. # Only print full traceback for unexpected errors
  104. if not is_known_error:
  105. import traceback
  106. traceback.print_exc()
  107. sys.exit(1)
  108. finally:
  109. scale.close()
  110. if __name__ == "__main__":
  111. main()