Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
13940c2
docs: Überblick und Machbarkeitsanalyse (OWL/OpenDrop auf ESP32)
claude Oct 14, 2026
076dc66
chore: .gitignore for build output and local captures/logs
claude Oct 14, 2026
7b15d5c
awdl: platform-independent AWDL frame build/parse + host test
claude Oct 14, 2026
92da2c9
chore: untrack native host test binary, ignore it
claude Oct 14, 2026
37a0a05
spikes: ESP32-C3 firmware for the M0 AWDL hardware tests
claude Oct 14, 2026
86b756c
spikes: use internal esp_wifi_set_tx_done_cb for IDF < 5.5 compatibility
claude Oct 14, 2026
faf88aa
spikes: create default event loop before Wi-Fi init
claude Oct 14, 2026
27831a5
tools: Spike 3 AWDL unicast injector (scapy)
claude Oct 14, 2026
170ef68
tools: fix ACK-check guidance in injector (RA is the sender, not the C3)
claude Oct 14, 2026
cfc865a
tools: self-triggering, gap-timed USB throughput meter
claude Oct 14, 2026
8333651
awdl: M1 step 1 - sync math, election, peer table, extended parser
claude Oct 14, 2026
9b57ee1
awdl: fix two defects found by port verification
claude Oct 14, 2026
b6f1368
awdl: M1 step 2 - node brain + scheduler, sync to a real master
claude Oct 14, 2026
2609f4b
awdl: report 0 on first sync lock, real drift thereafter
claude Oct 14, 2026
c08b8b1
awdl: advertise the master's channel-6 slots, not a flat all-6 sequence
claude Oct 14, 2026
c2aab9f
awdl: harden channel-sequence mirroring (review follow-ups)
claude Oct 15, 2026
c4096a6
bridge: M2.0 - COBS USB host-link transport + echo (host-tested)
claude Oct 15, 2026
611d5b0
bridge: M2.0 review follow-ups (startup race, short writes, cleanup)
claude Oct 15, 2026
c79f9f7
bridge: fix M2.0 echo corruption - console must leave the USB port to…
claude Oct 15, 2026
d64db87
bridge: require the right usb_serial_jtag component per IDF version
claude Oct 15, 2026
00ffb2c
bridge: default to modern usb_serial_jtag component; print the choice
claude Oct 15, 2026
8834cdb
bridge: pin target esp32c3 in bridge defaults (fixes undefined usb_se…
claude Oct 15, 2026
df714a1
fix(host): clear COBS reassembly buffer before yielding each frame
claude Oct 15, 2026
51ebd13
feat(m2.1): host TAP bridge (awdl0) + device CMD/FRAME plumbing
claude Oct 15, 2026
b425e6c
fix(m2.1): harden bridge disconnect/error handling and CMD replies
claude Oct 15, 2026
48c2050
feat(m2.2): forward received AWDL data frames to the host (awdl0 RX p…
claude Oct 16, 2026
94235e4
fix(m2.2): include esp_err.h in bridge build; keep bridge_start failu…
claude Oct 16, 2026
2cd6a05
feat(m2.3a): host->radio AWDL data TX path (slot-gated) + radio stats…
claude Oct 17, 2026
eb50456
fix(m2.3a): cap TX payload at 1460 so built frames fit the 1500B radi…
claude Oct 17, 2026
8839fd7
feat(m2.3b): auto host neighbour table from AWDL peer events
claude Oct 17, 2026
2a50ca6
fix(m2.3b): drop 802.11 retries, keep peer events off the AWDL task, …
claude Oct 17, 2026
db430f3
fix(host): learn awdl0 neighbours from traffic instead of guessing EU…
claude Oct 17, 2026
f743055
feat(diag): unicast data TX counters and a `tx` command
claude Oct 17, 2026
ef1dba0
feat(m2.4): prepare AirDrop receive via patched OpenDrop over awdl0
claude Oct 18, 2026
3142a5d
fix(m2.4): fold in the review of the AirDrop receive tooling
claude Oct 18, 2026
22e2925
fix(install): offline up-to-date check, keep the old venv until the n…
claude Oct 18, 2026
fe5a567
feat(c5): build the firmware for the ESP32-C5 and make the 5 GHz spik…
claude Oct 20, 2026
26958e3
fix(c5): keep channels inside the EU bands, review follow-ups for the…
claude Oct 20, 2026
22074fe
feat(c5): probe where the FCS sits in promiscuous buffers, check IPv6…
claude Oct 21, 2026
5323d4a
feat(c5): find the real end of received frames from the AWDL TLV chain
claude Oct 21, 2026
5cb43bb
docs(c5): record the measured frame layout, fcs_bad is expected on th…
claude Oct 21, 2026
38a7721
docs(c5): record the first ESP32-C5 measurements
claude Oct 21, 2026
6fc593f
docs(c5): first AirDrop receive works on the C5 bridge at channel 44
claude Oct 21, 2026
0c48dec
feat(bridge): usbtest to measure the device->host ceiling, deeper RX …
claude Oct 21, 2026
0d032eb
feat(bridge): usbtest raw, writing the USB FIFO directly by polling
claude Oct 21, 2026
d03eb4b
docs(c5): USB-Serial/JTAG caps the C5 bridge at about 255 kB/s
claude Oct 21, 2026
a54cd18
feat(c5): follow the master's 5 GHz channel automatically
claude Oct 21, 2026
2e903af
feat(m3): send to an iPhone via a BLE wake from the PC's Bluetooth
claude Oct 21, 2026
caa8c3b
fix(m3): show why a send found no receiver
claude Oct 22, 2026
79c7fb0
fix(m3): the target needs its share sheet OPEN, not closed
claude Oct 22, 2026
7930ff0
fix(host): never bring awdl0 up with a random MAC
claude Oct 22, 2026
1e6ecbd
fix(m3): tell a blocked /Discover apart from a non-discoverable device
claude Oct 22, 2026
6413188
fix(m3): start the browse before the BLE wake
claude Oct 22, 2026
bf03306
feat(tools): mdns_probe.py, check mDNS on awdl0 reaches a program
claude Oct 22, 2026
efea1cf
tools: zeroconf_probe.py - watch OpenDrop's zeroconf browse per socket
claude Oct 22, 2026
2d45348
tools: point at the expiring 'Everyone for 10 Minutes' when nothing a…
claude Oct 22, 2026
c5d81f9
opendrop_send: record how far the /Discover TCP got before giving up
claude Oct 22, 2026
73a2204
feat(m3): switchable advertised sequence, mirror or verbatim (bridge …
claude Oct 22, 2026
0085a9f
docs(m3): sending to an iPad works with the PC's Bluetooth wake
claude Oct 23, 2026
eae1816
feat(m3): AirDrop BLE wake from the ESP's own Bluetooth
claude Oct 23, 2026
a50ff8e
docs(m3): sending with the ESP's own BLE wake works on hardware
claude Oct 23, 2026
b432051
fix(bridge): win the RX queue back from Bluetooth
claude Oct 23, 2026
98f70b4
docs(c5): 38 MB receive at ~0.6 MB/s, drops do not limit it
claude Oct 23, 2026
905090d
docs(m4): design for lwIP on the ESP, socket proxy and one host app
claude Oct 23, 2026
1bd1c52
feat(m4.1): the AWDL link as an lwIP interface, IP mode, HELLO, ping6
claude Oct 23, 2026
67a1769
fix(m4.1): send the first MLD reports, record the first IP-mode ping
claude Oct 23, 2026
8667007
feat(m4.2): UDP through the ESP, host app skeleton, espdrop find
claude Oct 23, 2026
5e284c4
docs(m4.2): espdrop find finds the iPad through the ESP's own stack
claude Oct 23, 2026
fd41648
feat(m4.3): TCP connect in the device's socket proxy (firmware part)
claude Oct 23, 2026
d26ad94
feat(m4.3): espdrop send over the ESP's socket proxy (host part)
claude Oct 23, 2026
25f7c3d
docs(m4.3): espdrop send works on hardware
claude Oct 23, 2026
3b83342
feat(m4.4): espdrop receive over the ESP's own IP stack
claude Oct 23, 2026
9940b07
feat(m4.5): Windows environment for the host app (Python, no .exe)
claude Oct 23, 2026
1117029
fix(m4.5): open the COM port with DTR and RTS off on Windows
claude Oct 23, 2026
2edaf2a
fix(m4.5): announce and pack files as ./name on Windows
claude Oct 23, 2026
4c92122
docs(m4.5): espdrop send works on Windows
claude Oct 23, 2026
5589d85
feat(gui-core): long-running session, accept prompt, receiver choice
claude Oct 24, 2026
d1d7ee2
feat(power): Wi-Fi off while nothing needs AWDL
claude Oct 24, 2026
e98e76c
feat(gui): PySide6 desktop app on the session core
claude Oct 24, 2026
d1b039d
feat(power): listen for AirDrop over BLE and wake AWDL on demand
claude Oct 24, 2026
f743ec9
feat(gui): autostart in the background, send from the file manager
claude Oct 26, 2026
67948fc
feat(gui): redesign in Apple's look
claude Oct 29, 2026
8100306
Release list: defaults, Downloads, icons, theme switch, clipboard
claude Oct 31, 2026
d744b9b
Flash the ESP's firmware from the app
claude Oct 31, 2026
172fe22
The app as a native program: Nuitka build for Windows and Linux
claude Oct 31, 2026
44ed92e
CI: firmware, the app for Windows and Linux, tests
claude Oct 31, 2026
5d58b5d
Build: the program's own path, espdrop.cmd, license list
claude Oct 31, 2026
ee3307b
Build smoke test: compare the program path normalized
claude Oct 31, 2026
13b6b4a
Build: own entry beside the package, the .dist folder
claude Oct 31, 2026
754a6f9
Build: bundle fleep's data file; say which file OpenDrop lacks
claude Oct 31, 2026
58b14ed
Build: check OpenDrop without its source; X11 libraries in the program
claude Oct 31, 2026
21dffee
CI: libxcb-shape0 for the Linux build
claude Oct 31, 2026
410d82e
This PC's name by default; drop iOS's ._ metadata files
claude Nov 1, 2026
350cf4e
Docs: which Linux the program runs on
claude Nov 1, 2026
3f7c3c8
One file per platform; release title and text
claude Nov 1, 2026
32d96c7
Never remove the running build's own unpacked folder
claude Nov 1, 2026
004d459
The app in English too, with a language setting
claude Nov 1, 2026
0c83375
The program installs itself; the repository README
claude Nov 1, 2026
a4459f8
Windows: no WinError 6 when the app starts other programs
claude Nov 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Line ends that must not depend on the checkout's core.autocrlf: cmd.exe
# misreads batch files with bare LF, bash rejects scripts with CRLF.
*.cmd text eol=crlf
*.sh text eol=lf
141 changes: 141 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# EspDrop - builds: the ESP32-C5 firmware with ESP-IDF, then the app compiled
# with Nuitka (Python -> C) for Windows and Linux with that firmware inside,
# and the tests. A tag v* also drafts a GitHub release with the files.
#
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (C) 2026 EspDrop contributors

name: build

on:
push:
branches: [main, test]
tags: ["v*"]
paths: ["host/**", "firmware/**", "tools/**", ".github/workflows/build.yml"]
pull_request:
paths: ["host/**", "firmware/**", "tools/**", ".github/workflows/build.yml"]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: build-${{ github.ref }}
cancel-in-progress: true

jobs:
firmware:
name: firmware (ESP32-C5)
runs-on: ubuntu-latest
container: espressif/idf:v6.0.3
steps:
- uses: actions/checkout@v4
- name: Build the bridge
shell: bash
run: |
. "$IDF_PATH/export.sh"
cd firmware/spikes
idf.py -B build_c5_bridge -DSDKCONFIG=build_c5_bridge/sdkconfig -DIDF_TARGET=esp32c5 \
-DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.bridge.defaults" build
- name: One image of the whole flash
shell: bash
run: |
. "$IDF_PATH/export.sh"
python tools/firmware_image.py firmware/spikes/build_c5_bridge -o espdrop-esp32c5.bin
- uses: actions/upload-artifact@v4
with:
name: firmware
path: espdrop-esp32c5.bin
if-no-files-found: error

test:
name: tests
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Libraries (libarchive; what Qt needs offscreen)
run: |
sudo apt-get update
sudo apt-get install -y libarchive13t64 libegl1 libgl1 libxkbcommon0 libfontconfig1 libdbus-1-3
- name: The venv with the patched OpenDrop
run: python tools/opendrop_install.py --venv "$RUNNER_TEMP/venv"
- name: Host tests
env:
QT_QPA_PLATFORM: offscreen
run: '"$RUNNER_TEMP/venv/bin/python" -m unittest discover -s host/tests'
- name: Firmware host tests (protocol logic, no ESP)
run: make -C firmware/spikes/components/awdl/test/host run

app:
name: app (${{ matrix.name }})
needs: firmware
strategy:
fail-fast: false
matrix:
include:
- name: Windows
os: windows-latest
- name: Linux
os: ubuntu-22.04 # the oldest glibc we build on: runs on newer ones
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: actions/download-artifact@v4
with:
name: firmware
path: build/firmware
- name: Libraries (Linux)
if: runner.os == 'Linux'
# libarchive and Qt's X11 plugin's xcb libraries go into the program
# (users need not install them); xvfb for the start test under X11.
run: |
sudo apt-get update
sudo apt-get install -y libarchive13 libegl1 libgl1 libxkbcommon0 libfontconfig1 libdbus-1-3 \
libxcb-cursor0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 \
libxcb-shape0 libxcb-xkb1 libxkbcommon-x11-0 libxcb-util1 xvfb
- name: The venv with the patched OpenDrop, and Nuitka
shell: bash
run: |
python tools/opendrop_install.py --venv "$RUNNER_TEMP/venv"
if [ "$RUNNER_OS" = Windows ]; then PY="$RUNNER_TEMP/venv/Scripts/python.exe"; else PY="$RUNNER_TEMP/venv/bin/python"; fi
"$PY" -m pip install -q nuitka zstandard # zstandard: the single file's packing
if [ "$RUNNER_OS" = Linux ]; then "$PY" -m pip install -q patchelf; fi
echo "PY=$PY" >> "$GITHUB_ENV"
- name: Compile (Nuitka) and check the program
shell: bash
run: |
if [ "$RUNNER_OS" = Linux ]; then export PATH="$RUNNER_TEMP/venv/bin:$PATH"; fi
"$PY" tools/build_app.py --firmware build/firmware/espdrop-esp32c5.bin --smoke --smoke-install
- uses: actions/upload-artifact@v4
with:
name: EspDrop-${{ matrix.name }}
path: |
build/EspDrop-*
build/THIRD_PARTY-*.txt
if-no-files-found: error

release:
name: draft release
if: startsWith(github.ref, 'refs/tags/v')
needs: [firmware, test, app]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4 # the release text (first: it cleans the folder)
- uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- uses: softprops/action-gh-release@v2
with:
draft: true # published by hand, after a look
name: EspDrop ${{ github.ref_name }}
body_path: docs/RELEASE.md
files: dist/*
44 changes: 44 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# --- Build output -----------------------------------------------------------
build/
**/build/
firmware/**/sdkconfig
firmware/**/sdkconfig.old
firmware/**/managed_components/
firmware/**/dependencies.lock
*.o
*.a
*.elf
*.bin
*.map

# --- Python -----------------------------------------------------------------
__pycache__/
*.pyc
.venv/
venv/
.pytest_cache/

# --- Editors ----------------------------------------------------------------
.vscode/
.idea/
*.swp
.DS_Store

# --- Privacy: never commit captures or logs ---------------------------------
# Wi-Fi/BLE captures and serial logs contain MAC addresses and device names
# ("Anna's iPhone") of everyone nearby. Keep them local.
*.pcap
*.pcapng
*.cap
captures/
logs/
*.log

# Native host test binary
test_awdl_frame
test_awdl_cobs
test_awdl_power

# Separate build dirs for the bridge-mode / per-chip firmware
build_bridge/
firmware/**/build_*/
85 changes: 84 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,85 @@
# EspDrop
A AirDrop implementation for Windows and Linux using an ESP32 as a cheap Wifi Adapter

**AirDrop for Windows and Linux**, with an ESP32-C5 board on USB as the
radio. Send files from your PC to an iPhone, iPad or Mac, and receive from
them, the same way Apple devices do it among themselves: no app on the iPhone,
no cloud, no cable to the phone.

A PC's own Wi-Fi card cannot speak AWDL, the Wi-Fi link AirDrop runs on, and
Bluetooth alone is not enough either. EspDrop moves that part onto a small,
cheap ESP32-C5 board: its firmware runs AWDL and the Bluetooth signal that
wakes Apple devices, plus a small IPv6 stack; the desktop app does AirDrop
itself on top (based on OpenDrop) and talks to the board over its USB port.

## What you need

- An **ESP32-C5** board with USB, e.g. the Waveshare ESP32-C5-Zero (the one
EspDrop is tested with). The app puts the firmware on it, over the same USB
cable, no buttons or tools needed.
- A PC with **Windows 10 or 11** (64 bit) or **Linux** on a 64-bit PC (glibc
2.35 or newer: Ubuntu 22.04+, Debian 12+, Fedora 36+, Linux Mint 21+, Arch,
…). No administrator rights, driver or firewall rule.
- On the iPhone/iPad: AirDrop set to **Everyone**, unlocked.

## Install

1. Download the program for your system from
[Releases](https://github.com/AntiWireless/EspDrop/releases):
`EspDrop-…-windows-x64.exe` or `EspDrop-…-linux-x86_64`.
2. Start it (Linux: `chmod +x` it first). It installs itself – Windows into
`%LOCALAPPDATA%\Programs\EspDrop` with a Start menu entry and an entry under
*Installed apps*, Linux into `~/.local/bin` with an entry in the
applications menu – and removes the download from the Downloads folder.
Windows asks once because the program is not signed: *More info* → *Run
anyway*.
3. Plug in the ESP32-C5. Under *General → ESP firmware* the app offers to
install its firmware; that takes about a minute.

Starting a newer download later updates the installed program. *Uninstall*
in the app's *…* menu (or under *Installed apps* on Windows) removes it
again; received files stay.

On Linux the user needs access to the board's serial port: `sudo usermod
-aG dialout $USER` (Arch: `uucp`), then log out and in again.

## Use

- **Receive:** set *Receiving from* to *10 minutes* or *Everyone*; on the
iPhone, share something and pick your PC. Every transfer asks first; files
land in Downloads (optionally also on the clipboard).
- **Send:** *Search*, then drop files on a device or click it – or right-click
files: *Send with EspDrop* in Explorer, *Open with → AirDrop (EspDrop)* in a
Linux file manager.
- The app starts with your login and waits quietly in the notification area;
the ESP's radio sleeps while nothing needs it and wakes when an Apple device
nearby wants to share.
- English or German (*General → Language*), light or dark.

Folders cannot be sent yet, only files; Apple's *Contacts Only* mode is not
supported (it needs an Apple ID).

## Build it yourself

| part | where | how |
|---|---|---|
| firmware (ESP-IDF 6) | [`firmware/`](firmware/README.md) | `idf.py` builds, `tools/firmware_image.py` makes the image |
| desktop app (Python, PySide6, patched OpenDrop) | [`host/`](host/README.md) | `tools/opendrop_install.py`, then `tools/espdrop.py gui` |
| the single-file programs | [`tools/build_app.py`](tools/build_app.py) | Nuitka (Python → C) |

GitHub Actions ([`.github/workflows/build.yml`](.github/workflows/build.yml))
builds the firmware, both programs and runs the tests on every push; a tag
`v*` drafts a release. The project's notes, experiments and test plans (in
German) are in [`docs/`](docs/).

## Credits and license

EspDrop builds on the reverse engineering of AWDL and AirDrop by the
[Open Wireless Link](https://owlink.org) project of TU Darmstadt's Secure
Mobile Networking Lab: [OWL](https://github.com/seemoo-lab/owl) (AWDL) and
[OpenDrop](https://github.com/seemoo-lab/opendrop) (AirDrop), and on the
patches in [jedbillyb/airdrop-mt7921](https://github.com/jedbillyb/airdrop-mt7921)
that make OpenDrop work with current iOS.

GPL-3.0-or-later, see [LICENSE](LICENSE). EspDrop is an interoperability
project and is not affiliated with or endorsed by Apple. AirDrop, iPhone,
iPad and Mac are trademarks of Apple Inc.
Loading
Loading