beacon monitor (experimental)

This commit is contained in:
Klaus K Wilting 2018-07-31 00:00:24 +02:00
parent 7b70009cb1
commit b0fa9868b1
18 changed files with 151 additions and 114 deletions

View File

@ -226,8 +226,6 @@ function Converter(decoded, port) {
# Remote control
The device listenes for remote control commands on LoRaWAN Port 2.
Each command is followed by exactly one parameter.
Multiple command/parameter pairs can be concatenated and sent in one single payload downlink.
Note: all settings are stored in NVRAM and will be reloaded when device starts. To reset device to factory settings press button (if device has one), or send remote command 09 02 09 00 unconfirmed(!) once.
@ -313,11 +311,16 @@ Note: all settings are stored in NVRAM and will be reloaded when device starts.
0 ... 100 percentage of luminosity (100% = full light)
e.g. 50 -> 50% of luminosity [default]
0x11 set Beacon monitor mode on/off
0x11 set beacon monitor mode on/off
0 = Beacon monitor mode off [default]
1 = Beacon monitor mode on, enables proximity alarm if test beacons are seen
0x12 set or reset a beacon MAC
byte 1 = beacon ID (0..255)
bytes 2..9 = beacon MAC
0x80 get device configuration
Device answers with it's current configuration on Port 3.

View File

@ -12,11 +12,11 @@
; ---> SELECT TARGET PLATFORM HERE! <---
[platformio]
;env_default = test
;env_default = generic
env_default = generic
;env_default = heltec
;env_default = ttgov1
;env_default = ttgov2
env_default = ttgov21
;env_default = ttgov21
;env_default = ttgobeam
;env_default = lopy
;env_default = lopy4
@ -28,9 +28,8 @@ env_default = ttgov21
description = Paxcounter is a proof-of-concept ESP32 device for metering passenger flows in realtime. It counts how many mobile devices are around.
[common_env_data]
;platform_espressif32 = espressif32@1.0.2
;platform_espressif32 = espressif32@1.1.2
platform_espressif32 = https://github.com/platformio/platform-espressif32.git#feature/stage
platform_espressif32 = espressif32@1.2.0
;platform_espressif32 = https://github.com/platformio/platform-espressif32.git#feature/stage
board_build.partitions = no_ota.csv
lib_deps_all =
lib_deps_display =
@ -132,6 +131,7 @@ lib_deps =
${common_env_data.lib_deps_gps}
build_flags =
${common_env_data.build_flags}
-mfix-esp32-psram-cache-issue
[env:fipy]
platform = ${common_env_data.platform_espressif32}
@ -173,6 +173,7 @@ lib_deps =
${common_env_data.lib_deps_gps}
build_flags =
${common_env_data.build_flags}
-mfix-esp32-psram-cache-issue
[env:lolin32litelora]
platform = ${common_env_data.platform_espressif32}

View File

@ -1,3 +1,11 @@
std::array<uint32_t, 5>::iterator it;
#ifndef _BEACON_ARRAY_H
#define _BEACON_ARRAY_H
std::array<uint32_t, 5> beacons = {0,0,0,0,0};
std::array<uint64_t, 0xfe>::iterator it;
std::array<uint64_t, 0xfe> beacons = {0x0102030405060708, 0xaaaaaaaaaaaaaaaa,
0xaaaaaaaaaaaaaaa1, 0x0000807abf6f522b,
0x0000807abfedb08e, 0x00005BEEB69AA8FC,
0x0000DB53A5362400};
#endif

View File

@ -1,5 +1,5 @@
#ifndef _HOMECYCLE_H
#define _HOMECYCLE_H
#ifndef _CYCLIC_H
#define _CYCLIC_H
void doHomework(void);
void checkHousekeeping(void);

View File

@ -48,6 +48,9 @@ extern portMUX_TYPE timerMux;
extern volatile int SendCycleTimerIRQ, HomeCycleIRQ, DisplayTimerIRQ,
ChannelTimerIRQ, ButtonPressedIRQ;
extern std::array<uint64_t, 0xfe>::iterator it;
extern std::array<uint64_t, 0xfe> beacons;
#ifdef HAS_GPS
#include "gps.h"
#endif

View File

@ -4,7 +4,7 @@
#define HAS_SPI 1 // comment out if device shall not send data via SPI
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
#define BOARD_HAS_PSRAM // use extra 4MB extern RAM
#define BOARD_HAS_PSRAM // use extra 4MB external RAM
#define HAS_LED GPIO_NUM_21 // on board green LED
#define HAS_BUTTON GPIO_NUM_39 // on board button "BOOT" (next to reset button)

View File

@ -209,21 +209,10 @@ void onEvent(ev_t ev) {
sprintf(display_line6, "RSSI %d SNR %d", LMIC.rssi,
(signed char)LMIC.snr / 4);
// check if payload received on command port, then call remote command
// interpreter
// check if command is received on command port, then call interpreter
if ((LMIC.txrxFlags & TXRX_PORT) &&
(LMIC.frame[LMIC.dataBeg - 1] == RCMDPORT)) {
// caution: buffering LMIC values here because rcommand() can modify
// LMIC.frame
unsigned char *buffer = new unsigned char[MAX_LEN_FRAME];
memcpy(buffer, LMIC.frame, MAX_LEN_FRAME); // Copy data from cfg to
// char*
int i, k = LMIC.dataBeg, l = LMIC.dataBeg + LMIC.dataLen - 2;
for (i = k; i <= l; i += 2) {
rcommand(buffer[i], buffer[i + 1]);
}
delete[] buffer; // free memory
}
(LMIC.frame[LMIC.dataBeg - 1] == RCMDPORT))
rcommand(LMIC.frame + LMIC.dataBeg, LMIC.dataLen);
}
break;
@ -251,5 +240,4 @@ void lorawan_loop(void *pvParameters) {
}
}
#endif // HAS_LORA

View File

@ -14,6 +14,7 @@ void os_getDevKey(u1_t *buf);
void os_getArtEui(u1_t *buf);
void os_getDevEui(u1_t *buf);
void printKeys(void);
void printKey(const char *name, const uint8_t *key, uint8_t len, bool lsb);
void lorawan_loop(void *pvParameters);
#endif

View File

@ -6,9 +6,6 @@
#include "vendor_array.h"
#endif
#include "beacon_array.h"
#include "senddata.h"
// Local logging tag
static const char TAG[] = "wifi";
@ -18,7 +15,6 @@ static wifi_country_t wifi_country = {WIFI_MY_COUNTRY, WIFI_CHANNEL_MIN,
WIFI_COUNTRY_POLICY_MANUAL};
*/
static wifi_country_t wifi_country = {WIFI_MY_COUNTRY, WIFI_CHANNEL_MIN,
WIFI_CHANNEL_MAX,
WIFI_COUNTRY_POLICY_MANUAL};
@ -31,21 +27,28 @@ uint16_t reset_salt(void) {
return salt;
}
uint8_t isBeacon(uint32_t mac) {
uint8_t isBeacon(uint64_t mac) {
it = std::find(beacons.begin(), beacons.end(), mac);
if (it != beacons.end())
return std::distance(beacons.begin(), it);
else
return 0;
return 0xff;
}
uint64_t macConvert(uint8_t *paddr) {
return ((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);
}
bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) {
char buff[16]; // temporary buffer for printf
bool added = false;
uint8_t beaconID; // beacon number in test monitor mode
uint16_t hashedmac; // temporary buffer for generated hash value
uint32_t addr2int, vendor2int; // temporary buffer for MAC and Vendor OUI
uint8_t beaconID; // beacon number in test monitor mode
uint16_t hashedmac; // temporary buffer for generated hash value
uint32_t addr2int,
vendor2int; // temporary buffer for shortened MAC and Vendor OUI
// only last 3 MAC Address bytes are used for MAC address anonymization
// but since it's uint32 we take 4 bytes to avoid 1st value to be 0
@ -94,8 +97,8 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) {
// in beacon monitor mode check if seen MAC is a known beacon
if (cfg.monitormode) {
beaconID = isBeacon(addr2int);
if (beaconID) {
beaconID = isBeacon(macConvert(paddr));
if (beaconID != 0xff) {
ESP_LOGI(TAG, "Beacon ID#%d detected", beaconID);
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
blink_LED(COLOR_WHITE, 2000);
@ -115,7 +118,6 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) {
added ? "new " : "known",
sniff_type == MAC_SNIFF_WIFI ? "WiFi" : "BLTH", rssi, buff,
hashedmac, macs_wifi, macs_ble, ESP.getFreeHeap());
#ifdef VENDORFILTER
} else {
// Very noisy

View File

@ -8,6 +8,8 @@
#include "hash.h"
#include "led.h"
#include "senddata.h"
#define MAC_SNIFF_WIFI 0
#define MAC_SNIFF_BLE 1
@ -33,5 +35,6 @@ void wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type);
bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type);
void ChannelSwitchIRQ(void);
void wifi_channel_loop(void *pvParameters);
uint64_t macConvert(uint8_t *paddr);
#endif

View File

@ -27,6 +27,7 @@ licenses. Refer to LICENSE.txt file in repository for more details.
#include "globals.h"
#include "main.h"
configData_t cfg; // struct holds current device configuration
char display_line6[16], display_line7[16]; // display buffers
uint8_t channel = 0; // channel rotation counter
@ -46,7 +47,7 @@ portMUX_TYPE timerMux =
// handler shared variables
std::set<uint16_t> macs; // associative container holding unique MAC
// adress hashes (Wifi + BLE)
// adress hashes (Wifi + BLE)
// initialize payload encoder
PayloadConvert payload(PAYLOAD_BUFFER_SIZE);

View File

@ -5,7 +5,8 @@
#include "macsniff.h"
#include "configmanager.h"
#include "senddata.h"
#include "homecycle.h"
#include "cyclic.h"
#include "beacon_array.h"
#include <esp_spi_flash.h> // needed for reading ESP32 chip attributes
#include <esp_event_loop.h> // needed for Wifi event handler

View File

@ -9,7 +9,7 @@
// Payload send cycle and encoding
#define SEND_SECS 30 // payload send cycle [seconds/2] -> 60 sec.
#define PAYLOAD_ENCODER 1 // payload encoder: 1=Plain, 2=Packed, 3=CayenneLPP dynamic, 4=CayenneLPP packed
#define PAYLOAD_ENCODER 2 // payload encoder: 1=Plain, 2=Packed, 3=CayenneLPP dynamic, 4=CayenneLPP packed
// Set this to include BLE counting and vendor filter functions
#define VENDORFILTER 1 // comment out if you want to count things, not people

View File

@ -50,7 +50,6 @@ public:
private:
uint8_t *buffer;
uint8_t cursor;
};
#elif PAYLOAD_ENCODER == 2 // format packed
@ -66,7 +65,6 @@ private:
void writeTemperature(float temperature);
void writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f, bool g,
bool h);
};
#elif (PAYLOAD_ENCODER == 3 || PAYLOAD_ENCODER == 4) // format cayenne lpp
@ -74,12 +72,13 @@ private:
uint8_t *buffer;
uint8_t maxsize;
uint8_t cursor;
};
#else
#error "No valid payload converter defined"
#endif
};
extern PayloadConvert payload;
#endif // _PAYLOAD_H_

View File

@ -1,7 +1,5 @@
// remote command interpreter
// parses multiple number of command / value pairs from LoRaWAN remote command
// port (RCMDPORT) checks commands and executes each command with 1 argument per
// command
// remote command interpreter, parses and executes commands with arguments in
// array
// Basic Config
#include "globals.h"
@ -60,8 +58,8 @@ void switch_lora(uint8_t sf, uint8_t tx) {
#endif // HAS_LORA
// set of functions that can be triggered by remote commands
void set_reset(uint8_t val) {
switch (val) {
void set_reset(uint8_t val[]) {
switch (val[0]) {
case 0: // restart device
ESP_LOGI(TAG, "Remote command: restart device");
sprintf(display_line6, "Reset pending");
@ -84,13 +82,13 @@ void set_reset(uint8_t val) {
}
};
void set_rssi(uint8_t val) {
cfg.rssilimit = val * -1;
void set_rssi(uint8_t val[]) {
cfg.rssilimit = val[0] * -1;
ESP_LOGI(TAG, "Remote command: set RSSI limit to %d", cfg.rssilimit);
};
void set_sendcycle(uint8_t val) {
cfg.sendcycle = val;
void set_sendcycle(uint8_t val[]) {
cfg.sendcycle = val[0];
// update send cycle interrupt
timerAlarmWrite(sendCycle, cfg.sendcycle * 2 * 10000, true);
// reload interrupt after each trigger of channel switch cycle
@ -98,8 +96,8 @@ void set_sendcycle(uint8_t val) {
cfg.sendcycle * 2);
};
void set_wifichancycle(uint8_t val) {
cfg.wifichancycle = val;
void set_wifichancycle(uint8_t val[]) {
cfg.wifichancycle = val[0];
// update channel rotation interrupt
timerAlarmWrite(channelSwitch, cfg.wifichancycle * 10000, true);
@ -108,8 +106,8 @@ void set_wifichancycle(uint8_t val) {
cfg.wifichancycle / float(100));
};
void set_blescantime(uint8_t val) {
cfg.blescantime = val;
void set_blescantime(uint8_t val[]) {
cfg.blescantime = val[0];
ESP_LOGI(TAG, "Remote command: set BLE scan time to %.1f seconds",
cfg.blescantime / float(100));
#ifdef BLECOUNTER
@ -121,8 +119,8 @@ void set_blescantime(uint8_t val) {
#endif
};
void set_countmode(uint8_t val) {
switch (val) {
void set_countmode(uint8_t val[]) {
switch (val[0]) {
case 0: // cyclic unconfirmed
cfg.countermode = 0;
ESP_LOGI(TAG, "Remote command: set counter mode to cyclic unconfirmed");
@ -138,11 +136,12 @@ void set_countmode(uint8_t val) {
}
};
void set_screensaver(uint8_t val) {
ESP_LOGI(TAG, "Remote command: set screen saver to %s ", val ? "on" : "off");
switch (val) {
void set_screensaver(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: set screen saver to %s ",
val[0] ? "on" : "off");
switch (val[0]) {
case 1:
cfg.screensaver = val;
cfg.screensaver = 1;
break;
default:
cfg.screensaver = 0;
@ -150,11 +149,11 @@ void set_screensaver(uint8_t val) {
}
};
void set_display(uint8_t val) {
ESP_LOGI(TAG, "Remote command: set screen to %s", val ? "on" : "off");
switch (val) {
void set_display(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: set screen to %s", val[0] ? "on" : "off");
switch (val[0]) {
case 1:
cfg.screenon = val;
cfg.screenon = 1;
break;
default:
cfg.screenon = 0;
@ -162,11 +161,11 @@ void set_display(uint8_t val) {
}
};
void set_gps(uint8_t val) {
ESP_LOGI(TAG, "Remote command: set GPS mode to %s", val ? "on" : "off");
switch (val) {
void set_gps(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: set GPS mode to %s", val[0] ? "on" : "off");
switch (val[0]) {
case 1:
cfg.gpsmode = val;
cfg.gpsmode = 1;
break;
default:
cfg.gpsmode = 0;
@ -174,11 +173,23 @@ void set_gps(uint8_t val) {
}
};
void set_monitor(uint8_t val) {
ESP_LOGI(TAG, "Remote command: set Monitor mode to %s", val ? "on" : "off");
switch (val) {
void set_beacon(uint8_t val[]) {
if ( (sizeof(*val) / sizeof(val[0]) == 9) && (val[0] <= 0xff ) ) {
uint8_t id = val[0]; // use first parameter as beacon storage id
memmove(val, val + 1, 8); // strip off storage id
beacons[id] = macConvert(val); // store beacon MAC in array
ESP_LOGI(TAG, "Remote command: set beacon ID#%d", id);
printKey("MAC", val, 8, false); // show beacon MAC
} else
ESP_LOGW(TAG, "Remote command: set beacon called with invalid parameters");
};
void set_monitor(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: set beacon monitor mode to %s",
val ? "on" : "off");
switch (val[0]) {
case 1:
cfg.monitormode = val;
cfg.monitormode = 1;
break;
default:
cfg.monitormode = 0;
@ -186,21 +197,22 @@ void set_monitor(uint8_t val) {
}
};
void set_lorasf(uint8_t val) {
void set_lorasf(uint8_t val[]) {
#ifdef HAS_LORA
ESP_LOGI(TAG, "Remote command: set LoRa SF to %d", val);
switch_lora(val, cfg.txpower);
ESP_LOGI(TAG, "Remote command: set LoRa SF to %d", val[0]);
switch_lora(val[0], cfg.txpower);
#else
ESP_LOGW(TAG, "Remote command: LoRa not implemented");
#endif // HAS_LORA
};
void set_loraadr(uint8_t val) {
void set_loraadr(uint8_t val[]) {
#ifdef HAS_LORA
ESP_LOGI(TAG, "Remote command: set LoRa ADR mode to %s", val ? "on" : "off");
switch (val) {
ESP_LOGI(TAG, "Remote command: set LoRa ADR mode to %s",
val[0] ? "on" : "off");
switch (val[0]) {
case 1:
cfg.adrmode = val;
cfg.adrmode = 1;
break;
default:
cfg.adrmode = 0;
@ -212,9 +224,9 @@ void set_loraadr(uint8_t val) {
#endif // HAS_LORA
};
void set_blescan(uint8_t val) {
ESP_LOGI(TAG, "Remote command: set BLE scanner to %s", val ? "on" : "off");
switch (val) {
void set_blescan(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: set BLE scanner to %s", val[0] ? "on" : "off");
switch (val[0]) {
case 0:
cfg.blescan = 0;
macs_ble = 0; // clear BLE counter
@ -231,12 +243,12 @@ void set_blescan(uint8_t val) {
}
};
void set_wifiant(uint8_t val) {
void set_wifiant(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: set Wifi antenna to %s",
val ? "external" : "internal");
switch (val) {
val[0] ? "external" : "internal");
switch (val[0]) {
case 1:
cfg.wifiant = val;
cfg.wifiant = 1;
break;
default:
cfg.wifiant = 0;
@ -247,12 +259,12 @@ void set_wifiant(uint8_t val) {
#endif
};
void set_vendorfilter(uint8_t val) {
void set_vendorfilter(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: set vendorfilter mode to %s",
val ? "on" : "off");
switch (val) {
val[0] ? "on" : "off");
switch (val[0]) {
case 1:
cfg.vendorfilter = val;
cfg.vendorfilter = 1;
break;
default:
cfg.vendorfilter = 0;
@ -260,29 +272,29 @@ void set_vendorfilter(uint8_t val) {
}
};
void set_rgblum(uint8_t val) {
void set_rgblum(uint8_t val[]) {
// Avoid wrong parameters
cfg.rgblum = (val >= 0 && val <= 100) ? (uint8_t)val : RGBLUMINOSITY;
cfg.rgblum = (val[0] >= 0 && val[0] <= 100) ? (uint8_t)val[0] : RGBLUMINOSITY;
ESP_LOGI(TAG, "Remote command: set RGB Led luminosity %d", cfg.rgblum);
};
void set_lorapower(uint8_t val) {
void set_lorapower(uint8_t val[]) {
#ifdef HAS_LORA
ESP_LOGI(TAG, "Remote command: set LoRa TXPOWER to %d", val);
switch_lora(cfg.lorasf, val);
ESP_LOGI(TAG, "Remote command: set LoRa TXPOWER to %d", val[0]);
switch_lora(cfg.lorasf, val[0]);
#else
ESP_LOGW(TAG, "Remote command: LoRa not implemented");
#endif // HAS_LORA
};
void get_config(uint8_t val) {
void get_config(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: get device configuration");
payload.reset();
payload.addConfig(cfg);
senddata(CONFIGPORT);
};
void get_status(uint8_t val) {
void get_status(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: get device status");
#ifdef HAS_BATTERY_PROBE
uint16_t voltage = read_voltage();
@ -294,7 +306,7 @@ void get_status(uint8_t val) {
senddata(STATUSPORT);
};
void get_gps(uint8_t val) {
void get_gps(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: get gps status");
#ifdef HAS_GPS
gps_read();
@ -317,24 +329,36 @@ cmd_t table[] = {{0x01, set_rssi, true}, {0x02, set_countmode, true},
{0x0b, set_wifichancycle, true}, {0x0c, set_blescantime, true},
{0x0d, set_vendorfilter, false}, {0x0e, set_blescan, true},
{0x0f, set_wifiant, true}, {0x10, set_rgblum, true},
{0x11, set_monitor, true}, {0x80, get_config, false},
{0x81, get_status, false}, {0x84, get_gps, false}};
{0x11, set_monitor, true}, {0x12, set_beacon, false},
{0x80, get_config, false}, {0x81, get_status, false},
{0x84, get_gps, false}};
// check and execute remote command
void rcommand(uint8_t cmd, uint8_t arg) {
void rcommand(uint8_t cmd[], uint8_t cmdlength) {
if (cmdlength == 0)
return;
int i =
sizeof(table) / sizeof(table[0]); // number of commands in command table
bool store_flag = false;
while (i--) {
if (cmd == table[i].nam) { // check if valid command
table[i].func(arg); // then execute assigned function
if (table[i].store)
if (cmd[0] == table[i].opcode) { // lookup command in opcode table
if (cmdlength) {
memmove(cmd, cmd + 1,
cmdlength - 1); // cutout opcode from parameter array
table[i].func(cmd); // execute assigned function with given parameters
} else
table[i].func(0); // execute assigned function with dummy as parameter
if (table[i].store) // ceck if function needs to store configuration after
// execution
store_flag =
true; // set save flag if function needs to store configuration
break; // exit check loop, since command was found
}
}
if (store_flag)
saveConfig(); // if save flag is set: store new configuration in NVS to make
// it persistent
}
} // rcommand()

View File

@ -3,15 +3,17 @@
#include "senddata.h"
#include "configmanager.h"
#include "lorawan.h"
#include "macsniff.h"
// table of remote commands and assigned functions
typedef struct {
const uint8_t nam;
void (*func)(uint8_t);
const uint8_t opcode;
void (*func)(uint8_t []);
const bool store;
} cmd_t;
void rcommand(uint8_t cmd, uint8_t arg);
void rcommand(uint8_t cmd[], uint8_t cmdlength);
void switch_lora(uint8_t sf, uint8_t tx);
#endif

View File

@ -8,6 +8,7 @@ void senddata(uint8_t port) {
if (LMIC.opmode & OP_TXRXPEND) {
ESP_LOGI(TAG, "LoRa busy, data not sent");
sprintf(display_line7, "LORA BUSY");
// to be done: add queuing here, e.g. use RTos xQueueSend
} else {
LMIC_setTxData2(
PAYLOAD_ENCODER <= 2 ? port