mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-08-11 00:30:12 -04:00
228 lines
7.1 KiB
Python
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()
|