From 06bff84a76936f2c8862c92601190c4a048206d8 Mon Sep 17 00:00:00 2001 From: Tim Huyeng Date: Sat, 19 Nov 2022 18:07:39 +0100 Subject: [PATCH 01/16] initial mkdocs commit --- docs/additions.md | 69 ++++++ docs/display.md | 14 ++ docs/hardware.md | 59 +++++ {img => docs/img}/Paxcounter-Clock2.png | Bin {img => docs/img}/Paxcounter-LEDmatrix.jpg | Bin {img => docs/img}/Paxcounter-Screen.png | Bin {img => docs/img}/Paxcounter-lolin.gif | Bin {img => docs/img}/Paxcounter-title.jpg | Bin .../img}/Paxcounter-ttgo-tdisplay.jpg | Bin .../img}/Paxcounter-ttgo-twristband.jpg | Bin {img => docs/img}/Paxcounter-ttgo.jpg | Bin {img => docs/img}/TTGO T-Beam.jpg | Bin {img => docs/img}/TTGO-case.jpg | Bin {img => docs/img}/TTGO-curves.jpg | Bin {img => docs/img}/paxcounter-curves.jpg | Bin .../img}/paxcounter_downlink_example.png | Bin docs/index.md | 28 +++ docs/installation.md | 45 ++++ docs/integrations.md | 16 ++ docs/led.md | 17 ++ docs/legalnote.md | 17 ++ docs/license-credits.md | 34 +++ docs/payloadformat.md | 103 +++++++++ docs/remotecontrol.md | 202 ++++++++++++++++++ mkdocs.yml | 28 +++ 25 files changed, 632 insertions(+) create mode 100644 docs/additions.md create mode 100644 docs/display.md create mode 100644 docs/hardware.md rename {img => docs/img}/Paxcounter-Clock2.png (100%) rename {img => docs/img}/Paxcounter-LEDmatrix.jpg (100%) rename {img => docs/img}/Paxcounter-Screen.png (100%) rename {img => docs/img}/Paxcounter-lolin.gif (100%) rename {img => docs/img}/Paxcounter-title.jpg (100%) rename {img => docs/img}/Paxcounter-ttgo-tdisplay.jpg (100%) rename {img => docs/img}/Paxcounter-ttgo-twristband.jpg (100%) rename {img => docs/img}/Paxcounter-ttgo.jpg (100%) rename {img => docs/img}/TTGO T-Beam.jpg (100%) rename {img => docs/img}/TTGO-case.jpg (100%) rename {img => docs/img}/TTGO-curves.jpg (100%) rename {img => docs/img}/paxcounter-curves.jpg (100%) rename {img => docs/img}/paxcounter_downlink_example.png (100%) create mode 100644 docs/index.md create mode 100644 docs/installation.md create mode 100644 docs/integrations.md create mode 100644 docs/led.md create mode 100644 docs/legalnote.md create mode 100644 docs/license-credits.md create mode 100644 docs/payloadformat.md create mode 100644 docs/remotecontrol.md create mode 100644 mkdocs.yml diff --git a/docs/additions.md b/docs/additions.md new file mode 100644 index 00000000..08208b9e --- /dev/null +++ b/docs/additions.md @@ -0,0 +1,69 @@ +# Sensors and Peripherals + +You can add up to 3 user defined sensors. Insert your sensor's payload scheme in [*sensor.cpp*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/sensor.cpp). Bosch BMP180 / BME280 / BME680 environment sensors are supported, to activate configure BME in board's hal file before build. Furthermore, SDS011, RTC DS3231, generic serial NMEA GPS, I2C LoPy GPS are supported, and to be configured in board's hal file. See [*generic.h*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/hal/generic.h) for all options and for proper configuration of BME280/BME680. + +Output of user sensor data can be switched by user remote control command 0x14 sent to Port 2. + +Output of sensor and peripheral data is internally switched by a bitmask register. Default mask can be tailored by editing *cfg.payloadmask* initialization value in [*configmanager.cpp*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/configmanager.cpp) following this scheme: + +| Bit | Sensordata | Default | +| --- | ------------- | ------- | +| 0 | Paxcounter | on | +| 1 | unused | off | +| 2 | BME280/680 | on | +| 3 | GPS* | on | +| 4 | User sensor 1 | on | +| 5 | User sensor 2 | on | +| 6 | User sensor 3 | on | +| 7 | Batterylevel | off | + +*) GPS data can also be combined with paxcounter payload on port 1, *#define GPSPORT 1* in paxcounter.conf to enable + +# Power saving mode + +Paxcounter supports a battery friendly power saving mode. In this mode the device enters deep sleep, after all data is polled from all sensors and the dataset is completeley sent through all user configured channels (LORAWAN / SPI / MQTT / SD-Card). Set *#define SLEEPCYCLE* in paxcounter.conf to enable power saving mode and to specify the duration of a sleep cycle. Power consumption in deep sleep mode depends on your hardware, i.e. if on board peripherals can be switched off or set to a chip specific sleep mode either by MCU or by power management unit (PMU) as found on TTGO T-BEAM v1.0/V1.1. See *power.cpp* for power management, and *reset.cpp* for sleep and wakeup logic. + +# Time sync + +Paxcounter can keep a time-of-day synced with external or on board time sources. Set *#define TIME_SYNC_INTERVAL* in paxcounter.conf to enable time sync. Supported external time sources are GPS, LORAWAN network time and LORAWAN application timeserver time. Supported on board time sources are the RTC of ESP32 and a DS3231 RTC chip, both are kept sycned as fallback time sources. Time accuracy depends on board's time base which generates the pulse per second. Supported are GPS PPS, SQW output of RTC, and internal ESP32 hardware timer. Time base is selected by #defines in the board's hal file, see example in [**generic.h**](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/hal/generic.h). Bonus: If your LORAWAN network does not support network time, you can run a Node-Red timeserver application using the enclosed [**Timeserver code**](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/Node-RED/Timeserver.json). Configure the MQTT nodes in Node-Red for the LORAWAN application used by your paxocunter device. Time can also be set without precision liability, by simple remote command, see section remote control. + +# Wall clock controller + +Paxcounter can be used to sync a wall clock which has a DCF77 or IF482 time telegram input. Set *#define HAS_IF482* or *#define HAS_DCF77* in board's hal file to setup clock controller. Use case of this function is to integrate paxcounter and clock. Accurary of the synthetic DCF77 signal depends on accuracy of on board's time base, see above. + +# Mobile PaxCounter using openSenseMap + +This describes how to set up a mobile PaxCounter:
Follow all steps so far for preparing the device, selecting the packed payload format. In `paxcounter.conf` set PAYLOAD_OPENSENSEBOX to 1. Register a new sensebox on https://opensensemap.org/. In the sensor configuration select "TheThingsNetwork" and set decoding profile to "LoRa serialization". Enter your TTN Application and Device ID. Setup decoding option using `[{"decoder":"latLng"},{"decoder":"uint16",sensor_id":"yoursensorid"}]` + +# SD-card + +Data can be stored on SD-card if the board provides an SD card interface, either with SPI or MMC mode. To enable this feature, specify interface mode and hardware pins in board's hal file (src/hal/): + + #define HAS_SDCARD 1 // SD-card interface, using SPI mode + OR + #define HAS_SDCARD 2 // SD-card interface, using MMC mode + + // Pins for SPI interface + #define SDCARD_CS (13) // fill in the correct numbers for your board + #define SDCARD_MOSI (15) + #define SDCARD_MISO (2) + #define SDCARD_SCLK (14) + +This is an example of a board with MMC SD-card interface: https://www.aliexpress.com/item/32915894264.html. For this board use file src/hal/ttgov21new.h and add the lines given above. + +Another approach would be this tiny board: https://www.aliexpress.com/item/32424558182.html (needs 5V). +In this case you choose the correct file for your ESP32-board in the src/hal-directory and add the lines given above. Edit the pin numbers given in the example, according to your wiring. + +Data is written on SD-card to a single file. After 3 write operations the data is flushed to the disk to minimize flash write cycles. Thus, up to the last 3 records of data will get lost when the PAXCOUNTER looses power during operation. + +Format of the resulting file is CSV, thus easy import in LibreOffice, Excel, Influx, etc. Each record contains timestamp (in ISO8601 format), paxcount (wifi and ble) and battery voltage (optional). Voltage is logged if the device has a battery voltage sensor (to be configured in board hal file). + +File contents example: +```csv + timestamp,wifi,ble[,voltage] + 2022-01-30T21:12:41Z,11,25[,4100] + 2022-01-30T21:14:24Z,10,21[,4070] + 2022-01-30T21:16:08Z,12,26[,4102] + 2022-01-30T21:17:52Z,11,26[,4076] +``` +If you want to change this, modify src/sdcard.cpp and include/sdcard.h. \ No newline at end of file diff --git a/docs/display.md b/docs/display.md new file mode 100644 index 00000000..b564bdf3 --- /dev/null +++ b/docs/display.md @@ -0,0 +1,14 @@ +# Display + +![Display Image](img/Paxcounter-Screen.png) + +If you're using a device with OLED display, or if you add such one to the I2C bus, the device shows live data on the display. You can flip display pages showing + +- recent count of pax +- histogram +- GPS data +- BME sensor data +- time of day +- blank page + +by pressing the button of the device. \ No newline at end of file diff --git a/docs/hardware.md b/docs/hardware.md new file mode 100644 index 00000000..69380cb0 --- /dev/null +++ b/docs/hardware.md @@ -0,0 +1,59 @@ +# Hardware + +**Supported ESP32 based boards**: + +*With LoRa radio data transfer*: + +- **LilyGo: [Paxcounter-Board*](https://www.aliexpress.com/item/32915894264.html?spm=a2g0o.productlist.0.0.3d656325QrcfQc&algo_pvid=4a150199-63e7-4d21-bdb1-b48164537744&algo_exp_id=4a150199-63e7-4d21-bdb1-b48164537744-2&pdp_ext_f=%7B%22sku_id%22%3A%2212000023374441919%22%7D)** +- TTGO: T1*, T2*, T3*, T-Beam, T-Fox +- Heltec: LoRa-32 v1 and v2 +- Pycom: LoPy, LoPy4, FiPy +- Radioshuttle.de: [ECO Power Board](https://www.radioshuttle.de/esp32-eco-power/esp32-eco-power-board/) +- WeMos: LoLin32 + [LoraNode32 shield](https://github.com/hallard/LoLin32-Lora), +LoLin32lite + [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-Lora) +- Adafruit ESP32 Feather + LoRa Wing + OLED Wing, #IoT Octopus32 (Octopus + ESP32 Feather) +- M5Stack: [Basic Core IoT*](https://m5stack.com/collections/m5-core/products/basic-core-iot-development-kit) + [Lora Module RA-01H](https://m5stack.com/collections/m5-module/products/lora-module-868mhz), [Fire IoT*](https://m5stack.com/collections/m5-core/products/fire-iot-development-kit) + +*Without LoRa*: + +- LilyGo: [T-Dongle S3*](https://github.com/Xinyuan-LilyGO/T-Dongle-S3) +- Pyom: WiPy +- WeMos: LoLin32, LoLin32 Lite, WeMos D32, [Wemos32 Oled](https://www.instructables.com/id/ESP32-With-Integrated-OLED-WEMOSLolin-Getting-Star/) +- Crowdsupply: [TinyPICO](https://www.crowdsupply.com/unexpected-maker/tinypico) +- TTGO: [T-Display](https://www.aliexpress.com/item/33048962331.html) +- TTGO: [T-Wristband](https://www.aliexpress.com/item/4000527495064.html) +- Generic ESP32 + +*) supports microSD/TF-card for local logging of paxcounter data + +Depending on board hardware following features are supported: +- LoRaWAN communication, supporting various payload formats (see enclosed .js converters) +- MQTT communication via TCP/IP and Ethernet interface (note: payload transmitted over MQTT will be base64 encoded) +- SPI serial communication to a local host +- LED (shows power & status) +- OLED Display (shows detailed status) +- RGB LED (shows colorized status) +- Button (short press: flip display page / long press: send alarm message) +- Battery voltage monitoring (analog read / AXP192 / IP5306) +- GPS (Generic serial NMEA, or Quectel L76 I2C) +- Environmental sensors (Bosch BMP180/BME280/BME680 I2C; SDS011 serial) +- Real Time Clock (Maxim DS3231 I2C) +- IF482 (serial) and DCF77 (gpio) time telegram generator +- Switch external power / battery +- LED Matrix display (similar to [this 64x16 model](https://www.instructables.com/id/64x16-RED-LED-Marquee/), can be ordered on [Aliexpress](https://www.aliexpress.com/item/P3-75-dot-matrix-led-module-3-75mm-high-clear-top1-for-text-display-304-60mm/32616683948.html)) +- SD-card (see section SD-card here) for logging pax data + +Target platform must be selected in `platformio.ini`.
+Hardware dependent settings (pinout etc.) are stored in board files in [/hal](https://github.com/cyberman54/ESP32-Paxcounter/tree/master/src/hal) directory. If you want to use a ESP32 board which is not yet supported, use hal file generic.h and tailor pin mappings to your needs. Pull requests for new boards welcome.
+ +Some 3D printable cases can be found (and, if wanted so, ordered) on Thingiverse, see +Heltec, +TTGOv2, +TTGOv2.1, +TTGO, +T-BEAM, +T-BEAM parts, +for example.
+ +Power consumption was metered at around 450 - 1000mW, depending on board and user settings in paxcounter.conf. +By default bluetooth sniffing not installed (#define *BLECOUNTER* 0 in paxcounter.conf). If you enable bluetooth be aware that this goes on expense of wifi sniffing results, because then wifi and bt stack must share the 2,4 GHz RF ressources of ESP32. If you need to sniff wifi and bt in parallel and need best possible results, use two boards - one for wifi only and one for bt only - and add counted results. diff --git a/img/Paxcounter-Clock2.png b/docs/img/Paxcounter-Clock2.png similarity index 100% rename from img/Paxcounter-Clock2.png rename to docs/img/Paxcounter-Clock2.png diff --git a/img/Paxcounter-LEDmatrix.jpg b/docs/img/Paxcounter-LEDmatrix.jpg similarity index 100% rename from img/Paxcounter-LEDmatrix.jpg rename to docs/img/Paxcounter-LEDmatrix.jpg diff --git a/img/Paxcounter-Screen.png b/docs/img/Paxcounter-Screen.png similarity index 100% rename from img/Paxcounter-Screen.png rename to docs/img/Paxcounter-Screen.png diff --git a/img/Paxcounter-lolin.gif b/docs/img/Paxcounter-lolin.gif similarity index 100% rename from img/Paxcounter-lolin.gif rename to docs/img/Paxcounter-lolin.gif diff --git a/img/Paxcounter-title.jpg b/docs/img/Paxcounter-title.jpg similarity index 100% rename from img/Paxcounter-title.jpg rename to docs/img/Paxcounter-title.jpg diff --git a/img/Paxcounter-ttgo-tdisplay.jpg b/docs/img/Paxcounter-ttgo-tdisplay.jpg similarity index 100% rename from img/Paxcounter-ttgo-tdisplay.jpg rename to docs/img/Paxcounter-ttgo-tdisplay.jpg diff --git a/img/Paxcounter-ttgo-twristband.jpg b/docs/img/Paxcounter-ttgo-twristband.jpg similarity index 100% rename from img/Paxcounter-ttgo-twristband.jpg rename to docs/img/Paxcounter-ttgo-twristband.jpg diff --git a/img/Paxcounter-ttgo.jpg b/docs/img/Paxcounter-ttgo.jpg similarity index 100% rename from img/Paxcounter-ttgo.jpg rename to docs/img/Paxcounter-ttgo.jpg diff --git a/img/TTGO T-Beam.jpg b/docs/img/TTGO T-Beam.jpg similarity index 100% rename from img/TTGO T-Beam.jpg rename to docs/img/TTGO T-Beam.jpg diff --git a/img/TTGO-case.jpg b/docs/img/TTGO-case.jpg similarity index 100% rename from img/TTGO-case.jpg rename to docs/img/TTGO-case.jpg diff --git a/img/TTGO-curves.jpg b/docs/img/TTGO-curves.jpg similarity index 100% rename from img/TTGO-curves.jpg rename to docs/img/TTGO-curves.jpg diff --git a/img/paxcounter-curves.jpg b/docs/img/paxcounter-curves.jpg similarity index 100% rename from img/paxcounter-curves.jpg rename to docs/img/paxcounter-curves.jpg diff --git a/img/paxcounter_downlink_example.png b/docs/img/paxcounter_downlink_example.png similarity index 100% rename from img/paxcounter_downlink_example.png rename to docs/img/paxcounter_downlink_example.png diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..37bafd67 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,28 @@ +# ESP32-Paxcounter +**Wifi & Bluetooth driven, LoRaWAN enabled, battery powered mini Paxcounter built on cheap ESP32 LoRa IoT boards** + +[Tutorial (in german language)](https://www.heise.de/select/make/2019/1/1551099236518668) + +[![CodeFactor](https://www.codefactor.io/repository/github/cyberman54/esp32-paxcounter/badge)](https://www.codefactor.io/repository/github/cyberman54/esp32-paxcounter) +[![PlatformIO CI](https://github.com/cyberman54/ESP32-Paxcounter/actions/workflows/build.yml/badge.svg?event=push)](https://github.com/cyberman54/ESP32-Paxcounter/actions/workflows/build.yml) + + + + + + + + + + + + +# Use case + +Paxcounter is an [ESP32](https://www.espressif.com/en/products/socs/esp32) MCU based device for metering passenger flows in realtime. It counts how many mobile devices are around. This gives an estimation how many people are around. Paxcounter detects Wifi and Bluetooth signals in the air, focusing on mobile devices by evaluating their MAC adresses. + +Intention of this project is to do this without intrusion in privacy: You don't need to track people owned devices, if you just want to count them. Therefore, Paxcounter does not persistenly store MAC adresses and does no kind of fingerprinting the scanned devices. + +Data can either be be stored on a local SD-card, transferred to cloud using LoRaWAN network (e.g. TheThingsNetwork or Helium) or MQTT over TCP/IP, or transmitted to a local host using serial (SPI) interface. + +You can build this project battery powered using ESP32 deep sleep mode and reach long uptimes with a single 18650 Li-Ion cell. \ No newline at end of file diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 00000000..2741bb4f --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,45 @@ +# Preparing + +## Install Platformio + +Install PlatformIO IDE for embedded development to make this project. Platformio integrates with your favorite IDE, choose eg. Visual Studio, Atom, Eclipse etc. + +Compile time configuration is spread across several files. Before compiling the code, edit or create the following files: + +## platformio.ini +Edit `platformio_orig.ini` (for ESP32 CPU based boards) *or* `platformio_orig_s3.ini` (for ESP32-S3 CPU based boards) and select desired board in section **board**. To add a new board, create an appropriate hardware abstraction layer file in hal subdirectory, and add a pointer to this file in section **board**. Copy or rename to `platformio.ini` in the root directory of the project. Now start Platformio. Note: Platformio is looking for `platformio.ini` in the root directory and won't start if it does not find this file! + +## paxcounter.conf +Edit `src/paxcounter_orig.conf` and tailor settings in this file according to your needs and use case. Please take care of the duty cycle regulations of the LoRaWAN network you're going to use. Copy or rename to `src/paxcounter.conf`. + +If your device has a **real time clock** it can be updated by either LoRaWAN network or GPS time, according to settings *TIME_SYNC_INTERVAL* and *TIME_SYNC_LORAWAN* in `paxcounter.conf`. + +## src/lmic_config.h +Edit `src/lmic_config.h` and tailor settings in this file according to your country and device hardware. Please take care of national regulations when selecting the frequency band for LoRaWAN. + +## src/loraconf.h +Create file `src/loraconf.h` using the template [src/loraconf_sample.h](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/loraconf_sample.h) and adjust settings to use your personal values. To join the network and activate your paxcounter, you must configure either OTAA or ABP join method. You should use OTAA, whenever possible. To understand the differences of the two methods, [this article](https://www.thethingsnetwork.org/docs/devices/registration.html) may be useful. + +To configure OTAA, leave `#define LORA_ABP` deactivated (commented). To use ABP, activate (uncomment) `#define LORA_ABP` in the file `src/loraconf.h`. +The file `src/loraconf_sample.h` contains more information about the values to provide. + +## src/ota.conf +Create file `src/ota.conf` using the template [src/ota.sample.conf](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/ota.sample.conf) and enter your WIFI network&key. These settings are used for downloading updates via WiFi, either from a remote https server, or locally via WebUI. If you want to use a remote server, you need a PAX.express repository. Enter your PAX.express credentials in ota.conf. If you don't need wireless firmware updates just rename ota.sample.conf to ota.conf. + +# Building + +Use PlatformIO with your preferred IDE for development and building this code. Make sure you have latest PlatformIO version. + +# Uploading + +- **by cable, via USB/UART interface:** +To upload the code via cable to your ESP32 board this needs to be switched from run to bootloader mode. Boards with USB bridge like Heltec and TTGO usually have an onboard logic which allows soft switching by the upload tool. In PlatformIO this happenes automatically.

+The LoPy/LoPy4/FiPy board needs to be set manually. See these +instructions how to do it. Don't forget to press on board reset button after switching between run and bootloader mode.

+The original Pycom firmware is not needed, so there is no need to update it before flashing Paxcounter. Just flash the compiled paxcounter binary (.elf file) on your LoPy/LoPy4/FiPy. If you later want to go back to the Pycom firmware, download the firmware from Pycom and flash it over. + +- **over the air (OTA), download via WiFi:** +After the ESP32 board is initially flashed and has joined a LoRaWAN network, the firmware can update itself by OTA. This process is kicked off by sending a remote control command (see below) via LoRaWAN to the board. The board then tries to connect via WiFi to a cloud service (PAX.express), checks for update, and if available downloads the binary and reboots with it. If something goes wrong during this process, the board reboots back to the current version. Prerequisites for OTA are: 1. You own a PAX.express repository, 2. you pushed the update binary to your PAX.express repository, 3. internet access via encrypted (WPA2) WiFi is present at the board's site, 4. WiFi credentials were set in ota.conf and initially flashed to the board. Step 2 runs automated, just enter the credentials in ota.conf and set `upload_protocol = custom` in platformio.ini. Then press build and lean back watching platformio doing build and upload. + +- **over the air (OTA), upload via WiFi:** +If option *BOOTMENU* is defined in `paxcounter.conf`, the ESP32 board will try to connect to a known WiFi access point each time cold starting (after a power cycle or a reset), using the WiFi credentials given in `ota.conf`. Once connected to the WiFi it will fire up a simple webserver, providing a bootstrap menu waiting for a user interaction (pressing "START" button in menu). This process will time out after *BOOTDELAY* seconds, ensuring booting the device to runmode. Once a user interaction in bootstrap menu was detected, the timeout will be extended to *BOOTTIMEOUT* seconds. During this time a firmware upload can be performed manually by user, e.g. using a smartphone in tethering mode providing the firmware upload file. diff --git a/docs/integrations.md b/docs/integrations.md new file mode 100644 index 00000000..faf8c743 --- /dev/null +++ b/docs/integrations.md @@ -0,0 +1,16 @@ +# Integration into "The Things Stack Community Edition" aka "The Things Stack V3" + +To use the ESP32-Paxcounter in The Things Stack Community Edition you need an account to reach the console. Go to: +- [The Things Stack Community Edition Console](https://console.cloud.thethings.network/) +- choose your region and go to applications +- create an application by clicking "**+ Add application**" and give it a id, name, etc. +- create a device by clicking "**+ Add end device**" +- Select the end device: choose the Brand "**Open Source Community Projects**" and the Model "**ESP32-Paxcounter**", leave Hardware Version to "**Unknown**" and select your **Firmware Version** and **Profile (Region)** +- Enter registration data: choose the **frequency plan** (for EU choose the recommended), set the **AppEUI** (Fill with zeros), set the **DeviceEUI** (generate), set the **AppKey** (generate), choose a **device ID** and hit "Register end device" +- got to Applications -> "your App ID" -> Payload formatters -> Uplink, choose "**Repository**" and hit "Save changes" + +The "Repository" payload decoder uses the packed format, explained below. If you want to use MyDevices from Cayenne you should use the Cayenne payload decoder instead. + +# TTN Mapper + +If you want your devices to be feeding the [TTN Mapper](https://ttnmapper.org/), just follow this manual: https://docs.ttnmapper.org/integration/tts-integration-v3.html - different than indicated in the manual you can leave the payload decoder to "Repository" for the ESP32-Paxcounter and you are fine. diff --git a/docs/led.md b/docs/led.md new file mode 100644 index 00000000..09f49c0f --- /dev/null +++ b/docs/led.md @@ -0,0 +1,17 @@ +# LED blink pattern + +**Mono color LED:** + +- Single Flash (50ms): seen a new Wifi or BLE device +- Quick blink (20ms on each 1/5 second): joining LoRaWAN network in progress or pending +- Small blink (10ms on each 1/2 second): LoRaWAN data transmit in progress or pending +- Long blink (200ms on each 2 seconds): LoRaWAN stack error + +**RGB LED:** + +- Green: seen a new Wifi device +- Magenta: seen a new BLE device +- Yellow: joining LoRaWAN network in progress or pending +- Pink: LORAWAN MAC transmit in progress +- Blue: LoRaWAN data transmit in progress or pending +- Red: LoRaWAN stack errors \ No newline at end of file diff --git a/docs/legalnote.md b/docs/legalnote.md new file mode 100644 index 00000000..27722abf --- /dev/null +++ b/docs/legalnote.md @@ -0,0 +1,17 @@ +# Legal note + +**Depending on your country's laws it may be illegal to sniff wireless networks for MAC addresses. Please check and respect your country's laws before using this code!** + +(e.g. US citizens may want to check [Section 18 U.S. Code § 2511](https://www.law.cornell.edu/uscode/text/18/2511) and [discussion](https://github.com/schollz/howmanypeoplearearound/issues/4) on this) + +(e.g. UK citizens may want to check [Data Protection Act 1998](https://ico.org.uk/media/1560691/wi-fi-location-analytics-guidance.pdf) and [GDPR 2018](https://ico.org.uk/for-organisations/guide-to-the-general-data-protection-regulation-gdpr/key-definitions/)) + +(e.g. Citizens in the the Netherlands and EU may want to read [this article](https://www.ivir.nl/publicaties/download/PrivacyInformatie_2016_6.pdf) and [this article](https://autoriteitpersoonsgegevens.nl/nl/nieuws/europese-privacytoezichthouders-publiceren-opinie-eprivacyverordening)) and [this decision](https://edpb.europa.eu/news/national-news/2021/dutch-dpa-fines-municipality-wi-fi-tracking_en) + +(e.g. Citizens in Germany may want to read [this article of Wissenschaftliche Dienste des Deutschen Bundestages](https://www.bundestag.de/resource/blob/538890/3dfae197d2c930693aa16d1619204f58/WD-3-206-17-pdf-data.pdf) + +Note: If you use this software you do this at your own risk. That means that you alone - not the authors of this software - are responsible for the legal compliance of an application using this or build from this software and/or usage of a device created using this software. You should take special care and get prior legal advice if you plan metering passengers in public areas and/or publish data drawn from doing so. + +# Privacy disclosure + +Paxcounter generates identifiers for sniffed Wifi or Bluetooth MAC adresses and and collects them temporary in the device's RAM for a configurable scan cycle time (default 60 seconds). After each scan cycle the collected identifiers are cleared. Identifiers are generated by using the last 2 bytes of universal MAC adresses. Personal MAC adresses remain untouched and are not evaluated. Identifiers and MAC adresses are never transferred to the LoRaWAN network. No persistent storing of MAC adresses, identifiers or timestamps and no other kind of analytics than counting are implemented in this code. Wireless networks are not touched by this code, but MAC adresses from wireless devices as well within as not within wireless networks, regardless if encrypted or unencrypted, are sniffed and processed by this code. \ No newline at end of file diff --git a/docs/license-credits.md b/docs/license-credits.md new file mode 100644 index 00000000..8b75fbe1 --- /dev/null +++ b/docs/license-credits.md @@ -0,0 +1,34 @@ +# License + +Copyright 2018-2022 Oliver Brandmueller + +Copyright 2018-2022 Klaus Wilting + + 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. + +# Credits + +Thanks to +- [Oliver Brandmüller](https://github.com/spmrider) for idea and initial setup of this project +- [Charles Hallard](https://github.com/hallard) for major code contributions to this project +- [robbi5](https://github.com/robbi5) for the payload converter +- [Caspar Armster](https://www.dasdigidings.de/) for the The Things Stack V3 payload converter +- [terrillmoore](https://github.com/mcci-catena) for maintaining the LMIC for arduino LoRaWAN stack +- [sbamueller](https://github.com/sbamueller) for writing the tutorial in Make Magazine +- [Stefan](https://github.com/nerdyscout) for paxcounter opensensebox integration +- [August Quint](https://github.com/AugustQu) for adding SD card data logger and SDS011 support +- [t-huyeng](https://github.com/t-huyeng) for adding a CI workflow to this project diff --git a/docs/payloadformat.md b/docs/payloadformat.md new file mode 100644 index 00000000..780c0523 --- /dev/null +++ b/docs/payloadformat.md @@ -0,0 +1,103 @@ + +# Payload format + +You can select different payload formats in `paxcounter.conf`: + +- ***Plain*** uses big endian format and generates json fields, e.g. useful for TTN console + +- ***Packed*** uses little endian format and generates json fields + +- [***CayenneLPP***](https://mydevices.com/cayenne/docs/lora/#lora-cayenne-low-power-payload-reference-implementation) generates MyDevices Cayenne readable fields + +**Decrepated information from the things network v2 >>** + +If you're using [TheThingsNetwork](https://www.thethingsnetwork.org/) (TTN) you may want to use a payload converter. Go to TTN Console - Application - Payload Formats and paste the code example below in tabs Decoder and Converter. This way your MQTT application can parse the fields `pax`, `ble` and `wifi`. + +To add your device to myDevices Cayenne platform select "Cayenne-LPP" from Lora device list and use the CayenneLPP payload encoder. + +To track a paxcounter device with on board GPS and at the same time contribute to TTN coverage mapping, you simply activate the [TTNmapper integration](https://www.thethingsnetwork.org/docs/applications/ttnmapper/) in TTN Console. Both formats *plain* and *packed* generate the fields `latitude`, `longitude` and `hdop` required by ttnmapper. Important: set TTN mapper port filter to '4' (paxcounter GPS Port). + +**<< Decrepated information from the things network v2** + +Hereafter described is the default *plain* format, which uses MSB bit numbering. Under /TTN in this repository you find some ready-to-go decoders which you may copy to your TTN console: + +[**plain_decoder.js**](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/TTN/plain_decoder.js) | +[**plain_converter.js**](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/TTN/plain_converter.js) | +[**packed_decoder.js**](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/TTN/packed_decoder.js) | +[**packed_converter.js**](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/TTN/packed_converter.js) + +**Port #1:** Paxcount data + + byte 1-2: Number of unique devices, seen on Wifi [00 00 if Wifi scan disabled] + byte 3-4: Number of unique devices, seen on Bluetooth [ommited if BT scan disabled] + +**Port #2:** Device status query result + + byte 1-2: Battery or USB Voltage [mV], 0 if no battery probe + byte 3-10: Uptime [seconds] + byte 11: CPU temperature [°C] + bytes 12-15: Free RAM [bytes] + byte 16: Last CPU core 0 reset reason + bytes 17-20: Number of restarts since last power cycle + +**Port #3:** Device configuration query result + + byte 1: Lora DR (0..15, see rcommand 0x05) [default 5] + byte 2: Lora TXpower (2..15) [default 15] + byte 3: Lora ADR (1=on, 0=off) [default 1] + byte 4: Screensaver status (1=on, 0=off) [default 0] + byte 5: Display status (1=on, 0=off) [default 0] + byte 6: Counter mode (0=cyclic unconfirmed, 1=cumulative, 2=cyclic confirmed) [default 0] + bytes 7-8: RSSI limiter threshold value (negative, MSB) [default 0] + byte 9: Scan and send cycle in seconds/2 (0..255) [default 120] + byte 10: Wifi channel hopping interval in seconds/100 (0..255), 0 means no hopping [default 50] + byte 11: Bluetooth channel switch interval in seconds/100 (0..255) [default 10] + byte 12: Bluetooth scanner status (1=on, 0=0ff) [default 1] + byte 13: Wifi antenna switch (0=internal, 1=external) [default 0] + byte 14: 0 (reserved) + byte 15: RGB LED luminosity (0..100 %) [default 30] + byte 16: Payloadmask (Bitmask, 0..255, see rcommand 0x14) + byte 17: 0 (reserved) + bytes 18-28: Software version (ASCII format, terminating with zero) + + +**Port #4:** GPS data (only if device has fature GPS, and GPS data is enabled and GPS has a fix) + + bytes 1-4: Latitude + bytes 5-8: Longitude + byte 9: Number of satellites + bytes 10-11: HDOP + bytes 12-13: Altitude [meter] + +**Port #5:** Button pressed alarm + + byte 1: static value 0x01 + +**Port #6:** (unused) + +**Port #7:** Environmental sensor data (only if device has feature BME) + + bytes 1-2: Temperature [°C] + bytes 3-4: Pressure [hPa] + bytes 5-6: Humidity [%] + bytes 7-8: Indoor air quality index (0..500), see below + + Indoor air quality classification: + 0-50 good + 51-100 average + 101-150 little bad + 151-200 bad + 201-300 worse + 301-500 very bad + +**Port #8:** Battery voltage data (only if device has feature BATT) + + bytes 1-2: Battery or USB Voltage [mV], 0 if no battery probe + +**Port #9:** Time/Date + + bytes 1-4: board's local time/date in UNIX epoch (number of seconds that have elapsed since January 1, 1970 (midnight UTC/GMT), not counting leap seconds) + +**Ports #10, #11, #12:** User sensor data + + Format is specified by user in function `sensor_read(uint8_t sensor)`, see `src/sensor.cpp`. diff --git a/docs/remotecontrol.md b/docs/remotecontrol.md new file mode 100644 index 00000000..67d1d681 --- /dev/null +++ b/docs/remotecontrol.md @@ -0,0 +1,202 @@ +# Remote control + +The device listenes for remote control commands on LoRaWAN Port 2. Multiple commands per downlink are possible by concatenating them, but must not exceed a maximum of 10 bytes per downlink. + +Note: settings can be stored in NVRAM to make them persistant (reloaded during device startup / restart). To store settings, use command 0x21. + +Send for example `83` `86` as Downlink on Port 2 to get battery status and time/date from the device. + + +0x01 set scan RSSI limit + + 1 ... 255 used for wifi and bluetooth scan radius (greater values increase scan radius, values 50...110 make sense) + 0 = RSSI limiter disabled [default] + +0x02 set counter mode + + 0 = cyclic unconfirmed, mac counter reset after each wifi scan cycle, data is sent only once [default] + 1 = cumulative counter, mac counter is never reset + 2 = cyclic confirmed, like 0 but data is resent until confirmation by network received + +0x03 set GPS data on/off + + 0 = GPS data off + 1 = GPS data on, sends GPS data on port 4 (default, use port 1 for mobile pax counter), if GPS is present and has a fix + +0x04 set display on/off + + 0 = display off + 1 = display on [default] + +0x05 set LoRa datarate + + 0 ... 15 see LoRaWAN regional parameters for details [default: 5] + + Example for EU868: + + DataRate Configuration Bit/s + 0 LoRa: SF12 / 125 kHz 250 + 1 LoRa: SF11 / 125 kHz 440 + 2 LoRa: SF10 / 125 kHz 980 + 3 LoRa: SF9 / 125 kHz 1760 + 4 LoRa: SF8 / 125 kHz 3125 + 5 LoRa: SF7 / 125 kHz 5470 + 6* LoRa: SF7 / 250 kHz 11000 + 7* FSK: 50 kbps 50000 + 8 .. 14 reserved for future use (RFU) + 15 ignored (device keeps current setting) + + *) not supported by TheThingsNetwork + +0x06 set LoRa TXpower + + 0 ... 255 desired TX power in dBm [default: 14] + +0x07 set LoRa Adaptive Data Rate mode + + 0 = ADR off + 1 = ADR on [default] + + If ADR is set to off, SF value is shown inverted on display. + +0x08 do nothing + + useful to clear pending commands from LoRaWAN server quere, or to check RSSI on device + +0x09 reset functions (send this command UNconfirmed only to avoid boot loops!) + + 0 = restart device (coldstart) + 1 = (reserved, currently does nothing) + 2 = reset device to factory settings and restart device + 3 = flush send queues + 4 = restart device (warmstart) + 8 = reboot device to maintenance mode (local web server) + 9 = reboot device to OTA update via Wifi mode + +0x0A set payload send cycle + + 5 ... 255 payload send cycle in seconds/2 + e.g. 120 -> payload is transmitted each 240 seconds [default] + +0x0B set Wifi channel hopping interval timer + + 0 ... 255 duration for scanning a wifi channel in seconds/100 + e.g. 50 -> each channel is scanned for 500 milliseconds [default] + 0 means no hopping, scanning on fixed single channel WIFI_CHANNEL_1 + +0x0C set Bluetooth channel switch interval timer + + 0 ... 255 duration for scanning a bluetooth advertising channel in seconds/100 + e.g. 8 -> each channel is scanned for 80 milliseconds [default] + +0x0E set Bluetooth scanner + + 0 = disabled + 1 = enabled [default] + +0x0F set WIFI antenna switch (works on LoPy/LoPy4/FiPy only) + + 0 = internal antenna [default] + 1 = external antenna + +0x10 set RGB led luminosity (works on LoPy/LoPy4/FiPy and LoRaNode32 shield only) + + 0 ... 100 percentage of luminosity (100% = full light) + e.g. 50 -> 50% of luminosity [default] + +0x13 set user sensor mode + + byte 1 = user sensor number (1..3) + byte 2 = sensor mode (0 = disabled / 1 = enabled [default]) + +0x14 set payload mask + + byte 1 = sensor data payload mask (0..255, meaning of bits see below) + 0x01 = COUNT_DATA + 0x02 = RESERVED_DATA + 0x04 = MEMS_DATA + 0x08 = GPS_DATA + 0x10 = SENSOR_1_DATA + 0x20 = SENSOR_2_DATA + 0x40 = SENSOR_3_DATA + 0x80 = BATT_DATA + bytes can be combined eg COUNT_DATA + SENSOR_1_DATA + BATT_DATA: `0x01 | 0x10 | 0x80 = 0x91` + +0x15 set BME data on/off + + 0 = BME data off + 1 = BME data on, sends BME data on port 7 [default] + +0x16 set battery data on/off + + 0 = battery data off [default] + 1 = battery data on, sends voltage on port 8 + +0x17 set Wifi scanner + + 0 = disabled + 1 = enabled [default] + +0x18 reserved + + unused, does nothing + +0x19 set sleep cycle + + bytes 1..2 = device sleep cycle in seconds/10 (MSB), 1 ... 255 + e.g. {0x04, 0xB0} -> device sleeps 200 minutes after each send cycle [default = 0] + +0x20 load device configuration + + Current device runtime configuration will be loaded from NVRAM, replacing current settings immediately (use with care!) + +0x21 store device configuration + + Current device runtime configuration is stored in NVRAM, will be reloaded after restart + +0x80 get device configuration + + Device answers with it's current configuration on Port 3. + +0x81 get device status + + Device answers with it's current status on Port 2. + +0x83 get battery status + + Device answers with battery voltage on Port 8. + +0x84 get device GPS status + + Device answers with it's current status on Port 4. + +0x85 get BME280 / BME680 sensor data + + Device answers with BME sensor data set on Port 7. + +0x86 get time/date + + Device answers with it's current time on Port 2: + + bytes 1..4 = time/date in UTC epoch seconds (LSB) + byte 5 = time source & status, see below + + bits 0..3 time source + 0x00 = GPS + 0x01 = RTC + 0x02 = LORA + 0x03 = unsynched + 0x04 = set (source unknown) + + bits 4..7 esp32 sntp time status + 0x00 = SNTP_SYNC_STATUS_RESET + 0x01 = SNTP_SYNC_STATUS_COMPLETED + 0x02 = SNTP_SYNC_STATUS_IN_PROGRESS + +0x87 sync time/date + + Device synchronizes it's time/date by calling the preconfigured time source. + +0x88 set time/date + + bytes 1..4 = time/date to set in UTC epoch seconds (MSB, e.g. https://www.epochconverter.com/hex) diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 00000000..26e29979 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,28 @@ +site_name: ESP32-Paxcounter +theme: + name: material + features: + - navigation.tracking +nav: + - Intro: index.md + - Hardware: hardware.md + - Getting Started: installation.md + - Display: display.md + - LED: led.md + - legalnote: legalnote.md + - Additions: additions.md + - Integrations: integrations.md + - Payload Format: payloadformat.md + - Remote control: remotecontrol.md + - Licence and Credits: license-credits.md + +markdown_extensions: + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences + - attr_list + - md_in_html + +repo_url: https://github.com/cyberman54/ESP32-Paxcounter \ No newline at end of file From 0e7085ea13b6acb570fa3dd7d79ca7319a6ee7f1 Mon Sep 17 00:00:00 2001 From: Tim Huyeng Date: Sun, 20 Nov 2022 19:15:53 +0100 Subject: [PATCH 02/16] smaller updates in docs --- docs/additions.md | 16 +++++----- docs/display-led.md | 32 ++++++++++++++++++++ docs/display.md | 14 --------- docs/{installation.md => getting-started.md} | 10 ++++-- docs/integrations.md | 3 +- docs/led.md | 17 ----------- docs/legalnote.md | 13 +++++--- docs/license-credits.md | 1 + docs/payloadformat.md | 27 ++++++++--------- docs/releases.md | 3 ++ docs/remotecontrol.md | 5 +-- mkdocs.yml | 26 +++++++++++++--- 12 files changed, 99 insertions(+), 68 deletions(-) create mode 100644 docs/display-led.md delete mode 100644 docs/display.md rename docs/{installation.md => getting-started.md} (92%) create mode 100644 docs/releases.md diff --git a/docs/additions.md b/docs/additions.md index 08208b9e..045df9c7 100644 --- a/docs/additions.md +++ b/docs/additions.md @@ -17,27 +17,27 @@ Output of sensor and peripheral data is internally switched by a bitmask registe | 6 | User sensor 3 | on | | 7 | Batterylevel | off | -*) GPS data can also be combined with paxcounter payload on port 1, *#define GPSPORT 1* in paxcounter.conf to enable +\*) GPS data can also be combined with paxcounter payload on port 1, *#define GPSPORT 1* in paxcounter.conf to enable # Power saving mode -Paxcounter supports a battery friendly power saving mode. In this mode the device enters deep sleep, after all data is polled from all sensors and the dataset is completeley sent through all user configured channels (LORAWAN / SPI / MQTT / SD-Card). Set *#define SLEEPCYCLE* in paxcounter.conf to enable power saving mode and to specify the duration of a sleep cycle. Power consumption in deep sleep mode depends on your hardware, i.e. if on board peripherals can be switched off or set to a chip specific sleep mode either by MCU or by power management unit (PMU) as found on TTGO T-BEAM v1.0/V1.1. See *power.cpp* for power management, and *reset.cpp* for sleep and wakeup logic. +Paxcounter supports a battery friendly power saving mode. In this mode the device enters deep sleep, after all data is polled from all sensors and the dataset is completeley sent through all user configured channels (LORAWAN / SPI / MQTT / SD-Card). Set *#define SLEEPCYCLE* in paxcounter.conf to enable power saving mode and to specify the duration of a sleep cycle. Power consumption in deep sleep mode depends on your hardware, i.e. if on board peripherals can be switched off or set to a chip specific sleep mode either by MCU or by power management unit (PMU) as found on TTGO T-BEAM v1.0/V1.1. See [*power.cpp*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/power.cpp) for power management, and [*reset.cpp*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/reset.cpp) for sleep and wakeup logic. # Time sync -Paxcounter can keep a time-of-day synced with external or on board time sources. Set *#define TIME_SYNC_INTERVAL* in paxcounter.conf to enable time sync. Supported external time sources are GPS, LORAWAN network time and LORAWAN application timeserver time. Supported on board time sources are the RTC of ESP32 and a DS3231 RTC chip, both are kept sycned as fallback time sources. Time accuracy depends on board's time base which generates the pulse per second. Supported are GPS PPS, SQW output of RTC, and internal ESP32 hardware timer. Time base is selected by #defines in the board's hal file, see example in [**generic.h**](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/hal/generic.h). Bonus: If your LORAWAN network does not support network time, you can run a Node-Red timeserver application using the enclosed [**Timeserver code**](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/Node-RED/Timeserver.json). Configure the MQTT nodes in Node-Red for the LORAWAN application used by your paxocunter device. Time can also be set without precision liability, by simple remote command, see section remote control. +Paxcounter can keep a time-of-day synced with external or on board time sources. Set *#define TIME_SYNC_INTERVAL* in paxcounter.conf to enable time sync. Supported external time sources are GPS, LORAWAN network time and LORAWAN application timeserver time. Supported on board time sources are the RTC of ESP32 and a DS3231 RTC chip, both are kept sycned as fallback time sources. Time accuracy depends on board's time base which generates the pulse per second. Supported are GPS PPS, SQW output of RTC, and internal ESP32 hardware timer. Time base is selected by #defines in the board's hal file, see example in [*generic.h*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/hal/generic.h). Bonus: If your LORAWAN network does not support network time, you can run a Node-Red timeserver application using the enclosed [**Timeserver code**](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/Node-RED/Timeserver.json). Configure the MQTT nodes in Node-Red for the LORAWAN application used by your paxocunter device. Time can also be set without precision liability, by simple remote command, see section remote control. # Wall clock controller -Paxcounter can be used to sync a wall clock which has a DCF77 or IF482 time telegram input. Set *#define HAS_IF482* or *#define HAS_DCF77* in board's hal file to setup clock controller. Use case of this function is to integrate paxcounter and clock. Accurary of the synthetic DCF77 signal depends on accuracy of on board's time base, see above. +Paxcounter can be used to sync a wall clock which has a DCF77 or IF482 time telegram input. Set `#define HAS_IF482` or `#define HAS_DCF77` in board's hal file to setup clock controller. Use case of this function is to integrate paxcounter and clock. Accurary of the synthetic DCF77 signal depends on accuracy of on board's time base, see above. # Mobile PaxCounter using openSenseMap -This describes how to set up a mobile PaxCounter:
Follow all steps so far for preparing the device, selecting the packed payload format. In `paxcounter.conf` set PAYLOAD_OPENSENSEBOX to 1. Register a new sensebox on https://opensensemap.org/. In the sensor configuration select "TheThingsNetwork" and set decoding profile to "LoRa serialization". Enter your TTN Application and Device ID. Setup decoding option using `[{"decoder":"latLng"},{"decoder":"uint16",sensor_id":"yoursensorid"}]` +This describes how to set up a mobile PaxCounter:
Follow all steps so far for preparing the device, selecting the packed payload format. In `paxcounter.conf` set `PAYLOAD_OPENSENSEBOX` to `1`. Register a new sensebox on [https://opensensemap.org/](https://opensensemap.org). In the sensor configuration select "TheThingsNetwork" and set decoding profile to "LoRa serialization". Enter your TTN Application and Device ID. Setup decoding option using `[{"decoder":"latLng"},{"decoder":"uint16",sensor_id":"yoursensorid"}]`. # SD-card -Data can be stored on SD-card if the board provides an SD card interface, either with SPI or MMC mode. To enable this feature, specify interface mode and hardware pins in board's hal file (src/hal/): +Data can be stored on SD-card if the board provides an SD card interface, either with SPI or MMC mode. To enable this feature, specify interface mode and hardware pins in board's hal file (src/hal/): #define HAS_SDCARD 1 // SD-card interface, using SPI mode OR @@ -56,7 +56,7 @@ In this case you choose the correct file for your ESP32-board in the src/hal-dir Data is written on SD-card to a single file. After 3 write operations the data is flushed to the disk to minimize flash write cycles. Thus, up to the last 3 records of data will get lost when the PAXCOUNTER looses power during operation. -Format of the resulting file is CSV, thus easy import in LibreOffice, Excel, Influx, etc. Each record contains timestamp (in ISO8601 format), paxcount (wifi and ble) and battery voltage (optional). Voltage is logged if the device has a battery voltage sensor (to be configured in board hal file). +Format of the resulting file is CSV, thus easy import in LibreOffice, Excel, InfluxDB, etc. Each record contains timestamp (in ISO8601 format), paxcount (wifi and ble) and battery voltage (optional). Voltage is logged if the device has a battery voltage sensor (to be configured in board hal file). File contents example: ```csv @@ -66,4 +66,4 @@ File contents example: 2022-01-30T21:16:08Z,12,26[,4102] 2022-01-30T21:17:52Z,11,26[,4076] ``` -If you want to change this, modify src/sdcard.cpp and include/sdcard.h. \ No newline at end of file +If you want to change this, modify `src/sdcard.cpp` and `include/sdcard.h`. \ No newline at end of file diff --git a/docs/display-led.md b/docs/display-led.md new file mode 100644 index 00000000..3f0f5631 --- /dev/null +++ b/docs/display-led.md @@ -0,0 +1,32 @@ +# Display + +![Display Image](img/Paxcounter-Screen.png) + +If you're using a device with OLED display, or if you add such one to the I2C bus, the device shows live data on the display. You can flip display pages showing + +- recent count of pax +- histogram +- GPS data +- BME sensor data +- time of day +- blank page + +by pressing the button of the device. + +# LED blink pattern + +**Mono color LED:** + +- Single Flash (50ms): seen a new Wifi or BLE device +- Quick blink (20ms on each 1/5 second): joining LoRaWAN network in progress or pending +- Small blink (10ms on each 1/2 second): LoRaWAN data transmit in progress or pending +- Long blink (200ms on each 2 seconds): LoRaWAN stack error + +**RGB LED:** + +- Green: seen a new Wifi device +- Magenta: seen a new BLE device +- Yellow: joining LoRaWAN network in progress or pending +- Pink: LORAWAN MAC transmit in progress +- Blue: LoRaWAN data transmit in progress or pending +- Red: LoRaWAN stack errors \ No newline at end of file diff --git a/docs/display.md b/docs/display.md deleted file mode 100644 index b564bdf3..00000000 --- a/docs/display.md +++ /dev/null @@ -1,14 +0,0 @@ -# Display - -![Display Image](img/Paxcounter-Screen.png) - -If you're using a device with OLED display, or if you add such one to the I2C bus, the device shows live data on the display. You can flip display pages showing - -- recent count of pax -- histogram -- GPS data -- BME sensor data -- time of day -- blank page - -by pressing the button of the device. \ No newline at end of file diff --git a/docs/installation.md b/docs/getting-started.md similarity index 92% rename from docs/installation.md rename to docs/getting-started.md index 2741bb4f..b0801e2e 100644 --- a/docs/installation.md +++ b/docs/getting-started.md @@ -2,12 +2,16 @@ ## Install Platformio -Install PlatformIO IDE for embedded development to make this project. Platformio integrates with your favorite IDE, choose eg. Visual Studio, Atom, Eclipse etc. +Install PlatformIO IDE for embedded development to build this project. Platformio integrates with your favorite IDE, choose e.g. Visual Studio Code, Atom, Eclipse etc. Compile time configuration is spread across several files. Before compiling the code, edit or create the following files: ## platformio.ini -Edit `platformio_orig.ini` (for ESP32 CPU based boards) *or* `platformio_orig_s3.ini` (for ESP32-S3 CPU based boards) and select desired board in section **board**. To add a new board, create an appropriate hardware abstraction layer file in hal subdirectory, and add a pointer to this file in section **board**. Copy or rename to `platformio.ini` in the root directory of the project. Now start Platformio. Note: Platformio is looking for `platformio.ini` in the root directory and won't start if it does not find this file! +Edit `platformio_orig.ini` (for ESP32 CPU based boards) *or* `platformio_orig_s3.ini` (for ESP32-S3 CPU based boards) and select desired board in section **board**. To add a new board, create an appropriate hardware abstraction layer file in hal subdirectory, and add a pointer to this file in section **board**. Copy or rename to `platformio.ini` in the root directory of the project. Now start Platformio. + +!!! info + + Note: Platformio is looking for `platformio.ini` in the root directory and won't start if it does not find this file! ## paxcounter.conf Edit `src/paxcounter_orig.conf` and tailor settings in this file according to your needs and use case. Please take care of the duty cycle regulations of the LoRaWAN network you're going to use. Copy or rename to `src/paxcounter.conf`. @@ -24,7 +28,7 @@ To configure OTAA, leave `#define LORA_ABP` deactivated (commented). To use ABP, The file `src/loraconf_sample.h` contains more information about the values to provide. ## src/ota.conf -Create file `src/ota.conf` using the template [src/ota.sample.conf](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/ota.sample.conf) and enter your WIFI network&key. These settings are used for downloading updates via WiFi, either from a remote https server, or locally via WebUI. If you want to use a remote server, you need a PAX.express repository. Enter your PAX.express credentials in ota.conf. If you don't need wireless firmware updates just rename ota.sample.conf to ota.conf. +Create file `src/ota.conf` using the template [src/ota_sample.conf](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/ota_sample.conf) and enter your WIFI network&key. These settings are used for downloading updates via WiFi, either from a remote https server, or locally via WebUI. If you want to use a remote server, you need a PAX.express repository. Enter your PAX.express credentials in ota.conf. If you don't need wireless firmware updates just rename ota.sample.conf to ota.conf. # Building diff --git a/docs/integrations.md b/docs/integrations.md index faf8c743..191cfdb6 100644 --- a/docs/integrations.md +++ b/docs/integrations.md @@ -1,6 +1,7 @@ # Integration into "The Things Stack Community Edition" aka "The Things Stack V3" To use the ESP32-Paxcounter in The Things Stack Community Edition you need an account to reach the console. Go to: + - [The Things Stack Community Edition Console](https://console.cloud.thethings.network/) - choose your region and go to applications - create an application by clicking "**+ Add application**" and give it a id, name, etc. @@ -13,4 +14,4 @@ The "Repository" payload decoder uses the packed format, explained below. If you # TTN Mapper -If you want your devices to be feeding the [TTN Mapper](https://ttnmapper.org/), just follow this manual: https://docs.ttnmapper.org/integration/tts-integration-v3.html - different than indicated in the manual you can leave the payload decoder to "Repository" for the ESP32-Paxcounter and you are fine. +If you want your devices to be feeding the [TTN Mapper](https://ttnmapper.org/), just follow this manual: [https://docs.ttnmapper.org/integration/tts-integration-v3.html](https://docs.ttnmapper.org/integration/tts-integration-v3.html) - different than indicated in the manual you can leave the payload decoder to "Repository" for the ESP32-Paxcounter and you are fine. diff --git a/docs/led.md b/docs/led.md index 09f49c0f..e69de29b 100644 --- a/docs/led.md +++ b/docs/led.md @@ -1,17 +0,0 @@ -# LED blink pattern - -**Mono color LED:** - -- Single Flash (50ms): seen a new Wifi or BLE device -- Quick blink (20ms on each 1/5 second): joining LoRaWAN network in progress or pending -- Small blink (10ms on each 1/2 second): LoRaWAN data transmit in progress or pending -- Long blink (200ms on each 2 seconds): LoRaWAN stack error - -**RGB LED:** - -- Green: seen a new Wifi device -- Magenta: seen a new BLE device -- Yellow: joining LoRaWAN network in progress or pending -- Pink: LORAWAN MAC transmit in progress -- Blue: LoRaWAN data transmit in progress or pending -- Red: LoRaWAN stack errors \ No newline at end of file diff --git a/docs/legalnote.md b/docs/legalnote.md index 27722abf..790551db 100644 --- a/docs/legalnote.md +++ b/docs/legalnote.md @@ -2,15 +2,18 @@ **Depending on your country's laws it may be illegal to sniff wireless networks for MAC addresses. Please check and respect your country's laws before using this code!** -(e.g. US citizens may want to check [Section 18 U.S. Code § 2511](https://www.law.cornell.edu/uscode/text/18/2511) and [discussion](https://github.com/schollz/howmanypeoplearearound/issues/4) on this) +- (e.g. US citizens may want to check [Section 18 U.S. Code § 2511](https://www.law.cornell.edu/uscode/text/18/2511) and [discussion](https://github.com/schollz/howmanypeoplearearound/issues/4) on this) -(e.g. UK citizens may want to check [Data Protection Act 1998](https://ico.org.uk/media/1560691/wi-fi-location-analytics-guidance.pdf) and [GDPR 2018](https://ico.org.uk/for-organisations/guide-to-the-general-data-protection-regulation-gdpr/key-definitions/)) +- (e.g. UK citizens may want to check [Data Protection Act 1998](https://ico.org.uk/media/1560691/wi-fi-location-analytics-guidance.pdf) and [GDPR 2018](https://ico.org.uk/for-organisations/guide-to-the-general-data-protection-regulation-gdpr/key-definitions/)) -(e.g. Citizens in the the Netherlands and EU may want to read [this article](https://www.ivir.nl/publicaties/download/PrivacyInformatie_2016_6.pdf) and [this article](https://autoriteitpersoonsgegevens.nl/nl/nieuws/europese-privacytoezichthouders-publiceren-opinie-eprivacyverordening)) and [this decision](https://edpb.europa.eu/news/national-news/2021/dutch-dpa-fines-municipality-wi-fi-tracking_en) +- (e.g. Citizens in the the Netherlands and EU may want to read [this article](https://www.ivir.nl/publicaties/download/PrivacyInformatie_2016_6.pdf) and [this article](https://autoriteitpersoonsgegevens.nl/nl/nieuws/europese-privacytoezichthouders-publiceren-opinie-eprivacyverordening) and [this decision](https://edpb.europa.eu/news/national-news/2021/dutch-dpa-fines-municipality-wi-fi-tracking_en)) -(e.g. Citizens in Germany may want to read [this article of Wissenschaftliche Dienste des Deutschen Bundestages](https://www.bundestag.de/resource/blob/538890/3dfae197d2c930693aa16d1619204f58/WD-3-206-17-pdf-data.pdf) +- (e.g. Citizens in Germany may want to read [this article of Wissenschaftliche Dienste des Deutschen Bundestages](https://www.bundestag.de/resource/blob/538890/3dfae197d2c930693aa16d1619204f58/WD-3-206-17-pdf-data.pdf)) -Note: If you use this software you do this at your own risk. That means that you alone - not the authors of this software - are responsible for the legal compliance of an application using this or build from this software and/or usage of a device created using this software. You should take special care and get prior legal advice if you plan metering passengers in public areas and/or publish data drawn from doing so. + +!!! info + + Note: If you use this software you do this at your own risk. That means that you alone - not the authors of this software - are responsible for the legal compliance of an application using this or build from this software and/or usage of a device created using this software. You should take special care and get prior legal advice if you plan metering passengers in public areas and/or publish data drawn from doing so. # Privacy disclosure diff --git a/docs/license-credits.md b/docs/license-credits.md index 8b75fbe1..bfb261a5 100644 --- a/docs/license-credits.md +++ b/docs/license-credits.md @@ -23,6 +23,7 @@ see file + +![Remote Control](img/paxcounter_downlink_example.png) 0x01 set scan RSSI limit diff --git a/mkdocs.yml b/mkdocs.yml index 26e29979..153d5871 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -3,18 +3,33 @@ theme: name: material features: - navigation.tracking + palette: + # Palette toggle for light mode + - media: "(prefers-color-scheme: light)" + scheme: default + toggle: + icon: material/brightness-7 + name: Switch to dark mode + + # Palette toggle for dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + toggle: + icon: material/brightness-4 + name: Switch to system preference + primary: indigo nav: - Intro: index.md - Hardware: hardware.md - - Getting Started: installation.md - - Display: display.md - - LED: led.md - - legalnote: legalnote.md + - Getting Started: getting-started.md + - Display & LED: display-led.md + - Legalnote: legalnote.md - Additions: additions.md - Integrations: integrations.md - Payload Format: payloadformat.md - Remote control: remotecontrol.md - Licence and Credits: license-credits.md + - Releases: releases.md markdown_extensions: - pymdownx.highlight: @@ -24,5 +39,8 @@ markdown_extensions: - pymdownx.superfences - attr_list - md_in_html + - admonition + - pymdownx.details + - pymdownx.superfences repo_url: https://github.com/cyberman54/ESP32-Paxcounter \ No newline at end of file From e17bd59f587012839eddef31f113b0bcf9f9d92a Mon Sep 17 00:00:00 2001 From: Tim Huyeng Date: Sun, 20 Nov 2022 20:18:13 +0100 Subject: [PATCH 03/16] add github workflow for docs --- .github/workflows/docs.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..01942ac9 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,16 @@ +name: publish-docs +on: + push: + branches: + - master + - mkdocs +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + with: + python-version: 3.x + - run: pip install mkdocs-material + - run: mkdocs gh-deploy --force From ccfc20a1404b709b353e69ccb168c0f791e3e68d Mon Sep 17 00:00:00 2001 From: Tim Huyeng Date: Sun, 20 Nov 2022 21:23:43 +0100 Subject: [PATCH 04/16] small docs updates --- docs/getting-started.md | 17 ++++++++++++++++- docs/hardware.md | 29 +++++++++++++++++------------ docs/index.md | 3 ++- docs/legalnote.md | 2 +- docs/remotecontrol.md | 23 ++++++++++++----------- mkdocs.yml | 4 ++-- 6 files changed, 50 insertions(+), 28 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index b0801e2e..2012f629 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -9,13 +9,21 @@ Compile time configuration is spread across several files. Before compiling the ## platformio.ini Edit `platformio_orig.ini` (for ESP32 CPU based boards) *or* `platformio_orig_s3.ini` (for ESP32-S3 CPU based boards) and select desired board in section **board**. To add a new board, create an appropriate hardware abstraction layer file in hal subdirectory, and add a pointer to this file in section **board**. Copy or rename to `platformio.ini` in the root directory of the project. Now start Platformio. +``` bash title="Copy with terminal" +cp platformio_orig.ini platformio.ini +``` + !!! info - Note: Platformio is looking for `platformio.ini` in the root directory and won't start if it does not find this file! + Platformio is looking for `platformio.ini` in the root directory and won't start if it does not find this file! ## paxcounter.conf Edit `src/paxcounter_orig.conf` and tailor settings in this file according to your needs and use case. Please take care of the duty cycle regulations of the LoRaWAN network you're going to use. Copy or rename to `src/paxcounter.conf`. +``` bash title="Copy with terminal" +cp src/paxcounter_orig.conf src/paxcounter.conf +``` + If your device has a **real time clock** it can be updated by either LoRaWAN network or GPS time, according to settings *TIME_SYNC_INTERVAL* and *TIME_SYNC_LORAWAN* in `paxcounter.conf`. ## src/lmic_config.h @@ -24,12 +32,19 @@ Edit `src/lmic_config.h` and tailor settings in this file according to your coun ## src/loraconf.h Create file `src/loraconf.h` using the template [src/loraconf_sample.h](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/loraconf_sample.h) and adjust settings to use your personal values. To join the network and activate your paxcounter, you must configure either OTAA or ABP join method. You should use OTAA, whenever possible. To understand the differences of the two methods, [this article](https://www.thethingsnetwork.org/docs/devices/registration.html) may be useful. +``` bash title="Copy with terminal" +cp src/loraconf_sample.h src/loraconf.h +``` + To configure OTAA, leave `#define LORA_ABP` deactivated (commented). To use ABP, activate (uncomment) `#define LORA_ABP` in the file `src/loraconf.h`. The file `src/loraconf_sample.h` contains more information about the values to provide. ## src/ota.conf Create file `src/ota.conf` using the template [src/ota_sample.conf](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/ota_sample.conf) and enter your WIFI network&key. These settings are used for downloading updates via WiFi, either from a remote https server, or locally via WebUI. If you want to use a remote server, you need a PAX.express repository. Enter your PAX.express credentials in ota.conf. If you don't need wireless firmware updates just rename ota.sample.conf to ota.conf. +``` bash title="Copy with terminal" +cp src/ota_sample.conf src/ota.conf +``` # Building Use PlatformIO with your preferred IDE for development and building this code. Make sure you have latest PlatformIO version. diff --git a/docs/hardware.md b/docs/hardware.md index 69380cb0..f61b614e 100644 --- a/docs/hardware.md +++ b/docs/hardware.md @@ -27,11 +27,12 @@ LoLin32lite + [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-L *) supports microSD/TF-card for local logging of paxcounter data Depending on board hardware following features are supported: + - LoRaWAN communication, supporting various payload formats (see enclosed .js converters) - MQTT communication via TCP/IP and Ethernet interface (note: payload transmitted over MQTT will be base64 encoded) - SPI serial communication to a local host -- LED (shows power & status) -- OLED Display (shows detailed status) +- [LED](display-led.md) (shows power & status) +- [OLED Display](display-led.md) (shows detailed status) - RGB LED (shows colorized status) - Button (short press: flip display page / long press: send alarm message) - Battery voltage monitoring (analog read / AXP192 / IP5306) @@ -46,14 +47,18 @@ Depending on board hardware following features are supported: Target platform must be selected in `platformio.ini`.
Hardware dependent settings (pinout etc.) are stored in board files in [/hal](https://github.com/cyberman54/ESP32-Paxcounter/tree/master/src/hal) directory. If you want to use a ESP32 board which is not yet supported, use hal file generic.h and tailor pin mappings to your needs. Pull requests for new boards welcome.
-Some 3D printable cases can be found (and, if wanted so, ordered) on Thingiverse, see -Heltec, -TTGOv2, -TTGOv2.1, -TTGO, -T-BEAM, -T-BEAM parts, -for example.
+### 3D printed cases +Some 3D printable cases can be found (and, if wanted so, ordered) on Thingiverse, see -Power consumption was metered at around 450 - 1000mW, depending on board and user settings in paxcounter.conf. -By default bluetooth sniffing not installed (#define *BLECOUNTER* 0 in paxcounter.conf). If you enable bluetooth be aware that this goes on expense of wifi sniffing results, because then wifi and bt stack must share the 2,4 GHz RF ressources of ESP32. If you need to sniff wifi and bt in parallel and need best possible results, use two boards - one for wifi only and one for bt only - and add counted results. +- Heltec +- TTGOv2 +- TTGOv2.1 +- TTGO +- T-BEAM +- T-BEAM parts + + +### Power consumption + +Power consumption was metered at around 450 - 1000mW, depending on board and user settings in `paxcounter.conf`. +By default, bluetooth sniffing not installed (`#define *BLECOUNTER* 0` in `paxcounter.conf`). If you enable bluetooth be aware that this goes on expense of wifi sniffing results, because then wifi and bt stack must share the 2,4 GHz RF ressources of ESP32. If you need to sniff wifi and bt in parallel and need best possible results, use two boards - one for wifi only and one for bt only - and add counted results. diff --git a/docs/index.md b/docs/index.md index 37bafd67..3a2cdad7 100644 --- a/docs/index.md +++ b/docs/index.md @@ -21,7 +21,8 @@ Paxcounter is an [ESP32](https://www.espressif.com/en/products/socs/esp32) MCU based device for metering passenger flows in realtime. It counts how many mobile devices are around. This gives an estimation how many people are around. Paxcounter detects Wifi and Bluetooth signals in the air, focusing on mobile devices by evaluating their MAC adresses. -Intention of this project is to do this without intrusion in privacy: You don't need to track people owned devices, if you just want to count them. Therefore, Paxcounter does not persistenly store MAC adresses and does no kind of fingerprinting the scanned devices. +!!! info + Intention of this project is to do this without intrusion in privacy: You don't need to track people owned devices, if you just want to count them. Therefore, Paxcounter does not persistenly store MAC adresses and *does no kind of fingerprinting the scanned devices. Data can either be be stored on a local SD-card, transferred to cloud using LoRaWAN network (e.g. TheThingsNetwork or Helium) or MQTT over TCP/IP, or transmitted to a local host using serial (SPI) interface. diff --git a/docs/legalnote.md b/docs/legalnote.md index 790551db..58bbaa66 100644 --- a/docs/legalnote.md +++ b/docs/legalnote.md @@ -13,7 +13,7 @@ !!! info - Note: If you use this software you do this at your own risk. That means that you alone - not the authors of this software - are responsible for the legal compliance of an application using this or build from this software and/or usage of a device created using this software. You should take special care and get prior legal advice if you plan metering passengers in public areas and/or publish data drawn from doing so. + If you use this software you do this at your own risk. That means that you alone - not the authors of this software - are responsible for the legal compliance of an application using this or build from this software and/or usage of a device created using this software. You should take special care and get prior legal advice if you plan metering passengers in public areas and/or publish data drawn from doing so. # Privacy disclosure diff --git a/docs/remotecontrol.md b/docs/remotecontrol.md index 51c60181..4dc83863 100644 --- a/docs/remotecontrol.md +++ b/docs/remotecontrol.md @@ -2,7 +2,8 @@ The device listenes for remote control commands on LoRaWAN Port 2. Multiple commands per downlink are possible by concatenating them, but must not exceed a maximum of 10 bytes per downlink. -Note: settings can be stored in NVRAM to make them persistant (reloaded during device startup / restart). To store settings, use command `0x21`. +!!! info + Settings can be stored in NVRAM to make them persistant (reloaded during device startup / restart). To store settings, use command `0x21`. Send for example `83` `86` as Downlink on Port 2 to get battery status and time/date from the device. @@ -35,17 +36,17 @@ Send for example `83` `86` as Downlink on Port 2 to get battery status and time/ Example for EU868: - DataRate Configuration Bit/s - 0 LoRa: SF12 / 125 kHz 250 - 1 LoRa: SF11 / 125 kHz 440 - 2 LoRa: SF10 / 125 kHz 980 - 3 LoRa: SF9 / 125 kHz 1760 - 4 LoRa: SF8 / 125 kHz 3125 - 5 LoRa: SF7 / 125 kHz 5470 - 6* LoRa: SF7 / 250 kHz 11000 - 7* FSK: 50 kbps 50000 + DataRate Configuration Bit/s + 0 LoRa: SF12 / 125 kHz 250 + 1 LoRa: SF11 / 125 kHz 440 + 2 LoRa: SF10 / 125 kHz 980 + 3 LoRa: SF9 / 125 kHz 1760 + 4 LoRa: SF8 / 125 kHz 3125 + 5 LoRa: SF7 / 125 kHz 5470 + 6* LoRa: SF7 / 250 kHz 11000 + 7* FSK: 50 kbps 50000 8 .. 14 reserved for future use (RFU) - 15 ignored (device keeps current setting) + 15 ignored (device keeps current setting) *) not supported by TheThingsNetwork diff --git a/mkdocs.yml b/mkdocs.yml index 153d5871..fd2000c1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -23,12 +23,12 @@ nav: - Hardware: hardware.md - Getting Started: getting-started.md - Display & LED: display-led.md - - Legalnote: legalnote.md + - Legal note: legalnote.md - Additions: additions.md - Integrations: integrations.md - Payload Format: payloadformat.md - Remote control: remotecontrol.md - - Licence and Credits: license-credits.md + - Licence & Credits: license-credits.md - Releases: releases.md markdown_extensions: From ce6c0f3bc476bda121682f980080b012730dcb23 Mon Sep 17 00:00:00 2001 From: Tim Huyeng Date: Sun, 20 Nov 2022 21:48:15 +0100 Subject: [PATCH 05/16] small updates for better `Table of contents` --- docs/additions.md | 12 ++++---- docs/hardware.md | 2 +- docs/remotecontrol.md | 66 +++++++++++++++++++++---------------------- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/docs/additions.md b/docs/additions.md index 045df9c7..a711986d 100644 --- a/docs/additions.md +++ b/docs/additions.md @@ -1,4 +1,4 @@ -# Sensors and Peripherals +## Sensors and Peripherals You can add up to 3 user defined sensors. Insert your sensor's payload scheme in [*sensor.cpp*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/sensor.cpp). Bosch BMP180 / BME280 / BME680 environment sensors are supported, to activate configure BME in board's hal file before build. Furthermore, SDS011, RTC DS3231, generic serial NMEA GPS, I2C LoPy GPS are supported, and to be configured in board's hal file. See [*generic.h*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/hal/generic.h) for all options and for proper configuration of BME280/BME680. @@ -19,23 +19,23 @@ Output of sensor and peripheral data is internally switched by a bitmask registe \*) GPS data can also be combined with paxcounter payload on port 1, *#define GPSPORT 1* in paxcounter.conf to enable -# Power saving mode +## Power saving mode Paxcounter supports a battery friendly power saving mode. In this mode the device enters deep sleep, after all data is polled from all sensors and the dataset is completeley sent through all user configured channels (LORAWAN / SPI / MQTT / SD-Card). Set *#define SLEEPCYCLE* in paxcounter.conf to enable power saving mode and to specify the duration of a sleep cycle. Power consumption in deep sleep mode depends on your hardware, i.e. if on board peripherals can be switched off or set to a chip specific sleep mode either by MCU or by power management unit (PMU) as found on TTGO T-BEAM v1.0/V1.1. See [*power.cpp*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/power.cpp) for power management, and [*reset.cpp*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/reset.cpp) for sleep and wakeup logic. -# Time sync +## Time sync Paxcounter can keep a time-of-day synced with external or on board time sources. Set *#define TIME_SYNC_INTERVAL* in paxcounter.conf to enable time sync. Supported external time sources are GPS, LORAWAN network time and LORAWAN application timeserver time. Supported on board time sources are the RTC of ESP32 and a DS3231 RTC chip, both are kept sycned as fallback time sources. Time accuracy depends on board's time base which generates the pulse per second. Supported are GPS PPS, SQW output of RTC, and internal ESP32 hardware timer. Time base is selected by #defines in the board's hal file, see example in [*generic.h*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/hal/generic.h). Bonus: If your LORAWAN network does not support network time, you can run a Node-Red timeserver application using the enclosed [**Timeserver code**](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/Node-RED/Timeserver.json). Configure the MQTT nodes in Node-Red for the LORAWAN application used by your paxocunter device. Time can also be set without precision liability, by simple remote command, see section remote control. -# Wall clock controller +## Wall clock controller Paxcounter can be used to sync a wall clock which has a DCF77 or IF482 time telegram input. Set `#define HAS_IF482` or `#define HAS_DCF77` in board's hal file to setup clock controller. Use case of this function is to integrate paxcounter and clock. Accurary of the synthetic DCF77 signal depends on accuracy of on board's time base, see above. -# Mobile PaxCounter using openSenseMap +## Mobile PaxCounter using openSenseMap This describes how to set up a mobile PaxCounter:
Follow all steps so far for preparing the device, selecting the packed payload format. In `paxcounter.conf` set `PAYLOAD_OPENSENSEBOX` to `1`. Register a new sensebox on [https://opensensemap.org/](https://opensensemap.org). In the sensor configuration select "TheThingsNetwork" and set decoding profile to "LoRa serialization". Enter your TTN Application and Device ID. Setup decoding option using `[{"decoder":"latLng"},{"decoder":"uint16",sensor_id":"yoursensorid"}]`. -# SD-card +## SD-card Data can be stored on SD-card if the board provides an SD card interface, either with SPI or MMC mode. To enable this feature, specify interface mode and hardware pins in board's hal file (src/hal/): diff --git a/docs/hardware.md b/docs/hardware.md index f61b614e..1e691864 100644 --- a/docs/hardware.md +++ b/docs/hardware.md @@ -1,6 +1,6 @@ # Hardware -**Supported ESP32 based boards**: +### Supported ESP32 based boards: *With LoRa radio data transfer*: diff --git a/docs/remotecontrol.md b/docs/remotecontrol.md index 4dc83863..cddb9719 100644 --- a/docs/remotecontrol.md +++ b/docs/remotecontrol.md @@ -9,28 +9,28 @@ Send for example `83` `86` as Downlink on Port 2 to get battery status and time/ ![Remote Control](img/paxcounter_downlink_example.png) -0x01 set scan RSSI limit +#### 0x01 set scan RSSI limit 1 ... 255 used for wifi and bluetooth scan radius (greater values increase scan radius, values 50...110 make sense) 0 = RSSI limiter disabled [default] -0x02 set counter mode +#### 0x02 set counter mode 0 = cyclic unconfirmed, mac counter reset after each wifi scan cycle, data is sent only once [default] 1 = cumulative counter, mac counter is never reset 2 = cyclic confirmed, like 0 but data is resent until confirmation by network received -0x03 set GPS data on/off +#### 0x03 set GPS data on/off 0 = GPS data off 1 = GPS data on, sends GPS data on port 4 (default, use port 1 for mobile pax counter), if GPS is present and has a fix -0x04 set display on/off +#### 0x04 set display on/off 0 = display off 1 = display on [default] -0x05 set LoRa datarate +#### 0x05 set LoRa datarate 0 ... 15 see LoRaWAN regional parameters for details [default: 5] @@ -50,22 +50,22 @@ Send for example `83` `86` as Downlink on Port 2 to get battery status and time/ *) not supported by TheThingsNetwork -0x06 set LoRa TXpower +#### 0x06 set LoRa TXpower 0 ... 255 desired TX power in dBm [default: 14] -0x07 set LoRa Adaptive Data Rate mode +#### 0x07 set LoRa Adaptive Data Rate mode 0 = ADR off 1 = ADR on [default] If ADR is set to off, SF value is shown inverted on display. -0x08 do nothing +#### 0x08 do nothing useful to clear pending commands from LoRaWAN server quere, or to check RSSI on device -0x09 reset functions (send this command UNconfirmed only to avoid boot loops!) +#### 0x09 reset functions (send this command UNconfirmed only to avoid boot loops!) 0 = restart device (coldstart) 1 = (reserved, currently does nothing) @@ -75,43 +75,43 @@ Send for example `83` `86` as Downlink on Port 2 to get battery status and time/ 8 = reboot device to maintenance mode (local web server) 9 = reboot device to OTA update via Wifi mode -0x0A set payload send cycle +#### 0x0A set payload send cycle 5 ... 255 payload send cycle in seconds/2 e.g. 120 -> payload is transmitted each 240 seconds [default] -0x0B set Wifi channel hopping interval timer +#### 0x0B set Wifi channel hopping interval timer 0 ... 255 duration for scanning a wifi channel in seconds/100 e.g. 50 -> each channel is scanned for 500 milliseconds [default] 0 means no hopping, scanning on fixed single channel WIFI_CHANNEL_1 -0x0C set Bluetooth channel switch interval timer +#### 0x0C set Bluetooth channel switch interval timer 0 ... 255 duration for scanning a bluetooth advertising channel in seconds/100 e.g. 8 -> each channel is scanned for 80 milliseconds [default] -0x0E set Bluetooth scanner +#### 0x0E set Bluetooth scanner 0 = disabled 1 = enabled [default] -0x0F set WIFI antenna switch (works on LoPy/LoPy4/FiPy only) +#### 0x0F set WIFI antenna switch (works on LoPy/LoPy4/FiPy only) 0 = internal antenna [default] 1 = external antenna -0x10 set RGB led luminosity (works on LoPy/LoPy4/FiPy and LoRaNode32 shield only) +#### 0x10 set RGB led luminosity (works on LoPy/LoPy4/FiPy and LoRaNode32 shield only) 0 ... 100 percentage of luminosity (100% = full light) e.g. 50 -> 50% of luminosity [default] -0x13 set user sensor mode +#### 0x13 set user sensor mode byte 1 = user sensor number (1..3) byte 2 = sensor mode (0 = disabled / 1 = enabled [default]) -0x14 set payload mask +#### 0x14 set payload mask byte 1 = sensor data payload mask (0..255, meaning of bits see below) 0x01 = COUNT_DATA @@ -124,59 +124,59 @@ Send for example `83` `86` as Downlink on Port 2 to get battery status and time/ 0x80 = BATT_DATA bytes can be combined eg COUNT_DATA + SENSOR_1_DATA + BATT_DATA: `0x01 | 0x10 | 0x80 = 0x91` -0x15 set BME data on/off +#### 0x15 set BME data on/off 0 = BME data off 1 = BME data on, sends BME data on port 7 [default] -0x16 set battery data on/off +#### 0x16 set battery data on/off 0 = battery data off [default] 1 = battery data on, sends voltage on port 8 -0x17 set Wifi scanner +#### 0x17 set Wifi scanner 0 = disabled 1 = enabled [default] -0x18 reserved +#### 0x18 reserved unused, does nothing -0x19 set sleep cycle +#### 0x19 set sleep cycle bytes 1..2 = device sleep cycle in seconds/10 (MSB), 1 ... 255 e.g. {0x04, 0xB0} -> device sleeps 200 minutes after each send cycle [default = 0] -0x20 load device configuration +#### 0x20 load device configuration Current device runtime configuration will be loaded from NVRAM, replacing current settings immediately (use with care!) -0x21 store device configuration +#### 0x21 store device configuration Current device runtime configuration is stored in NVRAM, will be reloaded after restart -0x80 get device configuration +#### 0x80 get device configuration Device answers with it's current configuration on Port 3. -0x81 get device status +#### 0x81 get device status Device answers with it's current status on Port 2. -0x83 get battery status +#### 0x83 get battery status Device answers with battery voltage on Port 8. -0x84 get device GPS status +#### 0x84 get device GPS status Device answers with it's current status on Port 4. -0x85 get BME280 / BME680 sensor data +#### 0x85 get BME280 / BME680 sensor data Device answers with BME sensor data set on Port 7. -0x86 get time/date +#### 0x86 get time/date Device answers with it's current time on Port 2: @@ -195,10 +195,10 @@ Send for example `83` `86` as Downlink on Port 2 to get battery status and time/ 0x01 = SNTP_SYNC_STATUS_COMPLETED 0x02 = SNTP_SYNC_STATUS_IN_PROGRESS -0x87 sync time/date +#### 0x87 sync time/date Device synchronizes it's time/date by calling the preconfigured time source. -0x88 set time/date +#### 0x88 set time/date - bytes 1..4 = time/date to set in UTC epoch seconds (MSB, e.g. https://www.epochconverter.com/hex) + bytes 1..4 = time/date to set in UTC epoch seconds (MSB, e.g. https://www.epochconverter.com/hex) \ No newline at end of file From 108044c184147c6be4e39941f024024d18618a62 Mon Sep 17 00:00:00 2001 From: Tim Huyeng Date: Mon, 21 Nov 2022 11:01:04 +0100 Subject: [PATCH 06/16] used instand navigation for SPA feeling --- mkdocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs.yml b/mkdocs.yml index fd2000c1..c4ec04b3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -2,6 +2,7 @@ site_name: ESP32-Paxcounter theme: name: material features: + - navigation.instant - navigation.tracking palette: # Palette toggle for light mode From cbc0e65db568747da7f07501dd7ca446b491f910 Mon Sep 17 00:00:00 2001 From: Tim Huyeng Date: Mon, 21 Nov 2022 15:08:53 +0100 Subject: [PATCH 07/16] added first logo draft --- docs/assets/paxcounter_logo_dark.bmp | Bin 0 -> 789710 bytes docs/assets/paxcounter_logo_white.bmp | Bin 0 -> 789710 bytes docs/assets/paxcounter_logo_white.svg | 48 ++++++++++++++++++++++++++ mkdocs.yml | 3 +- 4 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 docs/assets/paxcounter_logo_dark.bmp create mode 100644 docs/assets/paxcounter_logo_white.bmp create mode 100644 docs/assets/paxcounter_logo_white.svg diff --git a/docs/assets/paxcounter_logo_dark.bmp b/docs/assets/paxcounter_logo_dark.bmp new file mode 100644 index 0000000000000000000000000000000000000000..40e3ced70cbaafe25d031cb838e841c53bf6b514 GIT binary patch literal 789710 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 + + + + + PA co ter + + + + + + + + diff --git a/mkdocs.yml b/mkdocs.yml index c4ec04b3..24a3c7f6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,6 +1,7 @@ -site_name: ESP32-Paxcounter +site_name: Docs theme: name: material + logo: assets/paxcounter_logo_white.svg features: - navigation.instant - navigation.tracking From cec7e1916f8732b1cccd0d543186d3c6a6725159 Mon Sep 17 00:00:00 2001 From: Tim Huyeng Date: Mon, 21 Nov 2022 18:36:58 +0100 Subject: [PATCH 08/16] added some code references --- docs/getting-started.md | 79 +++++++++++++++++++++++++++++++++-------- docs/license-credits.md | 8 ++--- mkdocs.yml | 5 +-- 3 files changed, 72 insertions(+), 20 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 2012f629..a0415331 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -2,55 +2,106 @@ ## Install Platformio -Install PlatformIO IDE for embedded development to build this project. Platformio integrates with your favorite IDE, choose e.g. Visual Studio Code, Atom, Eclipse etc. +Install PlatformIO IDE for embedded development to build this project. Platformio integrates with your favorite IDE, choose e.g. [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=platformio.platformio-ide), Atom, Eclipse etc. Compile time configuration is spread across several files. Before compiling the code, edit or create the following files: ## platformio.ini Edit `platformio_orig.ini` (for ESP32 CPU based boards) *or* `platformio_orig_s3.ini` (for ESP32-S3 CPU based boards) and select desired board in section **board**. To add a new board, create an appropriate hardware abstraction layer file in hal subdirectory, and add a pointer to this file in section **board**. Copy or rename to `platformio.ini` in the root directory of the project. Now start Platformio. +### Selecting a board -``` bash title="Copy with terminal" -cp platformio_orig.ini platformio.ini +```ini linenums="6" title="Uncomment your board" +--8<-- "platformio_orig.ini:6:36" ``` +=== "Copy" + ``` bash + cp platformio_orig.ini platformio.ini + ``` +=== "Rename" + ``` bash + mv platformio_orig.ini platformio.ini + ``` + !!! info Platformio is looking for `platformio.ini` in the root directory and won't start if it does not find this file! + + ## paxcounter.conf Edit `src/paxcounter_orig.conf` and tailor settings in this file according to your needs and use case. Please take care of the duty cycle regulations of the LoRaWAN network you're going to use. Copy or rename to `src/paxcounter.conf`. -``` bash title="Copy with terminal" -cp src/paxcounter_orig.conf src/paxcounter.conf -``` +=== "Copy" + ``` bash + cp src/paxcounter_orig.conf src/paxcounter.conf + ``` +=== "Rename" + ``` bash + mv src/paxcounter_orig.conf src/paxcounter.conf + ``` If your device has a **real time clock** it can be updated by either LoRaWAN network or GPS time, according to settings *TIME_SYNC_INTERVAL* and *TIME_SYNC_LORAWAN* in `paxcounter.conf`. +```c linenums="85" title="paxcounter.conf" +--8<-- "src/paxcounter_orig.conf:85:85" +``` + ## src/lmic_config.h Edit `src/lmic_config.h` and tailor settings in this file according to your country and device hardware. Please take care of national regulations when selecting the frequency band for LoRaWAN. +```c linenums="9" title="national regulations in src/lmic_config.h " +--8<-- "src/lmic_config.h:9:18" +``` + ## src/loraconf.h Create file `src/loraconf.h` using the template [src/loraconf_sample.h](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/loraconf_sample.h) and adjust settings to use your personal values. To join the network and activate your paxcounter, you must configure either OTAA or ABP join method. You should use OTAA, whenever possible. To understand the differences of the two methods, [this article](https://www.thethingsnetwork.org/docs/devices/registration.html) may be useful. -``` bash title="Copy with terminal" -cp src/loraconf_sample.h src/loraconf.h -``` +=== "Copy" + ``` bash + cp src/loraconf_sample.h src/loraconf.h + ``` +=== "Rename" + ``` bash + mv src/loraconf_sample.h src/loraconf.h + ``` + To configure OTAA, leave `#define LORA_ABP` deactivated (commented). To use ABP, activate (uncomment) `#define LORA_ABP` in the file `src/loraconf.h`. The file `src/loraconf_sample.h` contains more information about the values to provide. -## src/ota.conf -Create file `src/ota.conf` using the template [src/ota_sample.conf](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/ota_sample.conf) and enter your WIFI network&key. These settings are used for downloading updates via WiFi, either from a remote https server, or locally via WebUI. If you want to use a remote server, you need a PAX.express repository. Enter your PAX.express credentials in ota.conf. If you don't need wireless firmware updates just rename ota.sample.conf to ota.conf. +=== "Activate OTAA (Default), Deactivate ABP" + ``` c linenums="18" title="src/loraconf.h" + --8<-- "src/loraconf_sample.h:18:18" + ``` +=== "Deactivate OTAA, Activate ABP" + ``` c linenums="18" title="src/loraconf.h" + #define LORA_ABP + ``` -``` bash title="Copy with terminal" -cp src/ota_sample.conf src/ota.conf -``` + +## src/ota.conf +Create file `src/ota.conf` using the template [src/ota_sample.conf](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/ota_sample.conf) and enter your WIFI network & key. These settings are used for downloading updates via WiFi, either from a remote https server, or locally via WebUI. If you want to use a remote server, you need a PAX.express repository. Enter your PAX.express credentials in ota.conf. If you don't need wireless firmware updates just rename ota.sample.conf to ota.conf. + +=== "Copy" + ``` bash + cp src/ota_sample.conf src/ota.conf + ``` +=== "Rename" + ``` bash + mv src/ota_sample.conf src/ota.conf + ``` # Building Use PlatformIO with your preferred IDE for development and building this code. Make sure you have latest PlatformIO version. # Uploading +!!! warning + + 1. After editing `paxcounter.conf`, use "clean" button before "build" in PlatformIO! + 2. Clear NVRAM of the board to delete previous stored runtime settings (`pio run -t erase`) + - **by cable, via USB/UART interface:** To upload the code via cable to your ESP32 board this needs to be switched from run to bootloader mode. Boards with USB bridge like Heltec and TTGO usually have an onboard logic which allows soft switching by the upload tool. In PlatformIO this happenes automatically.

The LoPy/LoPy4/FiPy board needs to be set manually. See these diff --git a/docs/license-credits.md b/docs/license-credits.md index bfb261a5..69f8a595 100644 --- a/docs/license-credits.md +++ b/docs/license-credits.md @@ -8,7 +8,7 @@ Copyright 2018-2022 Klaus Wilting 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 +[http://www.apache.org/licenses/LICENSE-2.0](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, @@ -16,9 +16,9 @@ Copyright 2018-2022 Klaus Wilting 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. +!!! info + 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. # Credits diff --git a/mkdocs.yml b/mkdocs.yml index 24a3c7f6..bf43eb44 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -43,6 +43,7 @@ markdown_extensions: - md_in_html - admonition - pymdownx.details - - pymdownx.superfences + - pymdownx.tabbed: + alternate_style: true -repo_url: https://github.com/cyberman54/ESP32-Paxcounter \ No newline at end of file +repo_url: https://github.com/cyberman54/ESP32-Paxcounter From 67fabe67edecd7d6b0b1e15d77a47dcb545c1877 Mon Sep 17 00:00:00 2001 From: Tim Huyeng Date: Mon, 21 Nov 2022 23:05:57 +0100 Subject: [PATCH 09/16] formatting and more code examples --- docs/additions.md | 69 ------------- docs/configuration/custom-sensors.md | 3 + docs/configuration/index.md | 146 +++++++++++++++++++++++++++ docs/hardware.md | 13 ++- docs/integrations.md | 10 +- docs/payloadformat.md | 42 ++++---- mkdocs.yml | 6 +- 7 files changed, 198 insertions(+), 91 deletions(-) delete mode 100644 docs/additions.md create mode 100644 docs/configuration/custom-sensors.md create mode 100644 docs/configuration/index.md diff --git a/docs/additions.md b/docs/additions.md deleted file mode 100644 index a711986d..00000000 --- a/docs/additions.md +++ /dev/null @@ -1,69 +0,0 @@ -## Sensors and Peripherals - -You can add up to 3 user defined sensors. Insert your sensor's payload scheme in [*sensor.cpp*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/sensor.cpp). Bosch BMP180 / BME280 / BME680 environment sensors are supported, to activate configure BME in board's hal file before build. Furthermore, SDS011, RTC DS3231, generic serial NMEA GPS, I2C LoPy GPS are supported, and to be configured in board's hal file. See [*generic.h*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/hal/generic.h) for all options and for proper configuration of BME280/BME680. - -Output of user sensor data can be switched by user remote control command 0x14 sent to Port 2. - -Output of sensor and peripheral data is internally switched by a bitmask register. Default mask can be tailored by editing *cfg.payloadmask* initialization value in [*configmanager.cpp*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/configmanager.cpp) following this scheme: - -| Bit | Sensordata | Default | -| --- | ------------- | ------- | -| 0 | Paxcounter | on | -| 1 | unused | off | -| 2 | BME280/680 | on | -| 3 | GPS* | on | -| 4 | User sensor 1 | on | -| 5 | User sensor 2 | on | -| 6 | User sensor 3 | on | -| 7 | Batterylevel | off | - -\*) GPS data can also be combined with paxcounter payload on port 1, *#define GPSPORT 1* in paxcounter.conf to enable - -## Power saving mode - -Paxcounter supports a battery friendly power saving mode. In this mode the device enters deep sleep, after all data is polled from all sensors and the dataset is completeley sent through all user configured channels (LORAWAN / SPI / MQTT / SD-Card). Set *#define SLEEPCYCLE* in paxcounter.conf to enable power saving mode and to specify the duration of a sleep cycle. Power consumption in deep sleep mode depends on your hardware, i.e. if on board peripherals can be switched off or set to a chip specific sleep mode either by MCU or by power management unit (PMU) as found on TTGO T-BEAM v1.0/V1.1. See [*power.cpp*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/power.cpp) for power management, and [*reset.cpp*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/reset.cpp) for sleep and wakeup logic. - -## Time sync - -Paxcounter can keep a time-of-day synced with external or on board time sources. Set *#define TIME_SYNC_INTERVAL* in paxcounter.conf to enable time sync. Supported external time sources are GPS, LORAWAN network time and LORAWAN application timeserver time. Supported on board time sources are the RTC of ESP32 and a DS3231 RTC chip, both are kept sycned as fallback time sources. Time accuracy depends on board's time base which generates the pulse per second. Supported are GPS PPS, SQW output of RTC, and internal ESP32 hardware timer. Time base is selected by #defines in the board's hal file, see example in [*generic.h*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/hal/generic.h). Bonus: If your LORAWAN network does not support network time, you can run a Node-Red timeserver application using the enclosed [**Timeserver code**](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/Node-RED/Timeserver.json). Configure the MQTT nodes in Node-Red for the LORAWAN application used by your paxocunter device. Time can also be set without precision liability, by simple remote command, see section remote control. - -## Wall clock controller - -Paxcounter can be used to sync a wall clock which has a DCF77 or IF482 time telegram input. Set `#define HAS_IF482` or `#define HAS_DCF77` in board's hal file to setup clock controller. Use case of this function is to integrate paxcounter and clock. Accurary of the synthetic DCF77 signal depends on accuracy of on board's time base, see above. - -## Mobile PaxCounter using openSenseMap - -This describes how to set up a mobile PaxCounter:
Follow all steps so far for preparing the device, selecting the packed payload format. In `paxcounter.conf` set `PAYLOAD_OPENSENSEBOX` to `1`. Register a new sensebox on [https://opensensemap.org/](https://opensensemap.org). In the sensor configuration select "TheThingsNetwork" and set decoding profile to "LoRa serialization". Enter your TTN Application and Device ID. Setup decoding option using `[{"decoder":"latLng"},{"decoder":"uint16",sensor_id":"yoursensorid"}]`. - -## SD-card - -Data can be stored on SD-card if the board provides an SD card interface, either with SPI or MMC mode. To enable this feature, specify interface mode and hardware pins in board's hal file (src/hal/): - - #define HAS_SDCARD 1 // SD-card interface, using SPI mode - OR - #define HAS_SDCARD 2 // SD-card interface, using MMC mode - - // Pins for SPI interface - #define SDCARD_CS (13) // fill in the correct numbers for your board - #define SDCARD_MOSI (15) - #define SDCARD_MISO (2) - #define SDCARD_SCLK (14) - -This is an example of a board with MMC SD-card interface: https://www.aliexpress.com/item/32915894264.html. For this board use file src/hal/ttgov21new.h and add the lines given above. - -Another approach would be this tiny board: https://www.aliexpress.com/item/32424558182.html (needs 5V). -In this case you choose the correct file for your ESP32-board in the src/hal-directory and add the lines given above. Edit the pin numbers given in the example, according to your wiring. - -Data is written on SD-card to a single file. After 3 write operations the data is flushed to the disk to minimize flash write cycles. Thus, up to the last 3 records of data will get lost when the PAXCOUNTER looses power during operation. - -Format of the resulting file is CSV, thus easy import in LibreOffice, Excel, InfluxDB, etc. Each record contains timestamp (in ISO8601 format), paxcount (wifi and ble) and battery voltage (optional). Voltage is logged if the device has a battery voltage sensor (to be configured in board hal file). - -File contents example: -```csv - timestamp,wifi,ble[,voltage] - 2022-01-30T21:12:41Z,11,25[,4100] - 2022-01-30T21:14:24Z,10,21[,4070] - 2022-01-30T21:16:08Z,12,26[,4102] - 2022-01-30T21:17:52Z,11,26[,4076] -``` -If you want to change this, modify `src/sdcard.cpp` and `include/sdcard.h`. \ No newline at end of file diff --git a/docs/configuration/custom-sensors.md b/docs/configuration/custom-sensors.md new file mode 100644 index 00000000..affb4679 --- /dev/null +++ b/docs/configuration/custom-sensors.md @@ -0,0 +1,3 @@ +# Custom sensors + +!!! todo \ No newline at end of file diff --git a/docs/configuration/index.md b/docs/configuration/index.md new file mode 100644 index 00000000..599da257 --- /dev/null +++ b/docs/configuration/index.md @@ -0,0 +1,146 @@ +# Configuration + +## Sensors and Peripherals + +You can add up to 3 user defined sensors. Insert your sensor's payload scheme in [*sensor.cpp*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/sensor.cpp). More examples and a detailed description can be found in the [sensor documentation](custom-sensors.md). + +### Supported Peripherals + +* Bosch BMP180 / BME280 / BME680 +* SDS011 +* RTC DS3231 +* generic serial NMEA GPS +* I2C Lopy GPS + +For these peripherals no additional code is needed. To activate configure them in the board's hal file before building the code. + + + + + +See [*generic.h*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/hal/generic.h) for all options and for proper configuration of BME280/BME680. + +=== "BME/ BMP Configuration" + ```c linenums="37" title="src/hal/generic.h" + --8<-- "src/hal/generic.h:37:49" + ``` +=== "SDS011 Configuration" + ```c linenums="51" title="src/hal/generic.h" + --8<-- "src/hal/generic.h:51:56" + ``` +=== "Custom Sensors Configuration" + ```c linenums="57" title="src/hal/generic.h" + --8<-- "src/hal/generic.h:57:60" + ``` + +=== "Complete `generic.h`" + ```c linenums="1" title="src/hal/generic.h" + --8<-- "src/hal/generic.h" + ``` + +Output of user sensor data can be switched by user remote control command `0x14` sent to Port 2. + +Output of sensor and peripheral data is internally switched by a bitmask register. Default mask can be tailored by editing *cfg.payloadmask* initialization value in [*configmanager.cpp*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/configmanager.cpp) following this scheme: + +| Bit | Sensordata | Default | +| --- | ------------- | ------- | +| 0 | Paxcounter | on | +| 1 | unused | off | +| 2 | BME280/680 | on | +| 3 | GPS* | on | +| 4 | User sensor 1 | on | +| 5 | User sensor 2 | on | +| 6 | User sensor 3 | on | +| 7 | Batterylevel | off | + +\*) GPS data can also be combined with paxcounter payload on port 1, `#define GPSPORT 1` in paxcounter.conf to enable + +```c linenums="101" title="src/paxcounter_orig.conf" +--8<-- "src/paxcounter_orig.conf:101:101" +``` + + +## Power saving mode + +Paxcounter supports a battery friendly power saving mode. In this mode the device enters deep sleep, after all data is polled from all sensors and the dataset is completeley sent through all user configured channels (LORAWAN / SPI / MQTT / SD-Card). Set *#define SLEEPCYCLE* in paxcounter.conf to enable power saving mode and to specify the duration of a sleep cycle. + +```c linenums="18" title="src/paxcounter_orig.conf" +--8<-- "src/paxcounter_orig.conf:18:18" +``` + + + Power consumption in deep sleep mode depends on your hardware, i.e. if on board peripherals can be switched off or set to a chip specific sleep mode either by MCU or by power management unit (PMU) as found on TTGO T-BEAM v1.0/V1.1. See [*power.cpp*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/power.cpp) for power management, and [*reset.cpp*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/reset.cpp) for sleep and wakeup logic. + + + +## Time sync + +Paxcounter can keep a time-of-day synced with external or on board time sources. Set `#define TIME_SYNC_INTERVAL` in `paxcounter.conf` to enable time sync. + +```c linenums="19" title="src/paxcounter_orig.conf" +--8<-- "src/paxcounter_orig.conf:87:87" +``` + +Supported external time sources are GPS, LORAWAN network time and LORAWAN application timeserver time. Supported on board time sources are the RTC of ESP32 and a DS3231 RTC chip, both are kept sycned as fallback time sources. Time accuracy depends on board's time base which generates the pulse per second. Supported are GPS PPS, SQW output of RTC, and internal ESP32 hardware timer. Time base is selected by #defines in the board's hal file, see example in [*generic.h*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/hal/generic.h). + +```c linenums="87" title="src/hal/generic.h" +--8<-- "src/hal/generic.h:87:96" +``` + + +!!! tip + + Bonus: If your LORAWAN network does not support network time, you can run a Node-Red timeserver application using the enclosed [**Timeserver code**](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/Node-RED/Timeserver.json). Configure the MQTT nodes in Node-Red for the LORAWAN application used by your paxocunter device. Time can also be set without precision liability, by simple remote command, see section remote control. + +## Wall clock controller + +Paxcounter can be used to sync a wall clock which has a DCF77 or IF482 time telegram input. Set `#define HAS_IF482` or `#define HAS_DCF77` in board's hal file to setup clock controller. Use case of this function is to integrate paxcounter and clock. Accurary of the synthetic DCF77 signal depends on accuracy of on board's time base, see above. + +## Mobile PaxCounter using openSenseMap + +This describes how to set up a mobile PaxCounter:
Follow all steps so far for preparing the device, selecting the packed payload format. In `paxcounter.conf` set `PAYLOAD_OPENSENSEBOX` to `1`. + +```c linenums="59" title="src/paxcounter_orig.conf" +--8<-- "src/paxcounter_orig.conf:59:59" +``` + + Register a new sensebox on [https://opensensemap.org/](https://opensensemap.org). In the sensor configuration select "TheThingsNetwork" and set decoding profile to "LoRa serialization". Enter your TTN Application and Device ID. Setup decoding option using: + +```json + [{"decoder":"latLng"},{"decoder":"uint16",sensor_id":"yoursensorid"}] +``` + +## SD-card + +Data can be stored on SD-card if the board provides an SD card interface, either with SPI or MMC mode. To enable this feature, specify interface mode and hardware pins in board's hal file (`src/hal/`): + +```c + #define HAS_SDCARD 1 // SD-card interface, using SPI mode + //OR + #define HAS_SDCARD 2 // SD-card interface, using MMC mode + + // Pins for SPI interface + #define SDCARD_CS (13) // fill in the correct numbers for your board + #define SDCARD_MOSI (15) + #define SDCARD_MISO (2) + #define SDCARD_SCLK (14) +``` + +This is an example of a board with MMC SD-card interface: [https://www.aliexpress.com/item/32915894264.html](https://www.aliexpress.com/item/32915894264.html). For this board use file [`src/hal/ttgov21new.h`](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/hal/ttgov21new.h) and add the lines given above. + +Another approach would be this tiny board: [https://www.aliexpress.com/item/32424558182.html](https://www.aliexpress.com/item/32424558182.html) (needs 5V). +In this case you choose the correct file for your ESP32-board in the src/hal-directory and add the lines given above. Edit the pin numbers given in the example, according to your wiring. + +Data is written on SD-card to a single file. After 3 write operations the data is flushed to the disk to minimize flash write cycles. Thus, up to the last 3 records of data will get lost when the Paxcounter looses power during operation. + +Format of the resulting file is CSV, thus easy import in LibreOffice, Excel, InfluxDB, etc. Each record contains timestamp (in ISO8601 format), paxcount (wifi and ble) and battery voltage (optional). Voltage is logged if the device has a battery voltage sensor (to be configured in board hal file). + +File contents example: +```csv + timestamp,wifi,ble[,voltage] + 2022-01-30T21:12:41Z,11,25[,4100] + 2022-01-30T21:14:24Z,10,21[,4070] + 2022-01-30T21:16:08Z,12,26[,4102] + 2022-01-30T21:17:52Z,11,26[,4076] +``` +If you want to change this, modify `src/sdcard.cpp` and `include/sdcard.h`. \ No newline at end of file diff --git a/docs/hardware.md b/docs/hardware.md index 1e691864..dd2b7c95 100644 --- a/docs/hardware.md +++ b/docs/hardware.md @@ -61,4 +61,15 @@ Some 3D printable cases can be found (and, if wanted so, ordered) on Thingiverse ### Power consumption Power consumption was metered at around 450 - 1000mW, depending on board and user settings in `paxcounter.conf`. -By default, bluetooth sniffing not installed (`#define *BLECOUNTER* 0` in `paxcounter.conf`). If you enable bluetooth be aware that this goes on expense of wifi sniffing results, because then wifi and bt stack must share the 2,4 GHz RF ressources of ESP32. If you need to sniff wifi and bt in parallel and need best possible results, use two boards - one for wifi only and one for bt only - and add counted results. + +By default, bluetooth sniffing not installed. If you enable bluetooth be aware that this goes on expense of wifi sniffing results, because then wifi and bt stack must share the 2,4 GHz RF ressources of ESP32. If you need to sniff wifi and bt in parallel and need best possible results, use two boards - one for wifi only and one for bt only - and add counted results. + + +=== "Deactivate BLE sniffing (Default)" + ``` c linenums="29" title="paxcounter.conf" + #define *BLECOUNTER* 0 + ``` +=== "Activate BLE sniffing" + ``` c linenums="29" title="paxcounter.conf" + #define *BLECOUNTER* 1 + ``` \ No newline at end of file diff --git a/docs/integrations.md b/docs/integrations.md index 191cfdb6..dbc573fd 100644 --- a/docs/integrations.md +++ b/docs/integrations.md @@ -1,4 +1,6 @@ -# Integration into "The Things Stack Community Edition" aka "The Things Stack V3" +# Integration LoRaWAN + +## "The Things Stack Community Edition" aka "The Things Stack V3" To use the ESP32-Paxcounter in The Things Stack Community Edition you need an account to reach the console. Go to: @@ -12,6 +14,10 @@ To use the ESP32-Paxcounter in The Things Stack Community Edition you need an ac The "Repository" payload decoder uses the packed format, explained below. If you want to use MyDevices from Cayenne you should use the Cayenne payload decoder instead. -# TTN Mapper +## TTN Mapper If you want your devices to be feeding the [TTN Mapper](https://ttnmapper.org/), just follow this manual: [https://docs.ttnmapper.org/integration/tts-integration-v3.html](https://docs.ttnmapper.org/integration/tts-integration-v3.html) - different than indicated in the manual you can leave the payload decoder to "Repository" for the ESP32-Paxcounter and you are fine. + +## ChirpStack + +!!! todo \ No newline at end of file diff --git a/docs/payloadformat.md b/docs/payloadformat.md index 9990d7ae..1edcace5 100644 --- a/docs/payloadformat.md +++ b/docs/payloadformat.md @@ -1,7 +1,7 @@ # Payload format -You can select different payload formats in `paxcounter.conf`: +You can select different payload formats in [`paxcounter.conf`](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/paxcounter_orig.conf): - ***Plain*** uses big endian format and generates json fields, e.g. useful for TTN console @@ -9,6 +9,12 @@ You can select different payload formats in `paxcounter.conf`: - [***CayenneLPP***](https://mydevices.com/cayenne/docs/lora/#lora-cayenne-low-power-payload-reference-implementation) generates MyDevices Cayenne readable fields + +```c linenums="20" title="src/paxcounter_orig.conf" +--8<-- "src/paxcounter_orig.conf:20:20" +``` + + !!! danger "Decrepated information from the things network v2" If you're using [TheThingsNetwork](https://www.thethingsnetwork.org/) (TTN) you may want to use a payload converter. Go to TTN Console - Application - Payload Formats and paste the code example below in tabs Decoder and Converter. This way your MQTT application can parse the fields `pax`, `ble` and `wifi`. @@ -41,22 +47,22 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering. **Port #3:** Device configuration query result - byte 1: Lora DR (0..15, see rcommand 0x05) [default 5] - byte 2: Lora TXpower (2..15) [default 15] - byte 3: Lora ADR (1=on, 0=off) [default 1] - byte 4: Screensaver status (1=on, 0=off) [default 0] - byte 5: Display status (1=on, 0=off) [default 0] - byte 6: Counter mode (0=cyclic unconfirmed, 1=cumulative, 2=cyclic confirmed) [default 0] - bytes 7-8: RSSI limiter threshold value (negative, MSB) [default 0] - byte 9: Scan and send cycle in seconds/2 (0..255) [default 120] - byte 10: Wifi channel hopping interval in seconds/100 (0..255), 0 means no hopping [default 50] - byte 11: Bluetooth channel switch interval in seconds/100 (0..255) [default 10] - byte 12: Bluetooth scanner status (1=on, 0=0ff) [default 1] - byte 13: Wifi antenna switch (0=internal, 1=external) [default 0] - byte 14: 0 (reserved) - byte 15: RGB LED luminosity (0..100 %) [default 30] - byte 16: Payloadmask (Bitmask, 0..255, see rcommand 0x14) - byte 17: 0 (reserved) + byte 1: Lora DR (0..15, see rcommand 0x05) [default 5] + byte 2: Lora TXpower (2..15) [default 15] + byte 3: Lora ADR (1=on, 0=off) [default 1] + byte 4: Screensaver status (1=on, 0=off) [default 0] + byte 5: Display status (1=on, 0=off) [default 0] + byte 6: Counter mode (0=cyclic unconfirmed, 1=cumulative, 2=cyclic confirmed) [default 0] + bytes 7-8: RSSI limiter threshold value (negative, MSB) [default 0] + byte 9: Scan and send cycle in seconds/2 (0..255) [default 120] + byte 10: Wifi channel hopping interval in seconds/100 (0..255), 0 means no hopping [default 50] + byte 11: Bluetooth channel switch interval in seconds/100 (0..255) [default 10] + byte 12: Bluetooth scanner status (1=on, 0=0ff) [default 1] + byte 13: Wifi antenna switch (0=internal, 1=external) [default 0] + byte 14: 0 (reserved) + byte 15: RGB LED luminosity (0..100 %) [default 30] + byte 16: Payloadmask (Bitmask, 0..255, see rcommand 0x14) + byte 17: 0 (reserved) bytes 18-28: Software version (ASCII format, terminating with zero) @@ -70,7 +76,7 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering. **Port #5:** Button pressed alarm - byte 1: static value 0x01 + byte 1: static value 0x01 **Port #6:** (unused) diff --git a/mkdocs.yml b/mkdocs.yml index bf43eb44..a3d0767e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -5,6 +5,8 @@ theme: features: - navigation.instant - navigation.tracking + - navigation.indexes + - content.code.annotate palette: # Palette toggle for light mode - media: "(prefers-color-scheme: light)" @@ -26,7 +28,9 @@ nav: - Getting Started: getting-started.md - Display & LED: display-led.md - Legal note: legalnote.md - - Additions: additions.md + - Configuration: + - configuration/index.md + - Custom Sensors: modifications/custom-sensors.md - Integrations: integrations.md - Payload Format: payloadformat.md - Remote control: remotecontrol.md From 92b0aac6c0ca2540ee3f699094ca48249adf0447 Mon Sep 17 00:00:00 2001 From: Tim Huyeng Date: Mon, 21 Nov 2022 23:14:51 +0100 Subject: [PATCH 10/16] fixed wrong path --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index a3d0767e..1fb1fb53 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -30,7 +30,7 @@ nav: - Legal note: legalnote.md - Configuration: - configuration/index.md - - Custom Sensors: modifications/custom-sensors.md + - Custom Sensors: configuration/custom-sensors.md - Integrations: integrations.md - Payload Format: payloadformat.md - Remote control: remotecontrol.md From c13fddda5b2ac683932dfd588fe0621f6fb20460 Mon Sep 17 00:00:00 2001 From: Tim Huyeng Date: Tue, 22 Nov 2022 09:46:53 +0100 Subject: [PATCH 11/16] initial doc for custom sensor --- docs/configuration/custom-sensors.md | 185 ++++++++++++++++++++++++++- 1 file changed, 184 insertions(+), 1 deletion(-) diff --git a/docs/configuration/custom-sensors.md b/docs/configuration/custom-sensors.md index affb4679..e7e8f44c 100644 --- a/docs/configuration/custom-sensors.md +++ b/docs/configuration/custom-sensors.md @@ -1,3 +1,186 @@ # Custom sensors -!!! todo \ No newline at end of file +You can add up to 3 user defined sensors. Insert your sensor's payload scheme in [`sensor.cpp`](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/sensor.cpp). +The following exampls show how to add a custom temperature and humidty sensor. + +1. Add variables or needed libaries +2. Add sensor specific code to `sensor_init` in [`sensor.cpp`](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/sensor.cpp) +3. Add sensor specific code to `sensor_read` function in [`sensor.cpp`](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/sensor.cpp) +4. Add Payload functions to [`payload.h`](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/include/payload.h) and [`payload.cpp`](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/payload.cpp) (Optional) +5. Use payload functions in [`sensor.cpp`](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/sensor.cpp) to send sensor data + +## Example 1: Custom Temperature and Humidity Sensor *GY-21* + +To use an custom sensor you first have to enable the Sensor which you want to use. For this you have to edit or add the `HAS_SENSOR_1` defines in either the `paxcounter.conf` or the `hal` file of your board. +=== "Activate Sensor 1" + ```c linenums="132" title="src/paxcounter_orig.conf" + #define HAS_SENSOR_1 1 + ``` +=== "Activate Sensor 2" + ```c linenums="132" title="src/paxcounter_orig.conf" + #define HAS_SENSOR_2 1 + ``` +=== "Activate Sensor 3" + ```c linenums="132" title="src/paxcounter_orig.conf" + #define HAS_SENSOR_3 1 + ``` + +You might also add a constant for your custom sensor in the `paxcounter.conf` file. This is optional but can be used to identify the sensor type. + +```c linenums="133" title="src/paxcounter_orig.conf" +#define HAS_GY21 1 // (1) +``` + +1. See usage of this in the example below +2. +### 1. Add variables or needed libaries +If you want to use any libary for you custom sensor you have to add it to the `platformio.ini` file. + +In this example we use the [`HTU2xD_SHT2x_Si70xx`](https://github.com/enjoyneering/HTU2xD_SHT2x_Si70xx.git) for the GY-21 sensor. + + +```yaml linenums="127" title="platformio.ini" +[env:usb] # (1) +upload_protocol = esptool +lib_deps = # (2) + ${common.lib_deps_all} + https://github.com/enjoyneering/HTU2xD_SHT2x_Si70xx.git + +``` + +1. Selected the env you want to use. In this example we use the `usb` env. +2. Add the libary to the `lib_deps` section. + +Add the import of libary in the `sensor.cpp` file. + +```c linenums="5" title="sensor.cpp" +#if (HAS_GY21) // (1) +#include +HTU2xD_SHT2x_SI70xx ht2x(HTU2xD_SENSOR, HUMD_12BIT_TEMP_14BIT); // sensor type, resolution +double temperature, humidity; +#endif // HAS_GY21 +``` + +1. Define `HAS_GY21` either in hal file of your board or in `paxcounter.conf` file. + +### 2. Add sensor specific code to `sensor_init` function + + +```c linenums="1" title="src/sensor.cpp" +#if (HAS_GY21) // (1) +if (ht2x.begin() != true) // reset sensor, set heater off, set resolution, check power + // (sensor doesn't operate correctly if VDD < +2.25v) +{ + ESP_LOGE(TAG, "HTU2xD/SHT2x not connected, fail or VDD < +2.25v"); +} else { + ESP_LOGE(TAG, "HTU2xD/SHT2x/GY21 found"); +} +#endif // HAS_GY21 +``` + +1. Define `HAS_GY21` either in hal file of your board or in `paxcounter.conf` file. + + +### 3. Add sensor specific code to sensor_read function + +In this case we choose that our custom sensor is Sensor 3. This means the data will be sent on `SENSOR3PORT` which is by default `12`. You can change this in the `paxcounter.conf` file. + +```c linenums="78" title="src/sensor.cpp" + case 3: + #if (HAS_GY21) + ESP_LOGE(TAG, "Reading Sensor 3, GY21"); // (1) + temperature = + ht2x.readTemperature(); // accuracy +-0.3C in range 0C..60C at 14-bit + delay(100); + humidity = + ht2x.readHumidity(); // accuracy +-2% in range 20%..80%/25C at 12-bit + ESP_LOGE(TAG, "GY21: Temperature: %f", temperature); // (2) + ESP_LOGE(TAG, "GY21: Humidity: %f", humidity); // (3) + #endif // HAS_GY21 + break; +``` + +1. These logs are only for debugging. You can remove them if you want. +2. These logs are only for debugging. You can remove them if you want. +3. These logs are only for debugging. You can remove them if you want. + +### 4. Payload functions for a custom sensor + +If you have added your custom sensor code as described before you can also add custom payload function if you need others than the provided ones. For this you have to change two files. First you have to add your payload function to the `payload.h` file. + +=== "Example for a custom temperature / humidity payload function" + ```c linenums="57" title="src/payload.h" + void addTempHum(float temperature, float humidity); + ``` + +Then you have to add your payload function to the `payload.cpp` file. You can provide functions for all payload formates (see [Payload Formats](../payloadformat.md)) or just add it for the one you are using. + +Example for a custom temperature / humidity payload function + +=== "Plain payload format" + ```c linenums="128" title="src/payload.cpp" + void PayloadConvert::addTempHum(float temperature, float humidity) { + int16_t temperature = (int16_t)(temperature); // float -> int + uint16_t humidity = (uint16_t)(humidity); // float -> int + buffer[cursor++] = highByte(temperature); + buffer[cursor++] = lowByte(temperature); + buffer[cursor++] = highByte(humidity); + buffer[cursor++] = lowByte(humidity); + } + ``` + +=== "Packed payload format" + ```c linenums="230" title="src/payload.cpp" + void PayloadConvert::addTempHum(float temperature, float humidity) { + writeFloat(temperature); + writeFloat(humidity); + } + ``` + +=== "Cayenne payload format" + ```c linenums="488" title="src/payload.cpp" + void PayloadConvert::addTempHum(float temperature, float humidity) { + // data value conversions to meet cayenne data type definition + // 0.1°C per bit => -3276,7 .. +3276,7 °C + int16_t temp = temperature * 10; + // 0.5% per bit => 0 .. 128 %C + uint16_t hum = humidity * 2; + #if (PAYLOAD_ENCODER == 3) + buffer[cursor++] = LPP_TEMPERATURE_CHANNEL; + #endif + buffer[cursor++] = LPP_TEMPERATURE; // 2 bytes 0.1 °C Signed MSB + buffer[cursor++] = highByte(temperature); + buffer[cursor++] = lowByte(temperature); + #if (PAYLOAD_ENCODER == 3) + buffer[cursor++] = LPP_HUMIDITY_CHANNEL; + #endif + buffer[cursor++] = LPP_HUMIDITY; // 1 byte 0.5 % Unsigned + buffer[cursor++] = humidity; + } + ``` + + +### 5. Sending the data + +After you have added your custom sensor code and payload function you can send the data. For this you have to add the following code to the `sensor.cpp` file. + +```c linenums="78" title="src/sensor.cpp" hl_lines="11" + case 3: + #if (HAS_GY21) + ESP_LOGE(TAG, "Reading Sensor 3, GY21"); + temperature = + ht2x.readTemperature(); // accuracy +-0.3C in range 0C..60C at 14-bit + delay(100); + humidity = + ht2x.readHumidity(); // accuracy +-2% in range 20%..80%/25C at 12-bit + ESP_LOGE(TAG, "GY21: Temperature: %f", temperature); + ESP_LOGE(TAG, "GY21: Humidity: %f", humidity); + payload.addTempHum(temperature, humidity); // (1) + #endif // HAS_GY21 + break; +``` + +1. Add your custom payload function here. + + +Now you can build and upload the code to your ESP. Do not forget to erease the flash before uploading since you probably changed the `paxcounter.conf` file. From 45057ecaf5435d65ac26de2a7394043dba257d84 Mon Sep 17 00:00:00 2001 From: Tim Huyeng Date: Tue, 22 Nov 2022 10:23:18 +0100 Subject: [PATCH 12/16] add first draft of favicon --- docs/assets/favicon.png | Bin 0 -> 2919 bytes mkdocs.yml | 1 + 2 files changed, 1 insertion(+) create mode 100644 docs/assets/favicon.png diff --git a/docs/assets/favicon.png b/docs/assets/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8f6c57aceadbbf3564e5d7fe8996d8951ecb704c GIT binary patch literal 2919 zcmZ{mcRUpSAICrT*-=W$PUcZY){%^4MMj*J?aU+2j*Buf6XENO+*fpFW=6JokybGUAM1{r~;_@p``=kN5li=lk`3JQGar>asHNFaZEq_3mh!{&D-ig3$k|xKCK$ zKgQ^H2OaR1JuA>9Il9Ur=a;NW0s4_~hUXGcF*Y2-uq+#fI= z0GM|4v~QV*U^X$K=4O_BeeL_7UFoQ1vR<0PqM3`lV~yUv+p^ATFjF-vVU{M;%t;8(;Q&l@w=hUjPa znsxoxh-by?v+Nj(sVP-Q(EXQkVak^6La#v&{yp}TkA&L^m}W7#pTvSLIZazc0suyy;>Gg^l4-nEFQ4mJ5;`GT?_O3^kmYd(! z$w#Cn|N z;JjbId8opKB7^UzM|(M8U_RQCq16Q`DtDM$VTYVa==~Lu&wwUm;U<80Qr;N+i2z@+ zQS%$sHtF9y=+!b~VQNTBg!7~uL|lIsHh%$74pLg*8x1v&eZUcUX$AQ}+pOiaw%7M_ z#j_H<8_OA?U(r{EGb}_uh;LboS>>+t80{^r=8}?3#>F7FVIWKKWqu0{`r?ug79;y* zt(yo{axJGk@1*gG0oBM>tb{Pyd4EoxO_MYAqr~pc>V)bdbs98B5fPi5xvFjH1-H?Q zw7UFRjuq`4rB8m$VBsmDsJ|&FMh5+{G7q&b<)R2w)v7mGe_f^BSFxilJ(9hCbP*JN zaeFl8eu}^3d33)eZnP(c^b;R#Wx)oVoS`UNrN(w#t-^UcZ(&VirZSDgm%jYL&q~AH z-;#sQb|x^OZA6ks3f}w4(4iwf{Oc~-q9U2D8di~r6$<_0l1c{Wa7o*%E9oD^UvJcH zKulp&+3Zsf_9oVvWWkCIft}fjdp=|Z_2eAVn1vk(Os%nSl})*Zay&jsiS}j!J7K;Z z+rRPSAl_EG#=gGilnFcvCnSLP60TojpFOz`Mi<5*#Ipt#lHE~C$*1IO2w?y}WqD~4+k2G*SmM|C-XvILsL3lUL++QZhoB?BTXCN-;rXDD z=&mcMwvLg!wUD4jO2`&D9#t7R3%mnr+x%8AXUorQnasV4%M18C8ns5b(5AZ)dHK5a zsdH24TO5Y(3aQoGKD>pBeh*K-3Fdd*vv)o_IMwQO?=GAElSc$J;TrQEdoEO zx6F!$mLZxkm@vw6zU~eGxydzFqh@9>U1AG8q@$?*Y-4PYgxJHn1x{nEPW=&iPIdRj za1wPhY1&}K@-*xnqBI9$FFA zs^ImjSH%KrxzT;n%z<$-L3XL7NW=g`M`YU#{EH9cDtu3yLy8N!gMEjese}F_SKMVb zOxc*0(y(h}xx^*#-MctnD{i`kM-BVJgv%JBPjWgcpb@xP0QWCk^&OWtbIr_97*ffz z58_6{eBm8;H2vnx;k=J=zi$rz$Q8_d=0ZPPZt(B+KOxr#T~0iL78${+pRa2+c@T$s9i7p>0Ky1wp zp#5-wUl4>8TD_C9W73d&n7u*UlvG?3XsC1atdV{L_^Ixn`)*RU+sYq6K8mLl7hr3eMich&n2?J&Nz{Sn-hgAC_EN-MtPGuI9J&Q+lnbN`G|%U*$2JE6DSzrih91_S)bA Date: Tue, 22 Nov 2022 14:33:36 +0100 Subject: [PATCH 13/16] Readme changes, added logo --- README.md | 589 ++------------------------ docs/assets/paxcounter_logo_white.png | Bin 0 -> 20536 bytes docs/configuration/custom-sensors.md | 2 +- docs/index.md | 35 +- mkdocs.yml | 3 +- 5 files changed, 52 insertions(+), 577 deletions(-) create mode 100644 docs/assets/paxcounter_logo_white.png diff --git a/README.md b/README.md index 0c474ac6..206676eb 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,30 @@ # ESP32-Paxcounter +![logo](docs/assets/paxcounter_logo_white.png) + **Wifi & Bluetooth driven, LoRaWAN enabled, battery powered mini Paxcounter built on cheap ESP32 LoRa IoT boards** -Tutorial (in german language): https://www.heise.de/select/make/2019/1/1551099236518668 +[Tutorial (in german language): heise.de](https://www.heise.de/select/make/2019/1/1551099236518668) [![CodeFactor](https://www.codefactor.io/repository/github/cyberman54/esp32-paxcounter/badge)](https://www.codefactor.io/repository/github/cyberman54/esp32-paxcounter) +[![PlatformIO CI](https://github.com/cyberman54/ESP32-Paxcounter/actions/workflows/build.yml/badge.svg?event=push)](https://github.com/cyberman54/ESP32-Paxcounter/actions/workflows/build.yml) +--- - - - - - - - - - +**Documentation**: https://cyberman54.github.io/ESP32-Paxcounter +**Source Code**: https://github.com/cyberman54/ESP32-Paxcounter + +--- + + + + + + + + + + # Use case @@ -23,563 +32,15 @@ Paxcounter is an [ESP32](https://www.espressif.com/en/products/socs/esp32) MCU b Intention of this project is to do this without intrusion in privacy: You don't need to track people owned devices, if you just want to count them. Therefore, Paxcounter does not persistenly store MAC adresses and does no kind of fingerprinting the scanned devices. -Data can either be be stored on a local SD-card, transferred to cloud using LoRaWAN network (e.g. TheThingsNetwork or Helium) or MQTT over TCP/IP, or transmitted to a local host using serial (SPI) interface. +Data can either be stored on a local SD-card, transferred to cloud using LoRaWAN network (e.g. TheThingsNetwork or Helium) or MQTT over TCP/IP, or transmitted to a local host using serial (SPI) interface. You can build this project battery powered using ESP32 deep sleep mode and reach long uptimes with a single 18650 Li-Ion cell. -# Hardware - -**Supported ESP32 based boards**: - -*With LoRa radio data transfer*: - -- **LilyGo: [Paxcounter-Board*](https://www.aliexpress.com/item/32915894264.html?spm=a2g0o.productlist.0.0.3d656325QrcfQc&algo_pvid=4a150199-63e7-4d21-bdb1-b48164537744&algo_exp_id=4a150199-63e7-4d21-bdb1-b48164537744-2&pdp_ext_f=%7B%22sku_id%22%3A%2212000023374441919%22%7D)** -- TTGO: T1*, T2*, T3*, T-Beam, T-Fox -- Heltec: LoRa-32 v1 and v2 -- Pycom: LoPy, LoPy4, FiPy -- Radioshuttle.de: [ECO Power Board](https://www.radioshuttle.de/esp32-eco-power/esp32-eco-power-board/) -- WeMos: LoLin32 + [LoraNode32 shield](https://github.com/hallard/LoLin32-Lora), -LoLin32lite + [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-Lora) -- Adafruit ESP32 Feather + LoRa Wing + OLED Wing, #IoT Octopus32 (Octopus + ESP32 Feather) -- M5Stack: [Basic Core IoT*](https://m5stack.com/collections/m5-core/products/basic-core-iot-development-kit) + [Lora Module RA-01H](https://m5stack.com/collections/m5-module/products/lora-module-868mhz), [Fire IoT*](https://m5stack.com/collections/m5-core/products/fire-iot-development-kit) - -*Without LoRa*: - -- LilyGo: [T-Dongle S3*](https://github.com/Xinyuan-LilyGO/T-Dongle-S3) -- Pyom: WiPy -- WeMos: LoLin32, LoLin32 Lite, WeMos D32, [Wemos32 Oled](https://www.instructables.com/id/ESP32-With-Integrated-OLED-WEMOSLolin-Getting-Star/) -- Crowdsupply: [TinyPICO](https://www.crowdsupply.com/unexpected-maker/tinypico) -- TTGO: [T-Display](https://www.aliexpress.com/item/33048962331.html) -- TTGO: [T-Wristband](https://www.aliexpress.com/item/4000527495064.html) -- Generic ESP32 - -*) supports microSD/TF-card for local logging of paxcounter data - -Depending on board hardware following features are supported: -- LoRaWAN communication, supporting various payload formats (see enclosed .js converters) -- MQTT communication via TCP/IP and Ethernet interface (note: payload transmitted over MQTT will be base64 encoded) -- SPI serial communication to a local host -- LED (shows power & status) -- OLED Display (shows detailed status) -- RGB LED (shows colorized status) -- Button (short press: flip display page / long press: send alarm message) -- Battery voltage monitoring (analog read / AXP192 / IP5306) -- GPS (Generic serial NMEA, or Quectel L76 I2C) -- Environmental sensors (Bosch BMP180/BME280/BME680 I2C; SDS011 serial) -- Real Time Clock (Maxim DS3231 I2C) -- IF482 (serial) and DCF77 (gpio) time telegram generator -- Switch external power / battery -- LED Matrix display (similar to [this 64x16 model](https://www.instructables.com/id/64x16-RED-LED-Marquee/), can be ordered on [Aliexpress](https://www.aliexpress.com/item/P3-75-dot-matrix-led-module-3-75mm-high-clear-top1-for-text-display-304-60mm/32616683948.html)) -- SD-card (see section SD-card here) for logging pax data - -Target platform must be selected in `platformio.ini`.
-Hardware dependent settings (pinout etc.) are stored in board files in /hal directory. If you want to use a ESP32 board which is not yet supported, use hal file generic.h and tailor pin mappings to your needs. Pull requests for new boards welcome.
- -Some 3D printable cases can be found (and, if wanted so, ordered) on Thingiverse, see -Heltec, -TTGOv2, -TTGOv2.1, -TTGO, -T-BEAM, -T-BEAM parts, -for example.
- -Power consumption was metered at around 450 - 1000mW, depending on board and user settings in paxcounter.conf. -By default bluetooth sniffing not installed (#define *BLECOUNTER* 0 in paxcounter.conf). If you enable bluetooth be aware that this goes on expense of wifi sniffing results, because then wifi and bt stack must share the 2,4 GHz RF ressources of ESP32. If you need to sniff wifi and bt in parallel and need best possible results, use two boards - one for wifi only and one for bt only - and add counted results. - -# Preparing - -## Install Platformio - -Install PlatformIO IDE for embedded development to make this project. Platformio integrates with your favorite IDE, choose eg. Visual Studio, Atom, Eclipse etc. - -Compile time configuration is spread across several files. Before compiling the code, edit or create the following files: - -## platformio.ini -Edit `platformio_orig.ini` (for ESP32 CPU based boards) *or* `platformio_orig_s3.ini` (for ESP32-S3 CPU based boards) and select desired board in section **board**. To add a new board, create an appropriate hardware abstraction layer file in hal subdirectory, and add a pointer to this file in section **board**. Copy or rename to `platformio.ini` in the root directory of the project. Now start Platformio. Note: Platformio is looking for `platformio.ini` in the root directory and won't start if it does not find this file! - -## paxcounter.conf -Edit `src/paxcounter_orig.conf` and tailor settings in this file according to your needs and use case. Please take care of the duty cycle regulations of the LoRaWAN network you're going to use. Copy or rename to `src/paxcounter.conf`. - -If your device has a **real time clock** it can be updated by either LoRaWAN network or GPS time, according to settings *TIME_SYNC_INTERVAL* and *TIME_SYNC_LORAWAN* in `paxcounter.conf`. - -## src/lmic_config.h -Edit `src/lmic_config.h` and tailor settings in this file according to your country and device hardware. Please take care of national regulations when selecting the frequency band for LoRaWAN. - -## src/loraconf.h -Create file `src/loraconf.h` using the template [src/loraconf_sample.h](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/loraconf_sample.h) and adjust settings to use your personal values. To join the network and activate your paxcounter, you must configure either OTAA or ABP join method. You should use OTAA, whenever possible. To understand the differences of the two methods, [this article](https://www.thethingsnetwork.org/docs/devices/registration.html) may be useful. - -To configure OTAA, leave `#define LORA_ABP` deactivated (commented). To use ABP, activate (uncomment) `#define LORA_ABP` in the file `src/loraconf.h`. -The file `src/loraconf_sample.h` contains more information about the values to provide. - -## src/ota.conf -Create file `src/ota.conf` using the template [src/ota.sample.conf](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/ota.sample.conf) and enter your WIFI network&key. These settings are used for downloading updates via WiFi, either from a remote https server, or locally via WebUI. If you want to use a remote server, you need a PAX.express repository. Enter your PAX.express credentials in ota.conf. If you don't need wireless firmware updates just rename ota.sample.conf to ota.conf. - -# Building - -Use PlatformIO with your preferred IDE for development and building this code. Make sure you have latest PlatformIO version. - -# Uploading - -- **by cable, via USB/UART interface:** -To upload the code via cable to your ESP32 board this needs to be switched from run to bootloader mode. Boards with USB bridge like Heltec and TTGO usually have an onboard logic which allows soft switching by the upload tool. In PlatformIO this happenes automatically.

-The LoPy/LoPy4/FiPy board needs to be set manually. See these -instructions how to do it. Don't forget to press on board reset button after switching between run and bootloader mode.

-The original Pycom firmware is not needed, so there is no need to update it before flashing Paxcounter. Just flash the compiled paxcounter binary (.elf file) on your LoPy/LoPy4/FiPy. If you later want to go back to the Pycom firmware, download the firmware from Pycom and flash it over. - -- **over the air (OTA), download via WiFi:** -After the ESP32 board is initially flashed and has joined a LoRaWAN network, the firmware can update itself by OTA. This process is kicked off by sending a remote control command (see below) via LoRaWAN to the board. The board then tries to connect via WiFi to a cloud service (PAX.express), checks for update, and if available downloads the binary and reboots with it. If something goes wrong during this process, the board reboots back to the current version. Prerequisites for OTA are: 1. You own a PAX.express repository, 2. you pushed the update binary to your PAX.express repository, 3. internet access via encrypted (WPA2) WiFi is present at the board's site, 4. WiFi credentials were set in ota.conf and initially flashed to the board. Step 2 runs automated, just enter the credentials in ota.conf and set `upload_protocol = custom` in platformio.ini. Then press build and lean back watching platformio doing build and upload. - -- **over the air (OTA), upload via WiFi:** -If option *BOOTMENU* is defined in `paxcounter.conf`, the ESP32 board will try to connect to a known WiFi access point each time cold starting (after a power cycle or a reset), using the WiFi credentials given in `ota.conf`. Once connected to the WiFi it will fire up a simple webserver, providing a bootstrap menu waiting for a user interaction (pressing "START" button in menu). This process will time out after *BOOTDELAY* seconds, ensuring booting the device to runmode. Once a user interaction in bootstrap menu was detected, the timeout will be extended to *BOOTTIMEOUT* seconds. During this time a firmware upload can be performed manually by user, e.g. using a smartphone in tethering mode providing the firmware upload file. - -# Legal note - -**Depending on your country's laws it may be illegal to sniff wireless networks for MAC addresses. Please check and respect your country's laws before using this code!** - -(e.g. US citizens may want to check [Section 18 U.S. Code § 2511](https://www.law.cornell.edu/uscode/text/18/2511) and [discussion](https://github.com/schollz/howmanypeoplearearound/issues/4) on this) - -(e.g. UK citizens may want to check [Data Protection Act 1998](https://ico.org.uk/media/1560691/wi-fi-location-analytics-guidance.pdf) and [GDPR 2018](https://ico.org.uk/for-organisations/guide-to-the-general-data-protection-regulation-gdpr/key-definitions/)) - -(e.g. Citizens in the the Netherlands and EU may want to read [this article](https://www.ivir.nl/publicaties/download/PrivacyInformatie_2016_6.pdf) and [this article](https://autoriteitpersoonsgegevens.nl/nl/nieuws/europese-privacytoezichthouders-publiceren-opinie-eprivacyverordening)) and [this decision](https://edpb.europa.eu/news/national-news/2021/dutch-dpa-fines-municipality-wi-fi-tracking_en) - -(e.g. Citizens in Germany may want to read [this article of Wissenschaftliche Dienste des Deutschen Bundestages](https://www.bundestag.de/resource/blob/538890/3dfae197d2c930693aa16d1619204f58/WD-3-206-17-pdf-data.pdf) - -Note: If you use this software you do this at your own risk. That means that you alone - not the authors of this software - are responsible for the legal compliance of an application using this or build from this software and/or usage of a device created using this software. You should take special care and get prior legal advice if you plan metering passengers in public areas and/or publish data drawn from doing so. - -# Privacy disclosure - -Paxcounter generates identifiers for sniffed Wifi or Bluetooth MAC adresses and and collects them temporary in the device's RAM for a configurable scan cycle time (default 60 seconds). After each scan cycle the collected identifiers are cleared. Identifiers are generated by using the last 2 bytes of universal MAC adresses. Personal MAC adresses remain untouched and are not evaluated. Identifiers and MAC adresses are never transferred to the LoRaWAN network. No persistent storing of MAC adresses, identifiers or timestamps and no other kind of analytics than counting are implemented in this code. Wireless networks are not touched by this code, but MAC adresses from wireless devices as well within as not within wireless networks, regardless if encrypted or unencrypted, are sniffed and processed by this code. - -# LED blink pattern - -**Mono color LED:** - -- Single Flash (50ms): seen a new Wifi or BLE device -- Quick blink (20ms on each 1/5 second): joining LoRaWAN network in progress or pending -- Small blink (10ms on each 1/2 second): LoRaWAN data transmit in progress or pending -- Long blink (200ms on each 2 seconds): LoRaWAN stack error - -**RGB LED:** - -- Green: seen a new Wifi device -- Magenta: seen a new BLE device -- Yellow: joining LoRaWAN network in progress or pending -- Pink: LORAWAN MAC transmit in progress -- Blue: LoRaWAN data transmit in progress or pending -- Red: LoRaWAN stack error - -# Display - -If you're using a device with OLED display, or if you add such one to the I2C bus, the device shows live data on the display. You can flip display pages showing - -- recent count of pax -- histogram -- GPS data -- BME sensor data -- time of day -- blank page - -by pressing the button of the device. - -# Sensors and Peripherals - -You can add up to 3 user defined sensors. Insert your sensor's payload scheme in [*sensor.cpp*](src/sensor.cpp). Bosch BMP180 / BME280 / BME680 environment sensors are supported, to activate configure BME in board's hal file before build. Furthermore, SDS011, RTC DS3231, generic serial NMEA GPS, I2C LoPy GPS are supported, and to be configured in board's hal file. See [*generic.h*](src/hal/generic.h) for all options and for proper configuration of BME280/BME680. - -Output of user sensor data can be switched by user remote control command 0x14 sent to Port 2. - -Output of sensor and peripheral data is internally switched by a bitmask register. Default mask can be tailored by editing *cfg.payloadmask* initialization value in [*configmanager.cpp*](src/configmanager.cpp) following this scheme: - -| Bit | Sensordata | Default -| --- | ------------- | ------- -| 0 | Paxcounter | on -| 1 | unused | off -| 2 | BME280/680 | on -| 3 | GPS* | on -| 4 | User sensor 1 | on -| 5 | User sensor 2 | on -| 6 | User sensor 3 | on -| 7 | Batterylevel | off - -*) GPS data can also be combined with paxcounter payload on port 1, *#define GPSPORT 1* in paxcounter.conf to enable - -# Power saving mode - -Paxcounter supports a battery friendly power saving mode. In this mode the device enters deep sleep, after all data is polled from all sensors and the dataset is completeley sent through all user configured channels (LORAWAN / SPI / MQTT / SD-Card). Set *#define SLEEPCYCLE* in paxcounter.conf to enable power saving mode and to specify the duration of a sleep cycle. Power consumption in deep sleep mode depends on your hardware, i.e. if on board peripherals can be switched off or set to a chip specific sleep mode either by MCU or by power management unit (PMU) as found on TTGO T-BEAM v1.0/V1.1. See *power.cpp* for power management, and *reset.cpp* for sleep and wakeup logic. - -# Time sync - -Paxcounter can keep a time-of-day synced with external or on board time sources. Set *#define TIME_SYNC_INTERVAL* in paxcounter.conf to enable time sync. Supported external time sources are GPS, LORAWAN network time and LORAWAN application timeserver time. Supported on board time sources are the RTC of ESP32 and a DS3231 RTC chip, both are kept sycned as fallback time sources. Time accuracy depends on board's time base which generates the pulse per second. Supported are GPS PPS, SQW output of RTC, and internal ESP32 hardware timer. Time base is selected by #defines in the board's hal file, see example in [**generic.h**](src/hal/generic.h). Bonus: If your LORAWAN network does not support network time, you can run a Node-Red timeserver application using the enclosed [**Timeserver code**](/src/Node-RED/Timeserver.json). Configure the MQTT nodes in Node-Red for the LORAWAN application used by your paxocunter device. Time can also be set without precision liability, by simple remote command, see section remote control. - -# Wall clock controller - -Paxcounter can be used to sync a wall clock which has a DCF77 or IF482 time telegram input. Set *#define HAS_IF482* or *#define HAS_DCF77* in board's hal file to setup clock controller. Use case of this function is to integrate paxcounter and clock. Accurary of the synthetic DCF77 signal depends on accuracy of on board's time base, see above. - -# Mobile PaxCounter using openSenseMap - -This describes how to set up a mobile PaxCounter:
Follow all steps so far for preparing the device, selecting the packed payload format. In `paxcounter.conf` set PAYLOAD_OPENSENSEBOX to 1. Register a new sensebox on https://opensensemap.org/. In the sensor configuration select "TheThingsNetwork" and set decoding profile to "LoRa serialization". Enter your TTN Application and Device ID. Setup decoding option using `[{"decoder":"latLng"},{"decoder":"uint16",sensor_id":"yoursensorid"}]` - -# SD-card - -Data can be stored on SD-card if the board provides an SD card interface, either with SPI or MMC mode. To enable this feature, specify interface mode and hardware pins in board's hal file (src/hal/): - - #define HAS_SDCARD 1 // SD-card interface, using SPI mode - OR - #define HAS_SDCARD 2 // SD-card interface, using MMC mode - - // Pins for SPI interface - #define SDCARD_CS (13) // fill in the correct numbers for your board - #define SDCARD_MOSI (15) - #define SDCARD_MISO (2) - #define SDCARD_SCLK (14) - -This is an example of a board with MMC SD-card interface: https://www.aliexpress.com/item/32915894264.html. For this board use file src/hal/ttgov21new.h and add the lines given above. - -Another approach would be this tiny board: https://www.aliexpress.com/item/32424558182.html (needs 5V). -In this case you choose the correct file for your ESP32-board in the src/hal-directory and add the lines given above. Edit the pin numbers given in the example, according to your wiring. - -Data is written on SD-card to a single file. After 3 write operations the data is flushed to the disk to minimize flash write cycles. Thus, up to the last 3 records of data will get lost when the PAXCOUNTER looses power during operation. - -Format of the resulting file is CSV, thus easy import in LibreOffice, Excel, Influx, etc. Each record contains timestamp (in ISO8601 format), paxcount (wifi and ble) and battery voltage (optional). Voltage is logged if the device has a battery voltage sensor (to be configured in board hal file). - -File contents example: - - timestamp,wifi,ble[,voltage] - 2022-01-30T21:12:41Z,11,25[,4100] - 2022-01-30T21:14:24Z,10,21[,4070] - 2022-01-30T21:16:08Z,12,26[,4102] - 2022-01-30T21:17:52Z,11,26[,4076] - -If you want to change this, modify src/sdcard.cpp and include/sdcard.h. - -# Integration into "The Things Stack Community Edition" aka "The Things Stack V3" - -To use the ESP32-Paxcounter in The Things Stack Community Edition you need an account to reach the console. Go to: -- [The Things Stack Community Edition Console](https://console.cloud.thethings.network/) -- choose your region and go to applications -- create an application by clicking "**+ Add application**" and give it a id, name, etc. -- create a device by clicking "**+ Add end device**" -- Select the end device: choose the Brand "**Open Source Community Projects**" and the Model "**ESP32-Paxcounter**", leave Hardware Version to "**Unknown**" and select your **Firmware Version** and **Profile (Region)** -- Enter registration data: choose the **frequency plan** (for EU choose the recommended), set the **AppEUI** (Fill with zeros), set the **DeviceEUI** (generate), set the **AppKey** (generate), choose a **device ID** and hit "Register end device" -- got to Applications -> "your App ID" -> Payload formatters -> Uplink, choose "**Repository**" and hit "Save changes" - -The "Repository" payload decoder uses the packed format, explained below. If you want to use MyDevices from Cayenne you should use the Cayenne payload decoder instead. - -# TTN Mapper - -If you want your devices to be feeding the [TTN Mapper](https://ttnmapper.org/), just follow this manual: https://docs.ttnmapper.org/integration/tts-integration-v3.html - different than indicated in the manual you can leave the payload decoder to "Repository" for the ESP32-Paxcounter and you are fine. - -# Payload format - -You can select different payload formats in `paxcounter.conf`: - -- ***Plain*** uses big endian format and generates json fields, e.g. useful for TTN console - -- ***Packed*** uses little endian format and generates json fields - -- [***CayenneLPP***](https://mydevices.com/cayenne/docs/lora/#lora-cayenne-low-power-payload-reference-implementation) generates MyDevices Cayenne readable fields - -**Decrepated information from the things network v2 >>** - -If you're using [TheThingsNetwork](https://www.thethingsnetwork.org/) (TTN) you may want to use a payload converter. Go to TTN Console - Application - Payload Formats and paste the code example below in tabs Decoder and Converter. This way your MQTT application can parse the fields `pax`, `ble` and `wifi`. - -To add your device to myDevices Cayenne platform select "Cayenne-LPP" from Lora device list and use the CayenneLPP payload encoder. - -To track a paxcounter device with on board GPS and at the same time contribute to TTN coverage mapping, you simply activate the [TTNmapper integration](https://www.thethingsnetwork.org/docs/applications/ttnmapper/) in TTN Console. Both formats *plain* and *packed* generate the fields `latitude`, `longitude` and `hdop` required by ttnmapper. Important: set TTN mapper port filter to '4' (paxcounter GPS Port). - -**<< Decrepated information from the things network v2** - -Hereafter described is the default *plain* format, which uses MSB bit numbering. Under /TTN in this repository you find some ready-to-go decoders which you may copy to your TTN console: - -[**plain_decoder.js**](src/TTN/plain_decoder.js) | -[**plain_converter.js**](src/TTN/plain_converter.js) | -[**packed_decoder.js**](src/TTN/packed_decoder.js) | -[**packed_converter.js**](src/TTN/packed_converter.js) - -**Port #1:** Paxcount data - - byte 1-2: Number of unique devices, seen on Wifi [00 00 if Wifi scan disabled] - byte 3-4: Number of unique devices, seen on Bluetooth [ommited if BT scan disabled] - -**Port #2:** Device status query result - - byte 1-2: Battery or USB Voltage [mV], 0 if no battery probe - byte 3-10: Uptime [seconds] - byte 11: CPU temperature [°C] - bytes 12-15: Free RAM [bytes] - byte 16: Last CPU core 0 reset reason - bytes 17-20: Number of restarts since last power cycle - -**Port #3:** Device configuration query result - - byte 1: Lora DR (0..15, see rcommand 0x05) [default 5] - byte 2: Lora TXpower (2..15) [default 15] - byte 3: Lora ADR (1=on, 0=off) [default 1] - byte 4: Screensaver status (1=on, 0=off) [default 0] - byte 5: Display status (1=on, 0=off) [default 0] - byte 6: Counter mode (0=cyclic unconfirmed, 1=cumulative, 2=cyclic confirmed) [default 0] - bytes 7-8: RSSI limiter threshold value (negative, MSB) [default 0] - byte 9: Scan and send cycle in seconds/2 (0..255) [default 120] - byte 10: Wifi channel hopping interval in seconds/100 (0..255), 0 means no hopping [default 50] - byte 11: Bluetooth channel switch interval in seconds/100 (0..255) [default 10] - byte 12: Bluetooth scanner status (1=on, 0=0ff) [default 1] - byte 13: Wifi antenna switch (0=internal, 1=external) [default 0] - byte 14: 0 (reserved) - byte 15: RGB LED luminosity (0..100 %) [default 30] - byte 16: Payloadmask (Bitmask, 0..255, see rcommand 0x14) - byte 17: 0 (reserved) - bytes 18-28: Software version (ASCII format, terminating with zero) - - -**Port #4:** GPS data (only if device has fature GPS, and GPS data is enabled and GPS has a fix) - - bytes 1-4: Latitude - bytes 5-8: Longitude - byte 9: Number of satellites - bytes 10-11: HDOP - bytes 12-13: Altitude [meter] - -**Port #5:** Button pressed alarm - - byte 1: static value 0x01 - -**Port #6:** (unused) - -**Port #7:** Environmental sensor data (only if device has feature BME) - - bytes 1-2: Temperature [°C] - bytes 3-4: Pressure [hPa] - bytes 5-6: Humidity [%] - bytes 7-8: Indoor air quality index (0..500), see below - - Indoor air quality classification: - 0-50 good - 51-100 average - 101-150 little bad - 151-200 bad - 201-300 worse - 301-500 very bad - -**Port #8:** Battery voltage data (only if device has feature BATT) - - bytes 1-2: Battery or USB Voltage [mV], 0 if no battery probe - -**Port #9:** Time/Date - - bytes 1-4: board's local time/date in UNIX epoch (number of seconds that have elapsed since January 1, 1970 (midnight UTC/GMT), not counting leap seconds) - -**Ports #10, #11, #12:** User sensor data - - Format is specified by user in function `sensor_read(uint8_t sensor)`, see `src/sensor.cpp`. - -# Remote control - -The device listenes for remote control commands on LoRaWAN Port 2. Multiple commands per downlink are possible by concatenating them, but must not exceed a maximum of 10 bytes per downlink. - -Note: settings can be stored in NVRAM to make them persistant (reloaded during device startup / restart). To store settings, use command 0x21. - -Send for example `83` `86` as Downlink on Port 2 to get battery status and time/date from the device. - - -0x01 set scan RSSI limit - - 1 ... 255 used for wifi and bluetooth scan radius (greater values increase scan radius, values 50...110 make sense) - 0 = RSSI limiter disabled [default] - -0x02 set counter mode - - 0 = cyclic unconfirmed, mac counter reset after each wifi scan cycle, data is sent only once [default] - 1 = cumulative counter, mac counter is never reset - 2 = cyclic confirmed, like 0 but data is resent until confirmation by network received - -0x03 set GPS data on/off - - 0 = GPS data off - 1 = GPS data on, sends GPS data on port 4 (default, use port 1 for mobile pax counter), if GPS is present and has a fix - -0x04 set display on/off - - 0 = display off - 1 = display on [default] - -0x05 set LoRa datarate - - 0 ... 15 see LoRaWAN regional parameters for details [default: 5] - - Example for EU868: - - DataRate Configuration Bit/s - 0 LoRa: SF12 / 125 kHz 250 - 1 LoRa: SF11 / 125 kHz 440 - 2 LoRa: SF10 / 125 kHz 980 - 3 LoRa: SF9 / 125 kHz 1760 - 4 LoRa: SF8 / 125 kHz 3125 - 5 LoRa: SF7 / 125 kHz 5470 - 6* LoRa: SF7 / 250 kHz 11000 - 7* FSK: 50 kbps 50000 - 8 .. 14 reserved for future use (RFU) - 15 ignored (device keeps current setting) - - *) not supported by TheThingsNetwork - -0x06 set LoRa TXpower - - 0 ... 255 desired TX power in dBm [default: 14] - -0x07 set LoRa Adaptive Data Rate mode - - 0 = ADR off - 1 = ADR on [default] - - If ADR is set to off, SF value is shown inverted on display. - -0x08 do nothing - - useful to clear pending commands from LoRaWAN server quere, or to check RSSI on device - -0x09 reset functions (send this command UNconfirmed only to avoid boot loops!) - - 0 = restart device (coldstart) - 1 = (reserved, currently does nothing) - 2 = reset device to factory settings and restart device - 3 = flush send queues - 4 = restart device (warmstart) - 8 = reboot device to maintenance mode (local web server) - 9 = reboot device to OTA update via Wifi mode - -0x0A set payload send cycle - - 5 ... 255 payload send cycle in seconds/2 - e.g. 120 -> payload is transmitted each 240 seconds [default] - -0x0B set Wifi channel hopping interval timer - - 0 ... 255 duration for scanning a wifi channel in seconds/100 - e.g. 50 -> each channel is scanned for 500 milliseconds [default] - 0 means no hopping, scanning on fixed single channel WIFI_CHANNEL_1 - -0x0C set Bluetooth channel switch interval timer - - 0 ... 255 duration for scanning a bluetooth advertising channel in seconds/100 - e.g. 8 -> each channel is scanned for 80 milliseconds [default] - -0x0E set Bluetooth scanner - - 0 = disabled - 1 = enabled [default] - -0x0F set WIFI antenna switch (works on LoPy/LoPy4/FiPy only) - - 0 = internal antenna [default] - 1 = external antenna - -0x10 set RGB led luminosity (works on LoPy/LoPy4/FiPy and LoRaNode32 shield only) - - 0 ... 100 percentage of luminosity (100% = full light) - e.g. 50 -> 50% of luminosity [default] - -0x13 set user sensor mode - - byte 1 = user sensor number (1..3) - byte 2 = sensor mode (0 = disabled / 1 = enabled [default]) - -0x14 set payload mask - - byte 1 = sensor data payload mask (0..255, meaning of bits see below) - 0x01 = COUNT_DATA - 0x02 = RESERVED_DATA - 0x04 = MEMS_DATA - 0x08 = GPS_DATA - 0x10 = SENSOR_1_DATA - 0x20 = SENSOR_2_DATA - 0x40 = SENSOR_3_DATA - 0x80 = BATT_DATA - bytes can be combined eg COUNT_DATA + SENSOR_1_DATA + BATT_DATA: `0x01 | 0x10 | 0x80 = 0x91` - -0x15 set BME data on/off - - 0 = BME data off - 1 = BME data on, sends BME data on port 7 [default] - -0x16 set battery data on/off - - 0 = battery data off [default] - 1 = battery data on, sends voltage on port 8 - -0x17 set Wifi scanner - - 0 = disabled - 1 = enabled [default] - -0x18 reserved - - unused, does nothing - -0x19 set sleep cycle - - bytes 1..2 = device sleep cycle in seconds/10 (MSB), 1 ... 255 - e.g. {0x04, 0xB0} -> device sleeps 200 minutes after each send cycle [default = 0] - -0x20 load device configuration - - Current device runtime configuration will be loaded from NVRAM, replacing current settings immediately (use with care!) - -0x21 store device configuration - - Current device runtime configuration is stored in NVRAM, will be reloaded after restart - -0x80 get device configuration - - Device answers with it's current configuration on Port 3. - -0x81 get device status - - Device answers with it's current status on Port 2. - -0x83 get battery status - - Device answers with battery voltage on Port 8. - -0x84 get device GPS status - - Device answers with it's current status on Port 4. - -0x85 get BME280 / BME680 sensor data - - Device answers with BME sensor data set on Port 7. - -0x86 get time/date - - Device answers with it's current time on Port 2: - - bytes 1..4 = time/date in UTC epoch seconds (LSB) - byte 5 = time source & status, see below - - bits 0..3 time source - 0x00 = GPS - 0x01 = RTC - 0x02 = LORA - 0x03 = unsynched - 0x04 = set (source unknown) - - bits 4..7 esp32 sntp time status - 0x00 = SNTP_SYNC_STATUS_RESET - 0x01 = SNTP_SYNC_STATUS_COMPLETED - 0x02 = SNTP_SYNC_STATUS_IN_PROGRESS - -0x87 sync time/date - - Device synchronizes it's time/date by calling the preconfigured time source. - -0x88 set time/date - - bytes 1..4 = time/date to set in UTC epoch seconds (MSB, e.g. https://www.epochconverter.com/hex) - # License -Copyright 2018-2022 Oliver Brandmueller +Copyright 2018-2022 Oliver Brandmueller -Copyright 2018-2022 Klaus Wilting +Copyright 2018-2022 Klaus Wilting Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -593,13 +54,13 @@ Copyright 2018-2022 Klaus Wilting See the License for the specific language governing permissions and limitations under the License. -NOTICE: +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. # Credits -Thanks to +Thanks to - [Oliver Brandmüller](https://github.com/spmrider) for idea and initial setup of this project - [Charles Hallard](https://github.com/hallard) for major code contributions to this project - [robbi5](https://github.com/robbi5) for the payload converter @@ -608,4 +69,4 @@ Thanks to - [sbamueller](https://github.com/sbamueller) for writing the tutorial in Make Magazine - [Stefan](https://github.com/nerdyscout) for paxcounter opensensebox integration - [August Quint](https://github.com/AugustQu) for adding SD card data logger and SDS011 support -- [t-huyeng](https://github.com/t-huyeng) for adding a CI workflow to this project +- [t-huyeng](https://github.com/t-huyeng) for adding a CI workflow to this project \ No newline at end of file diff --git a/docs/assets/paxcounter_logo_white.png b/docs/assets/paxcounter_logo_white.png new file mode 100644 index 0000000000000000000000000000000000000000..143185fc61d3588201fb3831c805a19a13839755 GIT binary patch literal 20536 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 B5w`#U literal 0 HcmV?d00001 diff --git a/docs/configuration/custom-sensors.md b/docs/configuration/custom-sensors.md index e7e8f44c..2ef28bf9 100644 --- a/docs/configuration/custom-sensors.md +++ b/docs/configuration/custom-sensors.md @@ -183,4 +183,4 @@ After you have added your custom sensor code and payload function you can send t 1. Add your custom payload function here. -Now you can build and upload the code to your ESP. Do not forget to erease the flash before uploading since you probably changed the `paxcounter.conf` file. +Now you can build and upload the code to your ESP. Do not forget to erase the flash before uploading since you probably changed the `paxcounter.conf` file. diff --git a/docs/index.md b/docs/index.md index 3a2cdad7..64972312 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,4 +1,8 @@ # ESP32-Paxcounter + +![logo](assets/paxcounter_logo_white.png) + + **Wifi & Bluetooth driven, LoRaWAN enabled, battery powered mini Paxcounter built on cheap ESP32 LoRa IoT boards** [Tutorial (in german language)](https://www.heise.de/select/make/2019/1/1551099236518668) @@ -6,6 +10,26 @@ [![CodeFactor](https://www.codefactor.io/repository/github/cyberman54/esp32-paxcounter/badge)](https://www.codefactor.io/repository/github/cyberman54/esp32-paxcounter) [![PlatformIO CI](https://github.com/cyberman54/ESP32-Paxcounter/actions/workflows/build.yml/badge.svg?event=push)](https://github.com/cyberman54/ESP32-Paxcounter/actions/workflows/build.yml) +--- + +**Documentation**: https://cyberman54.github.io/ESP32-Paxcounter + +**Source Code**: https://github.com/cyberman54/ESP32-Paxcounter + +--- + +## Use case + +Paxcounter is an [ESP32](https://www.espressif.com/en/products/socs/esp32) MCU based device for metering passenger flows in realtime. It counts how many mobile devices are around. This gives an estimation how many people are around. Paxcounter detects Wifi and Bluetooth signals in the air, focusing on mobile devices by evaluating their MAC adresses. + +!!! info + Intention of this project is to do this without intrusion in privacy: You don't need to track people owned devices, if you just want to count them. Therefore, Paxcounter does not persistenly store MAC adresses and *does no kind of fingerprinting the scanned devices. + +Data can either be be stored on a local SD-card, transferred to cloud using LoRaWAN network (e.g. TheThingsNetwork or Helium) or MQTT over TCP/IP, or transmitted to a local host using serial (SPI) interface. + +You can build this project battery powered using ESP32 deep sleep mode and reach long uptimes with a single 18650 Li-Ion cell. + +## Impressions @@ -16,14 +40,3 @@ - -# Use case - -Paxcounter is an [ESP32](https://www.espressif.com/en/products/socs/esp32) MCU based device for metering passenger flows in realtime. It counts how many mobile devices are around. This gives an estimation how many people are around. Paxcounter detects Wifi and Bluetooth signals in the air, focusing on mobile devices by evaluating their MAC adresses. - -!!! info - Intention of this project is to do this without intrusion in privacy: You don't need to track people owned devices, if you just want to count them. Therefore, Paxcounter does not persistenly store MAC adresses and *does no kind of fingerprinting the scanned devices. - -Data can either be be stored on a local SD-card, transferred to cloud using LoRaWAN network (e.g. TheThingsNetwork or Helium) or MQTT over TCP/IP, or transmitted to a local host using serial (SPI) interface. - -You can build this project battery powered using ESP32 deep sleep mode and reach long uptimes with a single 18650 Li-Ion cell. \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index b74c4e18..ffd65c39 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,4 +1,5 @@ -site_name: Docs +site_name: Documentation +site_description: ESP32-Paxcounter Documentation theme: name: material logo: assets/paxcounter_logo_white.svg From a0b48384e60a24d77d2a56f25547e785e6bb25ac Mon Sep 17 00:00:00 2001 From: Tim Huyeng Date: Tue, 22 Nov 2022 15:37:36 +0100 Subject: [PATCH 14/16] small fixes, better readability for normal md rendering. --- docs/configuration/custom-sensors.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/configuration/custom-sensors.md b/docs/configuration/custom-sensors.md index 2ef28bf9..6f8516dc 100644 --- a/docs/configuration/custom-sensors.md +++ b/docs/configuration/custom-sensors.md @@ -3,7 +3,7 @@ You can add up to 3 user defined sensors. Insert your sensor's payload scheme in [`sensor.cpp`](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/sensor.cpp). The following exampls show how to add a custom temperature and humidty sensor. -1. Add variables or needed libaries +1. Add variables or needed libraries 2. Add sensor specific code to `sensor_init` in [`sensor.cpp`](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/sensor.cpp) 3. Add sensor specific code to `sensor_read` function in [`sensor.cpp`](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/sensor.cpp) 4. Add Payload functions to [`payload.h`](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/include/payload.h) and [`payload.cpp`](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/payload.cpp) (Optional) @@ -11,16 +11,19 @@ The following exampls show how to add a custom temperature and humidty sensor. ## Example 1: Custom Temperature and Humidity Sensor *GY-21* -To use an custom sensor you first have to enable the Sensor which you want to use. For this you have to edit or add the `HAS_SENSOR_1` defines in either the `paxcounter.conf` or the `hal` file of your board. +To use a custom sensor you first have to enable the Sensor which you want to use. For this you have to edit or add `HAS_SENSOR_1` in either the `paxcounter.conf` or the `hal` file of your board. === "Activate Sensor 1" + ```c linenums="132" title="src/paxcounter_orig.conf" #define HAS_SENSOR_1 1 ``` === "Activate Sensor 2" + ```c linenums="132" title="src/paxcounter_orig.conf" #define HAS_SENSOR_2 1 ``` === "Activate Sensor 3" + ```c linenums="132" title="src/paxcounter_orig.conf" #define HAS_SENSOR_3 1 ``` @@ -32,8 +35,8 @@ You might also add a constant for your custom sensor in the `paxcounter.conf` fi ``` 1. See usage of this in the example below -2. -### 1. Add variables or needed libaries + +### 1. Add variables or needed libraries If you want to use any libary for you custom sensor you have to add it to the `platformio.ini` file. In this example we use the [`HTU2xD_SHT2x_Si70xx`](https://github.com/enjoyneering/HTU2xD_SHT2x_Si70xx.git) for the GY-21 sensor. @@ -118,6 +121,7 @@ Then you have to add your payload function to the `payload.cpp` file. You can pr Example for a custom temperature / humidity payload function === "Plain payload format" + ```c linenums="128" title="src/payload.cpp" void PayloadConvert::addTempHum(float temperature, float humidity) { int16_t temperature = (int16_t)(temperature); // float -> int @@ -130,6 +134,7 @@ Example for a custom temperature / humidity payload function ``` === "Packed payload format" + ```c linenums="230" title="src/payload.cpp" void PayloadConvert::addTempHum(float temperature, float humidity) { writeFloat(temperature); @@ -138,6 +143,7 @@ Example for a custom temperature / humidity payload function ``` === "Cayenne payload format" + ```c linenums="488" title="src/payload.cpp" void PayloadConvert::addTempHum(float temperature, float humidity) { // data value conversions to meet cayenne data type definition From 87d9dcfb84dd6729e7bd22c2096d8a5fb287d2b9 Mon Sep 17 00:00:00 2001 From: Tim Huyeng Date: Tue, 22 Nov 2022 15:44:24 +0100 Subject: [PATCH 15/16] update build workflow to only run if changes inside docs folder --- .github/workflows/docs.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 01942ac9..e31a8db0 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -4,6 +4,9 @@ on: branches: - master - mkdocs + paths: + - 'mkdocs.yml' + - 'docs/**' jobs: deploy: runs-on: ubuntu-latest From e6c1bd9dc65d377e74681993231aeef5c56b2cbf Mon Sep 17 00:00:00 2001 From: Tim Huyeng Date: Tue, 22 Nov 2022 23:09:33 +0100 Subject: [PATCH 16/16] smaller fixes in docs --- docs/configuration/index.md | 18 +++++++----------- docs/getting-started.md | 14 ++++++++++++++ docs/hardware.md | 6 ++++-- docs/led.md | 0 4 files changed, 25 insertions(+), 13 deletions(-) delete mode 100644 docs/led.md diff --git a/docs/configuration/index.md b/docs/configuration/index.md index 599da257..d2c724b7 100644 --- a/docs/configuration/index.md +++ b/docs/configuration/index.md @@ -2,7 +2,7 @@ ## Sensors and Peripherals -You can add up to 3 user defined sensors. Insert your sensor's payload scheme in [*sensor.cpp*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/sensor.cpp). More examples and a detailed description can be found in the [sensor documentation](custom-sensors.md). +You can add up to 3 user defined sensors. Insert your sensor's payload scheme in [`sensor.cpp`](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/sensor.cpp). More examples and a detailed description can be found in the [sensor documentation](custom-sensors.md). ### Supported Peripherals @@ -14,11 +14,7 @@ You can add up to 3 user defined sensors. Insert your sensor's payload scheme in For these peripherals no additional code is needed. To activate configure them in the board's hal file before building the code. - - - - -See [*generic.h*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/hal/generic.h) for all options and for proper configuration of BME280/BME680. +See [`generic.h`](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/hal/generic.h) for all options and for proper configuration of BME280/BME680. === "BME/ BMP Configuration" ```c linenums="37" title="src/hal/generic.h" @@ -40,7 +36,7 @@ See [*generic.h*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src Output of user sensor data can be switched by user remote control command `0x14` sent to Port 2. -Output of sensor and peripheral data is internally switched by a bitmask register. Default mask can be tailored by editing *cfg.payloadmask* initialization value in [*configmanager.cpp*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/configmanager.cpp) following this scheme: +Output of sensor and peripheral data is internally switched by a bitmask register. Default mask can be tailored by editing *cfg.payloadmask* initialization value in [`configmanager.cpp`](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/configmanager.cpp) following this scheme: | Bit | Sensordata | Default | | --- | ------------- | ------- | @@ -62,14 +58,14 @@ Output of sensor and peripheral data is internally switched by a bitmask registe ## Power saving mode -Paxcounter supports a battery friendly power saving mode. In this mode the device enters deep sleep, after all data is polled from all sensors and the dataset is completeley sent through all user configured channels (LORAWAN / SPI / MQTT / SD-Card). Set *#define SLEEPCYCLE* in paxcounter.conf to enable power saving mode and to specify the duration of a sleep cycle. +Paxcounter supports a battery friendly power saving mode. In this mode the device enters deep sleep, after all data is polled from all sensors and the dataset is completeley sent through all user configured channels (LORAWAN / SPI / MQTT / SD-Card). Set `#define SLEEPCYCLE` in paxcounter.conf to enable power saving mode and to specify the duration of a sleep cycle. ```c linenums="18" title="src/paxcounter_orig.conf" --8<-- "src/paxcounter_orig.conf:18:18" ``` - Power consumption in deep sleep mode depends on your hardware, i.e. if on board peripherals can be switched off or set to a chip specific sleep mode either by MCU or by power management unit (PMU) as found on TTGO T-BEAM v1.0/V1.1. See [*power.cpp*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/power.cpp) for power management, and [*reset.cpp*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/reset.cpp) for sleep and wakeup logic. + Power consumption in deep sleep mode depends on your hardware, i.e. if on board peripherals can be switched off or set to a chip specific sleep mode either by MCU or by power management unit (PMU) as found on TTGO T-BEAM v1.0/V1.1. See [`power.cpp`](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/power.cpp) for power management, and [`reset.cpp`](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/reset.cpp) for sleep and wakeup logic. @@ -81,7 +77,7 @@ Paxcounter can keep a time-of-day synced with external or on board time sources. --8<-- "src/paxcounter_orig.conf:87:87" ``` -Supported external time sources are GPS, LORAWAN network time and LORAWAN application timeserver time. Supported on board time sources are the RTC of ESP32 and a DS3231 RTC chip, both are kept sycned as fallback time sources. Time accuracy depends on board's time base which generates the pulse per second. Supported are GPS PPS, SQW output of RTC, and internal ESP32 hardware timer. Time base is selected by #defines in the board's hal file, see example in [*generic.h*](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/hal/generic.h). +Supported external time sources are GPS, LORAWAN network time and LORAWAN application timeserver time. Supported on board time sources are the RTC of ESP32 and a DS3231 RTC chip, both are kept sycned as fallback time sources. Time accuracy depends on board's time base which generates the pulse per second. Supported are GPS PPS, SQW output of RTC, and internal ESP32 hardware timer. Time base is selected by #defines in the board's hal file, see example in [`generic.h`](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/hal/generic.h). ```c linenums="87" title="src/hal/generic.h" --8<-- "src/hal/generic.h:87:96" @@ -90,7 +86,7 @@ Supported external time sources are GPS, LORAWAN network time and LORAWAN applic !!! tip - Bonus: If your LORAWAN network does not support network time, you can run a Node-Red timeserver application using the enclosed [**Timeserver code**](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/Node-RED/Timeserver.json). Configure the MQTT nodes in Node-Red for the LORAWAN application used by your paxocunter device. Time can also be set without precision liability, by simple remote command, see section remote control. + If your LORAWAN network does not support network time, you can run a Node-Red timeserver application using the enclosed [**Timeserver code**](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/Node-RED/Timeserver.json). Configure the MQTT nodes in Node-Red for the LORAWAN application used by your paxocunter device. Time can also be set without precision liability, by simple remote command, see section remote control. ## Wall clock controller diff --git a/docs/getting-started.md b/docs/getting-started.md index a0415331..c2e09a8d 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -23,6 +23,17 @@ Edit `platformio_orig.ini` (for ESP32 CPU based boards) *or* `platformio_orig_s3 mv platformio_orig.ini platformio.ini ``` +??? info "platformio_orig_s3.ini" + === "Copy" + ``` bash + cp platformio_orig_s3.ini platformio.ini + ``` + === "Rename" + ``` bash + mv platformio_orig_s3.ini platformio.ini + ``` + + !!! info Platformio is looking for `platformio.ini` in the root directory and won't start if it does not find this file! @@ -41,6 +52,9 @@ Edit `src/paxcounter_orig.conf` and tailor settings in this file according to yo mv src/paxcounter_orig.conf src/paxcounter.conf ``` + + + If your device has a **real time clock** it can be updated by either LoRaWAN network or GPS time, according to settings *TIME_SYNC_INTERVAL* and *TIME_SYNC_LORAWAN* in `paxcounter.conf`. ```c linenums="85" title="paxcounter.conf" diff --git a/docs/hardware.md b/docs/hardware.md index dd2b7c95..2730f036 100644 --- a/docs/hardware.md +++ b/docs/hardware.md @@ -66,10 +66,12 @@ By default, bluetooth sniffing not installed. If you enable bluetooth be aware t === "Deactivate BLE sniffing (Default)" + ``` c linenums="29" title="paxcounter.conf" - #define *BLECOUNTER* 0 + #define BLECOUNTER 0 ``` === "Activate BLE sniffing" + ``` c linenums="29" title="paxcounter.conf" - #define *BLECOUNTER* 1 + #define BLECOUNTER 1 ``` \ No newline at end of file diff --git a/docs/led.md b/docs/led.md deleted file mode 100644 index e69de29b..00000000