Merge pull request #19 from cyberman54/master

syncing with master
This commit is contained in:
Verkehrsrot 2018-04-02 22:50:45 +02:00 committed by GitHub
commit 2657596646
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 2011 additions and 1700 deletions

View File

@ -71,7 +71,7 @@ Note: If you use this software you do this at your own risk. That means that you
# Privacy disclosure
Paxcounter generates identifiers for sniffed MAC adresses and collects them temporary in the device's RAM for a configurable scan cycle time (default 240 seconds). After each scan cycle the collected identifiers are cleared. Identifiers are generated by salting and hashing MAC adresses. The random salt value changes after each scan cycle. 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. If the bluetooth option in the code is enabled, bluetooth MACs are scanned and processed by the included BLE stack, and counted by this code.
Paxcounter generates identifiers for sniffed MAC adresses and collects them temporary in the device's RAM for a configurable scan cycle time (default 240 seconds). After each scan cycle the collected identifiers are cleared. Identifiers are generated by salting and hashing MAC adresses. The random salt value changes after each scan cycle. 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. If the bluetooth option in the code is enabled, bluetooth MACs are scanned and processed by the included BLE stack, then hashed and counted by this code.
# Payload format description
@ -140,31 +140,41 @@ Note: all settings are stored in NVRAM and will be reloaded when device starts.
1 = reset MAC counter to zero
2 = reset device to factory settings
0x0A set Wifi scan cycle timer
0x0A set Wifi scan cycle and payload transmit cycle
0 ... 255 duration of a wifi scan cycle in seconds/2
e.g. 120 -> 1 cycle runs for 240 seconds
0 ... 255 duration of a wifi scan cycle in seconds/2, after this payload is sent
e.g. 120 -> 1 cycle runs for 240 seconds [default]
0x0B set Wifi channel switch interval timer
0 ... 255 timeout for scanning 1 wifi channel in seconds/100
e.g. 50 -> each channel is scanned for 0,5 seconds
e.g. 50 -> each channel is scanned for 0,5 seconds [default]
0x0C set BLE scan cycle timer
0 ... 255 duration of a BLE scan cycle in seconds
e.g. 30 -> 1 cycle runs for 30 seconds
e.g. 15 -> 1 cycle runs for 15 seconds [default]
0x0D set BLE scan mode
0x0D set BLE scan cycle frequency
0 = disabled [default]
1 = enabled
run BLE scan once after 0 ... 255 full wifi scans
e.g. 2 -> BLE scan runs once after each 2nd wifi scan [default]
0x0E set WIFI antenna switch (works on LoPy/LoPy4 only)
0x0E set BLE scan mode
0 = disabled
1 = enabled [default]
0x0F set WIFI antenna switch (works on LoPy/LoPy4 only)
0 = internal antenna [default]
1 = external antenna
0x10 set RGB led luminosity (works on LoPy/LoPy4 and LoRaNode32 shield only)
0 ... 100 percentage of luminosity (100% = full light)
e.g. 50 -> 50% of luminosity [default]
0x80 get device configuration
device answers with it's current configuration:
@ -179,9 +189,11 @@ Note: all settings are stored in NVRAM and will be reloaded when device starts.
byte 9: Wifi scan cycle duration in seconds/2 (0..255)
byte 10: Wifi channel switch interval in seconds/100 (0..255)
byte 11: BLE scan cycle duration in seconds (0..255)
byte 12: BLE scan mode (1=on, 0=0ff)
byte 13: Wifi antenna switch (0=internal, 1=external)
bytes 14-23: Software version (ASCII format)
byte 12: BLE scan frequency, do once after (0..255) full wifi scans
byte 13: BLE scan mode (1=on, 0=0ff)
byte 14: Wifi antenna switch (0=internal, 1=external)
byte 15: RGB LED luminosity (0..100 %)
bytes 16-25: Software version (ASCII format)
0x81 get device uptime
@ -212,3 +224,7 @@ Copyright 2018 Klaus Wilting <verkehrsrot@arcor.de>
NOTICE:
Parts of the source files in this repository are made available under different licenses,
see file <A HREF="https://github.com/cyberman54/ESP32-Paxcounter/blob/master/LICENSE">LICENSE.txt</A> in this repository. Refer to each individual source file for more details.
# Credits
Thanks to Charles Hallard (https://github.com/hallard) for major contributions to this project.

View File

@ -95,7 +95,7 @@ monitor_baud = 115200
lib_deps =
U8g2@>2.21.7
ESP32 BLE Arduino@>=0.4.9
; NeoPixelBus
SmartLeds
build_flags =
;set log level, we need build_flag for this, otherwise we can't use ESP_LOGx in arduino framework
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
@ -118,7 +118,7 @@ monitor_baud = 115200
lib_deps =
U8g2@>2.21.7
ESP32 BLE Arduino@>=0.4.9
; NeoPixelBus
SmartLeds
build_flags =
;set log level, we need build_flag for this, otherwise we can't use ESP_LOGx in arduino framework
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
@ -142,7 +142,7 @@ upload_speed = 256000
lib_deps =
U8g2
ESP32 BLE Arduino@>=0.4.9
; NeoPixelBus
SmartLeds
build_flags =
;set log level, we need build_flag for this, otherwise we can't use ESP_LOGx in arduino framework
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
@ -166,7 +166,7 @@ upload_speed = 921600
lib_deps =
U8g2
ESP32 BLE Arduino@>=0.4.9
; NeoPixelBus
SmartLeds
build_flags =
;set log level, we need build_flag for this, otherwise we can't use ESP_LOGx in arduino framework
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
@ -180,4 +180,3 @@ build_flags =
-include "src/hal/lolin32_lora.h"
;FreeRTOS single core operation, switches off core 1 (see arduino-esp32/cores/esp32/main.cpp)
; -DCONFIG_FREERTOS_UNICORE

View File

@ -1,32 +0,0 @@
// Basic Config
#include "globals.h"
#ifdef BLECOUNTER
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
// Local logging tag
static const char *TAG = "blecount";
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
}
};
void BLECount() {
u8x8.clearLine(3);
u8x8.drawString(0,3,"BLE Scan...");
BLEDevice::init(PROGNAME);
BLEScan* pBLEScan = BLEDevice::getScan(); //create new scan
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
BLEScanResults foundDevices = pBLEScan->start(cfg.blescancycle);
u8x8.clearLine(3);
u8x8.setCursor(0,3);
blenum=foundDevices.getCount();
u8x8.printf("BLE#: %-5i",blenum);
}
#endif

View File

@ -28,9 +28,12 @@ void defaultConfig() {
cfg.rssilimit = 0; // threshold for rssilimiter, negative value!
cfg.wifiscancycle = SEND_SECS; // wifi scan cycle [seconds/2]
cfg.wifichancycle = WIFI_CHANNEL_SWITCH_INTERVAL; // wifi channel switch cycle [seconds/100]
cfg.blescancycle = BLESCANTIME; // BLE scan cycle [seconds]
cfg.blescan = 0; // 0=disabled, 1=enabled
cfg.blescantime = BLESCANTIME; // BLE scan cycle duration [seconds]
cfg.blescancycle = BLESCANCYCLE; // do a BLE scan after [BLESCANCYCLE] full Wifi scan cycles
cfg.blescan = 1; // 0=disabled, 1=enabled
cfg.wifiant = 0; // 0=internal, 1=external (for LoPy/LoPy4)
cfg.rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%)
strncpy( cfg.version, PROGVERSION, sizeof(cfg.version)-1 );
}
@ -103,8 +106,11 @@ void saveConfig() {
if( nvs_get_i8(my_handle, "wifichancycle", &flash8) != ESP_OK || flash8 != cfg.wifichancycle )
nvs_set_i8(my_handle, "wifichancycle", cfg.wifichancycle);
if( nvs_get_i8(my_handle, "blescantime", &flash8) != ESP_OK || flash8 != cfg.blescantime )
nvs_set_i8(my_handle, "blescantime", cfg.blescantime);
if( nvs_get_i8(my_handle, "blescancycle", &flash8) != ESP_OK || flash8 != cfg.blescancycle )
nvs_set_i8(my_handle, "blescancycle", cfg.blescancycle);
nvs_set_i8(my_handle, "blescantime", cfg.blescancycle);
if( nvs_get_i8(my_handle, "blescanmode", &flash8) != ESP_OK || flash8 != cfg.blescan )
nvs_set_i8(my_handle, "blescanmode", cfg.blescan);
@ -112,6 +118,9 @@ void saveConfig() {
if( nvs_get_i8(my_handle, "wifiant", &flash8) != ESP_OK || flash8 != cfg.wifiant )
nvs_set_i8(my_handle, "wifiant", cfg.wifiant);
if( nvs_get_i8(my_handle, "rgblum", &flash8) != ESP_OK || flash8 != cfg.rgblum )
nvs_set_i8(my_handle, "rgblum", cfg.rgblum);
if( nvs_get_i16(my_handle, "rssilimit", &flash16) != ESP_OK || flash16 != cfg.rssilimit )
nvs_set_i16(my_handle, "rssilimit", cfg.rssilimit);
@ -236,11 +245,27 @@ void loadConfig() {
saveConfig();
}
if( nvs_get_i8(my_handle, "rgblum", &flash8) == ESP_OK ) {
cfg.rgblum = flash8;
ESP_LOGI(TAG, "rgbluminosity = %i", flash8);
} else {
ESP_LOGI(TAG, "RGB luminosity set to default %i", cfg.rgblum);
saveConfig();
}
if( nvs_get_i8(my_handle, "blescantime", &flash8) == ESP_OK ) {
cfg.blescantime = flash8;
ESP_LOGI(TAG, "blescantime = %i", flash8);
} else {
ESP_LOGI(TAG, "BLEscantime set to default %i", cfg.blescantime);
saveConfig();
}
if( nvs_get_i8(my_handle, "blescancycle", &flash8) == ESP_OK ) {
cfg.blescancycle = flash8;
ESP_LOGI(TAG, "blescancycle = %i", flash8);
} else {
ESP_LOGI(TAG, "BLEscan cycle set to default %i", cfg.blescancycle);
ESP_LOGI(TAG, "BLEscancycle set to default %i", cfg.blescancycle);
saveConfig();
}

View File

@ -3,6 +3,8 @@
// std::set for unified array functions
#include <set>
#include <array>
#include <algorithm>
// OLED Display
#include <U8x8lib.h>
@ -11,6 +13,12 @@
#include <lmic.h>
#include <hal/hal.h>
#ifdef HAS_RGB_LED
#include <SmartLeds.h>
#endif
#include "rgb_led.h"
#include "macsniff.h"
// Struct holding devices's runtime configuration
typedef struct {
int8_t lorasf; // 7-12, lora spreadfactor
@ -22,9 +30,11 @@ typedef struct {
int16_t rssilimit; // threshold for rssilimiter, negative value!
int8_t wifiscancycle; // wifi scan cycle [seconds/2]
int8_t wifichancycle; // wifi channel switch cycle [seconds/100]
int8_t blescancycle; // BLE scan cycle [seconds]
int8_t blescantime; // BLE scan cycle duration [seconds]
int8_t blescancycle; // BLE scan frequency, once after [blescancycle] full wifi scans
int8_t blescan; // 0=disabled, 1=enabled
int8_t wifiant; // 0=internal, 1=external (for LoPy/LoPy4)
int8_t rgblum; // RGB Led luminosity (0 100%)
char version[10]; // Firmware version
} configData_t;
@ -32,9 +42,9 @@ extern configData_t cfg;
extern uint8_t mydata[];
extern uint64_t uptimecounter;
extern osjob_t sendjob;
extern uint16_t macnum, blenum, salt;
extern int countermode, screensaver, adrmode, lorasf, txpower, rlim;
extern int countermode, screensaver, adrmode, lorasf, txpower, rlim, salt;
extern bool joinstate;
extern std::set<uint16_t> wifis;
extern std::set<uint16_t> macs;
#ifdef HAS_DISPLAY
@ -45,4 +55,5 @@ extern std::set<uint16_t> macs;
#ifdef BLECOUNTER
extern int scanTime;
extern std::set<uint16_t> bles;
#endif

View File

@ -6,6 +6,7 @@
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
#define HAS_LED NOT_A_PIN // Led os on same pin than Lora SS pin, to avoid pb, we don't use it
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
// Anyway shield is on over the LoLin32 board, so we won't be able to see this LED
#define HAS_RGB_LED 13 // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED
#define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield
@ -30,4 +31,3 @@
#define OLED_RST U8X8_PIN_NONE // Not reset pin
#define OLED_SDA 21 // ESP32 GPIO21 (Pin21) -- OLED SDA
#define OLED_SCL 22 // ESP32 GPIO22 (Pin22) -- OLED SCL

View File

@ -5,8 +5,8 @@
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
#define HAS_LED NOT_A_PIN // Led os on same pin than Lora SS pin, to avoid pb, we don't use it
// Anyway shield is on over the LoLin32 board, so we won't be able to see this LED
#define HAS_LED 22 // ESP32 GPIO12 (pin22) On Board LED
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
#define HAS_RGB_LED 13 // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED
#define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield
#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
@ -30,4 +30,3 @@
#define OLED_RST U8X8_PIN_NONE // Not reset pin
#define OLED_SDA 14 // ESP32 GPIO14 (Pin14) -- OLED SDA
#define OLED_SCL 12 // ESP32 GPIO12 (Pin12) -- OLED SCL

View File

@ -4,6 +4,7 @@
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
#define HAS_LED GPIO_NUM_2 // white LED on board
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
#define HAS_BUTTON GPIO_NUM_0 // button "PRG" on board
// re-define pin definitions of pins_arduino.h

View File

@ -79,10 +79,22 @@ void printKeys(void) {
#endif // VERBOSE
void do_send(osjob_t* j){
mydata[0] = (macnum & 0xff00) >> 8;
mydata[1] = macnum & 0x00ff;
mydata[2] = (blenum & 0xff00) >> 8;
mydata[3] = blenum & 0x00ff;
uint16_t data;
// Total BLE+WIFI unique MACs seen
data = (uint16_t) macs.size();
mydata[0] = (data & 0xff00) >> 8;
mydata[1] = data & 0xff;
// Sum of unique BLE MACs seen
data = (uint16_t) bles.size();
mydata[2] = (data & 0xff00) >> 8;
mydata[3] = data & 0xff;
// Sum of unique WIFI MACs seen
// TBD ?
//data = (uint16_t) wifis.size();
//mydata[4] = (data & 0xff00) >> 8;
//mydata[5] = data & 0xff;
// Check if there is not a current TX/RX job running
if (LMIC.opmode & OP_TXRXPEND) {
@ -95,7 +107,6 @@ void do_send(osjob_t* j){
ESP_LOGI(TAG, "Packet queued");
u8x8.clearLine(7);
u8x8.drawString(0, 7, "PACKET QUEUED");
set_onboard_led(1);
}
// Next TX is scheduled after TX_COMPLETE event.
}
@ -162,7 +173,6 @@ void onEvent (ev_t ev) {
ESP_LOGI(TAG, "EV_TXCOMPLETE (includes waiting for RX windows)");
u8x8.clearLine(7);
u8x8.drawString(0, 7, "TX COMPLETE");
set_onboard_led(0);
if (LMIC.txrxFlags & TXRX_ACK) {
ESP_LOGI(TAG, "Received ack");
u8x8.clearLine(7);
@ -224,3 +234,4 @@ void onEvent (ev_t ev) {
break;
}
}

141
src/macsniff.cpp Normal file
View File

@ -0,0 +1,141 @@
// Basic Config
#include "main.h"
#include "globals.h"
#ifdef BLECOUNTER
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
#endif
#ifdef VENDORFILTER
#include <array>
#include <algorithm>
#include "vendor_array.h"
#endif
// Local logging tag
static const char *TAG = "macsniff";
static wifi_country_t wifi_country = {.cc=WIFI_MY_COUNTRY, .schan=WIFI_CHANNEL_MIN, .nchan=WIFI_CHANNEL_MAX, .policy=WIFI_COUNTRY_POLICY_MANUAL};
uint16_t currentScanDevice = 0;
bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) {
char counter [6]; // uint16_t -> 2 byte -> 5 decimals + '0' terminator -> 6 chars
char macbuf [21]; // uint64_t -> 8 byte -> 20 decimals + '0' terminator -> 21 chars
char typebuff[8];
uint64_t addr2int;
uint32_t vendor2int;
uint16_t hashedmac;
std::pair<std::set<uint16_t>::iterator, bool> newmac;
addr2int = ( (uint64_t)paddr[0] ) | ( (uint64_t)paddr[1] << 8 ) | ( (uint64_t)paddr[2] << 16 ) | \
( (uint64_t)paddr[3] << 24 ) | ( (uint64_t)paddr[4] << 32 ) | ( (uint64_t)paddr[5] << 40 );
#ifdef VENDORFILTER
vendor2int = ( (uint32_t)paddr[2] ) | ( (uint32_t)paddr[1] << 8 ) | ( (uint32_t)paddr[0] << 16 );
// No vendor filter for BLE
if ( (sniff_type==MAC_SNIFF_BLE) || std::find(vendors.begin(), vendors.end(), vendor2int) != vendors.end() ) {
#endif
// salt and hash MAC, and if new unique one, store identifier in container and increment counter on display
// https://en.wikipedia.org/wiki/MAC_Address_Anonymization
addr2int |= (uint64_t) salt << 48; // prepend 16-bit salt to 48-bit MAC
snprintf(macbuf, 21, "%llx", addr2int); // convert unsigned 64-bit salted MAC to 16 digit hex string
hashedmac = rokkit(macbuf, 5); // hash MAC string, use 5 chars to fit hash in uint16_t container
newmac = macs.insert(hashedmac); // add hashed MAC to total container if new unique
if (sniff_type == MAC_SNIFF_WIFI ) {
newmac = wifis.insert(hashedmac); // add hashed MAC to wifi container if new unique
strcpy(typebuff, "WiFi");
} else if (sniff_type == MAC_SNIFF_BLE ) {
newmac = bles.insert(hashedmac); // add hashed MAC to BLE container if new unique
strcpy(typebuff, "BLE ");
}
if (newmac.second) { // first time seen this WIFI or BLE MAC
snprintf(counter, 6, "%i", macs.size()); // convert 16-bit MAC counter to decimal counter value
u8x8.draw2x2String(0, 0, counter);
ESP_LOGI(TAG, "%s RSSI %04d -> Hash %04x -> #%05i", typebuff, rssi, hashedmac, macs.size());
} else { // already seen WIFI or BLE MAC
ESP_LOGI(TAG, "%s RSSI %04d -> already seen", typebuff, rssi);
}
#ifdef VENDORFILTER
} else {
// Very noisy
//ESP_LOGI(TAG, "Filtered MAC %02X:%02X:%02X:%02X:%02X:%02X", paddr[0],paddr[1],paddr[2],paddr[3],paddr[5],paddr[5]);
}
#endif
// True if MAC WiFi/BLE was new
return newmac.second;
}
#ifdef BLECOUNTER
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
uint8_t *p = (uint8_t *) advertisedDevice.getAddress().getNative();
// Current devices seen on this scan session
currentScanDevice++;
mac_add(p, advertisedDevice.getRSSI(), MAC_SNIFF_BLE);
u8x8.setCursor(12,3);
u8x8.printf("%d", currentScanDevice);
}
};
void BLECount() {
int blenum = 0; // Total device seen on this scan session
currentScanDevice = 0; // Set 0 seen device on this scan session
u8x8.clearLine(3);
u8x8.drawString(0,3,"BLE Scan...");
BLEDevice::init(PROGNAME);
BLEScan* pBLEScan = BLEDevice::getScan(); //create new scan
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
BLEScanResults foundDevices = pBLEScan->start(cfg.blescantime);
blenum=foundDevices.getCount();
u8x8.clearLine(3);
u8x8.setCursor(0,3);
u8x8.printf("BLE#: %-5i %-3i",bles.size(), blenum);
}
#endif
void wifi_sniffer_init(void) {
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
cfg.nvs_enable = 0; // we don't need any wifi settings from NVRAM
wifi_promiscuous_filter_t filter = {.filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT}; // we need only MGMT frames
ESP_ERROR_CHECK(esp_wifi_init(&cfg)); // configure Wifi with cfg
ESP_ERROR_CHECK(esp_wifi_set_country(&wifi_country)); // set locales for RF and channels
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); // we don't need NVRAM
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL));
ESP_ERROR_CHECK(esp_wifi_set_promiscuous_filter(&filter)); // set MAC frame filter
ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(&wifi_sniffer_packet_handler));
ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true)); // now switch on monitor mode
}
void wifi_sniffer_set_channel(uint8_t channel) {
esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
}
void wifi_sniffer_packet_handler(void* buff, wifi_promiscuous_pkt_type_t type) {
const wifi_promiscuous_pkt_t *ppkt = (wifi_promiscuous_pkt_t *)buff;
const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)ppkt->payload;
const wifi_ieee80211_mac_hdr_t *hdr = &ipkt->hdr;
if (( cfg.rssilimit == 0 ) || (ppkt->rx_ctrl.rssi > cfg.rssilimit )) { // rssi is negative value
uint8_t *p = (uint8_t *) hdr->addr2;
mac_add(p, ppkt->rx_ctrl.rssi, MAC_SNIFF_WIFI) ;
} else {
ESP_LOGI(TAG, "WiFi RSSI %04d -> ignoring (limit: %i)", ppkt->rx_ctrl.rssi, cfg.rssilimit);
}
yield();
}

28
src/macsniff.h Normal file
View File

@ -0,0 +1,28 @@
// ESP32 Functions
#include <esp_wifi.h>
#define MAC_SNIFF_WIFI 0
#define MAC_SNIFF_BLE 1
typedef struct {
unsigned frame_ctrl:16;
unsigned duration_id:16;
uint8_t addr1[6]; /* receiver address */
uint8_t addr2[6]; /* sender address */
uint8_t addr3[6]; /* filtering address */
unsigned sequence_ctrl:16;
uint8_t addr4[6]; /* optional */
} wifi_ieee80211_mac_hdr_t;
typedef struct {
wifi_ieee80211_mac_hdr_t hdr;
uint8_t payload[0]; /* network data ended with 4 bytes csum (CRC32) */
} wifi_ieee80211_packet_t;
void BLECount();
void wifi_sniffer_init(void);
void wifi_sniffer_set_channel(uint8_t channel);
void wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type);
// function defined in rokkithash.cpp
uint32_t rokkit(const char * , int );

View File

@ -25,6 +25,9 @@ Refer to LICENSE.txt file in repository for more details.
#include "main.h"
#include "globals.h"
// std::set for unified array functions
#include <set>
// OLED driver
#include <U8x8lib.h>
@ -42,11 +45,17 @@ configData_t cfg; // struct holds current device configuration
osjob_t sendjob, initjob; // LMIC
// Initialize global variables
uint16_t macnum = 0, blenum = 0, salt;
int macnum = 0, salt;
uint64_t uptimecounter = 0;
bool joinstate = false;
std::set<uint16_t> macs; // associative container holds filtered MAC adresses
std::set<uint16_t> macs; // associative container holds total of unique MAC adress hashes (Wifi + BLE)
std::set<uint16_t> wifis; // associative container holds unique Wifi MAC adress hashes
#ifdef BLECOUNTER
std::set<uint16_t> bles; // associative container holds unique BLE MAC adresses hashes
int scanTime;
#endif
// this variable will be changed in the ISR, and read in main loop
static volatile bool ButtonTriggered = false;
@ -67,29 +76,19 @@ void eraseConfig(void);
void saveConfig(void);
void loadConfig(void);
/* begin LMIC specific parts ------------------------------------------------------------ */
#ifdef HAS_LED
void set_onboard_led(int st);
#endif
// LMIC enhanced Pin mapping
const lmic_pinmap lmic_pins = {
.mosi = PIN_SPI_MOSI,
.miso = PIN_SPI_MISO,
.sck = PIN_SPI_SCK,
.nss = PIN_SPI_SS,
.rxtx = LMIC_UNUSED_PIN,
.rst = RST,
.dio = {DIO0, DIO1, DIO2}
};
/* begin LMIC specific parts ------------------------------------------------------------ */
// defined in lorawan.cpp
void gen_lora_deveui(uint8_t * pdeveui);
void RevBytes(unsigned char* b, size_t c);
#ifdef VERBOSE
void printKeys(void);
#endif
// LMIC functions
void onEvent(ev_t ev);
void do_send(osjob_t* j);
#endif // VERBOSE
// LMIC callback functions
void os_getDevKey (u1_t *buf) {
@ -112,6 +111,21 @@ void os_getDevEui (u1_t* buf) {
gen_lora_deveui(buf); // generate DEVEUI from device's MAC
}
// LMIC enhanced Pin mapping
const lmic_pinmap lmic_pins = {
.mosi = PIN_SPI_MOSI,
.miso = PIN_SPI_MISO,
.sck = PIN_SPI_SCK,
.nss = PIN_SPI_SS,
.rxtx = LMIC_UNUSED_PIN,
.rst = RST,
.dio = {DIO0, DIO1, DIO2}
};
// LMIC functions
void onEvent(ev_t ev);
void do_send(osjob_t* j);
// LoRaWAN Initjob
static void lora_init (osjob_t* j) {
// reset MAC state
@ -125,8 +139,48 @@ static void lora_init (osjob_t* j) {
// LMIC Task
void lorawan_loop(void * pvParameters) {
configASSERT( ( ( uint32_t ) pvParameters ) == 1 ); // FreeRTOS check
static bool led_state ;
bool new_led_state ;
while(1) {
uint16_t color;
os_runloop_once();
// All follow is Led management
// Let join at the begining of if sequence,
// is prior to send because joining state send data
if ( LMIC.opmode & (OP_JOINING | OP_REJOIN) ) {
color = COLOR_YELLOW;
// Joining Quick blink 20ms on each 1/5 second
new_led_state = ((millis() % 200) < 20) ? HIGH : LOW;
// Small blink 10ms on each 1/2sec (not when joining)
} else if (LMIC.opmode & (OP_TXDATA | OP_TXRXPEND)) {
color = COLOR_BLUE;
new_led_state = ((millis() % 500) < 20) ? HIGH : LOW;
// This should not happen so indicate a pb
} else if ( LMIC.opmode & (OP_TXDATA | OP_TXRXPEND | OP_JOINING | OP_REJOIN) == 0 ) {
color = COLOR_RED;
// Heartbeat long blink 200ms on each 2 seconds
new_led_state = ((millis() % 2000) < 200) ? HIGH : LOW;
} else {
rgb_set_color(COLOR_NONE);
}
// led need to change state ?
// avoid digitalWrite() for nothing
if (led_state != new_led_state) {
if (new_led_state == HIGH) {
set_onboard_led(1);
rgb_set_color(color);
} else {
set_onboard_led(0);
rgb_set_color(COLOR_NONE);
}
led_state = new_led_state;
}
vTaskDelay(10/portTICK_PERIOD_MS);
yield();
}
@ -159,12 +213,18 @@ void lorawan_loop(void * pvParameters) {
void set_onboard_led(int st){
#ifdef HAS_LED
switch (st) {
case 1: digitalWrite(HAS_LED, HIGH); break;
case 0: digitalWrite(HAS_LED, LOW); break;
#ifdef LED_ACTIVE_LOW
case 1: digitalWrite(HAS_LED, LOW); break;
case 0: digitalWrite(HAS_LED, HIGH); break;
#else
case 1: digitalWrite(HAS_LED, HIGH); break;
case 0: digitalWrite(HAS_LED, LOW); break;
#endif
}
#endif
};
#ifdef HAS_BUTTON
// Button Handling, board dependent -> perhaps to be moved to hal/<$board.h>
// IRAM_ATTR necessary here, see https://github.com/espressif/arduino-esp32/issues/855
@ -186,40 +246,43 @@ void wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type);
void wifi_sniffer_loop(void * pvParameters) {
configASSERT( ( ( uint32_t ) pvParameters ) == 1 ); // FreeRTOS check
uint8_t channel = 1;
uint8_t channel=0;
int nloop=0, lorawait=0;
while (true) {
nloop++;
vTaskDelay(cfg.wifichancycle*10 / portTICK_PERIOD_MS);
nloop++; // acutal number of wifi loops, controls cycle when data is sent
vTaskDelay(cfg.wifichancycle*10 / portTICK_PERIOD_MS);
yield();
wifi_sniffer_set_channel(channel);
channel = (channel % WIFI_CHANNEL_MAX) + 1;
channel = (channel % WIFI_CHANNEL_MAX) + 1; // rotates variable channel 1..WIFI_CHANNEL_MAX
wifi_sniffer_set_channel(channel);
ESP_LOGI(TAG, "Wifi set channel %d", channel);
u8x8.setCursor(0,5);
u8x8.printf(!cfg.rssilimit ? "RLIM: off" : "RLIM: %4i", cfg.rssilimit);
u8x8.setCursor(11,5);
u8x8.printf("ch:%02i", channel);
u8x8.setCursor(0,4);
u8x8.printf("MAC#: %-5i", wifis.size());
// duration of one wifi scan loop reached? then send data and begin new scan cycle
if( nloop >= ((100 / cfg.wifichancycle) * (cfg.wifiscancycle * 2)) ) {
u8x8.setPowerSave(!cfg.screenon); // set display on if enabled
nloop = 0; // reset wlan sniffing loop counter
// execute BLE count if BLE function is enabled
#ifdef BLECOUNTER
if (cfg.blescan)
BLECount();
#endif
// Prepare and execute LoRaWAN data upload
u8x8.setCursor(0,4);
u8x8.printf("MAC#: %-5i", macnum);
do_send(&sendjob); // send payload
if( nloop >= ( (100 / cfg.wifichancycle) * (cfg.wifiscancycle * 2)) +1 ) {
u8x8.setPowerSave(!cfg.screenon); // set display on if enabled
nloop=0; channel=0; // reset wifi scan + channel loop counter
do_send(&sendjob); // Prepare and execute LoRaWAN data upload
vTaskDelay(500/portTICK_PERIOD_MS);
yield();
// clear counter if not in cumulative counter mode
if (cfg.countermode != 1) {
macs.clear(); // clear macs container
salt = random(65536); // get new 16bit random for salting hashes
macnum = 0;
u8x8.clearLine(0); u8x8.clearLine(1); // clear Display counter
macs.clear(); // clear all macs container
wifis.clear(); // clear Wifi macs couner
#ifdef BLECOUNTER
bles.clear(); // clear BLE macs counter
#endif
salt = random(65536); // get new 16bit random for salting hashes
u8x8.clearLine(0); u8x8.clearLine(1); // clear Display counter
}
// wait until payload is sent, while wifi scanning and mac counting task continues
@ -238,11 +301,21 @@ void wifi_sniffer_loop(void * pvParameters) {
u8x8.clearLine(6);
if (cfg.screenon && cfg.screensaver) vTaskDelay(2000/portTICK_PERIOD_MS); // pause for displaying results
if (cfg.screenon && cfg.screensaver) {
vTaskDelay(2000/portTICK_PERIOD_MS); // pause for displaying results
}
yield();
u8x8.setPowerSave(1 && cfg.screensaver); // set display off if screensaver is enabled
}
}
} // end of send data cycle
else {
#ifdef BLECOUNTER // execute BLE count if BLE function is enabled
if (nloop % (WIFI_CHANNEL_MAX * cfg.blescancycle) == 0 ) { // once after cfg.blescancycle Wifi scans, do a BLE scan
if (cfg.blescan) // execute BLE count if BLE function is enabled
BLECount();
}
#endif
} // end of channel rotation loop
} // end of infinite wifi scan loop
}
/* end wifi specific parts ------------------------------------------------------------ */
@ -336,6 +409,7 @@ void setup() {
#endif
ESP_LOGI(TAG, "Starting %s %s", PROGNAME, PROGVERSION);
rgb_set_color(COLOR_NONE);
// system event handler for wifi task, needed for wifi_sniffer_init()
esp_event_loop_init(NULL, NULL);
@ -380,7 +454,7 @@ void setup() {
antenna_init();
#endif
// initialize salt value using esp_random() called by random in arduino-esp32 core
// initialize salt value using esp_random() called by random() in arduino-esp32 core
salt = random(65536); // get new 16bit random for salting hashes
// initialize display

View File

@ -1,30 +1,35 @@
// program version
#define PROGVERSION "1.2.62" // use max 10 chars here!
#define PROGVERSION "1.2.85" // use max 10 chars here!
#define PROGNAME "PAXCNT"
// Verbose enables serial output
#define VERBOSE 1 // comment out to silence the device, for mute use build option
#define VERBOSE 1 // comment out to silence the device, for mute use build option
// set this to include BLE counting and vendor filter functions
#define VENDORFILTER 1 // comment out if you want to count things, not people
#define BLECOUNTER 1 // comment out if you don't want BLE count
#define VENDORFILTER 1 // comment out if you want to count things, not people
#define BLECOUNTER 1 // comment out if you don't want BLE count
// BLE scan time
#define BLESCANTIME 30 // [seconds]
#define BLESCANTIME 15 // [seconds]
#define BLESCANCYCLE 2 // BLE scan once after each <BLECYCLE> wifi scans
// WiFi Sniffer cycle interval
#define SEND_SECS 120 // [seconds/2] -> 240 sec.
#define SEND_SECS 120 // [seconds/2] -> 240 sec.
//#define SEND_SECS 30 // [seconds/2] -> 60 sec.
// WiFi sniffer config
#define WIFI_CHANNEL_MIN 1 // start channel number where scan begings
#define WIFI_CHANNEL_MAX 13 // total channel number to scan
#define WIFI_MY_COUNTRY "EU" // for Wifi RF settings
#define WIFI_CHANNEL_SWITCH_INTERVAL 50 // [seconds/100] -> 0,5 sec.
#define WIFI_CHANNEL_MIN 1 // start channel number where scan begings
#define WIFI_CHANNEL_MAX 13 // total channel number to scan
#define WIFI_MY_COUNTRY "EU" // select locale for Wifi RF settings
#define WIFI_CHANNEL_SWITCH_INTERVAL 50 // [seconds/100] -> 0,5 sec.
// Default LoRa Spreadfactor
#define LORASFDEFAULT 9 // 7 ... 12
#define MAXLORARETRY 500 // maximum count of TX retries if LoRa busy
#define RCMDPORT 2 // LoRaWAN Port on which device listenes for remote commands
#define LORASFDEFAULT 9 // 7 ... 12 SF, according to LoRaWAN specs
#define MAXLORARETRY 500 // maximum count of TX retries if LoRa busy
#define RCMDPORT 2 // LoRaWAN Port on which device listenes for remote commands
// Default RGB LED luminosity (in %)
#define RGBLUMINOSITY 50 // 50%
// LMIC settings
// define hardware independent LMIC settings here, settings of standard library in /lmic/config.h will be ignored
@ -99,10 +104,10 @@
// implementation is optimized for speed on 32-bit processors using
// fairly big lookup tables, but it takes up big amounts of flash on the
// AVR architecture.
// #define USE_ORIGINAL_AES
#define USE_ORIGINAL_AES
//
// This selects the AES implementation written by Ideetroon for their
// own LoRaWAN library. It also uses lookup tables, but smaller
// byte-oriented ones, making it use a lot less flash space (but it is
// also about twice as slow as the original).
#define USE_IDEETRON_AES
// #define USE_IDEETRON_AES

View File

@ -67,8 +67,9 @@ void set_reset(int val) {
break;
case 1: // reset MAC counter
ESP_LOGI(TAG, "Remote command: reset MAC counter");
macs.clear(); // clear macs container
macnum = 0;
macs.clear(); // clear all macs container
wifis.clear(); // clear Wifi macs container
bles.clear(); // clear BLE macs container
salt = random(65536); // get new 16bit random for salting hashes
u8x8.clearLine(0); u8x8.clearLine(1); // clear Display counter
u8x8.clearLine(5);
@ -103,9 +104,14 @@ void set_wifichancycle(int val) {
ESP_LOGI(TAG, "Remote command: set Wifi channel switch interval to %i seconds", cfg.wifichancycle/100);
};
void set_blescantime(int val) {
cfg.blescantime = val;
ESP_LOGI(TAG, "Remote command: set BLE scan time to %i seconds", cfg.blescantime);
};
void set_blescancycle(int val) {
cfg.blescancycle = val;
ESP_LOGI(TAG, "Remote command: set Wifi channel cycle duration to %i seconds", cfg.blescancycle);
ESP_LOGI(TAG, "Remote command: set BLE scan cycle to %i", cfg.blescancycle);
};
void set_countmode(int val) {
@ -180,6 +186,12 @@ void set_wifiant(int val) {
#endif
};
void set_rgblum(int val) {
// Avoid wrong parameters
cfg.rgblum = (val>=0 && val<=100) ? (uint8_t) val : RGBLUMINOSITY;
ESP_LOGI(TAG, "Remote command: set RGB Led luminosity %d", cfg.rgblum);
};
void set_lorapower(int val) {
ESP_LOGI(TAG, "Remote command: set LoRa TXPOWER to %i", val);
switch_lora(cfg.lorasf, val);
@ -237,9 +249,11 @@ cmd_t table[] = {
{0x09, set_reset, false},
{0x0a, set_wifiscancycle, true},
{0x0b, set_wifichancycle, true},
{0x0c, set_blescancycle, true},
{0x0d, set_blescan, true},
{0x0e, set_wifiant, true},
{0x0c, set_blescantime, true},
{0x0d, set_blescancycle, true},
{0x0e, set_blescan, true},
{0x0f, set_wifiant, true},
{0x10, set_rgblum, true},
{0x80, get_config, false},
{0x81, get_uptime, false},
{0x82, get_cputemp, false}

87
src/rgb_led.cpp Normal file
View File

@ -0,0 +1,87 @@
// Basic Config
#include "main.h"
#include "globals.h"
#ifdef HAS_RGB_LED
// RGB Led instance
SmartLed rgb_led(LED_WS2812, 1, HAS_RGB_LED);
// Luminosity from 0 to 100%
uint8_t rgb_luminosity = 50 ;
float rgb_CalcColor(float p, float q, float t)
{
if (t < 0.0f)
t += 1.0f;
if (t > 1.0f)
t -= 1.0f;
if (t < 1.0f / 6.0f)
return p + (q - p) * 6.0f * t;
if (t < 0.5f)
return q;
if (t < 2.0f / 3.0f)
return p + ((q - p) * (2.0f / 3.0f - t) * 6.0f);
return p;
}
// ------------------------------------------------------------------------
// Hue, Saturation, Lightness color members
// HslColor using H, S, L values (0.0 - 1.0)
// L should be limited to between (0.0 - 0.5)
// ------------------------------------------------------------------------
RGBColor rgb_hsl2rgb(float h, float s, float l)
{
RGBColor RGB_color;
float r;
float g;
float b;
if (s == 0.0f || l == 0.0f)
{
r = g = b = l; // achromatic or black
}
else
{
float q = l < 0.5f ? l * (1.0f + s) : l + s - (l * s);
float p = 2.0f * l - q;
r = rgb_CalcColor(p, q, h + 1.0f / 3.0f);
g = rgb_CalcColor(p, q, h);
b = rgb_CalcColor(p, q, h - 1.0f / 3.0f);
}
RGB_color.R = (uint8_t)(r * 255.0f);
RGB_color.G = (uint8_t)(g * 255.0f);
RGB_color.B = (uint8_t)(b * 255.0f);
return RGB_color;
}
void rgb_set_color(uint16_t hue) {
if (hue == COLOR_NONE) {
// Off
rgb_led[0] = Rgb(0,0,0);
} else {
// see http://www.workwithcolor.com/blue-color-hue-range-01.htm
// H (is color from 0..360) should be between 0.0 and 1.0
// S is saturation keep it to 1
// L is brightness should be between 0.0 and 0.5
// rgb_luminosity is between 0 and 100 (percent)
RGBColor target = rgb_hsl2rgb( hue / 360.0f, 1.0f, 0.005f * cfg.rgblum);
//uint32_t color = target.R<<16 | target.G<<8 | target.B;
rgb_led[0] = Rgb(target.R, target.G, target.B);
}
// Show
rgb_led.show();
}
#else
// No RGB LED empty functions
void rgb_set_color(uint16_t hue) {}
#endif

30
src/rgb_led.h Normal file
View File

@ -0,0 +1,30 @@
#pragma once
// value for HSL color
// see http://www.workwithcolor.com/blue-color-hue-range-01.htm
#define COLOR_RED 0
#define COLOR_ORANGE 30
#define COLOR_ORANGE_YELLOW 45
#define COLOR_YELLOW 60
#define COLOR_YELLOW_GREEN 90
#define COLOR_GREEN 120
#define COLOR_GREEN_CYAN 165
#define COLOR_CYAN 180
#define COLOR_CYAN_BLUE 210
#define COLOR_BLUE 240
#define COLOR_BLUE_MAGENTA 275
#define COLOR_MAGENTA 300
#define COLOR_PINK 350
#define COLOR_WHITE 360
#define COLOR_NONE 999
struct RGBColor
{
uint8_t R;
uint8_t G;
uint8_t B;
};
// Exported Functions
void rgb_set_color(uint16_t hue);

File diff suppressed because it is too large Load Diff

View File

@ -1,98 +0,0 @@
// Basic Config
#include "main.h"
#include "globals.h"
// ESP32 Functions
#include <esp_wifi.h>
#ifdef VENDORFILTER
#include <array>
#include <algorithm>
#include "vendor_array.h"
#endif
// Local logging tag
static const char *TAG = "wifisniffer";
// function defined in rokkithash.cpp
uint32_t rokkit(const char * , int );
static wifi_country_t wifi_country = {.cc=WIFI_MY_COUNTRY, .schan=WIFI_CHANNEL_MIN, .nchan=WIFI_CHANNEL_MAX, .policy=WIFI_COUNTRY_POLICY_MANUAL};
typedef struct {
unsigned frame_ctrl:16;
unsigned duration_id:16;
uint8_t addr1[6]; /* receiver address */
uint8_t addr2[6]; /* sender address */
uint8_t addr3[6]; /* filtering address */
unsigned sequence_ctrl:16;
uint8_t addr4[6]; /* optional */
} wifi_ieee80211_mac_hdr_t;
typedef struct {
wifi_ieee80211_mac_hdr_t hdr;
uint8_t payload[0]; /* network data ended with 4 bytes csum (CRC32) */
} wifi_ieee80211_packet_t;
extern void wifi_sniffer_init(void);
extern void wifi_sniffer_set_channel(uint8_t channel);
extern void wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type);
void wifi_sniffer_init(void) {
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
cfg.nvs_enable = 0; // we don't need any wifi settings from NVRAM
wifi_promiscuous_filter_t filter = {.filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT}; // we need only MGMT frames
ESP_ERROR_CHECK(esp_wifi_init(&cfg)); // configure Wifi with cfg
ESP_ERROR_CHECK(esp_wifi_set_country(&wifi_country)); // set locales for RF and channels
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); // we don't need NVRAM
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL));
ESP_ERROR_CHECK(esp_wifi_set_promiscuous_filter(&filter)); // set MAC frame filter
ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(&wifi_sniffer_packet_handler));
ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true)); // now switch on monitor mode
}
void wifi_sniffer_set_channel(uint8_t channel) {
esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
}
void wifi_sniffer_packet_handler(void* buff, wifi_promiscuous_pkt_type_t type) {
const wifi_promiscuous_pkt_t *ppkt = (wifi_promiscuous_pkt_t *)buff;
const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)ppkt->payload;
const wifi_ieee80211_mac_hdr_t *hdr = &ipkt->hdr;
char counter [6]; // uint16_t -> 2 byte -> 5 decimals + '0' terminator -> 6 chars
char macbuf [21]; // uint64_t -> 8 byte -> 10 decimals + '0' terminator -> 21 chars
uint64_t addr2int;
uint32_t vendor2int;
uint16_t hashedmac;
std::pair<std::set<uint16_t>::iterator, bool> newmac;
if (( cfg.rssilimit == 0 ) || (ppkt->rx_ctrl.rssi > cfg.rssilimit )) { // rssi is negative value
addr2int = ( (uint64_t)hdr->addr2[0] ) | ( (uint64_t)hdr->addr2[1] << 8 ) | ( (uint64_t)hdr->addr2[2] << 16 ) | \
( (uint64_t)hdr->addr2[3] << 24 ) | ( (uint64_t)hdr->addr2[4] << 32 ) | ( (uint64_t)hdr->addr2[5] << 40 );
#ifdef VENDORFILTER // uses vendor array with prefiltered OUIs (no local nd no group MACs, bits 0+1 in 1st byte of OUI)
vendor2int = ( (uint32_t)hdr->addr2[2] ) | ( (uint32_t)hdr->addr2[1] << 8 ) | ( (uint32_t)hdr->addr2[0] << 16 );
if ( std::find(vendors.begin(), vendors.end(), vendor2int) != vendors.end() ) {
#endif
// salt and hash MAC, and if new unique one, store identifier in container and increment counter on display
// https://en.wikipedia.org/wiki/MAC_Address_Anonymization
addr2int |= (uint64_t) salt << 48; // prepend 16-bit salt to 48-bit MAC
snprintf(macbuf, 21, "%llx", addr2int); // convert unsigned 64-bit salted MAC to 16 digit hex string
hashedmac = rokkit(macbuf, 5); // hash MAC string, use 5 chars to fit hash in uint16_t container
newmac = macs.insert(hashedmac); // store hashed MAC only if first time seen
if (newmac.second) { // if first time seen MAC
macnum++; // increment MAC counter
snprintf(counter, 6, "%i", macnum); // convert 16-bit MAC counter to decimal counter value
u8x8.draw2x2String(0, 0, counter); // display counter
ESP_LOGI(TAG, "#%05i: RSSI %04d -> Hash %04x", macnum, ppkt->rx_ctrl.rssi, hashedmac);
}
#ifdef VENDORFILTER
}
#endif
} else
ESP_LOGI(TAG, "RSSI %04d -> ignoring (limit: %i)", ppkt->rx_ctrl.rssi, cfg.rssilimit);
yield();
}