{"id":51135473,"url":"https://github.com/irjudson/seestar","last_synced_at":"2026-06-25T17:30:35.325Z","repository":{"id":353601605,"uuid":"1218533687","full_name":"irjudson/seestar","owner":"irjudson","description":"Wi-Fi wedge hack, mount control, and recovery tools for the ZWO Seestar S50 smart telescope.","archived":false,"fork":false,"pushed_at":"2026-04-24T17:03:13.000Z","size":546,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-24T17:15:49.227Z","etag":null,"topics":["astronomy","astrophotography","hardware-hacking","original","python","seestar","telescope","zwo"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/irjudson.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-23T01:11:26.000Z","updated_at":"2026-04-24T17:03:17.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/irjudson/seestar","commit_stats":null,"previous_names":["irjudson/seestar"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/irjudson/seestar","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/irjudson%2Fseestar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/irjudson%2Fseestar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/irjudson%2Fseestar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/irjudson%2Fseestar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/irjudson","download_url":"https://codeload.github.com/irjudson/seestar/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/irjudson%2Fseestar/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34786225,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-25T02:00:05.521Z","response_time":101,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["astronomy","astrophotography","hardware-hacking","original","python","seestar","telescope","zwo"],"created_at":"2026-06-25T17:30:31.970Z","updated_at":"2026-06-25T17:30:35.314Z","avatar_url":"https://github.com/irjudson.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Seestar S50 — Firmware Analysis, Wi-Fi Wedge Fix \u0026 Recovery\n\nTooling and analysis around the ZWO Seestar S50 smart telescope: protocol\nreverse-engineering, firmware extraction/diff/patching, license generation,\nrecovery workflows, and — now-resolved — the Wi-Fi chip wedge that affects\na cohort of units upgrading past app 5.82.\n\n\u003e **Disclaimer.** This project is not affiliated with, endorsed by, or\n\u003e sponsored by ZWO. It is independent reverse-engineering and repair\n\u003e tooling developed on a personally-owned device. The bcmdhd kernel\n\u003e module whose behaviour is discussed here is GPL-licensed. Using any\n\u003e of the tools or patches described may void your device warranty and\n\u003e could change how the device interacts with future official updates.\n\u003e Use at your own risk. See [CONTRIBUTING.md](CONTRIBUTING.md) to report\n\u003e additional affected units or contribute test data.\n\n## Canonical docs\n\n- **[SEESTAR_WIFI_WEDGE_FIX.md](SEESTAR_WIFI_WEDGE_FIX.md)** — user-facing\n  writeup of the bug and the 1-symbol fix. Paste-ready for a forum / gist;\n  this is what other affected S50 owners should read.\n- **[UPGRADE_PROCEDURE_VERIFIED.md](UPGRADE_PROCEDURE_VERIFIED.md)** —\n  step-by-step replayable procedure for upgrading an affected unit 5.50 → 7.32.\n- **[UPGRADE_PROBLEM_SUMMARY.md](UPGRADE_PROBLEM_SUMMARY.md)** — the\n  investigation log (`[V]` verified / `[I]` inferred / `[?]` open tags).\n  Historical record of every hypothesis we explored, most of which were\n  refuted.\n- **[IMAGE_EXTRACTION.md](IMAGE_EXTRACTION.md)** — how to capture a current\n  `seestarOS.img` from a running device (SSH or rkdeveloptool), verify it, and\n  flash it as a recovery baseline.\n- **[HACKING.md](HACKING.md)** — short orientation card.\n\n## Status (2026-04-22)\n\n**Root cause identified and fix verified end-to-end** across every firmware\nversion from 5.50 (fw_2.6.1) to 7.32 (fw_3.1.2).\n\n| Question | Answer |\n|---|---|\n| What's broken? | Oct 2025 rebuild of `bcmdhd.ko` imports `mmc_hw_reset`. The RV1126 dwmmc controller on the S50 doesn't advertise `cap-mmc-hw-reset`, and on a subset of chips the resulting behaviour leaves the SDIO bus wedged at `HT Avail timeout (clkctl 0x50)`. Why only some S50s wedge is still unresolved — the DTB state is identical across working and broken units. |\n| What's the fix? | `objcopy --redefine-sym mmc_hw_reset=mmc_sw_reset` on the driver — one-symbol ELF patch, no kernel rebuild. |\n| How do I diagnose my S50? | `./tools/wifi-driver-check.sh --ip \u003cseestar-ip\u003e` |\n| How do I fix my unit? | `./tools/swap_driver.sh patched --ip \u003cseestar-ip\u003e` |\n\n## Quick-start (affected device)\n\n```bash\n# 1. What's running on this unit, and is it wedged?\n./tools/wifi-driver-check.sh --ip 169.254.100.100\n\n# 2. Install the patched driver\n./tools/swap_driver.sh patched --ip 169.254.100.100\n# → reboots device\n\n# 3. Verify everything came up clean\n./tools/verify_functional.sh --ip 169.254.100.100\n```\n\n## Quick-start (upgrading an affected device)\n\n```bash\n# 1. Stage the patched driver + post-upgrade recovery script on the device\n./tools/prepare_for_upgrade.sh --to patched --ip 169.254.100.100\n\n# 2. Push the new firmware with your upgrade tool (iscope will overwrite\n#    the driver with the stock Oct 2025 broken variant — expected)\npython3 tools/seestar_firmware_flash.py \\\n    --host 169.254.100.100 \\\n    firmware/decompiled/seestar_v3.1.2_decompiled/resources/assets/iscope\n\n# 3. After the device wedges, SSH in and trigger the pre-staged recovery\nssh pi@169.254.100.100 'sudo /home/pi/post_upgrade_swap.sh'\n\n# 4. Verify after reboot (~90s)\n./tools/verify_functional.sh --ip 169.254.100.100\n```\n\n## Repo layout\n\n```\ntools/                        Active CLI utilities (see table below)\ntools/lib/common.sh           Shared constants + SSH helpers for tool scripts\ntools/desktop-setup/          Host-side udev rules for USB-ethernet auto-config\nanalysis/output/              Written investigation reports (markdown)\nanalysis/driver_diff/         Symbol/string diffs between the two bcmdhd builds\nanalysis/ghidra_diff/scripts/ Our Ghidra scripts (ZWO binaries themselves are not shipped)\nanalysis/src/                 Analysis tooling (APK downloader, etc.)\n```\n\nThe Python client library has moved to its own repo:\n**https://github.com/irjudson/seestar-api**\n\nThe following paths are **not included** in this repo (gitignored); the\ntools regenerate or extract what they need on demand:\n\n```\nfirmware/packages/            ZWO firmware bundles — copyright ZWO\nfirmware/decompiled/          jadx output of ZWO APKs — copyright ZWO\nfirmware/signed/              Pre-signed iscope files\nfirmware/factory/             Extracted factory driver (re-extract via tools/extract_factory_bcmdhd.sh)\nfirmware/experimental/        Built artifacts (patched .ko — rebuild per SEESTAR_WIFI_WEDGE_FIX.md)\nbaseline-2.42/seestarOS.img   Factory image for rkdeveloptool recovery (donor unit, vintage Apr 2024)\nbaseline-current/             Device images captured from your own unit (see IMAGE_EXTRACTION.md)\ns50-fs/                       Your device filesystem snapshot\napks/                         ZWO APK archive — copyright ZWO\nanalysis/jadx/                Decompiled APK contents — copyright ZWO\nanalysis/ghidra_diff/binaries Extracted ZWO binaries\nanalysis/ghidra_diff/out*/    Ghidra decompilation output\nanalysis/fw_5.82_failure_dmesg/ Device-specific capture (contains ZWO binaries too)\n```\n\nThe Python client library lives at [github.com/irjudson/seestar-api](https://github.com/irjudson/seestar-api).\n\n## Tools\n\n### Driver + upgrade workflow\n\n| Tool | Purpose |\n|---|---|\n| `wifi-driver-check.sh` | Fingerprint bcmdhd.ko + scan dmesg; emit `FACTORY_SAFE` / `PATCHED_SAFE` / `WEDGED_NOW` / `REGRESSED_AT_RISK` / `UNKNOWN_DRIVER` verdict |\n| `swap_driver.sh \u003cfactory\\|patched\u003e` | Install a known-good driver variant and reboot |\n| `prepare_for_upgrade.sh [--to factory\\|patched]` | Stage driver + post-upgrade recovery script before pushing firmware |\n| `verify_functional.sh` | 9-point health check (driver classification, chip state, AP, hostapd, station, imager, sound-33, RPC ports) |\n| `audit_bcmdhd_across_versions.sh` | Tabulate bcmdhd.ko md5 + compile date across every fw package |\n| `extract_factory_bcmdhd.sh` | Extract the Jul 2023 driver from `baseline-2.42/seestarOS.img` |\n\n### Firmware push / recovery\n\n| Tool | Purpose |\n|---|---|\n| `seestar_firmware_flash.py` | Push signed iscope to the device via JSON-RPC on ports 4350/4361 |\n| `sign_firmware.py` | SHA-1 + RSA PKCS1v15 sign a tar.bz2 as an iscope |\n| `seestar-recovery.sh` | Last-resort rkdeveloptool maskrom reflash + post-flash setup |\n| `extract_firmware.py` | Extract fw package bundles |\n\n### Device image capture\n\n| Tool | Purpose |\n|---|---|\n| `extract_device_image.sh [--ssh-usb\\|--ssh-wifi\\|--rkdeveloptool]` | Capture a current `seestarOS.img` from a running device (SSH) or in loader mode (rkdeveloptool); outputs gzipped GPT image of p1–p7 to `baseline-current/` |\n| `verify_extracted_image.sh \u003cimage.img.gz\u003e` | Validate a captured image: GPT integrity, ext4 mounts, bcmdhd.ko fingerprint, ap_id donor-unit check, ASIAIR app version |\n\n\u003e **Note:** image capture has been validated on a live unit; restore from a captured image via `rkdeveloptool` has **not yet been tested end-to-end**. Treat `baseline-current/` images as snapshots for inspection and diff, not as verified recovery media.\n\n### On-device config\n\n| Tool | Purpose |\n|---|---|\n| `install_license_rpc.py` | Install license via `pi_encrypt` RPC (no SSH required) |\n| `get_license.py` | Fetch a valid license from `api.seestar.com/v1/activation` |\n| `set_wifi_country.py` | Send `set_wifi_country` RPC |\n| `enable_station_mode.sh` | Flip `wpa_svr=1` + trigger home-network reassoc |\n| `usb_ether_fix.sh` | On-device: unwedge dwc3 gadget, reload g_ether |\n\n### Shared\n\n| Tool | Purpose |\n|---|---|\n| `lib/common.sh` | SSH setup, driver md5 constants, `DRIVER_PATH`, pass/fail print helpers |\n| `desktop-setup/` | Linux udev rule + helper to bring up a Seestar USB-ethernet interface on the host |\n\n## Three known driver fingerprints (`bcmdhd.ko`)\n\n| md5 | Compile | Behavior | Source |\n|---|---|---|---|\n| `4cfbf203772770d246db12505b744003` | Jul 7 2023 | Verified working | baseline-2.42 seestarOS.img partition 6 |\n| `1fc70c15691fa675fa3e4661aa783a12` | Oct 17 2025 | Verified working | objcopy `mmc_hw_reset→mmc_sw_reset` patch of stock Oct 2025 |\n| `8b75e5cd33fcf850dd673129d1842312` | Oct 17 2025 | **Wedges affected units** | Ships in every fw_2.6.4+ package |\n\n## Hardware notes\n\n- SoC: Rockchip RV1126, 32-bit ARM (armhf), kernel 4.19.111\n- WiFi/BT: Broadcom BCM43456 (Ampak AP6256 module), `bcmdhd.ko` driver, `WL_REG_ON=-1` (no GPIO reset)\n- Boot partition: eMMC with GPT, factory image `baseline-2.42/seestarOS.img`\n- UART: J2 pads near ESP32, 1.8V logic, 1500000 baud — kernel output only, no getty\n- USB-C: dwc3 gadget, defaults to g_mass_storage; switches to g_ether when `/home/pi/en_eth=1`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Firjudson%2Fseestar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Firjudson%2Fseestar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Firjudson%2Fseestar/lists"}