openshuttercounter

module
v0.0.0-...-2bd45f3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 28, 2026 License: MIT

README

openshuttercounter

Read the shutter actuation count of a camera, on Linux, macOS and Windows, for free.

$ openshuttercounter

  Canon EOS 5D Mark IV  (USB 04a9:3281)

  Shutter actuations           26,374   (MPU 0x1001)
  Mirror actuations            26,148   (MPU 0x100b)
  Total counter                28,054   (MPU 0x1015)

  Rated life 150,000 actuations - 17.6% used

One static binary. No libgphoto2, no vendor SDK, no Windows-only tooling.

Why this exists

I wanted the shutter count on my own 5D Mark IV. It ought to be a solved problem — the camera knows the number, the cable is right there — but every open source tool I tried came back empty, and the ones that worked were paid, closed and Windows only. For a number the camera will happily tell you, that seemed a poor state of affairs, so I went looking for why.

It turns out the free tools are not at fault. Canon moved the counter, and nobody had published the way to the new place. So I worked it out and wrote it down, which is really what this repository is: the answer, in enough detail that the next person does not have to go looking.

Up to roughly the DIGIC 5 generation, Canon exposed a shutter counter as PTP device property 0xD1AC. gphoto2 reads it as /main/status/shuttercounter and it still works on a 5D Mark III.

On DIGIC 6 and newer bodies — 5D Mark IV, 5DS, 5DS R, 1D X Mark II, 80D and everything after — that property is gone. GetDevicePropValue on 0xD1AC returns OperationNotSupported. This is why gphoto2, FreeShutterCounter, EOS Digital Info and every other free tool comes up empty on a modern body: not an oversight on their part, but a wall Canon put there.

The counters were never in the PTP property tree. They live in the memory of the MPU, the separate microcontroller that drives the shutter and mirror, and they are reachable through a text command channel Canon carries over two vendor PTP opcodes. That channel, its framing and the MPU address map are documented in full in docs/PROTOCOL.md.

Status

Canon is the only manufacturer implemented so far, and exactly one body has been confirmed by somebody holding it.

State
Canon protocol and framing Confirmed on an EOS 5D Mark IV, firmware 1.2.1
Canon MPU address map Confirmed on the same body, over an exact count of 20 frames
Cameras that answer the standard PTP property Should work on any make; tried first on every camera
Other Canon bodies Very likely the same channel, but the addresses are unconfirmed
Other manufacturers Not implemented yet
Linux backend (usbfs) Implemented
macOS backend (libusb via purego) Implemented, not yet run against a camera
Windows backend (still image device) Implemented, not yet run against a camera

If your camera is not on the confirmed list, try it anyway. It costs nothing, it cannot write to the camera, and whether it works or not the result is worth reporting:

openshuttercounter --trace mycamera.json

That produces a file recording everything exchanged with the camera, which is enough for anyone else to confirm the reading or work out what went wrong. CONTRIBUTING.md explains what to do with it. This is the single most useful contribution to the project.

Install

# macOS, via Homebrew
brew install mvisonneau/tap/openshuttercounter

# Windows, via Scoop
scoop bucket add https://github.com/mvisonneau/scoops
scoop install openshuttercounter

# Anywhere with a Go toolchain
go install github.com/mvisonneau/openshuttercounter/cmd/openshuttercounter@latest

Binaries, .deb, .rpm and .apk packages are attached to every release.

Usage

openshuttercounter                          # read the connected camera
openshuttercounter --raw                    # also show the raw replies
openshuttercounter --json                   # machine readable output
openshuttercounter --trace session.json     # record the whole conversation
openshuttercounter list                     # what is connected
openshuttercounter models                   # cameras this build knows
openshuttercounter decode session.json      # print a recorded conversation

decode reads traces written by openshuttercounter, and USB captures in pcap or pcapng format for anyone who already has one. Comparing two traces taken an exact number of frames apart is how a camera gets confirmed.

Setting up the camera

Whatever the platform:

  • Built-in Wi-Fi fully disabled. On many bodies enabling it disables the USB port entirely, and the camera simply will not appear.
  • A card in the camera.
  • A data cable, plugged in directly rather than through a hub.
  • USB 2 ports proved noticeably more reliable than USB 3 during this work.
Linux

The .deb and .rpm packages install a udev rule that lets you read a camera without root. Installing another way, copy it yourself:

sudo cp helpers/udev/99-canon-ptp.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules && sudo udevadm trigger

Then replug the camera. If you get device is claimed by another process, that is gvfs, which grabs cameras on sight:

gio mount -u gphoto2://
systemctl --user mask gvfs-gphoto2-volume-monitor
macOS

USB access goes through libusb, loaded at runtime:

brew install libusb

macOS hands every camera to ptpcamerad as soon as it appears. openshuttercounter stops it and retries automatically when the device is busy; launchd restarts it afterwards.

Windows

No driver replacement is needed: the tool opens the still image device interface the stock driver already exposes, so the manufacturer's own software keeps working. Close anything else that might have the camera open.

Safety

This tool only reads.

On Canon bodies it sends MonOpen, MonReadAndGetData and MonClose, and nothing else. That restraint is not incidental: the same channel carries FA_SetProperty, FA_SetCalendar and MonWrite, which write factory state — calibration data, the camera's calendar, and the actuation counters themselves. This code refuses to build those commands at all, and the refusal is covered by a test.

Malformed payloads to Canon's 0x9052 put the camera into Err 70. That happened twice while the channel was being worked out, and cleared on a battery pull with no lasting damage, but it is the reason the command framing here is asserted byte-for-byte rather than written from a description. If those tests ever fail, do not run the result against a camera.

Do not sweep opcodes blind in the 0x90500x905F range. It contains write and call primitives, and these bodies also advertise UpdateFirmware, FormatStore and DeleteObject.

How it works

Cameras that still answer the standard PTP shutter counter property are read that way, whatever the make. Canon bodies that do not are read over the factory command channel:

0x9052   send a command   params (0,1) if a reply follows, else (0,0)
         data-OUT: <name NUL> <u32 argc> <argc x 20-byte argument record>
0x9053   read the reply   params (0,0,1)

Both opcodes require a data phase. Calling either with parameters alone returns DeviceBusy — which is exactly what made this channel look closed for seven years, including to libgphoto2's maintainer.

MonReadAndGetData(2, address, length) reads MPU memory:

MPU address Contents Confirmed reading
0x1001 Shutter actuations 26,354 → 26,374 after 20 frames
0x100B Mirror actuations 26,128 → 26,148
0x1015 Larger total counter 28,034 → 28,054
0x1198 Error log, not a counter unchanged

Full details, including the reply encoding and the evidence for it, are in docs/PROTOCOL.md.

Repository layout

Everything except pkg/manufacturers/<name> is manufacturer agnostic, so supporting a second maker means adding a package rather than rearranging the rest.

Path Contents
cmd/, internal/ The CLI
pkg/manufacturers What a camera maker has to provide to be readable
pkg/manufacturers/canon Canon's factory channel, counter decoding and model list
pkg/ptp PTP over USB, enough of it to send vendor opcodes
pkg/usb The three USB backends and the descriptor parsing they share
pkg/trace Recording and reading back a conversation with a camera
pkg/pcap Reading USB captures, for traces that came from elsewhere
docs/ The protocol reference

Credit

The trail starts on a SourceForge thread with a title that says it all — There must be a way to read shutter actuations from Digi6/6+ — opened by Garfield in April 2017.

DD4DA answered it in May 2018 with the first public mention of MonOpen, MonReadAndGetData and MonClose, noting that the procedure appears nowhere in Canon's SDK. Marcus Meissner, libgphoto2's maintainer, followed up in February 2019 with the first hypothesis about the opcode — "It might be PTP Command 0x905f with 1 argument, like 0x17 for mirror count and 0x0d for shutter count?" — reported DeviceBusy on an EOS 750D, and committed a wrapper for it the same day. ptp_canon_eos_905f() is still in the tree, and has never been wired to anything.

The Magic Lantern community's Canon 40D thread carries the firmware symbol dump listing the Mon* family against ../MpuMonitor.c, which is what established that these are MPU monitor functions rather than anything to do with the main processor.

Still open, and what this is ultimately for: gphoto2 #81 (EOS 5DS), libgphoto2 #270 (EOS 750D) and libgphoto2 #308 (EOS 6D Mark II).

What this project adds: the opcodes are 0x9052 / 0x9053, not 0x905F; the interface is a text command channel rather than a counter selector; the missing precondition was simply the data phase; and an argument record's first word is a type tag, which may be an ASCII code rather than the integer 2.

Licence

MIT. See LICENSE.

MIT rather than the Apache-2.0 used across the rest of these repositories, deliberately: the natural home for this work is libgphoto2, which is LGPL-2.1, and Apache-2.0 cannot be merged into it. MIT can.

Directories

Path Synopsis
cmd
internal
app
cli
cmd
pkg
manufacturers
Package manufacturers defines what openshuttercounter needs from a camera maker in order to report its actuation counters, so that support for a second one is a matter of adding a package rather than rearranging this one.
Package manufacturers defines what openshuttercounter needs from a camera maker in order to report its actuation counters, so that support for a second one is a matter of adding a package rather than rearranging this one.
manufacturers/canon
Package canon implements the factory command channel Canon EOS bodies carry over two vendor PTP opcodes, and the shutter and mirror actuation counters that live behind it.
Package canon implements the factory command channel Canon EOS bodies carry over two vendor PTP opcodes, and the shutter and mirror actuation counters that live behind it.
pcap
Package pcap extracts a PTP conversation from a USB capture.
Package pcap extracts a PTP conversation from a USB capture.
ptp
Package ptp implements just enough of PTP over USB (PIMA 15740) to drive a camera directly, without going through libgphoto2.
Package ptp implements just enough of PTP over USB (PIMA 15740) to drive a camera directly, without going through libgphoto2.
trace
Package trace records and reads back a PTP conversation with a camera.
Package trace records and reads back a PTP conversation with a camera.
usb
Package usb provides the small amount of USB plumbing openshuttercounter needs in order to speak PTP to a camera: enumerate still image devices, claim the interface and move bulk transfers in both directions.
Package usb provides the small amount of USB plumbing openshuttercounter needs in order to speak PTP to a camera: enumerate still image devices, claim the interface and move bulk transfers in both directions.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL