Open-source project
cyberman54/ESP32-Paxcounter avatar
cyberman54/ESP32-Paxcounter

ESP32-Paxcounter: anonymous crowd counting with WiFi and Bluetooth on ESP32 boards

Wifi & BLE driven passenger flow metering with cheap ESP32 boards

2,127 stars457 forksC++NOASSERTION

At a glance

What is it?
ESP32-Paxcounter is an Apache-2.0 C++ project that counts nearby mobile devices by detecting WiFi and Bluetooth signals on cheap ESP32 LoRa boards, without storing MAC addresses persistently. Data flows to LoRaWAN networks like TheThingsNetwork or Helium, to MQTT, or to a local SD card.
Who is it for?
ESP32-Paxcounter suits engineers building anonymous crowd density sensors for transit, events or retail spaces where a LoRaWAN or MQTT backend is already available and headcount estimation without individual tracking is the goal. Skip it if you need precise person counts rather than device estimates, or if you cannot reach paxcounter.org for build and configuration documentation before starting, since the README does not include those steps.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 34 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • cyberman54/ESP32-Paxcounter2,127 ★ · +12
Star history
Repository2026-10-192026-11-07
cyberman54/ESP32-Paxcounter2.1k2.1k

12 Stars in the last 19 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Counting mobile devices without a persistent record: the privacy constraint built into the design

ESP32-Paxcounter estimates how many people are in a space by counting nearby mobile devices. An ESP32 board continuously scans for WiFi and Bluetooth signals, reads MAC addresses from those signals to identify distinct devices, and reports an aggregate count. Each scan captures device presence but writes no MAC addresses to storage. No fingerprinting algorithm runs against the scanned data, so there is no per-device history that could be retrieved later.

Counting and tracking are treated as distinct requirements in this design. Knowing a device is present right now does not require a record of when it was present before, and Paxcounter keeps only the current aggregate count.

What the device counts is mobile devices, not people directly. A person carrying 2 phones and a tablet appears as 3 separate counts. A space with no people but an active access point registers nothing, since the scan evaluates mobile device MAC addresses rather than all WiFi traffic in range. The relationship between device count and person count depends on how many devices people in the target environment carry, a figure the README does not document.

WiFi and BLE scanning in parallel, alongside environment sensors

Detection runs on 2 radio interfaces at once. The ESP32's WiFi radio reads probe requests and beacon frames from nearby devices, while the Bluetooth Low Energy radio reads advertising packets broadcast by phones and wearables. Both signal types carry MAC addresses, and the firmware uses them to maintain a count of distinct devices seen within the scan window.

The effective scan range is not documented in the README, and that gap matters for deployment planning. Range for both WiFi and BLE varies with transmit power, antenna design and physical obstructions, so the actual detection radius depends on the specific board and its environment rather than a single published figure.

Environment sensors run alongside the people counter. Repository topics include bme680 (temperature, humidity, gas), gps (GPS position) and dcf77 (time signal from the German long-wave broadcast), indicating the firmware can acquire and transmit environmental data in the same LoRaWAN or MQTT message as device counts. The README describes the device as a multi-sensor appliance, which means a single deployed board carries ambient environmental monitoring alongside crowd estimation in one combined data stream.

Build with PlatformIO from paxcounter.org, or buy the pre-flashed LILYGO board

The README does not list build or flash commands. platformio_orig.ini and platformio_orig_s3.ini at the repository root confirm PlatformIO as the build system, and the presence of 2 ini files suggests separate configuration targets (the _s3 suffix indicates the ESP32-S3 variant). The repository shows active PlatformIO CI. Full build and configuration documentation lives at paxcounter.org, which the README names as the documentation home.

For engineers who want to avoid the build step, the README links to the LILYGO Paxcounter LoRa board on AliExpress as a ready-to-go pre-flashed option. That path removes the PlatformIO requirement at the cost of running a pre-built firmware binary rather than one compiled from this repository's source.

A German-language tutorial published in Make Magazine and linked from heise.de covers the hardware assembly in detail. No English-language walkthrough is linked from the README, so non-German speakers go to paxcounter.org for setup guidance.

4 data output options: SD card, LoRaWAN, MQTT and serial SPI

The firmware sends counts through 4 distinct output channels. An SD card attached to the board stores data locally with no network required. LoRaWAN sends counts wirelessly over unlicensed spectrum to a network server; the README names TheThingsNetwork and Helium as the 2 supported LoRaWAN providers. MQTT sends data to a broker over TCP/IP, requiring WiFi or Ethernet connectivity at the deployment site. A serial SPI interface connects the board to a local host computer for direct-wire data transfer.

Those 4 paths differ in infrastructure dependency and data access pattern. LoRaWAN covers long distances without local WiFi, but the deployment area must have gateway coverage and the device needs registration with the network provider before data flows. MQTT reaches a broker immediately but requires the board to maintain a network connection. SD card avoids all networking but requires a physical card retrieval to read the data.

The README lists all 4 without stating a preference or describing the configuration keys for any of them. The README also does not document the device registration steps for TheThingsNetwork or Helium, which is a gap for first deployments. paxcounter.org documents the configuration and registration procedures.

Deep sleep on an 18650 cell: the power budget and its undocumented details

Battery-powered operation uses ESP32 deep sleep mode to reduce power draw between scan cycles. During deep sleep, the radios shut off and the processor halts until a timer fires. That timer wakes the board for the next scan window, the board scans, transmits if it has something to send, and returns to sleep. One 18650 Li-Ion cell is the documented power source for this mode in the README.

Actual run time is not stated in the README. How long a cell lasts depends on 3 configurable factors: how frequently the board wakes to scan, what radio power level it uses for BLE and WiFi, and how often it transmits a count over LoRaWAN or MQTT. Frequent short scans with frequent transmissions drain the cell faster than a longer sleep interval with infrequent LoRaWAN uplinks. Those parameters are configured through paxcounter.org.

Photographs in the repository show the board in a case (TTGO-case) and in a wristband form factor (paxcounter-ttgo-twristband). No battery circuit diagram or capacity specification appears in the README.

When LoRaWAN coverage is absent: MQTT and SD card for fixed and offline deployments

LoRaWAN coverage is uneven. Dense urban areas served by TheThingsNetwork or Helium have enough gateway density to make the wireless path reliable, but rural sites or indoor locations with thick concrete walls may have no gateway in range. MQTT and SD card are the fallback paths, each requiring a different trade-off.

MQTT needs a network connection at the site. A site with WiFi or Ethernet is almost always a fixed installation rather than a remote battery-powered deployment, so choosing MQTT over LoRaWAN changes the deployment category. SD card avoids every network dependency, but someone must physically retrieve the card to access the data, which makes ongoing monitoring impractical without frequent site visits.

Serial SPI is the 4th path, suited to a stationary setup where the board attaches to a Raspberry Pi or similar host. The README does not describe which path the default configuration uses or what configuration key switches between them. paxcounter.org documents that.

Apache 2.0 at the base, per-file exceptions in LICENSE.txt, and a 2-year release gap before v3.8.0

Copyright in the README names 2 primary contributors: Oliver Brandmueller and Klaus Wilting, 2018-2022. The base licence is Apache License, Version 2.0. A NOTICE in the README states that parts of the source carry different licences and points to LICENSE.txt for the details. GitHub cannot classify the effective licence from those file-level differences, which is why the repository metadata shows Other rather than Apache-2.0.

Readers redistributing firmware or using source files in derivative products should read LICENSE.txt before shipping. Some components may carry terms that differ from what the Apache 2.0 base permits.

Release history shows v3.6.2 on 2024-04-29, v3.6.3 on 2024-06-15, then a gap of over 2 years before v3.8.0 on 2026-08-07. The last push was on 2026-10-04 and the repository is not archived. Credits in the README list 10 named contributors covering payload converters, LoRaWAN stack maintenance, CI workflow, documentation, opensensebox integration and SD card support.

Editorial conclusion

ESP32-Paxcounter suits engineers building anonymous crowd density sensors for transit, events or retail spaces where a LoRaWAN or MQTT backend is already available and headcount estimation without individual tracking is the goal. Skip it if you need precise person counts rather than device estimates, or if you cannot reach paxcounter.org for build and configuration documentation before starting, since the README does not include those steps. Before ordering hardware, read LICENSE.txt in full, since some source files carry different terms than the base Apache 2.0 text.

Frequently asked questions

What is ESP32-Paxcounter?

ESP32-Paxcounter is a C++ project for ESP32 LoRa boards that estimates crowd size by counting nearby mobile devices via WiFi and Bluetooth signals. It does not store MAC addresses persistently and performs no fingerprinting.

How does ESP32-Paxcounter count people?

It reads MAC addresses from WiFi probe requests and BLE advertising packets to count distinct nearby mobile devices. Each scan records device presence without writing any address to storage.

What hardware does ESP32-Paxcounter run on?

It runs on ESP32 LoRa IoT boards. The README links to the LILYGO Paxcounter LoRa board on AliExpress as a ready-to-go pre-flashed option alongside the PlatformIO source build path.

How does ESP32-Paxcounter transmit data?

Data can go to a local SD card, to a LoRaWAN network such as TheThingsNetwork or Helium, to an MQTT broker over TCP/IP, or to a local host via serial SPI interface.

Can ESP32-Paxcounter run on battery?

The README documents battery-powered operation using ESP32 deep sleep mode, with a single 18650 Li-Ion cell as the described power source for that mode.

Official sources

  1. cyberman54/ESP32-Paxcounter on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/cyberman54-esp32-paxcounter.svg)](https://hysenlabs.com/projects/cyberman54-esp32-paxcounter)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/cyberman54-esp32-paxcounter.svg)](https://hysenlabs.com/projects/cyberman54-esp32-paxcounter)

Was this useful?