{"id":50318688,"url":"https://github.com/packetthrower/etch341","last_synced_at":"2026-05-29T02:01:51.155Z","repository":{"id":360263035,"uuid":"1248754196","full_name":"packetThrower/etch341","owner":"packetThrower","description":"Software for use with a CH341","archived":false,"fork":false,"pushed_at":"2026-05-25T17:49:14.000Z","size":187,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-25T18:31:29.626Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/packetThrower.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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-05-25T02:47:32.000Z","updated_at":"2026-05-25T17:49:18.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/packetThrower/etch341","commit_stats":null,"previous_names":["packetthrower/etch341"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/packetThrower/etch341","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/packetThrower%2Fetch341","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/packetThrower%2Fetch341/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/packetThrower%2Fetch341/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/packetThrower%2Fetch341/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/packetThrower","download_url":"https://codeload.github.com/packetThrower/etch341/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/packetThrower%2Fetch341/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33633468,"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-05-29T02:00:06.066Z","response_time":107,"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":[],"created_at":"2026-05-29T02:01:50.027Z","updated_at":"2026-05-29T02:01:51.149Z","avatar_url":"https://github.com/packetThrower.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"resources/icons/128x128@2x.png\" width=\"128\" height=\"128\" alt=\"etch341 icon — wireframe SOIC-8 chip with gold leads\"\u003e\n\u003c/p\u003e\n\n# etch341\n\n[![CI](https://img.shields.io/github/actions/workflow/status/packetThrower/etch341/ci.yml?branch=main\u0026style=flat-square\u0026logo=github\u0026label=CI)](https://github.com/packetThrower/etch341/actions/workflows/ci.yml)\n[![Release](https://img.shields.io/github/v/release/packetThrower/etch341?style=flat-square\u0026logo=github\u0026label=release\u0026include_prereleases)](https://github.com/packetThrower/etch341/releases/latest)\n[![Downloads](https://img.shields.io/github/downloads/packetThrower/etch341/total?style=flat-square\u0026logo=github\u0026label=downloads)](https://github.com/packetThrower/etch341/releases)\n[![Rust](https://img.shields.io/badge/Rust-stable-CE422B?style=flat-square\u0026logo=rust\u0026logoColor=white)](Cargo.toml)\n[![License: GPL v3+](https://img.shields.io/badge/license-GPLv3%2B-blue?style=flat-square)](LICENSE)\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/public/etch341.png\" alt=\"etch341 GUI showing the Detect pane with a chip identified, the workflow sidebar, and the activity log\" width=\"900\"\u003e\n\u003c/p\u003e\n\n## Minimum OS versions\n\n**macOS** (Apple Silicon and Intel)\n\n[![macOS 11+](https://img.shields.io/badge/macOS-11%2B-333?style=flat-square\u0026logo=apple\u0026logoColor=white)](#install)\n[![Apple Silicon](https://img.shields.io/badge/Apple%20Silicon-arm64-333?style=flat-square\u0026logo=apple\u0026logoColor=white)](#install)\n[![Intel](https://img.shields.io/badge/Intel-x86__64-333?style=flat-square\u0026logo=apple\u0026logoColor=white)](#install)\n\n**Windows** (x64 and ARM64)\n\n[![Windows 10 21H2+](https://img.shields.io/badge/Windows%2010%2021H2%2B-x64%20%2F%20arm64-0078D4?style=flat-square\u0026logo=windows\u0026logoColor=white)](#windows)\n\n**Linux** (amd64 and arm64)\n\n[![Ubuntu 22.04+](https://img.shields.io/badge/Ubuntu-22.04%2B-E95420?style=flat-square\u0026logo=ubuntu\u0026logoColor=white)](#linux)\n[![Debian 12+](https://img.shields.io/badge/Debian-12%2B-A81D33?style=flat-square\u0026logo=debian\u0026logoColor=white)](#linux)\n\nThe GUI uses the GPUI rendering stack, which on Linux requires a\nVulkan-capable GPU with current Mesa drivers. The headless CLI\n(`cargo install --no-default-features` or `etch341 \u003csubcommand\u003e` on\nthe released binary) has no graphics requirements.\n\nCross-platform CLI + GUI flash programmer for the **CH341A** USB SPI/I²C\ninterface. Userspace USB on Linux + macOS (no driver to install).\nWindows uses the built-in **WinUSB** kernel driver, but it needs a\none-time Zadig bind to the CH341A — see [Install → Windows](#windows).\n\n## Status\n\nWorking programmer for SPI NOR up to 32 MB+, on both 3.3V and 1.8V chips\n(with a CH341A V1.7+ module for the 1.8V parts). Chips ≤ 16 MB use\nstandard 3-byte addressing; \u003e 16 MB chips use the 4-byte opcode variants\n(0x13 / 0x12 / 0x21 / 0xDC) automatically based on the chip's size. Round-trip validated\nagainst a real Macronix MX25U4033E on an NVIDIA GTX 1060 — full\nerase → blank-check → write → verify cycle landed byte-identical (SHA-256\nmatch) to the original VBIOS.\n\n| Feature | CLI | GUI |\n| --- | --- | --- |\n| Detect (JEDEC ID → chip lookup) | ✅ | ✅ |\n| Read | ✅ | ✅ |\n| Erase (full + range) | ✅ | ✅ (arm/confirm) |\n| Write (with erase + verify) | ✅ | ✅ (arm/confirm + file picker) |\n| Verify | ✅ | ✅ (file picker) |\n| Blank check | ✅ | ✅ |\n| Status registers (SR1/2/3 decode) | ✅ (`sr`) | ✅ |\n| SFDP parameter table | ✅ (`sfdp`) | ✅ (Detect pane) |\n| Security registers / OTP (read / erase / write) | ✅ (`otp`) | ✅ (arm/confirm) |\n| Settings (clock, accent, updates, …) | ✅ (`--speed`) | ✅ |\n| 4-byte addressing (\u003e16 MB chips) | ✅ | ✅ |\n| I²C scan / read / write / verify / blank-check / erase | ⚠️ \\* | — |\n\n\u003e \\* **Scan + read are silicon-validated** against a real 24C02\n\u003e (the probe ACK-bit polarity assumption held — the chip ACKed at\n\u003e `0x50` and round-tripped a 256-byte read). The **write / erase\n\u003e path is still owed a clean-chip validation**: the first attempt\n\u003e bricked a part by clocking it past spec, which is why I²C now\n\u003e defaults to 100 kHz and refuses anything above 400 kHz. Reports\n\u003e from a healthy chip welcome via GitHub Issues.\n\n66 unit tests covering the SPI / I²C protocols (including the SFDP\nparser and the OTP / security-register ops), the high-level ops,\nand the inspect/search primitives, all running against mock transports\nor pure inputs. Hardware-touching tests are gated behind\n`--features hardware`.\n\n### Hardware-validated\n\n- **Macronix MX25U4033E** (1.8V, 4 Mbit) on a GTX 1060 VBIOS chip\n  (GP106 PG410). Full erase → write → verify cycle returns the chip\n  to a byte-identical state (matching SHA-256 across pre- and\n  post-cycle reads).\n- **CH341A V1.7** mini programmer with on-board ZIF socket + SOIC-8\n  clip. The V1.7 has a 1.8V mode that older V1.3 boards lack — required\n  for the U-series Macronix chips and most modern GPU VBIOS.\n\nOther chips in `chips/chips.toml` are entered from datasheets but\nhaven't been individually exercised against silicon. If you run a\nJEDEC `detect` on a chip and the response decodes correctly to a\nnamed entry, the rest of the operations are very likely to work\n(they're chip-agnostic at the protocol level).\n\n### Partially hardware-validated\n\n- **I²C / 24Cxx EEPROMs.** `scan` and `read` are confirmed against a\n  real 24C02 — the chip ACKed at `0x50` and round-tripped a 256-byte\n  read, which validated the probe ACK-bit polarity assumption. The\n  **write / erase path hasn't had a clean-chip run yet**: the first\n  attempt locked up the part by clocking it past its 400 kHz spec\n  (etch341 now defaults I²C to 100 kHz and rejects anything above\n  400). A write-then-read-back loop on a healthy chip is still owed.\n  If you run it, please open an issue with the result.\n\n## Install\n\n### Prerequisites\n\n- Rust 1.85+ (uses 2024 edition)\n- A C compiler (cc / clang) — `rusb` builds libusb-1.0 from source\n  and links it statically into the binary, so there's no\n  system-wide libusb install needed at build or runtime\n- A CH341A USB programmer (the common \"black module\" or the \"V1.3 mini\" with\n  on-board ZIF socket both work)\n\nIf you'd rather grab a pre-built artifact instead of compiling, head\nto the [Releases page](https://github.com/packetThrower/etch341/releases) —\neach release ships native installers for arm64 + amd64 on all three\nplatforms:\n\n| Platform | Installer / Package | Also |\n|---|---|---|\n| macOS | `etch341-\u003cver\u003e-\u003carch\u003e-macos.dmg` (drag-to-install) | `etch341-macOS-\u003carch\u003e-\u003cver\u003e.zip` (bare `.app` bundle) |\n| Windows | `etch341-\u003cver\u003e-\u003carch\u003e-windows-setup.exe` (NSIS) | `etch341-\u003cver\u003e-\u003carch\u003e-windows.msi` (MSI, stable tags only) |\n| Windows | — | `etch341-\u003cver\u003e-\u003carch\u003e-windows.zip` (portable bare `.exe`) |\n| Linux (Debian/Ubuntu) | `etch341-\u003cver\u003e-\u003carch\u003e-linux.deb` | — |\n| Linux (Fedora/openSUSE/RHEL) | `etch341-\u003cver\u003e-1.\u003crpm-arch\u003e.rpm` | — |\n| Linux (Arch) | `etch341-\u003cver\u003e-1-\u003crpm-arch\u003e.pkg.tar.zst` | — |\n| Linux (any) | `etch341-\u003cver\u003e-\u003carch\u003e-linux.AppImage` (universal) | — |\n\nThat's 19 artifacts per release (6 macOS + 6 Windows + 8 Linux,\nplus a `SHA256SUMS` line for everything). The Linux `.deb` /\n`.rpm` / `.pkg.tar.zst` installs drop the udev rule into\n`/usr/lib/udev/rules.d/` automatically; the AppImage and bare\nbinaries don't (run the manual `sudo cp` step from the Linux\nsection below the first time).\n\n### macOS\n\nThe easiest path is the Homebrew tap, which always tracks the\nlatest stable release and auto-updates with `brew upgrade`:\n\n```sh\nbrew install packetThrower/tap/etch341\n```\n\nOr build from source:\n\n```sh\ncargo install --path .\n```\n\nNo driver setup needed — macOS leaves the CH341A's vendor interface\nalone, and libusb is bundled into the binary.\n\n### Linux\n\n```sh\nsudo cp platform/udev/99-ch341a.rules /etc/udev/rules.d/\nsudo udevadm control --reload\ncargo install --path .\n```\n\nThe udev rule lets unprivileged users open the device. Without it you'll\nhit `PermissionDenied`.\n\n### Windows\n\nWindows doesn't ship a generic userspace USB driver, so the CH341A\neither enumerates as an unknown device or gets claimed by a vendor\nserial-port driver — either way, libusb can't open it. The one-time\nfix is to bind the **WinUSB** generic driver to the device:\n\n1. Plug in the CH341A.\n2. Run [Zadig](https://zadig.akeo.ie/) (≈600 KB, no installer).\n3. In Zadig's `Options` menu, enable `List All Devices`.\n4. Select the entry with VID `0x1A86` / PID `0x5512`, choose **WinUSB**\n   from the driver dropdown, and click `Install Driver`.\n5. Install etch341 from the Scoop bucket (auto-updates via `scoop update`):\n\n   ```powershell\n   scoop bucket add packetThrower https://github.com/packetThrower/scoop-bucket\n   scoop install etch341\n   ```\n\n   Or build from source: `cargo install --path .`.\n\nYou only need to do steps 1–4 once per machine. If `etch341 detect`\nreports `DeviceNotFound` on Windows after running it once, the driver\nbinding is usually the cause — re-check in Zadig that the device is\nstill bound to WinUSB and not to a vendor driver that took over after\nan update.\n\n## Usage\n\n### CLI\n\n```sh\netch341 detect                       # identify the chip\netch341 read -o bios.bin             # dump entire chip to file\netch341 read -o -                    # dump to stdout (pipe to anything)\netch341 read -o - | sha256sum        # hash a chip without a temp file\netch341 read -o head.bin --length 0x1000   # first 4 KB only\netch341 write -i bios.bin            # erase + program + verify\netch341 write -i bios.bin --no-erase --no-verify   # raw program\netch341 erase                        # full chip erase\netch341 erase --range 0x10000:0x10000   # erase one 64 KB block\netch341 verify -i bios.bin           # compare without writing\netch341 blank-check                  # confirm all 0xFF\netch341 sr                           # dump SR1/SR2/SR3 with decoded bits\netch341 sfdp                         # decode the chip's SFDP table\netch341 otp read                     # dump the security / OTP registers\n```\n\nI²C EEPROMs (24Cxx family) use the nested `i2c` subcommand.\n\n\u003e ⚠️ **The I²C path hasn't been hardware-validated yet.** Code,\n\u003e protocol, and tests are all in place but nobody's run it against\n\u003e a real 24Cxx chip. Your first run is also our bring-up. If `i2c\n\u003e scan` returns either an empty list or every address (rather than\n\u003e just the chip's address(es)), the most likely culprit is the\n\u003e CH341A ACK-bit polarity assumption in `src/ch341.rs::i2c_probe` —\n\u003e open an issue with the verbose-mode (`-v i2c scan`) output and\n\u003e we'll get it sorted.\n\nUnlike SPI NOR there's no JEDEC ID register, so the chip must be\nselected explicitly with `-c`:\n\n```sh\netch341 i2c scan                            # list 7-bit addrs that ACK\netch341 -c 24C256 i2c read -o eeprom.bin    # dump entire chip\netch341 -c 24C256 i2c write -i eeprom.bin   # program + verify\netch341 -c 24C256 i2c verify -i eeprom.bin  # compare without writing\netch341 -c 24C02 i2c blank-check            # confirm all 0xFF\netch341 -c 24C02 i2c erase                  # write 0xFF to every byte\n```\n\n`--straps \u003c0..7\u003e` selects the A0/A1/A2 pin value if the chip is wired\nnon-default. The 24C04/08/16 use bit-stuffing in the slave address\nfor their high memory bits; this is handled automatically.\n\nSupported families: 24C01 / 02 / 04 / 08 / 16 / 32 / 64 / 128 / 256 /\n512. Other 24Cxx chips work if you add an entry to `chips/i2c_chips.toml`.\n\nThe CLI also has three offline inspection commands that work on flash\ndump files (no hardware required):\n\n```sh\netch341 chips                            # list every supported chip\netch341 chips --find mx25                # substring filter on name or JEDEC\netch341 chips --bus i2c                  # filter to one bus family\n\netch341 strings -i dump.bin              # printable ASCII strings ≥4 chars\netch341 strings -i dump.bin --min-len 8  # noisier-but-richer threshold\n\netch341 search \"55 AA\" -i dump.bin       # find hex pattern (spaces optional)\netch341 search \"Award\" -i dump.bin       # ASCII (case-insensitive)\netch341 search \"DEADBEEF\" -i dump.bin --context 32   # widen the gutter\n```\n\n`search` parses the pattern as hex when the condensed form is even-length\nand all hex digits (`55AA`, `DE AD BE EF`); anything else is taken as\nASCII. Matched bytes print in upper-case hex; surrounding context stays\nlower-case for an at-a-glance visual contrast.\n\nGlobal flags:\n\n- `-v, --verbose` — log every SPI or I²C transaction to stderr.\n  Invaluable for debugging in-circuit issues and for spotting wiring\n  problems (every `-\u003e OUT` line should be followed by a sensible\n  `\u003c- IN`; missing IN bytes mean either the chip isn't responding or\n  the bus is mis-wired).\n- `-c, --chip \u003cNAME\u003e` — for SPI, overrides JEDEC autodetect with a\n  chip name from `chips/chips.toml` (e.g. `W25Q128JV`). For I²C and\n  for `--dry-run` it's **required** (there's no JEDEC equivalent on\n  I²C, and dry-run has no hardware to autodetect).\n- `-s, --speed \u003cKHZ\u003e` — bus clock speed. Supported rates on the\n  CH341A: 20, 100, 400, 750. SPI defaults to 750; **I²C defaults to\n  100 and rejects anything above 400** (the 24Cxx family is spec'd\n  at 400 kHz max — over-clocking one bricked a part during bring-up).\n- `-n, --dry-run` — for hardware-touching commands, validate\n  everything possible (chip name in DB, input file is readable,\n  start + length fits the chip) and print a `[dry-run]` summary of\n  what would happen. Never opens the CH341. Useful for sanity-\n  checking flags before you actually pull the trigger on an erase or\n  write. Offline commands (`chips`, `strings`, `search`) ignore the\n  flag because they don't touch hardware anyway.\n\n### GUI\n\n```sh\netch341      # no subcommand → opens the GUI window\n```\n\nBuild the CLI-only variant (no GPUI fetch, much smaller binary, faster\nbuild) with:\n\n```sh\ncargo build --release --no-default-features\n```\n\n## Hardware notes\n\n### In-circuit programming on enterprise hardware\n\nIn-circuit attempts on **server-class boards, dual-BIOS systems, and\nfirewalls** frequently fail. The host's SPI controller actively drives MISO\nlow even when the board is \"powered off\" — `etch341 detect` returns\n`JEDEC ID : 0x000000` and the verbose log shows clean command bytes going\nout but nothing meaningful coming back.\n\nDiagnose with the loopback test:\n\n```sh\netch341 detect -v       # with clip OFF the chip; nothing else changed\n```\n\n- `\u003c- IN [4]: ffffffff` → CH341A is healthy; the target board is fighting us\n- `\u003c- IN [4]: 00000000` → CH341A or wiring problem, not the target\n\nRemedies, in order of effort:\n\n1. Use a loose chip in the CH341A's on-board ZIF socket\n2. Lift pin 8 (VCC) of the in-circuit chip and inject 3.3V externally\n3. Hot-air the chip off and use the ZIF\n\n### Voltage\n\nThe black-module CH341A has a 3.3V/5V jumper near the USB end. **3.3V\nis correct for every 3.3V family in `chips/chips.toml`** (W25Q, W25X,\nMX25L, GD25Q, SST25VF, AT25SF, EN25QH, P25Q, IS25LP). The 1.8V\nfamilies (W25Q*JW, MX25U, GD25LQ) need a 1.8V-capable programmer —\neither the V1.7 module's separate 1.8V switch or a level-shifter\nadapter; running them at 3.3V will damage them. **5V will damage\nevery chip in the DB** — don't flip the jumper to 5V unless you\nknow exactly why.\n\n### Pin 1\n\nThe SOIC-8 clip's red wire = pin 1. The chip's pin 1 is marked with a dot\nor notch on the package. About half of first-attempt failures are\nclip-reversed.\n\n## Architecture\n\n```\nsrc/\n├── main.rs       entry point; no-args → GUI, subcommand → CLI\n├── cli.rs        clap derive definitions + dispatch\n├── error.rs      thiserror enum\n├── ch341.rs      USB layer; impls both SpiTransport and I2cTransport\n├── spi.rs        SPI NOR opcodes + SpiTransport trait + helpers\n├── ops.rs        high-level SPI read / erase / write / verify / blank / detect\n├── i2c.rs        24Cxx protocol + I2cTransport trait + helpers\n├── i2c_ops.rs    high-level I²C scan / read / write / verify / blank / erase\n├── chipdb.rs     TOML chip DB loader (SPI + I²C, embedded at build)\n├── inspect.rs    parse-pattern / extract-strings / find-pattern shared by CLI + GUI\n├── prefs.rs      ~/.config/etch341/prefs.toml load/save (GUI settings)\n└── gui/          GPUI frontend; behind the `gui` cargo feature (default-on)\n\nchips/chips.toml      58 SPI NOR entries across Winbond (W25X, W25Q,\n                      W25Q*JW 1.8V), Macronix (MX25L, MX25U 1.8V),\n                      GigaDevice (GD25Q, GD25LQ 1.8V), SST25VF,\n                      AT25SF, EON EN25QH, PUYA P25Q, ISSI IS25LP\nchips/i2c_chips.toml  10 I²C EEPROM entries (24C01 .. 24C512)\n```\n\nThe `SpiTransport` trait abstracts the USB layer so the high-level ops can\nbe unit-tested against a deterministic mock (`src/spi.rs::test_support::MockSpi`).\nThe `Ch341` struct is the production implementation.\n\n## Development\n\n```sh\njust build        # full build (CLI + GUI; first time pulls the gpui git dep)\njust build-cli    # CLI only, much faster\njust test         # unit tests, no hardware\njust run -- detect -v\n```\n\nOr use Cargo directly:\n\n```sh\ncargo build --no-default-features    # CLI only\ncargo test  --no-default-features\ncargo run                            # GUI\ncargo run   --no-default-features -- detect -v\n```\n\nThe app icon is `build/appicon.svg` (a top-down wireframe of a\nSOIC-8 in the family palette of Baudrun + PortFinder). The other\nicon files in `build/` and `resources/icons/` are generated from\nit by `build/make-icon.sh` — re-run that script after editing the\nSVG (requires `rsvg-convert`, ImageMagick's `magick`, and\n`iconutil` for the macOS `.icns`).\n\n## License\n\nGPL-3.0-or-later. See [LICENSE](LICENSE) for the full text.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpacketthrower%2Fetch341","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpacketthrower%2Fetch341","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpacketthrower%2Fetch341/lists"}