Install on WSL2 - oshri-almog/esp32c5-kismet-wifi-interface GitHub Wiki
This page runs Kismet with ESP32-C5 support inside WSL2 on a Windows PC, so the Kismet server and the boards are on the same computer without Docker or a second machine. It is for Windows users who are at home in a Linux shell. It covers building Kismet in WSL2, the two ways to get the boards to it, and the points where WSL2 differs from an ordinary Linux machine.
Kismet's own documentation says that WSL2 has no direct access to USB hardware, and that capture from it works through Kismet's remote capture (Kismet on Windows). That leaves two ways:
| A: boards on Windows, Python remote helper | B: boards attached to WSL2 with usbipd-win | |
|---|---|---|
| Where the boards appear | Windows, as COM ports (COM14) |
Inside WSL2, as /dev/ttyACM0
|
| What feeds Kismet | The Python remote helper on Windows, over remote capture | The C helper (kismet_cap_esp32c5), started by Kismet inside WSL2 |
| Extra software on Windows | Python 3.10 or newer | usbipd-win |
| The board can also be used from Windows | Yes, once the helper is stopped | No, not while it is attached to WSL2 |
| Tested | Yes: Windows 11, a real board | No |
Use way A. Way B is described so that someone can try it; nobody has run it with these boards yet.
flowchart LR
subgraph PC["Windows PC"]
subgraph WIN["Windows"]
B1["Board on COM14"] --> PH["Python remote helper"]
B2["Board, attached to WSL2"] --> U["usbipd-win"]
BR["Browser<br/>http://127.0.0.1:2501"]
end
subgraph WSL["WSL2, Ubuntu 24.04"]
K["Kismet server<br/>web port 2501"]
C["C helper<br/>kismet_cap_esp32c5"] --> K
end
PH -- "A: remote capture to 127.0.0.1:2501 (tested)" --> K
U -- "B: /dev/ttyACM0 (untested)" --> C
BR --> K
end
How It Works explains the helpers, and Choosing a Setup compares this setup with Docker Desktop, a Raspberry Pi and a Linux machine.
| What | Result |
|---|---|
| Building Kismet in WSL2 | WSL2 Ubuntu 24.04 (x86_64) on Windows 11 Pro. Kismet at commit cfe427074 with the ESP32-C5 source, built and installed as root into /root/kismet-install. It reports Kismet 2026.09.0-cfe427074. |
| Way A with a real board | One board on COM32, the Python remote helper on Windows (Python 3.13.2), Kismet in WSL2 with WSL's default networking (no .wslconfig). On 2026-10-02 (firmware image 01a50bd6), with an API key, a Wi-Fi source found 82 Wi-Fi devices in about 40 s and came back by itself after Kismet restarts, through 127.0.0.1 and through localhost. Later that day the current helper ran Wi-Fi with a login, and Bluetooth LE and 802.15.4 with an API key, through 127.0.0.1; the 802.15.4 source saw no packets, with no Zigbee or Thread devices nearby. Earlier, with an older version of the helper, Wi-Fi on both bands, Bluetooth LE and 802.15.4 sources all ran, with a login; a second board, on COM30, was wedged with Windows error 31 for most of that run (Install on Windows explains the error). |
| The fake board | The project's end-to-end tests with the fake board and a real Kismet, for the C helper and for the Python remote helper (Development and Testing), ran in WSL2. |
| Not tested | Way B (usbipd-win); any board inside WSL2; Kismet in WSL2 as a normal user; reaching Kismet in WSL2 from another machine; Windows 10. |
-
WSL2 with Ubuntu 24.04, the tested distribution. It must run under WSL 2, not WSL 1: in PowerShell,
wsl --list --verboseshows the version of each distribution. Other distributions should work the same way but have not been tried. If WSL or Ubuntu 24.04 is not installed yet, run this in an administrator PowerShell, restart if Windows asks, then open Ubuntu from the Start menu and create your Linux user:wsl --install -d Ubuntu-24.04
-
Memory. WSL2 takes its memory from the same pool as Windows, and as Docker Desktop if that is running. Kismet's C++ needs about 1.5 GB for each compiler that runs in parallel. This is why the build below uses
-j4and no more. -
Disk. On the Raspberry Pi build, the installed
kismetprogram alone is about 490 MB, because Kismet is built with debug information. It was not measured in WSL2, and neither was the whole build tree. -
Boards flashed with this project's firmware (Flashing the Firmware). Flash from Windows, for example with the web flasher in Chrome or Edge; the firmware tools do not need WSL2.
-
One Kismet on port 2501. A Kismet in Docker Desktop on the same PC publishes port 2501 on Windows too: the demo on
127.0.0.1, thekismetservice on all interfaces. Stop one, or give Docker's another port (Install with Docker).
The build is the same as on any Ubuntu machine, and Building Kismet with ESP32-C5 Support explains each part. The commands here follow the build that ran in WSL2, with the two WSL2 problems they avoid: running out of memory during make, and make install failing for want of a kismet group. The package list is the one the Raspberry Pi build used, plus python3 and curl.
Open Ubuntu and start a root shell. The tested build ran as root, so every command below runs in that shell and ~ is /root:
sudo -iapt-get update
apt-get install -y build-essential git pkg-config autoconf automake python3 curl \
libwebsockets-dev zlib1g-dev libnl-3-dev libnl-genl-3-dev libcap-dev libpcap-dev \
libnm-dev libdw-dev libsqlite3-dev libsensors-dev libusb-1.0-0-dev libmosquitto-dev \
libpcre2-dev libssl-devautoconf, automake and python3 are for add-to-kismet.sh, and curl is for creating an API key in step 4.
git clone https://github.com/oshri-almog/esp32c5-kismet-wifi-interface.git ~/esp32c5-kismet-wifi-interface
git clone https://github.com/kismetwireless/kismet.git ~/src/kismet
git -C ~/src/kismet checkout cfe427074
sh ~/esp32c5-kismet-wifi-interface/kismet/add-to-kismet.sh ~/src/kismetcfe427074 is the Kismet commit the ESP32-C5 source was developed and tested against; Git reports a "detached HEAD", which is expected. No Kismet release contains the source yet, so Kismet has to be built this way. add-to-kismet.sh prints an edited <file> line for each change and ends by suggesting ./configure && make; use the configure command below instead.
cd ~/src/kismet
./configure --prefix=$HOME/kismet-install --disable-python-tools --disable-librtlsdr --disable-ubertooth --disable-bladerf --disable-btgeigerThis is the line the WSL2 test tree's config.log records. The --disable-... options leave out support for other hardware (RTL-SDR dongles, Ubertooth, bladeRF, a Bluetooth LE Geiger counter) that these boards do not need, as Building Kismet with ESP32-C5 Support explains; without --disable-librtlsdr, configure stops unless librtlsdr's development files are installed, and the package list above does not have them.
At the end, check that the summary has ESP32-C5: yes and Websocket datasources: yes. The first means the ESP32-C5 source is in the build; the second, that the C helper can do remote capture.
nice make -j4Warning: Do not give
makeone job per core in WSL2. On the test PC (20 cores),make -j20used up the memory that WSL2 shares with Windows: Windows was down to 1.2 GB free and the WSL distribution had to be terminated. With Docker Desktop running, there is even less to spare.make -j4finished normally.
If WSL2 stops responding during the build, stop the distribution from PowerShell, using the name wsl --list --verbose shows, then open Ubuntu again and rerun nice make -j4:
wsl --terminate Ubuntu-24.04The time of the WSL2 build was not recorded. On the same PC, a Docker Desktop build, which leaves out more optional parts of Kismet, spent about 17.5 minutes on configure, compile and install at -j4.
make install INSTGRP=root SUIDGROUP=root MANGRP=rootA plain make install fails in WSL2 with /usr/bin/install: invalid group 'kismet'. Kismet installs some of its own capture helpers (for example kismet_cap_rz_killerbee, built whenever libusb is found) with a kismet group, and Ubuntu in WSL2 has no such group. The variables use root instead. MANGRP=root has no effect at this Kismet commit but does no harm. The alternative is to create the group first, with groupadd kismet, and run make install without the variables.
The install ends with Kismet has NOT been installed suid-root. This means you will need to start it as root.... That advice is for Kismet's Wi-Fi and Bluetooth helpers; the ESP32-C5 source needs only a serial port.
~/kismet-install/bin/kismet --versionIt prints Kismet 2026.09.0-cfe427074 or similar (2026.09 is the year and month of your build, the last number is always 0, and cfe427074 is the Kismet commit) and exits with status 1, which is normal for Kismet.
Kismet keeps its web login, API keys and server ID in a .kismet folder in the home directory of the user it runs as. It takes that home directory from the system's user database, not from $HOME. Running as root, that is /root/.kismet, whatever $HOME says. In the WSL2 tests, a Kismet started with HOME pointing elsewhere still read /root/.kismet/kismet_httpd.conf, found no login there, and asked for one.
Set the login before the first start. Change admin and the password:
mkdir -p ~/.kismet
printf 'httpd_username=%s\nhttpd_password=%s\n' admin 'choose-a-long-password' > ~/.kismet/kismet_httpd.conf
chmod 600 ~/.kismet/kismet_httpd.confWithout the file, Kismet logs This is the first time Kismet has been run as this user. and the first browser to open the web UI chooses the login. In the tested setup only this PC can reach the web UI (see Ports between WSL2 and Windows), but set it anyway.
To run a second Kismet for tests without touching /root/.kismet, give it its own home with --homedir. That Kismet reads its login from .kismet/kismet_httpd.conf inside that folder and keeps its API keys there, so write the login first. Stop the other Kismet before you start this one: both want port 2501. Change admin and the password:
mkdir -p /tmp/kismet-test/.kismet
printf 'httpd_username=%s\nhttpd_password=%s\n' admin 'choose-a-long-password' > /tmp/kismet-test/.kismet/kismet_httpd.conf
chmod 600 /tmp/kismet-test/.kismet/kismet_httpd.conf
~/kismet-install/bin/kismet --homedir /tmp/kismet-test --no-ncurses --no-loggingWithout the login file, the first browser to open the web UI chooses the login. The project's end-to-end tests start Kismet this way.
Kismet writes its log into the folder it starts in, so start it in one:
mkdir -p ~/kismet-logs
cd ~/kismet-logs
~/kismet-install/bin/kismet --no-ncursesLeave this window open: Kismet runs until you press Ctrl+C in it. Lines worth knowing:
| Line | Meaning |
|---|---|
HTTP server listening on 0.0.0.0:2501 |
The web UI and remote capture are up. |
No data sources defined; Kismet will not capture anything until a source is added. |
Expected for way A: the sources arrive over remote capture. |
ERROR: Tried to re-register duplicate alert FLIPPERZERO |
Printed at every start of this Kismet version; harmless. |
ALERT: ROOTUSER ... |
Kismet is running as root. It works; Kismet's advice is to run as a normal user. |
Open http://127.0.0.1:2501 in a browser on Windows and log in. Kismet Configuration covers kismet_site.conf, which lives in ~/kismet-install/etc/ here.
Inside WSL2, Kismet listens on port 2501 on all interfaces. WSL2 forwards that port to Windows through its port forwarder (wslrelay.exe), which listens on 127.0.0.1:2501 only. This is WSL's default networking mode, NAT. With networkingMode=mirrored in .wslconfig the forwarding works differently, and that has not been tried.
In the default mode:
-
From Windows (the browser, the Python remote helper): use
127.0.0.1:2501. The forwarder listens on IPv4 only, and Windows trieslocalhostas the IPv6::1first. With an earlier version of the helper,localhostcost about 2 s per connection (measured: 2.07 s throughlocalhost, 0.001 s through127.0.0.1). The helper now tries127.0.0.1first forlocalhost, and while Kismet is up it connects as fast either way. While Kismet is down, each refused attempt still goes on to::1, so the retries come about 9 s apart instead of about 7 s.127.0.0.1stays the safe choice. - From another machine on the network: the forwarder does not listen there, so a helper elsewhere cannot reach Kismet in WSL2 this way. No other way has been tried. If boards on other machines should feed this Kismet, run Kismet on a Raspberry Pi, a Linux machine or in Docker Desktop instead (Choosing a Setup). A Windows helper feeding Kismet on a Pi across the network works (Guide: Windows Boards to a Pi); one feeding Docker Desktop on another PC has not been tested.
-
Legacy TCP remote capture, port 3501: Kismet listens on it on
127.0.0.1inside WSL2, and it has no login. Windows reaches it on127.0.0.1:3501: the Python remote helper with--connect 127.0.0.1:3501 --tcpcaptured through it. With no login, any program on this PC can feed Kismet that way, so the websocket on 2501 is the better choice (Remote Capture). -
Docker Desktop publishes its ports on Windows too (the demo on
127.0.0.1:2501, thekismetservice on port 2501 of all interfaces), so a Kismet container and Kismet in WSL2 get in each other's way. Run one at a time, or publish the container on another port.
-
Install the Python remote helper on Windows and find the boards: steps 1 to 3 of Install on Windows.
-
Open a second Ubuntu window (Kismet is running in the first) and create an API key with the
datasourcerole, with the login from step 2:curl -u admin:choose-a-long-password --data-urlencode 'json={"name": "windows-helper", "role": "datasource", "duration": 0}' http://127.0.0.1:2501/auth/apikey/generate.cmdIt prints the key, 32 hex characters. The name must be unique on this Kismet. You can also create the key in the web UI, under Settings, API Keys. A key is safer than the login: it can feed sources and nothing else, and it keeps the admin password off the helper's command line.
-
In PowerShell on Windows, in the project folder, put the key in
KISMET_CAP_APIKEYand start the helper. Change the key andCOM14to yours:$env:KISMET_CAP_APIKEY = "3F9A6C1E07B24D58A1C9E2F4608B7D35" python -m esp32c5_kismet.remote --connect 127.0.0.1:2501 --source esp32c5-COM14
-
Kismet in WSL2 logs
New remote source esp32c5-COM14 (E5C50001-...) connected, and the source appears in the web UI under Data Sources. For Zigbee and Thread use--source esp32c5zigbee-COM14, for Bluetooth LE--source esp32c5btle-COM14, and repeat--sourcefor more boards.
Note: Only capture on networks and devices you own or are authorised to test.
What the Windows-to-WSL2 test showed, with the earlier helper:
| Radio | About 3 minutes of capture |
|---|---|
| Wi-Fi | 128 Wi-Fi devices, 60 of them on 2.4 GHz channels; access points on 5 GHz too, 21 frequencies within a minute |
| Bluetooth LE | 16 devices, each showing channel 37 (Bluetooth LE Capture explains why) |
| 802.15.4 | The source ran and hopped channels 11 to 26; with no Zigbee or Thread devices nearby it saw no packets |
-
Kismet restarts. On 2026-10-02, when Kismet in WSL2 was stopped for 10 s and started again, the helper reconnected on its own and was capturing 2.2 to 2.7 s after Kismet's port answered (3.8 to 4.5 s with
localhost). How soon depends on where the helper is in its wait: it tries again 5 s after each failed attempt, and Windows takes about 2 s to report a refused one (about 4 s withlocalhost), so it can take up to about 7 s (9 s withlocalhost). The source comes back under the same ID (E5C50001-0000-0000-0000-<MAC>for Wi-Fi), because the ID is made from the board's MAC and the radio. -
Stopping. Ctrl+C in the helper's window stops it. Kismet then shows the source as stopped with the error
websocket connection closed, which is expected.
Install on Windows covers the helper in full: logins, keeping it running, COM port problems and the firewall.
Warning: Nobody has tried this with these boards. The usbipd-win steps come from its own documentation (usbipd-win, WSL support); what happens after them is what the C helper does on any Linux machine.
usbipd-win shares a USB device from Windows over USB/IP, and WSL2 attaches it as if it were plugged in. While a board is attached, Windows cannot use it: its COM port is gone until you detach it.
-
Update WSL's kernel. usbipd-win's documentation asks for a recent one, which supports most USB devices:
wsl --update -
Install usbipd-win, with winget or with the
.msifrom its releases page:winget install usbipd
-
Stop everything on Windows that has the board open: the Python remote helper, a serial terminal, esptool.
-
Open an Ubuntu window, start a root shell in it as in step 1, and leave it open. The WSL2 virtual machine has to be running when you attach, and steps 8 and 9 run in this shell: Kismet was installed as root, into
/root/kismet-install, which a normal user's~does not point to.sudo -i
-
In an administrator PowerShell, list the USB devices:
usbipd list
Find the board: its VID:PID is
303a:1001. Every Espressif chip on its native USB port has that ID, so with other ESP32 boards plugged in, match the COM port number in the description. Note its BUSID, for example2-3. -
Share the board, still as administrator. This is needed once per board and survives reboots. Change
2-3to your BUSID:usbipd bind --busid 2-3
-
Attach it to WSL2. A normal PowerShell is enough:
usbipd attach --wsl --busid 2-3
-
In the root shell from step 4, check that the board is there and that the C helper sees it:
ls -l /dev/ttyACM* ~/kismet-install/bin/kismet_cap_esp32c5 --list 2>&1
--listshould show the board three times, once per radio, as on any Linux machine; it writes to stderr and exits with status 2, which is normal. -
Stop the Kismet from step 3 with Ctrl+C in its window. Only one Kismet can have port 2501: a second one exits with
Could not initialize HTTP server on 0.0.0.0:2501, could not bind socket. Then, in the root shell from step 4, start Kismet again with the board as a local source. ChangettyACM0to the board's port:cd ~/kismet-logs ~/kismet-install/bin/kismet --no-ncurses -c 'esp32c5-ttyACM0:name=c5-wifi'
Look for
INFO: c5-wifi capturing (wifi).esp32c5zigbee-ttyACM0andesp32c5btle-ttyACM0select the other radios; Source Definitions has every form. Kismet runs as root here, so it can open the port. As a normal user you would need the port's group, as on Install on Linux.Instead of restarting, you can leave the Kismet from step 3 running and enable the board in the web UI's Data Sources panel, where the C helper offers it once per radio (
esp32c5-ttyACM0,esp32c5zigbee-ttyACM0,esp32c5btle-ttyACM0), as on Install on Linux. -
To give the board back to Windows, stop Kismet and detach it:
usbipd detach --busid 2-3
Points to check if you try this:
-
Attaching does not last. usbipd-win's documentation says to attach again after a reboot, when WSL restarts, and when the device resets or is unplugged and plugged in again. Its
--auto-attachoption keeps a window running that attaches the device again whenever it comes back:usbipd attach --wsl --busid 2-3 --auto-attach. -
A radio switch reboots the board. On a Raspberry Pi most switches left the board's USB device in place. Some made it go away and come back, with its tty back within about 0.3 to 2.5 s, and the helpers usually carried on. Now and then such a board answers nothing afterwards until it is reset or replugged (Troubleshooting). Through usbipd-win a device that comes back like this may also need attaching again, which
--auto-attachwould do. None of this has been tried through usbipd. - Flashing ends with a hard reset of the board. Detach the board and flash it from Windows (Flashing the Firmware).
-
Several boards: bind and attach each one. Their
ttyACMnumbers can change from one attach to the next, and whether WSL2 makes the stable/dev/serial/by-id/names that Linux has is unknown.--listshows each board's MAC, which tells them apart. -
Docker Desktop: usbipd-win's WSL support page says that once a device is attached to WSL, it can be used in any WSL 2 distribution, so the
kismetservice in Docker Desktop might see the boards too. Also untested; see Install with Docker.
WSL2 is where the project's C helper tests (tests/c/run.sh) and both Kismet end-to-end tests with the fake board, tests/kismet_e2e.sh for the C helper and tests/remote_e2e.sh for the Python remote helper, were run. The end-to-end tests start their own Kismet with --homedir, so they leave /root/.kismet alone. tests/kismet_e2e.sh starts its Kismet on port 2501, so stop the Kismet from step 3 first; tests/remote_e2e.sh uses ports 2511 and 3511 and does not clash with it. Development and Testing has the commands, and Try It Without Hardware shows the fake board on its own.
| What you see | Cause | Fix |
|---|---|---|
WSL2, or all of Windows, stops responding during make
|
Too many compilers for the memory WSL2 shares with Windows |
wsl --terminate the distribution from PowerShell, then nice make -j4
|
/usr/bin/install: invalid group 'kismet' |
WSL2's Ubuntu has no kismet group |
make install INSTGRP=root SUIDGROUP=root MANGRP=root, or groupadd kismet first |
ESP32-C5 missing from the configure summary |
add-to-kismet.sh did not run on this tree |
Run it again and read its output, then ./configure again |
Error reading config file '/root/.kismet/kismet_httpd.conf': No such file or directory, and REST calls fail |
Kismet reads .kismet from the user database's home, not $HOME
|
Create the login in /root/.kismet (step 2), or pass --homedir
|
Unable to open KismetDB log at ... and Kismet exits |
Kismet was started in a folder it cannot write to |
cd ~/kismet-logs first, or set log_prefix in kismet_site.conf
|
The helper on Windows logs [WinError 10061] No connection could be made because the target machine actively refused it
|
Kismet in WSL2 is not running | Start it (step 3). The helper keeps retrying, about every 7 s |
| The helper connects about 2 s late, or retries about every 9 s while Kismet is down |
--connect localhost, which tries ::1 as well (an earlier helper tried it first) |
Use --connect 127.0.0.1:2501
|
Way B: no /dev/ttyACM0 in WSL2 |
The board is not attached, or the WSL2 kernel is old |
usbipd list should say Attached; run wsl --update
|
Troubleshooting covers Kismet and the helpers in general.
- Guide: First Capture: see devices appear in Kismet.
- Install on Windows: everything about the Python remote helper.
- Remote Capture and Kismet Configuration.
- Guide: Running as a Service sets Kismet up to start by itself on Linux. In WSL2 that has not been tried.