Spoolbuddy init and diagnostic improvements (#814)

Spoolbuddy init and diagnostic improvements (#814)
This commit is contained in:
Keybored
2026-03-26 16:16:09 +01:00
committed by GitHub
parent b75f65fe64
commit eb3450b1d0
4 changed files with 316 additions and 416 deletions
+125 -270
View File
@@ -15,38 +15,23 @@ Wiring (from spoolbuddy/README.md):
PN5180 RST -> Pi Pin 18 (GPIO24)
"""
import os
import sys
import time
import gpiod
import spidev
# ---------------------------------------------------------------------------
# Pin assignments (BCM numbering)
# ---------------------------------------------------------------------------
BUSY_PIN = 25 # Pin 22
RST_PIN = 24 # Pin 18
NSS_PIN = 23 # Pin 16 (manual CS)
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "daemon")))
# ---------------------------------------------------------------------------
# SPI command instruction codes (NXP PN5180 datasheet Table 5)
# ---------------------------------------------------------------------------
CMD_WRITE_REGISTER = 0x00
CMD_WRITE_REGISTER_OR_MASK = 0x01
CMD_WRITE_REGISTER_AND_MASK = 0x02
CMD_READ_REGISTER = 0x04
CMD_READ_REGISTER_MULTIPLE = 0x05
CMD_WRITE_EEPROM = 0x06
CMD_READ_EEPROM = 0x07
CMD_SEND_DATA = 0x09
CMD_READ_DATA = 0x0A
CMD_LOAD_RF_CONFIG = 0x11
CMD_RF_ON = 0x16
CMD_RF_OFF = 0x17
# ---------------------------------------------------------------------------
# Register addresses (32-bit each)
# ---------------------------------------------------------------------------
from pn5180 import ( # noqa: E402
NSS_PIN as DRIVER_NSS_PIN,
PN5180,
RST_PIN as DRIVER_RST_PIN,
SPI_BUS as DRIVER_SPI_BUS,
SPI_DEVICE as DRIVER_SPI_DEVICE,
)
REG_SYSTEM_CONFIG = 0x00
REG_IRQ_ENABLE = 0x01
REG_IRQ_STATUS = 0x02
@@ -60,25 +45,9 @@ REG_RX_STATUS = 0x13
REG_CRC_TX_CONFIG = 0x19
REG_RF_STATUS = 0x1D
REG_SYSTEM_STATUS = 0x24
REG_SIGPRO_CONFIG = 0x1A # Signal Processing Configuration
REG_TEMP_CONTROL = 0x25
REGISTER_NAMES = {
REG_SYSTEM_CONFIG: "SYSTEM_CONFIG",
REG_IRQ_ENABLE: "IRQ_ENABLE",
REG_IRQ_STATUS: "IRQ_STATUS",
REG_IRQ_CLEAR: "IRQ_CLEAR",
REG_TRANSCEIVE_CONTROL: "TRANSCEIVE_CONTROL",
REG_TIMER1_RELOAD: "TIMER1_RELOAD",
REG_TIMER1_CONFIG: "TIMER1_CONFIG",
REG_RX_WAIT_CONFIG: "RX_WAIT_CONFIG",
REG_CRC_RX_CONFIG: "CRC_RX_CONFIG",
REG_RX_STATUS: "RX_STATUS",
REG_CRC_TX_CONFIG: "CRC_TX_CONFIG",
REG_RF_STATUS: "RF_STATUS",
REG_SYSTEM_STATUS: "SYSTEM_STATUS",
REG_TEMP_CONTROL: "TEMP_CONTROL",
}
# ---------------------------------------------------------------------------
# EEPROM addresses
# ---------------------------------------------------------------------------
@@ -89,224 +58,39 @@ EEPROM_EEPROM_VERSION = 0x14 # 2 bytes
EEPROM_IRQ_PIN_CONFIG = 0x1A # 1 byte
def _find_gpio_chip():
"""Find the right gpiochip for Raspberry Pi GPIO pins.
def _check_spi_device_access() -> str:
"""Check that the configured spidev exists and can be opened."""
spi_path = f"/dev/spidev{DRIVER_SPI_BUS}.{DRIVER_SPI_DEVICE}"
if not os.path.exists(spi_path):
raise FileNotFoundError(f"SPI device not found: {spi_path}")
RPi 5 uses gpiochip4, RPi 4 uses gpiochip0.
fd = os.open(spi_path, os.O_RDWR)
os.close(fd)
return spi_path
def _self_test_control_pins(nfc: PN5180):
"""Toggle NSS and RST pins and print observed line state.
Uses public set_pin/get_pin methods to avoid direct access to driver internals.
"""
for path in ["/dev/gpiochip4", "/dev/gpiochip0"]:
try:
chip = gpiod.Chip(path)
info = chip.get_info()
# RPi 4: pinctrl-bcm2711, RPi 5: pinctrl-rp1
if "pinctrl" in info.label:
return chip
chip.close()
except (FileNotFoundError, PermissionError, OSError):
continue
raise RuntimeError("Could not find Raspberry Pi GPIO chip")
for pin_name, pin_num in (("NSS", DRIVER_NSS_PIN), ("RST", DRIVER_RST_PIN)):
nfc.set_pin(pin_num, True)
time.sleep(0.005)
active_state = nfc.get_pin(pin_num)
nfc.set_pin(pin_num, False)
time.sleep(0.005)
inactive_state = nfc.get_pin(pin_num)
class PN5180:
"""Low-level driver for the PN5180 NFC frontend over SPI."""
# Restore idle-high level used by this driver.
nfc.set_pin(pin_num, True)
def __init__(
self,
spi_bus=0,
spi_device=0,
spi_speed_hz=500_000,
busy_pin=BUSY_PIN,
rst_pin=RST_PIN,
nss_pin=NSS_PIN,
):
# GPIO setup via libgpiod
self._chip = _find_gpio_chip()
try:
self._busy_line = self._chip.request_lines(
consumer="pn5180-diag",
config={busy_pin: gpiod.LineSettings(direction=gpiod.line.Direction.INPUT)},
)
self._rst_line = self._chip.request_lines(
consumer="pn5180-diag",
config={
rst_pin: gpiod.LineSettings(
direction=gpiod.line.Direction.OUTPUT,
output_value=gpiod.line.Value.ACTIVE,
)
},
)
self._nss_line = self._chip.request_lines(
consumer="pn5180-diag",
config={
nss_pin: gpiod.LineSettings(
direction=gpiod.line.Direction.OUTPUT,
output_value=gpiod.line.Value.ACTIVE,
)
},
)
except OSError as e:
self._chip.close()
if getattr(e, "errno", None) == 16:
raise RuntimeError(
"GPIO line is busy (another process owns PN5180 pins). "
"Stop spoolbuddy service before running diagnostics: "
"sudo systemctl stop spoolbuddy"
) from e
raise
self._busy_pin = busy_pin
self._rst_pin = rst_pin
self._nss_pin = nss_pin
# SPI setup - mode 0 (CPOL=0, CPHA=0), MSB first
self._spi = spidev.SpiDev()
self._spi.open(spi_bus, spi_device)
self._spi.max_speed_hz = spi_speed_hz
self._spi.mode = 0b00
self._spi.bits_per_word = 8
self._spi.no_cs = True
def close(self):
self._spi.close()
self._busy_line.release()
self._rst_line.release()
self._nss_line.release()
self._chip.close()
# -- low-level helpers --------------------------------------------------
def _busy_is_high(self):
return self._busy_line.get_value(self._busy_pin) == gpiod.line.Value.ACTIVE
def _wait_busy(self, timeout_s=1.0):
"""Block until BUSY goes LOW (PN5180 ready)."""
deadline = time.monotonic() + timeout_s
while self._busy_is_high():
if time.monotonic() > deadline:
raise TimeoutError("PN5180 BUSY line did not go low")
time.sleep(0.001)
def _cs_low(self):
self._nss_line.set_value(self._nss_pin, gpiod.line.Value.INACTIVE)
time.sleep(0.000005)
def _cs_high(self):
self._nss_line.set_value(self._nss_pin, gpiod.line.Value.ACTIVE)
time.sleep(0.000100)
def _send_command(self, tx_data, rx_len=0):
"""Send an SPI command frame and optionally read a response frame.
The PN5180 SPI protocol is half-duplex:
1. Send command frame (NSS held low for entire frame).
2. Wait for BUSY high then low (command processed).
3. If a response is expected, clock out rx_len bytes in a second frame.
"""
self._wait_busy()
# Transmit command (manual CS)
self._cs_low()
self._spi.xfer2(list(tx_data))
self._cs_high()
if rx_len == 0:
# Write-only command - wait for processing
time.sleep(0.001)
self._wait_busy()
return None
# Wait for PN5180 to process command (BUSY goes high then low)
time.sleep(0.001)
self._wait_busy()
# Read response (manual CS)
self._cs_low()
rx = self._spi.xfer2([0xFF] * rx_len)
self._cs_high()
time.sleep(0.001)
self._wait_busy()
return bytes(rx)
# -- register operations ------------------------------------------------
def read_register(self, addr):
"""Read a 32-bit register. Returns int."""
resp = self._send_command([CMD_READ_REGISTER, addr], rx_len=4)
return int.from_bytes(resp, "little")
def write_register(self, addr, value):
"""Write a 32-bit value to a register."""
self._send_command(
[
CMD_WRITE_REGISTER,
addr,
value & 0xFF,
(value >> 8) & 0xFF,
(value >> 16) & 0xFF,
(value >> 24) & 0xFF,
]
print(
f" {pin_name} pin {pin_num}: "
f"ACTIVE->{'ACTIVE' if active_state else 'INACTIVE'}, "
f"INACTIVE->{'ACTIVE' if inactive_state else 'INACTIVE'}"
)
def write_register_or_mask(self, addr, mask):
self._send_command(
[
CMD_WRITE_REGISTER_OR_MASK,
addr,
mask & 0xFF,
(mask >> 8) & 0xFF,
(mask >> 16) & 0xFF,
(mask >> 24) & 0xFF,
]
)
def write_register_and_mask(self, addr, mask):
self._send_command(
[
CMD_WRITE_REGISTER_AND_MASK,
addr,
mask & 0xFF,
(mask >> 8) & 0xFF,
(mask >> 16) & 0xFF,
(mask >> 24) & 0xFF,
]
)
# -- EEPROM operations --------------------------------------------------
def read_eeprom(self, addr, length):
"""Read `length` bytes from EEPROM starting at `addr`."""
return self._send_command([CMD_READ_EEPROM, addr, length], rx_len=length)
# -- reset --------------------------------------------------------------
def reset(self):
"""Hardware-reset the PN5180 via the RST pin."""
self._rst_line.set_value(self._rst_pin, gpiod.line.Value.INACTIVE)
time.sleep(0.01)
self._rst_line.set_value(self._rst_pin, gpiod.line.Value.ACTIVE)
time.sleep(0.05)
self._wait_busy(timeout_s=2.0)
# Clear all IRQ flags
self.write_register(REG_IRQ_CLEAR, 0xFFFFFFFF)
# -- version / identity -------------------------------------------------
def get_product_version(self):
data = self.read_eeprom(EEPROM_PRODUCT_VERSION, 2)
return f"{data[1]}.{data[0]}"
def get_firmware_version(self):
data = self.read_eeprom(EEPROM_FIRMWARE_VERSION, 2)
return f"{data[1]}.{data[0]}"
def get_eeprom_version(self):
data = self.read_eeprom(EEPROM_EEPROM_VERSION, 2)
return f"{data[1]}.{data[0]}"
def get_die_identifier(self):
data = self.read_eeprom(EEPROM_DIE_IDENTIFIER, 16)
return data.hex()
def run_diagnostics():
print("=" * 60)
@@ -315,28 +99,71 @@ def run_diagnostics():
nfc = None
try:
print("\n[1] SPI device check...")
spi_path = _check_spi_device_access()
print(f" SPI device OK: {spi_path}")
nfc = PN5180()
print("\n[2] Control pin self-test (NSS/RST)...")
_self_test_control_pins(nfc)
# Reset
print("\n[1] Hardware reset...")
print("\n[3] Hardware reset...")
nfc.reset()
print(" Reset OK")
# Version info
print("\n[2] Version info (EEPROM)")
print(f" Product version : {nfc.get_product_version()}")
print(f" Firmware version : {nfc.get_firmware_version()}")
print(f" EEPROM version : {nfc.get_eeprom_version()}")
print(f" Die identifier : {nfc.get_die_identifier()}")
print("\n[4] Version info (EEPROM)")
product = nfc.read_eeprom(EEPROM_PRODUCT_VERSION, 2)
firmware = nfc.read_eeprom(EEPROM_FIRMWARE_VERSION, 2)
eeprom = nfc.read_eeprom(EEPROM_EEPROM_VERSION, 2)
die_id = nfc.read_eeprom(EEPROM_DIE_IDENTIFIER, 16)
print(f" Product version : {product[1]}.{product[0]}")
print(f" Firmware version : {firmware[1]}.{firmware[0]}")
print(f" EEPROM version : {eeprom[1]}.{eeprom[0]}")
print(f" Die identifier : {die_id.hex()}")
# Register dump
print("\n[3] Register dump")
for addr, name in sorted(REGISTER_NAMES.items()):
val = nfc.read_register(addr)
print("\n[5] Register dump")
# Use register names from the script (not in pn5180.py)
REGISTER_NAMES_DUMP = {
0x00: "SYSTEM_CONFIG",
0x01: "IRQ_ENABLE",
0x02: "IRQ_STATUS",
0x03: "IRQ_CLEAR",
0x04: "TRANSCEIVE_CONTROL",
0x0C: "TIMER1_RELOAD",
0x0F: "TIMER1_CONFIG",
0x11: "RX_WAIT_CONFIG",
0x12: "CRC_RX_CONFIG",
0x13: "RX_STATUS",
0x19: "CRC_TX_CONFIG",
0x1A: "SIGPRO_CONFIG",
0x1D: "RF_STATUS",
0x24: "SYSTEM_STATUS",
0x25: "TEMP_CONTROL",
}
for addr, name in sorted(REGISTER_NAMES_DUMP.items()):
val = nfc.read_reg(addr)
print(f" 0x{addr:02X} {name:<24s} = 0x{val:08X}")
# SIGPRO_CONFIG ISO/IEC14443 mode check
sigpro_val = nfc.read_reg(REG_SIGPRO_CONFIG)
sigpro_mode = (sigpro_val >> 0) & 0b111
baudrate_map = {
0b100: "106 kBd (ISO/IEC14443 type A/B)",
0b101: "212 kBd (FeliCa 212 kBd)",
0b110: "424 kBd (FeliCa 424 kBd)",
0b111: "848 kBd",
}
baudrate_str = baudrate_map.get(sigpro_mode, "Unknown or reserved")
print(f"\n[5b] SIGPRO_CONFIG (0x1A) bits 2:0 = 0b{sigpro_mode:03b} ({baudrate_str})")
# IRQ status breakdown
irq = nfc.read_register(REG_IRQ_STATUS)
print(f"\n[4] IRQ status flags (0x{irq:08X})")
irq = nfc.read_reg(REG_IRQ_STATUS)
print(f"\n[6] IRQ status flags (0x{irq:08X})")
irq_flags = [
(0, "RX_IRQ"),
(1, "TX_IRQ"),
@@ -356,20 +183,48 @@ def run_diagnostics():
print(f" bit {bit:2d}: {name:<28s} [{state}]")
# RF status
rf = nfc.read_register(REG_RF_STATUS)
print(f"\n[5] RF status (0x{rf:08X})")
rf = nfc.read_reg(REG_RF_STATUS)
print(f"\n[7] RF status (0x{rf:08X})")
tx_rf_on = bool(rf & (1 << 0))
rx_en = bool(rf & (1 << 1))
print(f" TX RF active : {tx_rf_on}")
print(f" RX enabled : {rx_en}")
# System status
sys_stat = nfc.read_register(REG_SYSTEM_STATUS)
print(f"\n[6] System status (0x{sys_stat:08X})")
sys_stat = nfc.read_reg(REG_SYSTEM_STATUS)
print(f"\n[8] System status (0x{sys_stat:08X})")
# System status bit breakdown
sys_stat_bits = [
(9, "LDO_TVDD_OK"),
(8, "PARAMETER_ERROR"),
(7, "SYNTAX_ERROR"),
(6, "SEMANTIC_ERROR"),
(5, "STBY_PREVENT_RFLD"),
(4, "BOOT_TEMP"),
(3, "BOOT_SOFT_RESET"),
(2, "BOOT_WUC"),
(1, "BOOT_RFLD"),
(0, "BOOT_POR"),
]
for bit, symbol in sys_stat_bits:
state = "SET" if sys_stat & (1 << bit) else "---"
print(f" bit {bit:2d}: {symbol:<18s} [{state}]")
# Temperature
temp_ctrl = nfc.read_register(REG_TEMP_CONTROL)
print(f"\n[7] Temp control register (0x{temp_ctrl:08X})")
temp_ctrl = nfc.read_reg(REG_TEMP_CONTROL)
print(f"\n[9] Temp control register (0x{temp_ctrl:08X})")
# TEMP_DELTA bits 1:0
temp_delta = (temp_ctrl >> 0) & 0b11
temp_delta_map = {
0b00: "85°C",
0b01: "115°C",
0b10: "125°C",
0b11: "135°C",
}
temp_delta_str = temp_delta_map.get(temp_delta, "Unknown")
print(f" bits 1:0 TEMP_DELTA = 0b{temp_delta:02b} ({temp_delta_str})")
print("\n" + "=" * 60)
print("Diagnostics complete - PN5180 is responding over SPI.")
+66 -112
View File
@@ -1,17 +1,20 @@
#!/usr/bin/env python3
"""NAU7802 Scale Diagnostic - ported from SpoolBuddy Rust firmware.
"""NAU7802 Scale Diagnostic.
I2C address: 0x2A
Bus: /dev/i2c-1 (GPIO2/GPIO3 on RPi)
"""
import os
import struct
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)
@@ -49,115 +52,6 @@ PU_OSCS = 0x40 # Oscillator select
PU_AVDDS = 0x80 # AVDD source select
class NAU7802:
def __init__(self, bus=I2C_BUS, addr=NAU7802_ADDR):
self._bus_num = bus
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 _update_bits(self, reg: int, mask: int, value: int):
cur = self.read_reg(reg)
self.write_reg(reg, (cur & ~mask) | (value & mask))
def _set_bit(self, reg: int, bit: int, enabled: bool):
mask = 1 << bit
self._update_bits(reg, mask, mask if enabled else 0)
def _set_field(self, reg: int, shift: int, width: int, value: int):
mask = ((1 << width) - 1) << shift
self._update_bits(reg, mask, value << shift)
def init(self):
"""Initialize NAU7802 using the Adafruit library startup sequence."""
# Reset
self._set_bit(REG_PU_CTRL, 0, True) # RR=1
time.sleep(0.010)
self._set_bit(REG_PU_CTRL, 0, False) # RR=0
self._set_bit(REG_PU_CTRL, 1, True) # PUD=1
time.sleep(0.001)
# Enable digital + analog and allow analog section to settle.
self._set_bit(REG_PU_CTRL, 1, True) # PUD=1
self._set_bit(REG_PU_CTRL, 2, True) # PUA=1
time.sleep(0.600)
# Start conversion cycle (PU_CS bit 4) after power-up.
self._set_bit(REG_PU_CTRL, 4, True)
# Wait for power-up ready (PU_PUR bit 3)
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")
# Check revision register low nibble (Adafruit expects 0xF).
revision = self.read_reg(REG_REVISION)
print(f" Revision: 0x{revision:02X}")
if (revision & 0x0F) != 0x0F:
raise RuntimeError(f"Unexpected NAU7802 revision register: 0x{revision:02X}")
# Internal LDO enable is PU_CTRL.AVDDS (bit 7); set LDO voltage to 3.0V.
self._set_bit(REG_PU_CTRL, 7, True) # AVDDS=1 (internal LDO)
self._set_field(REG_CTRL1, shift=3, width=3, value=0b101) # VLDO=3.0V
print(" LDO: 3.0V (internal)")
# Gain: 128x (bits 2:0 of CTRL1 = 0b111)
self._set_field(REG_CTRL1, shift=0, width=3, value=0b111)
print(" Gain: 128x")
# Sample rate: 10 SPS (CTRL2 bits 6:4 = 0b000)
self._set_field(REG_CTRL2, shift=4, width=3, value=0b000)
print(" Sample rate: 10 SPS")
# Adafruit tuning: disable ADC chopper clock (ADC bits 5:4 = 0b11)
self._set_field(REG_ADC, shift=4, width=2, value=0b11)
# Adafruit tuning: use low ESR caps (PGA bit 6 = 0)
self._set_bit(REG_PGA, 6, False)
# Start conversion cycle
self._set_bit(REG_PU_CTRL, 4, True)
print(" Conversion started")
# Flush the first reading — the NAU7802 always returns a stale
# max-scale value (0x7FFFFF) on the first conversion after power-up.
for _ in range(200):
if self.data_ready():
self.read_raw() # discard
print(" First reading flushed")
break
time.sleep(0.010)
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")
@@ -199,8 +93,68 @@ def main():
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():
@@ -210,7 +164,7 @@ def main():
print(" Timeout waiting for data ready")
sys.exit(1)
print("[3] Reading 10 samples (10 SPS = ~1 second)...")
print(" Reading 10 samples (10 SPS = ~1 second)...")
readings = []
for i in range(10):
# Wait for data ready