Skip to content

About

A CCleaner-style cleanup tool for ESP32 SD cards that finds and removes junk, temporary, hidden, and system files created by macOS and other operating systems.

Resources

Stars

6 stars

Watchers

2 watching

Forks

Latest commit

 

History

6 Commits

Folders and files

Repository files navigation

ESP32 Cleaner

CCleaner for your SD card, running on the ESP32 itself.

platform framework license


Main menu Results grouped by pack Per-item review list

The problem

Desktop operating systems leave hidden files on every card you plug in. A Mac adds a .DS_Store to each folder it opens, a ._ AppleDouble file next to each file it copies, and folders such as .Spotlight-V100, .Trashes and .fseventsd. Windows leaves Thumbs.db and a $RECYCLE.BIN. Linux leaves .Trash-1000 and lost+found. Android leaves LOST.DIR and .trashed-* files, and a Synology NAS leaves an @eaDir folder next to every photo.

The desktop hides these files. Your handheld's file browser and your firmware's directory listings show them, and they take up space and directory entries on the card.

Removing them usually means finding a computer, mounting the card, and knowing which files are safe to delete.

The solution

ESP32 Cleaner removes them on the device, with the card still in the slot.

  • Scan. Walks the whole card and lists every file and folder that a rule matches.
  • Review by pack, then by item. Results open as a summary with one row per pack, which you can tick or untick as a whole. Open a pack to tick items one by one. Nothing you left unticked is deleted.
  • Quick clean. Scans and removes in one step.
  • Dry run. Runs the whole clean and reports real numbers, but deletes nothing.
  • Your own rules. Glob patterns, added on the device or in a text file on the card.
  • Scales to large cards. Results go to a manifest file on the card. RAM holds one offset and one selection bit per item, so tens of thousands of results fit in a few hundred KB of SRAM.

Rule packs

Pack Default Matches
macOS junk on .DS_Store · ._* · .Spotlight-V100 · .Trashes · .fseventsd · .TemporaryItems · .DocumentRevisions-V100 · __MACOSX · .apdisk · .VolumeIcon.icns
Windows junk on Thumbs.db · ehthumbs.db · desktop.ini · $RECYCLE.BIN · RECYCLER · System Volume Information · ~$*
Linux junk on .Trash-* · .directory · .thumbnails · lost+found · *~
Android junk on LOST.DIR · .trashed-* · .pending-*
NAS junk on @eaDir · #recycle · .@__thumb · .AppleDouble · .AppleDB
Dev caches off __pycache__ · *.pyc · .pytest_cache · .mypy_cache · .ruff_cache
Logs & temp off, risky *.log · *.tmp · *.bak · *.old · *.crdownload · *.part
Empty folders off, risky folders with nothing in them
Empty files off, risky zero-byte files
Custom on whatever you add

The three risky packs ship disabled because they can match real data or folders that other software needs. Examples are a log you wanted to keep, the empty roms/gba folder a handheld expects, and the zero-byte .nomedia file that hides a folder from the Android gallery. You can turn them on in Rules. When a clean includes one, the confirm screen names the pack and asks you to press Enter a second time.

Dev caches is off because most cards have none. Python and the listed tools rebuild these caches on their next run.

The two empty packs match on content, not on a name. They apply only to items that no other rule matched, so an empty .DS_Store still counts as macOS junk. A folder that holds only junk is not empty until the junk is removed, so the scan after the clean finds it.

The firmware stores which packs are on, the dry-run switch and case-sensitive matching in NVS, so they survive a power cycle. Each pack is saved under its own name. A firmware update keeps your choices and starts any new pack at its default.

Rule packs, logs pack off Patterns inside a pack

Your own rules

A rule is a glob matched against the name of a file or folder, never against its path, so / has no special meaning.

* any run of characters, including none
? exactly one character
[a-z] [abc] one character from a set
[!abc] one character not from a set

Matching ignores case by default, because FAT cards do. You can change this in Settings.

Add rules on the device under Rules › Custom › + Add a rule, or write them on a computer into /sdcleaner.rules at the root of the card:

# ESP32 Cleaner - custom rules, one per line.
# Format: <target> <pattern>   target: f=file  d=dir  a=any
f *.dmp
d __pycache__
a cache

Settings › Reload rules reads an edited file without a reboot. The firmware refuses a pattern made only of * and ?, because it would match everything on the card.

When you add or delete a rule on the device, the firmware rewrites only that rule's line. It keeps your comments, blank lines and any line it could not read. It writes the new file in full before it removes the old one, so losing power during a save does not lose your rules.

Adding a rule on the device Settings

A clean, end to end

Scanning Scan. Walks the card, counts results per pack as it goes, and shows the folder it is in. esc stops it, so you can abandon a long scan of a full card.
Summary by pack Review by pack. One row per pack with its count and size. space ticks or unticks a whole pack, and enter opens it.
Per-item list Or item by item. Every result with its size. tab filters the list by pack. Nothing you left unticked is deleted.
Confirmation Confirm. Counts and sizes per pack. If the clean includes a risky pack, the screen names it and asks a second time.
Cleaning Clean. Progress against the number of items you picked, with the path it is working on. esc stops this too.
Done Done. How many items were removed, how much space they freed, and the card's usage now. This run was a dry run, so nothing was deleted.

These images are captured from a real Cardputer ADV. See Screenshots below.

Supported hardware

Device Status
M5Stack Cardputer ADV supported
M5Stack Cardputer (original) supported

One firmware image runs on both. It detects the board at runtime, which is how the splash and About screens name the exact device. To add another board, see Adding a board below.

Splash, naming the detected board About

Flash it

Each release has two images. Flash the -factory.bin from a computer at offset 0x0, or copy the plain .bin to the SD card and start it from M5Launcher.

esptool.py --chip esp32s3 write_flash 0x0 esp32-cleaner-<version>-cardputer-factory.bin

To build and flash from source:

pio run -e cardputer -t upload
pio device monitor

Test it

The modules with no hardware in them run on the host. These are the glob matcher, the manifest record format, the rules-file line parser and the layout arithmetic.

pio test -e native

To check a real scan against a known answer, seed a card with one of everything. The script also writes near-misses that must survive and a path too long to list.

./tools/seed-test-card.sh /Volumes/MYCARD

Keys

Movement keys repeat while held. Action keys do not.

Key Action
↑ ↓ (; .) move up / down
← → (, /) page up / page down
space toggle the item, pack or setting under the cursor
tab in a list, cycle the pack filter · in the rule editor, switch files / folders / any
a / n select all / select none
d delete the custom rule under the cursor
enter confirm
esc (`) back, and cancel a running scan or clean
del back · erase a character in the rule editor

On-screen hints use the names printed on the keycaps, not the characters the keys send. The firmware draws the arrows as shapes, so they appear in any font. Each board names its own controls through hal::Caps, so a device with buttons instead of a keyboard shows hints for its buttons.

Screenshots

The images in this file are captured from the device. The firmware streams its canvas over USB, so they show the real UI with real numbers.

pio run -e cardputer-shots -t upload      # same firmware plus a scripted screen tour
./tools/grab-screenshots.py               # writes docs/img/*.png

The same run checks the real scan and prints the results next to the images. It checks that the per-pack totals add up, that select all, select pack and select none agree with the manifest, that every recorded line parses back, and that the pack filters split the results with no overlap and nothing missing. The tour always cleans in dry-run mode, so regenerating the images never deletes anything.

The grabber uses only the Python standard library and encodes the PNGs itself. The device reports its own pixel byte order, so the grabber needs no install and no per-board settings.

Adding a board

Board specifics (display, input, SD wiring) live behind src/hal/hal.h. Nothing in src/main.cpp depends on a device, and no screen hardcodes a pixel offset. src/ui.h derives the whole layout from the panel size and the font at runtime.

  1. Write src/hal/hal_<board>.cpp, wrapped in #if defined(BOARD_<X>), implementing begin(), display(), update(), pollKeys(), sdConfig(), caps() and boardName().
  2. Map the board's input (buttons, touch, encoder or serial) onto the hal::KeyEvent flags. Name those controls in hal::Caps, so on-screen hints do not say ; on a device with no keyboard.
  3. If the board can't type characters, report caps().textInput = false. The on-device rule editor then disappears, and users edit /sdcleaner.rules on a computer. Everything else works unchanged.
  4. Add an env to platformio.ini with -D BOARD_<X>.

App code does not change.

Layout

src/
  main.cpp        screens and the state machine
  ui.h/.cpp       theme and widgets: header, footer, menu, list, scrollbar, bars
  layout.h/.cpp   screen geometry, derived from the panel (host-tested)
  rules.h/.cpp    packs, glob rules, NVS settings, /sdcleaner.rules
  rulefile.h/.cpp one line of /sdcleaner.rules (host-tested)
  scanner.h/.cpp  card walk, result manifest, selection, deletion
  glob.h/.cpp     the wildcard matcher (host-tested)
  manifest.h/.cpp the result record format (host-tested)
  screenshot.*    canvas-over-USB dump, documentation builds only
  hal/            board abstraction and one file per board
tools/
  seed-test-card.sh     write a card with one of everything, for testing
  grab-screenshots.py   collect docs/img/*.png from a running device

MIT · made by REZOR · rezor.me

About

A CCleaner-style cleanup tool for ESP32 SD cards that finds and removes junk, temporary, hidden, and system files created by macOS and other operating systems.

Resources

Stars

6 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages