diff --git a/setup script/sparrow_setup.sh b/setup script/sparrow_setup.sh index 7878130..b491cb7 100644 --- a/setup script/sparrow_setup.sh +++ b/setup script/sparrow_setup.sh @@ -96,6 +96,7 @@ AUDIO_BIRDS_MODEL_FILENAME_FINAL="model.onnx" REPO_URL="https://github.com/microsoft/SPARROW.git" CLONE_DIR="" USE_ROBIN=false +XBEE_FAMILY="${XBEE_FAMILY:-}" ONBOARDING_URL="https://server.sparrowstudio.azure.com/v1/onboarding" EMAIL="" @@ -118,9 +119,31 @@ command_exists() { command -v "$1" >/dev/null 2>&1; } install_uuidgen() { if command_exists uuidgen; then log "uuidgen already installed." + return 0 + fi + + mkdir -p /run/uuidd && chmod 755 /run/uuidd + apt-get update -y || log "WARN: apt-get update failed; continuing with cached indexes." + + # /usr/bin/uuidgen ships in the uuid-runtime binary package on every + # current Debian release (Bookworm and Trixie both build it from the + # util-linux source; util-linux itself does NOT ship uuidgen). + # Guard the install so set -e doesn't abort before we can log a + # diagnostic that actually helps the operator. + if apt-get install -y uuid-runtime; then + log "Installed uuid-runtime." else - mkdir -p /run/uuidd && chmod 755 /run/uuidd - apt-get update -y && apt-get install -y uuid-runtime + log "ERROR: 'apt install uuid-runtime' failed." + log " On Debian, /usr/bin/uuidgen is shipped by the uuid-runtime package." + log " If apt reported 'no installation candidate', /etc/apt/sources.list" + log " is likely missing the 'main' component, or set to the wrong suite." + log " Expected entry for Trixie:" + log " deb http://deb.debian.org/debian $(lsb_release -sc 2>/dev/null || echo trixie) main" + log " Fix sources.list, run 'sudo apt-get update', then re-run this script." + fi + + if ! command_exists uuidgen; then + return 1 fi } @@ -453,16 +476,40 @@ detect_xbee_port() { echo "$port" } +prompt_xbee_family() { + # Honor an env override so headless / repeat runs skip the prompt. + case "${XBEE_FAMILY:-}" in + 868|900) + log "XBEE_FAMILY=$XBEE_FAMILY (from env)" + return 0 + ;; + "") : ;; + *) + log "WARN: XBEE_FAMILY='$XBEE_FAMILY' is invalid; falling back to prompt." + ;; + esac + + if _yesno "Is the XBee radio an XBee-PRO 900 (rather than the default XBee SX 868)?"; then + XBEE_FAMILY=900 + else + XBEE_FAMILY=868 + fi + export XBEE_FAMILY + log "XBEE_FAMILY=$XBEE_FAMILY" +} + run_xbee_configure_if_needed() { [[ "${USE_ROBIN:-false}" == "true" ]] || { log "ROBIN not in use; skipping xbee_configure.py" return 0 } + prompt_xbee_family + local xbee_port xbee_port="$(detect_xbee_port)" || exit 1 - log "Running xbee_configure.py for ROBIN on port $xbee_port..." + log "Running xbee_configure.py for ROBIN on port $xbee_port (family=${XBEE_FAMILY:-868})..." ( cd "$SYSTEM_FOLDER/sparrow" python xbee_configure.py \ @@ -472,7 +519,8 @@ run_xbee_configure_if_needed() { --rf 1 \ --router 0 \ --netid 1234 \ - --node SPARROW_MASTER + --node SPARROW_MASTER \ + --family "${XBEE_FAMILY:-868}" ) } diff --git a/sparrow/xbee_configure.py b/sparrow/xbee_configure.py index 59ac84e..a2a128c 100644 --- a/sparrow/xbee_configure.py +++ b/sparrow/xbee_configure.py @@ -64,6 +64,7 @@ def configure_xbee( router_mode: int, network_id_hex: str | None, node_id: str | None, + family: str = "868", ): # 1) Probe to find current baud that allows +++ ser = None @@ -101,9 +102,16 @@ def configure_xbee( print(f"Setting CE={router_mode} (routing/messaging mode) ...") at(ser, f"CE{router_mode}") - # RF data rate: BR=1 for 80kbps (all nodes must match) - print(f"Setting BR={rf_rate} (RF data rate) ...") - at(ser, f"BR{rf_rate}") + # RF data rate: BR=1 for 80kbps (all nodes must match). + # BR is only a valid AT command on the XBee SX 868 family. On the + # XBee-PRO 900 / 900HP families the RF rate is fixed by the firmware + # image loaded on the module (Digi ships separate 10kbps / 200kbps + # firmware builds), so AT BR returns ERROR — skip the command entirely. + if family == "900": + print("Skipping BR: RF rate is fixed by firmware on XBee-PRO 900 family.") + else: + print(f"Setting BR={rf_rate} (RF data rate) ...") + at(ser, f"BR{rf_rate}") # Optional Network ID (hex). Example: 1234 if network_id_hex: @@ -153,12 +161,17 @@ def configure_xbee( print(f"[WARN] Re-opened at {final_baud} but couldn't re-enter command mode. " "This can happen if timing is off; API mode may still be set correctly.") else: - # Read back key params + # Read back key params. Skip BR on 900-family to avoid printing a + # scary-looking BR=ERROR that just reflects the fact BR isn't a + # valid AT command on that family (RF rate is fixed by firmware). ap = at(ser2, "AP", expect_ok=False) - br = at(ser2, "BR", expect_ok=False) ce = at(ser2, "CE", expect_ok=False) bd = at(ser2, "BD", expect_ok=False) - print(f"[VERIFY] AP={ap.strip()} BR={br.strip()} CE={ce.strip()} BD={bd.strip()}") + if family == "900": + print(f"[VERIFY] AP={ap.strip()} BR=n/a(firmware-fixed) CE={ce.strip()} BD={bd.strip()}") + else: + br = at(ser2, "BR", expect_ok=False) + print(f"[VERIFY] AP={ap.strip()} BR={br.strip()} CE={ce.strip()} BD={bd.strip()}") at(ser2, "CN", expect_ok=False) ser2.close() @@ -176,6 +189,10 @@ def main(): help="CE routing/messaging mode (0=standard router)") p.add_argument("--netid", default=None, help="Network ID (hex), e.g. 1234 or 0x1234") p.add_argument("--node", default=None, help="Node identifier (NI), e.g. SPARROW_GATEWAY") + p.add_argument("--family", choices=["868", "900"], default="868", + help="XBee module family. '868' (default) = XBee SX 868. " + "'900' = XBee-PRO 900 / 900HP; skips the BR command since " + "RF rate on that family is fixed by the loaded firmware image.") args = p.parse_args() probe = args.probe_baud[:] if args.probe_baud else DEFAULT_PROBE_BAUDS @@ -189,6 +206,7 @@ def main(): router_mode=args.router, network_id_hex=args.netid, node_id=args.node, + family=args.family, ) print("[DONE] Configuration complete.")