A free, cross-platform, and completely open-source C++ utility to reset the "Waste Ink Pad" counter on Epson printers.
EWR bypasses the need to pay for sketchy third-party reset keys (like WIC Reset) or run malicious, virus-flagged AdjProg.exe binaries. By dynamically generating IEEE 1284.4 hardware packets and utilizing a continuously updated database, EWR communicates directly with the printer's motherboard over USB to safely zero out the EEPROM waste counters.
|B) on the fly for your specific model. It manages the IEEE 1284.4 (D4) credit system properly, so nothing overflows a buffer or locks the printer up.SetupAPI with asynchronous OVERLAPPED I/O to safely drain the Windows Print Spooler buffers, plus a statically linked libusb for the vendor-specific interfaces usbprint.sys cannot reach - on ET-2xxx units the maintenance engine lives on one of those. Both transports run in one pass, usbprint.sys first. Zero custom drivers required.libusb to automatically detach the kernel driver (CUPS) for exclusive, raw hardware access.ewr_trace.log records every byte in both directions next to the binary. It is what makes a bug report solvable.ewr and database.json together.--ip <address> (below) goes straight to one.ewr.exe
* Linux / macOS: sudo ./ewr (raw USB access requires root)y.No internet connection is needed - the database ships in the download. When EWR can reach GitHub it picks up database updates at startup.
Download EWR only from the Releases page. Copies passed around elsewhere - in video descriptions, on forums - may not be what they claim to be. From v1.4.1 on, every release archive, including the EWR-SDK-* ones, carries a signed build-provenance attestation tying it to this repository and to the exact build that produced it. You can check it with the GitHub CLI, signed in (gh auth login):
gh attestation verify EWR-v1.5.1-win.zip -R RxNaison/Epson-Waste-Reset
✓ Verification succeeded! means the file is byte for byte what this repository's release workflow built, and its Build workflow line ends in the tag it was built from (@refs/tags/v1.5.1) - check that it is the version you meant to download. A modified file fails with HTTP 404: Not Found: no attestation exists for its contents. If an archive from v1.4.1 or later fails, don't run it. Releases before v1.4.1 have no attestation and fail the same way, so the check cannot vouch for them. This is not code signing: Windows may still warn that the app is unrecognized.
Running with no options is the supported path. These exist for diagnosis and for people packaging EWR into something larger.
| Option | What it does |
|---|---|
--status, -s |
Read-only: printer status, ink levels and waste counter values. Sends no writes. |
--list, -l |
List every Epson USB interface, and every Epson printer that answers on the network, with its IEEE 1284 device ID and database match, then exit. Read-only. |
--model <name> |
Skip the menu. Takes the exact name, an alias, or the name the printer reports (--model "EPSON ET-2803 Series"). |
--ip <address> |
Reach a printer on the network instead of USB, over SNMP (UDP 161): ewr --ip 192.168.1.100. Works with --status, --dry-run and the waste ink pad reset, with the same gates, confirmation and read-back as USB. Replay dumps and the other modes still need USB. Many recent models lock the EEPROM over the network but not over USB; EWR says so and stops before writing anything. |
--ip auto |
Search the network and pick the printer from a list, even when another printer is on USB. It asks at the keyboard, so it does not combine with --yes or --json: pass the address from --list instead. |
--interface <n> |
Pin the run to interface <n> from --list and disable the automatic fallback. |
--dry-run |
Detect, read, and show exactly what a reset would write - then stop. |
--dump |
Read the EEPROM into a timestamped file. Dump twice around a change and diff to map an unknown printer. |
--find-addresses |
For a model with no reset addresses yet: read the EEPROM three times, asking you to run a head cleaning between reads, and report the bytes that rise every time. Read-only. |
--find-key |
For a printer the database has no entry for: try every read key in the database and report which one, if any, the printer answers. Takes no --model. Read-only. |
--yes, -y |
Answer the reset confirmation with yes, for callers driving EWR non-interactively. See below. |
--force-yes |
--yes, and overrule the gates that would stop it: a model mismatch, a printer error, a database conflict. Last resort. |
--cartridge, -c |
Reset the cartridge ink levels instead of the waste ink pads, on models that carry a per-color ink map. |
--no-update |
Fully offline: no update check, no download, database.json left as it is. Use it while editing database.json. |
--usb-soft-reset |
Diagnostic only, Windows. Resets the USB channel once at the start of the run, then waits (up to 90 s) for the printer to finish the re-initialization the reset starts. |
--json |
Machine-readable output: one JSON object per line on stdout, for callers driving EWR from another language. See docs/json-output.md. |
--help, -h |
The same list, from the binary. |
On a terminal, EWR colors its output: message tags, ink and waste pad gauges, the printer's state. Set NO_COLOR=1 to turn that off. Piped output, log files and --json never carry color codes.
--yesewr --yes # reset the printer that answers, unattended
ewr --model L3150 --yes # ... or name the model yourself
--yes answers one question: the final "Reset the waste ink pad counters now?". Everything
else about the run is unchanged - the preflight read, the read-back verification, and every
gate that can stop it.
Without --model it takes the model the printer reports over IEEE 1284, the same entry the
menu offers on Enter, and stops if the printer names nothing the database recognises. So the
model it writes to is the one the printer identified itself as - never a guess and never a
menu default. What it deliberately does not do:
--model names something other than the
printer that answered, the run stops instead of writing. A wrong-model write is the hazard
the confirmation exists for, and no flag should be able to cause one.--yes declines them and exits non-zero.--yes
takes the waste ink pads. Reaching the ink reset takes --cartridge, named explicitly.Exit status is 0 when the reset completed and verified, 1 when it was stopped or failed,
2 for a bad command line.
Two EWR runs must not drive one printer: they share the USB handle and take each other's replies, and the run that loses one may be the one mid-write. A second run stops with a message instead of starting. The closing "Press Enter to exit" is skipped when EWR is not run from a terminal, so a caller that pipes its answers never waits on a keypress.
--json--json turns stdout into one JSON object per line: a hello, an event per step, and
one result carrying the outcome. Any language that can start a process can use it, and a
caller that reads only result and the exit code keeps working across EWR versions -
docs/json-output.md is the contract and what it promises.
import json, subprocess
run = subprocess.run(
["./ewr", "--model", "L3150", "--yes", "--json", "--no-update"],
stdout=subprocess.PIPE, stdin=subprocess.DEVNULL, text=True,
)
result = None
for line in run.stdout.splitlines():
msg = json.loads(line)
if msg["type"] == "event" and msg["total"]: # progress, e.g. 3 of 5 writes
print(f"{msg['index']}/{msg['total']} {msg['code']}")
elif msg["type"] == "result":
result = msg
if result["ok"]:
print("verified", result["data"]["writes"]["verified"], "of", result["data"]["writes"]["total"])
elif result["error_code"] == "blocked":
print("a gate stopped it, nothing was written:", result["error"])
else:
print("failed:", result["error_code"], result["error"])
--json never prompts, so answer the gates on the command line: --yes for the reset
confirmation, --force-yes to overrule a printer error as well. Without --model it uses
the model the printer reports. A run that would need an answer stops with
error_code: "blocked" and writes nothing.
To put the reset inside your program instead of spawning it - a GUI, a service, a
language binding - there is a C ABI in include/ewr/ewr_c.h, built as ewrc.dll /
libewrc.so alongside the CLI, with the same JSON coming back:
docs/c-api.md.
--cartridgeSome models carry a per-color cartridge ink map as well as waste pad counters, and a run
without flags offers the choice. --cartridge makes that choice on the command line instead:
ewr --model R220 --cartridge # ask, then reset the ink levels
ewr --model R220 --cartridge --yes # ... without asking
ewr --model R220 --cartridge --dry-run # show the ink writes, send nothing
It selects the target and nothing else - it is not a confirmation. On its own the reset still
arms on the word reset typed at the keyboard; --yes is what answers that. --dry-run
follows it, so the printed plan is the plan of the run you asked for.
EWR cannot refill ink. This reset rewrites the printer's accounting, so a genuinely empty cartridge will report full and can run dry mid-print, which damages the print head. It also only holds on printers that keep ink levels in their own EEPROM: where the cartridge carries a smart chip, the firmware treats the chip as the truth and writes the EEPROM back from it.
--force-yes--force-yes is --yes plus the two refusals above: it writes to a model the printer did not
report as itself, and it writes while the printer objects with an error or the database
disagrees with itself about this model's write path.
That is the combination this tool spends most of its code preventing, and every reason those
gates exist still applies when you pass it - a wrong-model write can misconfigure a printer
you cannot replace. It is here for the case where you know the detection is wrong and the
printer is right, not as an automation default. Prefer --model over forcing a mismatch.
What it does not touch: the preflight read, the address-width encoding guards, and the read-back verification. Those are not prompts, they are the write path, and no flag skips them.
Every run writes ewr_trace.log next to the executable, containing every packet sent and received. If EWR fails, or claims success and the error comes back after a power cycle, that file is what makes the problem solvable - please attach it to an issue rather than pasting the console output alone.
Useful first steps:
ewr --list shows every USB interface EWR can see and which one it would pick. On a printer that also scans, several will be listed.ewr --status reads the counters without writing anything, so it is always safe to run.If the printer does not answer, reports as busy, or the reset fails, try these before opening an issue - they settle most reports:
Get-Printer in PowerShell lists each printer with its driver. Microsoft IPP Class Driver means Windows Ready Print is driving the printer, and with it an XP-332 never answered EWR at all (ewr_trace.log said Printer did not answer the D4 Init transaction). Remove the printer in Settings > Bluetooth & devices > Printers & scanners, install Epson's driver, and run EWR again.libusb is fetched and built by CMake, so the first configure needs git and a network connection; it is then linked statically and ewr.exe ships alone.cmake, gcc, pkgconf, libusb-1.0-dev, and libcurl4-openssl-dev.brew install cmake libusb curl pkgconf# 1. Generate the build files
cmake -B build
# 2. Compile the project (Release mode)
cmake --build build --config Release
The compiled executable (ewr.exe or ewr) is placed in the repository root - the build pins the output directory there so the binary sits next to database.json and the models/ folder. Both are found relative to the executable, not your shell's working directory.
Running the test suite:
cmake -B build -DEWR_BUILD_TESTS=ON
cmake --build build --config Release
ctest --test-dir build --output-on-failure -C Release
On macOS the downloaded binary is quarantined by Gatekeeper. Clear it with xattr -d com.apple.quarantine ewr, or right-click the binary and choose Open.
Adding or fixing a printer means editing database.json and opening a pull request - no C++ required. database.json is the curated source of truth: the weekly rebuild merges upstream sources around your values and can never overwrite a hand edit. See CONTRIBUTING.md for the field reference and what makes a good pull request.
If your model is missing entirely, two things have to be found: a read key the printer answers to, and then its addresses. Start with the safe methods - EWR ships the tooling for both, and neither writes anything.
EWR cannot read a printer without its read key. Printers of one generation often share keys, so a missing model may answer to a key the database already has for a sibling:
ewr --find-key
It reads one byte with each of the ~130 distinct read keys in the database - a few minutes - and reports how the printer answered. A key that draws no answer is asked again, up to three times, before it is counted as unanswered:
--dump, --find-addresses below). Do not run a reset with that entry - the key fits, but its addresses are that model's, not yours.:41:NA;): the printer knows the command and its key is not in the database.||:;): the printer most likely does not take this read command at all.Either way, attach ewr_trace.log to an issue: it records how every key was answered.
Around 113 database entries carry a working read key but no reset addresses - EWR can read those printers, it just does not know which bytes hold the waste counter. They appear in the menu as (no reset addresses yet - read-only).
--find-addresses goes looking. It reads the EEPROM three times and asks you to run a head cleaning from the printer's own panel between reads, because a cleaning is the one action guaranteed to move the waste counter:
ewr --model ET-M1180 --find-addresses --no-update
Bytes that move the same way after both cleanings are reported, rising or falling, with their three readings side by side; a byte that moves only once is a timer or a page count and is dropped. A waste counter only ever rises, so the rising rows are the candidates and a falling one is something else - an ink level, a countdown. Two intervals is the minimum that tells those apart, which is why it takes three reads. Little-endian pairs are handled, so a low byte rolling over into its neighbour is reported as one 16-bit counter rather than discarded for falling. Nothing is written to the printer at any point, and the result is saved as <Model>-found-addresses.json.
Budget around twenty minutes: a full read of the low page takes a few minutes on older models, and you run it three times with two cleanings in between. A cleaning also uses real ink and pushes the waste counter up.
This records readings, not a reset plan. The saved file's pad_groups is empty on purpose: which of these is a waste counter, and what it resets to, is a judgement no dump can make - some pad groups reset certain bytes to 0x5E (94) rather than 0. Open an issue or a pull request with the file rather than writing anything back blind.
If you would rather do it by hand, --dump writes a single timestamped snapshot you can diff yourself:
ewr --model L3150 --dump --no-update
diff ewr_dump_L3150_1736900000.txt ewr_dump_L3150_1736903600.txt
A -- is a byte the printer never answered, so its value is unknown. A dump with any -- is reported as incomplete and exits 1 instead of 0; run it again rather than keeping it as a backup. If every value comes back as --, the read key is wrong for your printer - try a different sibling from the same model line.
Mind the mirror trap: a byte that returns to its old value by itself after a power cycle is being rewritten by the firmware from the cartridge chip. That level lives on the chip and cannot be reset from the PC.
Put what you find into database.json, check it with --dry-run, confirm with a real run, and open a pull request with the console output.
EWR would not exist without the people who reverse-engineered these protocols first. The database is assembled by an automated pipeline (scripts/build_db.py) that merges four upstream projects around the curated entries in this repository:
EWR is free and will stay free. If it saved you a paid reset key or a trip to the repair shop, you can support development:
TXjUX9gCDjRUDU3L3HB3vfXbPdnTXiPzoR1Ep5bY7xBaJpWnzEG4PCjq9hTEjms9J1SRNot into donating? A ⭐ or a note in Discussions about which model worked helps just as much.
Manipulating hardware via raw USB packets carries inherent risks. EWR is provided "as is" without warranty of any kind. By using this software, you accept full responsibility for your hardware.
| 日期 | 榜单 | 排名 | 周期新增 | 总 STAR |
|---|