Replace emoji glyphs not supported by Calibri, widen table separator columns so pandoc generates proportional LaTeX column widths (avoids the single-line cramped output in the troubleshooting table), and add YAML front-matter for consistent font/margin/table-spacing settings. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| .github | ||
| Codeck_chirpstack | ||
| docs | ||
| herramientas_operario | ||
| include | ||
| lib | ||
| shared | ||
| src | ||
| tools/usb_updater | ||
| ultimo_firmware | ||
| .clang-format | ||
| .gitignore | ||
| LICENSE | ||
| mkdocs.yml | ||
| platformio_orig.ini | ||
| platformio_orig_s3.ini | ||
| README.md | ||
ESP32-Paxcounter — Fork Ciberman / Antigravity
Wifi & Bluetooth driven, LoRaWAN enabled, Paxcounter and multi-sensor appliance, built on cheap ESP32 LoRa IoT boards
Tutorial (in german language): heise.de
Este repositorio es un fork personalizado del proyecto original ESP32-Paxcounter, adaptado para dispositivos LILYGO T3 v1.61 con múltiples extensiones de funcionalidad orientadas a entornos de producción con LoRaWAN/ChirpStack.
Rama activa:
feature/lilygo-t3v161
Ready-to-go Hardware: LILYGO® Paxcounter LoRa
Documentation (upstream): https://cyberman54.github.io/ESP32-Paxcounter
Source Code (upstream): https://github.com/cyberman54/ESP32-Paxcounter
Caso de uso
Paxcounter es un dispositivo basado en ESP32 para medir flujos de personas y datos multi-sensor en tiempo real. Detecta señales WiFi y Bluetooth en el aire, evaluando direcciones MAC de dispositivos móviles, sin almacenar ni fingerprintear de forma persistente dichas MACs.
Los datos pueden almacenarse en tarjeta SD local, transmitirse a la nube mediante LoRaWAN (TheThingsNetwork, Helium, ChirpStack) o MQTT, o enviarse por interfaz serie.
Funcionalidades añadidas en este fork
Resumen de cambios por commit
| Fecha | Commit | Descripción |
|---|---|---|
| 2026-01-10 | 467ff7e6 |
SD card: almacenamiento cíclico de datos en fichero rotativo |
| 2026-01-10 | 0f3a5190 |
Actualización libpax con mejoras en scan BLE/WiFi |
| 2026-01-15 | 2dfb477c |
Ajustes en aggregator (conteo y acumulación) |
| 2026-01-30 | 70054442 |
Dispositivos fijos, backfill histórico, nuevos comandos downlink |
| 2026-02-02 | 4f608eca |
Página dedicada DevEUI en display (9 páginas totales) |
| 2026-02-10 | 5ceaa6aa |
Backfill por tiempo (minutos hacia atrás) |
| 2026-02-11 | 2623a9ef |
Secuencia BLE on/off para optimizar escaneo WiFi |
| 2026-02-11 | fe6e3e6d |
Fix libpax: de submódulo roto a directorio regular |
| 2026-02-11 | 735344a3 |
Configuración remota de intervalos RSSI (D1/D2) por downlink |
| 2026-02-12 | 140d78b4 |
Carga devui externa por NVS; configuración remota de records_back |
1. Almacenamiento en SD card con fichero cíclico
Commit: 467ff7e6 — 2026-01-10
Los datos de conteo se graban en tarjeta SD en un fichero de tamaño máximo configurable. Cuando el fichero alcanza el límite, se rota automáticamente (escritura cíclica), garantizando almacenamiento continuo sin saturar la tarjeta.
Ficheros afectados: include/sdcard.h, src/sdcard.cpp, src/senddata.cpp
2. Clasificación de dispositivos fijos (Fixed Devices)
Commit: 70054442 — 2026-01-30
Sistema automático de clasificación que distingue dispositivos fijos (infraestructura: APs, routers, balizas BLE) de dispositivos móviles (personas). Los dispositivos fijos se excluyen del conteo de Pax.
- Hasta 512 dispositivos WiFi fijos y 256 BLE fijos en memoria
- Clasificación basada en: mínimo 3 apariciones consecutivas y varianza RSSI ≤ 10 dBm
- Persistencia en tarjeta SD (
load_fixed_devices_from_sd/save_fixed_devices_to_sd) - Comandos de gestión remota por downlink (ver sección Comandos Downlink)
Ficheros afectados: include/fixed_devices.h, src/fixed_devices.cpp, src/aggregator.cpp
3. Backfill de datos históricos
Commits: 70054442 (por registros), 5ceaa6aa (por tiempo) — 2026-01-30 / 2026-02-10
Permite solicitar por downlink el reenvío de registros históricos almacenados en el buffer del dispositivo. Útil para recuperar datos perdidos por conectividad intermitente.
- Modo BURST: todos los registros se encolan y transmiten lo antes posible (limitado por duty cycle LoRaWAN)
- Por número de registros (comando
0x94): indica cuántos registros pasados enviar - Por tiempo (comando
0x95): indica cuántos minutos hacia atrás recuperar (recomendado) - El payload histórico es idéntico al actual (46 bytes con timestamp), identificable por el timestamp
Ficheros afectados: src/senddata.cpp, src/rcommand.cpp, docs/downlink_commands.md
4. Doble envío histórico automático (records_back)
Commits: 140d78b4, cambios sin commit — 2026-02-12
Tras cada envío de datos en tiempo real, el dispositivo puede reenviar automáticamente los últimos N registros del buffer. El parámetro records_back controla cuántos registros se reenvían (0 = desactivado).
- Valor por defecto: 288 registros (equivale a 24 horas con intervalos de 5 minutos)
- Configurable de forma remota por downlink (comandos
0x9A/0x9B) - Se persiste en NVRAM
Ficheros afectados: include/globals.h, src/configmanager.cpp, src/senddata.cpp
5. Clasificación por distancia RSSI (D1/D2/D3)
Commits: a973e9b6, 735344a3 — 2026-01-10 / 2026-02-11
El conteo de MACs se segrega en tres bandas de distancia según la intensidad de señal:
| Banda | Criterio | Descripción |
|---|---|---|
| D1 | RSSI ≥ d1 | Muy cerca (ej. < 5 m) |
| D2 | d2 ≤ RSSI < d1 | Distancia media (ej. 5–15 m) |
| D3 | RSSI < d2 | Lejos (ej. > 15 m) |
Los umbrales d1 y d2 son configurables de forma remota para WiFi y BLE por separado (comandos 0x96, 0x97, 0x98). Se persisten en NVRAM.
Ficheros afectados: include/globals.h, src/aggregator.cpp, src/rcommand.cpp
6. Optimización del escaneo BLE/WiFi
Commit: 2623a9ef — 2026-02-11
Implementa una secuencia de encendido y apagado del módulo BLE sincronizada con el ciclo de escaneo WiFi, para reducir interferencias y mejorar la calidad de las mediciones WiFi mientras BLE está activo.
Ficheros afectados: src/main.cpp, src/rcommand.cpp, include/globals.h
7. Página DevEUI en display
Commit: 4f608eca — 2026-02-02
Se añade una página dedicada en el display OLED que muestra el DevEUI del dispositivo, facilitando la identificación en campo. El total de páginas pasa de 8 a 9, con mayor duración asignada a la página DevEUI.
Ficheros afectados: src/display.cpp, include/display.h
8. Aprovisionamiento de claves LoRaWAN por NVS y puerto serie
Commit: 140d78b4 (parcial) + cambios sin commit — 2026-02-12
Las claves OTAA (DevEUI, JoinEUI, AppKey) pueden almacenarse en la memoria no volátil (NVS) del ESP32 y cargarse dinámicamente en tiempo de ejecución, sin necesidad de recompilar el firmware.
Aprovisionamiento por puerto serie (nuevo)
Al arrancar, el dispositivo espera 8 segundos por el puerto serie un comando de provisión con el formato:
PXCSET DEVEUI=<16hex> JOINEUI=<16hex> APPKEY=<32hex>
Ejemplo:
PXCSET DEVEUI=70B3D57ED0000000 JOINEUI=0000000000000001 APPKEY=A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6
El dispositivo responde PXCOK SAVED y reinicia automáticamente con las nuevas claves. En caso de error en el formato hex: PXCERR BADHEX.
Si no hay claves válidas en NVS, el join LoRaWAN queda bloqueado (ESP_FAIL) como medida de seguridad.
Ficheros afectados: src/main.cpp, src/lorawan.cpp, lib/lorawan_keys/
9. Watchdog de escaneo BLE con recuperación escalada
Commit: c9fca192 — 2026-06-30
El stack BLE del ESP32 puede colgarse tras varias horas de ciclos repetidos de encendido/apagado (ver punto 6). Un nuevo task monitor (ble_watchdog_task) vigila un heartbeat actualizado por el scan_scheduler_task en cada fase WiFi/BLE.
- Nivel 1 — Reinicio suave: si el heartbeat se queda obsoleto, mata el task de escaneo, resetea el controlador BT (
btStop()/btStart()), reinicializa libpax y recrea el task. La sesión LoRaWAN no se pierde. - Nivel 2 — Reinicio completo: si el reinicio suave no recupera el heartbeat en un ciclo completo, se ejecuta un warm restart (
do_reset(true)), que conserva la sesión LoRaWAN guardada en RTC. - Estadísticas: cada evento se registra en un buffer circular de 30 días en NVS y en un log rotativo
/wd.csven la SD. Consultables por downlink (0x9C, ver tabla de comandos).
Ficheros afectados: include/ble_watchdog.h, src/ble_watchdog.cpp, src/main.cpp, include/globals.h, src/rcommand.cpp
10. Reinicio remoto del dispositivo
Commit: 833ecbd7 (documentación) — 2026-06-30
El comando set_reset (opcode 0x09) ya existía en el código pero no estaba documentado. Permite reiniciar el dispositivo en remoto por downlink, sin necesidad de acceso físico. El modo 0x04 (warm restart) es el recomendado para un reinicio simple: conserva la sesión LoRaWAN, evitando el coste de un rejoin.
Downlink: 09 04 (reinicio simple, conserva sesión LoRa)
Downlink: 09 00 (reinicio frío, fuerza rejoin)
Ver tabla completa de modos en docs/downlink_commands.md.
11. Actualización de firmware (OTA) desde tarjeta SD
Sin commit aún — en curso
Permite actualizar el firmware sin conexión LoRaWAN ni WiFi: basta con copiar el binario compilado a la tarjeta SD con un nombre determinado. Al arrancar, el dispositivo detecta el fichero, lo flashea y reinicia automáticamente.
Ficheros en la raíz de la SD:
| Fichero | Función |
|---|---|
/paxfw.bin |
Imagen de firmware compilada (firmware.bin de PlatformIO) |
/paxkeys.txt |
Opcional — claves LoRaWAN a aprovisionar antes de flashear (mismo formato que el comando serie PXCSET) |
Formato de /paxkeys.txt:
DEVEUI=<16hex> JOINEUI=<16hex> APPKEY=<32hex>
Flujo de arranque:
- Si existe
/paxfw.bin: se aprovisionan primero las claves de/paxkeys.txt(si está presente) en NVS — así un dispositivo nuevo, programado solo a través de la SD, se une a LoRaWAN con su identidad correcta desde el primer arranque del nuevo firmware. - Se flashea
/paxfw.bincon la libreríaUpdatede Arduino ESP32. - Si tiene éxito: el fichero se renombra a
.done(evita reflasheo en bucle) y el dispositivo reinicia con el nuevo firmware. - Si falla: se renombra a
.failed, se registra el error y el arranque continúa con el firmware actual.
Las claves LoRaWAN ya provisionadas en NVS (ver punto 8) no se pierden al actualizar firmware, ya que se almacenan independientemente del binario flasheado.
Ficheros afectados: include/ota_sd.h, src/ota_sd.cpp, src/main.cpp, lib/lorawan_keys/ (parser de claves compartido con el aprovisionamiento serie)
Comandos Downlink
El dispositivo escucha comandos remotos en el puerto LoRaWAN 2 (FPort 2). Se pueden concatenar varios comandos en un mismo downlink (máx. ~10 bytes). Metodología en dos pasos (más segura para pruebas): los cambios de configuración se aplican de inmediato pero no se persisten — si reinicias sin guardar, vuelve al último valor guardado. Para hacerlos permanentes, envía 0x21 (set_saveconfig) después.
Documentación detallada del rango extendido (fixed devices, backfill, RSSI bins): docs/downlink_commands.md. Documentación original del proyecto base: docs/remotecontrol.md.
Comandos estándar (proyecto base)
| Opcode | Función | Parámetros | Persiste solo |
|---|---|---|---|
0x01 |
Límite RSSI de escaneo | 1 byte: 0=desactivado, 1-255=radio | con 0x21 |
0x02 |
Modo de conteo | 1 byte: 0=cíclico, 1=acumulativo, 2=cíclico confirmado | con 0x21 |
0x03 |
GPS on/off | 1 byte: 0/1 | con 0x21 |
0x04 |
Display on/off | 1 byte: 0/1 | con 0x21 |
0x05 |
LoRa datarate | 1 byte: 0-15 | con 0x21 |
0x06 |
LoRa TX power | 1 byte: dBm | con 0x21 |
0x07 |
LoRa ADR on/off | 1 byte: 0/1 | con 0x21 |
0x08 |
No-op | — | — |
0x09 |
Reset/restart | 1 byte: 0=cold, 2=factory, 3=flush queue, 4=warm (recomendado), 8=maintenance, 9=OTA WiFi | — |
0x0A |
Ciclo de envío LoRa | 1 byte: segundos/2, mín. 5 (10s) | con 0x21 |
0x0B |
Intervalo salto canal WiFi | 1 byte: segundos/100 | con 0x21 |
0x0C |
Duración escaneo BLE | 1 byte: segundos/100 | con 0x21 |
0x0D |
Ventana sync wakeup | 2 bytes BE: segundos | con 0x21 |
0x0E |
Escáner BLE on/off | 1 byte: 0/1 | con 0x21 |
0x0F |
Antena WiFi (LoPy/FiPy) | 1 byte: 0=interna, 1=externa | con 0x21 |
0x10 |
Luminosidad RGB LED | 1 byte: 0-100% | con 0x21 |
0x11 |
Mapa de canales WiFi | 2 bytes BE: bitmask 0-8191 | con 0x21 |
0x13 |
Modo sensor usuario | 2 bytes: nº sensor (1-3), modo (0/1) | con 0x21 |
0x14 |
Máscara de payload | 1 byte: bitmask (ver docs/remotecontrol.md) |
con 0x21 |
0x15 |
Datos BME on/off | 1 byte: 0/1 | con 0x21 |
0x16 |
Datos batería on/off | 1 byte: 0/1 | con 0x21 |
0x17 |
Escáner WiFi on/off | 1 byte: 0/1 | con 0x21 |
0x18 |
Reservado (no-op) | — | — |
0x19 |
Ciclo de sueño | 2 bytes BE: segundos/10, 0=sin sueño | con 0x21 |
0x20 |
Cargar config desde NVRAM | — | — |
0x21 |
Guardar config en NVRAM | — | — |
0x80 |
Get config (puerto 3) | — | 28 bytes: incluye ble_percent, records_back, RSSI bins (ver docs/downlink_commands.md) |
0x81 |
Get status (puerto 2) | — | — |
0x83 |
Get batería (puerto 8) | — | — |
0x84 |
Get GPS (puerto 4) | — | — |
0x85 |
Get BME (puerto 7) | — | — |
0x86 |
Get hora/fecha (puerto 2) | — | — |
0x87 |
Sincronizar hora | — | — |
0x88 |
Set hora/fecha | 4 bytes BE: epoch UTC | — |
Comandos propios del fork
| Opcode | Función | Parámetros | Descripción |
|---|---|---|---|
0x1D |
ble_autotune_now |
— | Ajusta blescantime automáticamente según wifichanmap/wifichancycle actuales (no persiste, mandar 0x21 después si se quiere fijo) |
0x1E |
set_ble_percent |
1 byte: 0-100 | % de tiempo BLE dentro del ciclo de escaneo (resto es WiFi) |
0x90 |
get_fixed_wifi |
offset (2 bytes BE) | Lista MACs WiFi fijas (paginado) |
0x91 |
get_fixed_ble |
offset (2 bytes BE) | Lista MACs BLE fijas (paginado) |
0x92 |
get_agg_info |
— | Info del buffer histórico |
0x93 |
clear_fixed_devices |
1 byte: 0=WiFi, 1=BLE, 2=Ambos | Borra dispositivos fijos |
0x94 |
request_backfill |
num_records (2 bytes BE) | Backfill por número de registros |
0x95 |
request_backfill_by_time |
minutos (2 bytes BE) | Backfill por tiempo (recomendado) |
0x96 |
set_wifi_rssi_bins |
d1_abs, d2_abs (2 bytes) | Configura umbrales RSSI WiFi (D1/D2) |
0x97 |
set_ble_rssi_bins |
d1_abs, d2_abs (2 bytes) | Configura umbrales RSSI BLE (D1/D2) |
0x98 |
get_rssi_bins |
— | Consulta umbrales RSSI actuales |
0x99 |
set_flush |
— | Vacía el buffer de envío |
0x9A |
set_records_back |
valor (2 bytes BE) | Configura registros históricos doble envío |
0x9B |
get_records_back |
— | Consulta valor actual de records_back |
0x9C |
get_watchdog_stats |
días (1 byte) | Reinicios BLE/full de los últimos N días |
Ejemplos de uso (ChirpStack, FPort 2)
# Ciclo escaneo 30s con 50% BLE / 50% WiFi (sendcycle=15 -> 15x2=30s):
# Activo de inmediato, pero temporal -> añade "21" al final si quieres que sobreviva a un reinicio:
Hex: 0A 0F 1E 32 (solo para esta sesión, vuelve atrás al reiniciar)
Hex: 0A 0F 1E 32 21 (igual, pero además lo persiste en NVRAM)
# Backfill últimas 2 horas (120 min = 0x0078):
Hex: 95 00 78 | Base64: lQB4
# Configurar RSSI WiFi D1=-70dBm, D2=-87dBm:
Hex: 96 46 57 (70=0x46, 87=0x57)
# Consultar umbrales RSSI:
Hex: 98
# Configurar records_back = 288 (24h @ 5min):
Hex: 9A 01 20 (288 = 0x0120)
# Consultar records_back:
Hex: 9B
# Reinicio remoto simple, conserva sesión LoRa:
Hex: 09 04
Licencia
Copyright 2018-2022 Oliver Brandmueller ob@sysadm.in
Copyright 2018-2022 Klaus Wilting verkehrsrot@arcor.de
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
NOTICE: Parts of the source files in this repository are made available under different licenses, see file LICENSE.txt in this repository. Refer to each individual source file for more details.
Créditos (upstream)
Thanks to
- Oliver Brandmüller for idea and initial setup of this project
- Charles Hallard for major code contributions to this project
- robbi5 for the payload converter
- Caspar Armster for the The Things Stack V3 payload converter
- terrillmoore for maintaining the LMIC for arduino LoRaWAN stack
- sbamueller for writing the tutorial in Make Magazine
- Stefan for paxcounter opensensebox integration
- August Quint for adding SD card data logger and SDS011 support
- t-huyeng for adding a CI workflow and rework documentation
- TD-er for bugfixings and T-Beam documentation
Créditos fork Ciberman/Antigravity
Extensiones desarrolladas por:
- Mon (mon@phebus.es) — arquitectura, integración NVS/LoRaWAN, RSSI bins, SD cíclica, BLE/WiFi optimization
- nicolasPascual (nicolas@techfriendly.es) — backfill histórico, fixed devices, display DevEUI, documentación downlink
