Merge branch 'master' of https://github.com/cyberman54/ESP32-Paxcounter
This commit is contained in:
commit
d516c49df6
13
README.md
13
README.md
@ -6,6 +6,7 @@
|
||||
<img src="img/Paxcounter-title.jpg">
|
||||
<img src="img/Paxcounter-ttgo.jpg">
|
||||
<img src="img/Paxcounter-lolin.gif">
|
||||
<img src="img/Paxcounter-Screen.png">
|
||||
|
||||
# Use case
|
||||
|
||||
@ -49,7 +50,7 @@ Target platform must be selected in [platformio.ini](https://github.com/cyberman
|
||||
Hardware dependent settings (pinout etc.) are stored in board files in /hal directory.<br>
|
||||
|
||||
<b>3D printable cases</b> can be found (and, if wanted so, ordered) on Thingiverse, see
|
||||
<A HREF="https://www.thingiverse.com/thing:2670713">Heltec</A>, <A HREF="https://www.thingiverse.com/thing:2811127">TTGOv2</A>, <A HREF="https://www.thingiverse.com/thing:3005574">TTGOv2.1</A> for example.<br>
|
||||
<A HREF="https://www.thingiverse.com/thing:2670713">Heltec</A>, <A HREF="https://www.thingiverse.com/thing:2811127">TTGOv2</A>, <A HREF="https://www.thingiverse.com/thing:3005574">TTGOv2.1</A>, <A HREF="https://www.thingiverse.com/thing:3041339">T-BEAM</A> for example.<br>
|
||||
|
||||
<b>Power consumption</b> was metered at around 750 - 1000mW, depending on board and user settings in paxcounter.conf. If you are limited on battery, you may want to save around 30% power by disabling bluetooth (commenting out line *#define BLECOUNTER* in paxcounter.conf).
|
||||
|
||||
@ -115,7 +116,7 @@ Paxcounter generates identifiers for sniffed MAC adresses and collects them temp
|
||||
|
||||
# Payload format
|
||||
|
||||
You can select different payload formats in [paxcounter.conf](src/paxcounter.conf#L40):
|
||||
You can select different payload formats in [paxcounter.conf](src/paxcounter.conf#L12):
|
||||
|
||||
- ***Plain*** uses big endian format and generates json fields, e.g. useful for TTN console
|
||||
|
||||
@ -137,9 +138,10 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering.
|
||||
|
||||
**Port #2:** Device status query result
|
||||
|
||||
byte 1-2: Battery or USB Voltage [mV], 0 if unreadable
|
||||
byte 1-2: Battery or USB Voltage [mV], 0 if no battery probe
|
||||
byte 3-10: Uptime [seconds]
|
||||
bytes 11-14: CPU temperature [°C]
|
||||
bytes 15-18: Free RAM [bytes]
|
||||
|
||||
**Port #3:** Device configuration query result
|
||||
|
||||
@ -225,9 +227,9 @@ function Converter(decoded, port) {
|
||||
|
||||
# Remote control
|
||||
|
||||
The device listenes for remote control commands on LoRaWAN Port 2.
|
||||
The device listenes for remote control commands on LoRaWAN Port 2. Multiple commands per downlink are possible by concatenating them.
|
||||
|
||||
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.
|
||||
Note: all settings are stored in NVRAM and will be reloaded when device starts. To reset device to factory settings send remote command 09 02 09 00 unconfirmed(!) once.
|
||||
|
||||
0x01 set scan RSSI limit
|
||||
|
||||
@ -275,6 +277,7 @@ Note: all settings are stored in NVRAM and will be reloaded when device starts.
|
||||
0 = restart device
|
||||
1 = reset MAC counter to zero
|
||||
2 = reset device to factory settings
|
||||
3 = flush send queues
|
||||
|
||||
0x0A set LoRaWAN payload send cycle
|
||||
|
||||
|
BIN
img/Paxcounter-Screen.png
Normal file
BIN
img/Paxcounter-Screen.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
@ -11,8 +11,8 @@
|
||||
|
||||
; ---> SELECT TARGET PLATFORM HERE! <---
|
||||
[platformio]
|
||||
;env_default = test
|
||||
env_default = generic
|
||||
;env_default = ebox
|
||||
;env_default = heltec
|
||||
;env_default = ttgov1
|
||||
;env_default = ttgov2
|
||||
@ -40,25 +40,32 @@ lib_deps_gps =
|
||||
TinyGPSPlus@>=1.0.2
|
||||
Time@>=1.5
|
||||
build_flags =
|
||||
; we need build_flag for logging, otherwise we can't use ESP_LOGx in arduino framework
|
||||
; ---> NOTE: For production run set DEBUG_LEVEL level to NONE! <---
|
||||
; otherwise device may crash in dense environments due to serial buffer overflow
|
||||
; otherwise device may leak RAM
|
||||
;
|
||||
; -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE
|
||||
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_INFO
|
||||
; -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
|
||||
; -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
|
||||
; None
|
||||
; -DCORE_DEBUG_LEVEL=0
|
||||
; Error
|
||||
; -DCORE_DEBUG_LEVEL=1
|
||||
; Warn
|
||||
-DCORE_DEBUG_LEVEL=2
|
||||
; Info
|
||||
; -DCORE_DEBUG_LEVEL=3
|
||||
; Debug
|
||||
; -DCORE_DEBUG_LEVEL=4
|
||||
; Verbose
|
||||
; -DCORE_DEBUG_LEVEL=5
|
||||
;
|
||||
; override lora settings from LMiC library in lmic/config.h and use main.h instead
|
||||
-D_lmic_config_h_
|
||||
-include "src/paxcounter.conf"
|
||||
-include "src/hal/${PIOENV}.h"
|
||||
; -w
|
||||
-w
|
||||
|
||||
[env:test]
|
||||
[env:ebox]
|
||||
platform = ${common_env_data.platform_espressif32}
|
||||
framework = arduino
|
||||
board = heltec_wifi_lora_32
|
||||
board = esp32dev
|
||||
board_build.partitions = ${common_env_data.board_build.partitions}
|
||||
upload_speed = 115200
|
||||
monitor_speed = 115200
|
||||
@ -227,4 +234,4 @@ lib_deps =
|
||||
${common_env_data.lib_deps_gps}
|
||||
${common_env_data.lib_deps_display}
|
||||
build_flags =
|
||||
${common_env_data.build_flags}
|
||||
${common_env_data.build_flags}
|
||||
|
@ -18,7 +18,7 @@ function Decoder(bytes, port) {
|
||||
|
||||
if (port === 2) {
|
||||
// device status data
|
||||
return decode(bytes, [uint16, uptime, temperature], ['voltage', 'uptime', 'cputemp']);
|
||||
return decode(bytes, [uint16, uptime, temperature, uint32], ['voltage', 'uptime', 'cputemp', 'memory']);
|
||||
}
|
||||
|
||||
|
||||
@ -56,17 +56,9 @@ var bytesToInt = function (bytes) {
|
||||
return i;
|
||||
};
|
||||
|
||||
var uptime = function (bytes) {
|
||||
if (bytes.length !== uptime.BYTES) {
|
||||
throw new Error('uptime must have exactly 8 bytes');
|
||||
}
|
||||
return bytesToInt(bytes);
|
||||
};
|
||||
uptime.BYTES = 4;
|
||||
|
||||
var uint8 = function (bytes) {
|
||||
if (bytes.length !== uint8.BYTES) {
|
||||
throw new Error('int must have exactly 1 byte');
|
||||
throw new Error('uint8 must have exactly 1 byte');
|
||||
}
|
||||
return bytesToInt(bytes);
|
||||
};
|
||||
@ -74,12 +66,20 @@ uint8.BYTES = 1;
|
||||
|
||||
var uint16 = function (bytes) {
|
||||
if (bytes.length !== uint16.BYTES) {
|
||||
throw new Error('int must have exactly 2 bytes');
|
||||
throw new Error('uint16 must have exactly 2 bytes');
|
||||
}
|
||||
return bytesToInt(bytes);
|
||||
};
|
||||
uint16.BYTES = 2;
|
||||
|
||||
var uint32 = function (bytes) {
|
||||
if (bytes.length !== uint32.BYTES) {
|
||||
throw new Error('uint32 must have exactly 4 bytes');
|
||||
}
|
||||
return bytesToInt(bytes);
|
||||
};
|
||||
uint32.BYTES = 4;
|
||||
|
||||
var latLng = function (bytes) {
|
||||
if (bytes.length !== latLng.BYTES) {
|
||||
throw new Error('Lat/Long must have exactly 4 bytes');
|
||||
@ -88,6 +88,14 @@ var latLng = function (bytes) {
|
||||
};
|
||||
latLng.BYTES = 4;
|
||||
|
||||
var uptime = function (bytes) {
|
||||
if (bytes.length !== uptime.BYTES) {
|
||||
throw new Error('Uptime must have exactly 8 bytes');
|
||||
}
|
||||
return bytesToInt(bytes);
|
||||
};
|
||||
uptime.BYTES = 8;
|
||||
|
||||
var hdop = function (bytes) {
|
||||
if (bytes.length !== hdop.BYTES) {
|
||||
throw new Error('hdop must have exactly 2 bytes');
|
||||
@ -169,9 +177,10 @@ var decode = function (bytes, mask, names) {
|
||||
|
||||
if (typeof module === 'object' && typeof module.exports !== 'undefined') {
|
||||
module.exports = {
|
||||
uptime: uptime,
|
||||
uint8: uint8,
|
||||
uint16: uint16,
|
||||
uint32: uint32,
|
||||
uptime: uptime,
|
||||
temperature: temperature,
|
||||
humidity: humidity,
|
||||
latLng: latLng,
|
||||
|
@ -16,7 +16,7 @@ void readButton() {
|
||||
ESP_LOGI(TAG, "Button pressed");
|
||||
payload.reset();
|
||||
payload.addButton(0x01);
|
||||
senddata(BUTTONPORT);
|
||||
SendData(BUTTONPORT);
|
||||
}
|
||||
}
|
||||
#endif
|
@ -33,13 +33,16 @@ void doHomework() {
|
||||
|
||||
// check free memory
|
||||
if (esp_get_minimum_free_heap_size() <= MEM_LOW) {
|
||||
ESP_LOGW(TAG,
|
||||
ESP_LOGI(TAG,
|
||||
"Memory full, counter cleared (heap low water mark = %d Bytes / "
|
||||
"free heap = %d bytes)",
|
||||
esp_get_minimum_free_heap_size(), ESP.getFreeHeap());
|
||||
senddata(COUNTERPORT); // send data before clearing counters
|
||||
SendData(COUNTERPORT); // send data before clearing counters
|
||||
reset_counters(); // clear macs container and reset all counters
|
||||
reset_salt(); // get new salt for salting hashes
|
||||
|
||||
if (esp_get_minimum_free_heap_size() <= MEM_LOW) // check again
|
||||
esp_restart(); // memory leak, reset device
|
||||
}
|
||||
} // doHomework()
|
||||
|
||||
|
@ -97,12 +97,14 @@ void refreshtheDisplay() {
|
||||
u8x8.setPowerSave(!cfg.screenon);
|
||||
}
|
||||
|
||||
// if display is switched off we don't need to refresh it and save time
|
||||
// if display is switched off we don't refresh it and save time
|
||||
if (!DisplayState)
|
||||
return;
|
||||
|
||||
uint8_t msgWaiting = 0;
|
||||
char buff[16]; // 16 chars line buffer
|
||||
|
||||
// update counter (lines 0-1)
|
||||
char buff[16];
|
||||
snprintf(
|
||||
buff, sizeof(buff), "PAX:%-4d",
|
||||
(int)macs.size()); // convert 16-bit MAC counter to decimal counter value
|
||||
@ -112,7 +114,7 @@ void refreshtheDisplay() {
|
||||
// update Battery status (line 2)
|
||||
#ifdef HAS_BATTERY_PROBE
|
||||
u8x8.setCursor(0, 2);
|
||||
u8x8.printf("B:%.1fV", batt_voltage / 1000.0);
|
||||
u8x8.printf(batt_voltage > 4000 ? "B:USB " : "B:%.1fV", batt_voltage / 1000.0);
|
||||
#endif
|
||||
|
||||
// update GPS status (line 2)
|
||||
@ -166,8 +168,21 @@ void refreshtheDisplay() {
|
||||
|
||||
// update LMiC event display (line 7)
|
||||
u8x8.setCursor(0, 7);
|
||||
u8x8.printf("%-16s", display_line7);
|
||||
u8x8.printf("%-14s", display_line7);
|
||||
|
||||
// update LoRa send queue display (line 7)
|
||||
msgWaiting = uxQueueMessagesWaiting(LoraSendQueue);
|
||||
if (msgWaiting) {
|
||||
sprintf(buff, "%2d", msgWaiting);
|
||||
u8x8.setCursor(14, 7);
|
||||
u8x8.setInverseFont(1);
|
||||
u8x8.printf("%-2s", msgWaiting == SEND_QUEUE_SIZE ? "<>" : buff);
|
||||
u8x8.setInverseFont(0);
|
||||
} else
|
||||
u8x8.printf(" ");
|
||||
|
||||
#endif // HAS_LORA
|
||||
|
||||
} // refreshDisplay()
|
||||
|
||||
void IRAM_ATTR DisplayIRQ() {
|
||||
|
@ -4,11 +4,8 @@
|
||||
// The mother of all embedded development...
|
||||
#include <Arduino.h>
|
||||
|
||||
// needed for ESP_LOGx on arduino framework
|
||||
#include <esp32-hal-log.h>
|
||||
|
||||
// attn: increment version after modifications to configData_t truct!
|
||||
#define PROGVERSION "1.4.0" // use max 10 chars here!
|
||||
#define PROGVERSION "1.4.23" // use max 10 chars here!
|
||||
#define PROGNAME "PAXCNT"
|
||||
|
||||
// std::set for unified array functions
|
||||
@ -37,6 +34,13 @@ typedef struct {
|
||||
char version[10]; // Firmware version
|
||||
} configData_t;
|
||||
|
||||
// Struct holding payload for data send queue
|
||||
typedef struct {
|
||||
uint8_t MessageSize;
|
||||
uint8_t MessagePort;
|
||||
uint8_t Message[PAYLOAD_BUFFER_SIZE];
|
||||
} MessageBuffer_t;
|
||||
|
||||
// global variables
|
||||
extern configData_t cfg; // current device configuration
|
||||
extern char display_line6[], display_line7[]; // screen buffers
|
||||
@ -47,6 +51,7 @@ extern hw_timer_t *channelSwitch, *sendCycle;
|
||||
extern portMUX_TYPE timerMux;
|
||||
extern volatile int SendCycleTimerIRQ, HomeCycleIRQ, DisplayTimerIRQ,
|
||||
ChannelTimerIRQ, ButtonPressedIRQ;
|
||||
extern QueueHandle_t LoraSendQueue, SPISendQueue;
|
||||
|
||||
extern std::array<uint64_t, 0xff>::iterator it;
|
||||
extern std::array<uint64_t, 0xff> beacons;
|
||||
|
@ -1,11 +1,12 @@
|
||||
// Hardware related definitions for ebox ESP32-bit with RFM95 LoRa
|
||||
// Hardware related definitions for ebox ESP32-bit with external connected RFM95 LoRa
|
||||
|
||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||
#define CFG_sx1276_radio 1
|
||||
|
||||
#define HAS_LED GPIO_NUM_23 // blue LED on board
|
||||
#define HAS_BUTTON GPIO_NUM_0 // button "PROG" on board
|
||||
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
|
||||
|
||||
// re-define pin definitions of pins_arduino.h
|
||||
#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- SX1276 NSS (Pin19) SPI Chip Select Input
|
@ -1,7 +1,7 @@
|
||||
// Hardware related definitions for generic ESP32 boards
|
||||
|
||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa or has no LoRa
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||
|
||||
#define CFG_sx1276_radio 1 // select LoRa chip
|
||||
//#define CFG_sx1272_radio 1 // select LoRa chip
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Hardware related definitions for Heltec LoRa-32 Board
|
||||
|
||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||
#define CFG_sx1276_radio 1
|
||||
|
||||
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
|
||||
|
@ -5,4 +5,4 @@
|
||||
#define HAS_LED 22 // on board LED on GPIO22
|
||||
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
|
||||
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
@ -1,7 +1,7 @@
|
||||
// Hardware related definitions for Pycom LoPy Board (NOT LoPy4)
|
||||
|
||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||
#define CFG_sx1272_radio 1
|
||||
#define HAS_LED NOT_A_PIN // LoPy has no on board LED, so we use RGB LED on LoPy
|
||||
#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Hardware related definitions for Pycom LoPy Board (not: LoPy4)
|
||||
|
||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||
#define CFG_sx1276_radio 1
|
||||
#define HAS_LED NOT_A_PIN // LoPy4 has no on board LED, so we use RGB LED on LoPy4
|
||||
#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Hardware related definitions for TTGO T-Beam board
|
||||
|
||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||
#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 external RAM
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Hardware related definitions for TTGOv1 board
|
||||
|
||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||
#define CFG_sx1276_radio 1
|
||||
|
||||
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Hardware related definitions for TTGO V2 Board
|
||||
|
||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
||||
|
||||
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
||||
|
@ -6,8 +6,8 @@
|
||||
/ - labelled v1.6 on pcb -> "new"
|
||||
*/
|
||||
|
||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
||||
|
||||
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
||||
|
@ -107,20 +107,8 @@ void get_hard_deveui(uint8_t *pdeveui) {
|
||||
|
||||
#ifdef VERBOSE
|
||||
|
||||
// Display a key
|
||||
void printKey(const char *name, const uint8_t *key, uint8_t len, bool lsb) {
|
||||
const uint8_t *p;
|
||||
char keystring[len + 1] = "", keybyte[3];
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
p = lsb ? key + len - i - 1 : key + i;
|
||||
sprintf(keybyte, "%02X", *p);
|
||||
strncat(keystring, keybyte, 2);
|
||||
}
|
||||
ESP_LOGI(TAG, "%s: %s", name, keystring);
|
||||
}
|
||||
|
||||
// Display OTAA keys
|
||||
void printKeys(void) {
|
||||
void showLoraKeys(void) {
|
||||
// LMIC may not have used callback to fill
|
||||
// all EUI buffer so we do it here to a temp
|
||||
// buffer to be able to display them
|
||||
@ -184,8 +172,13 @@ void onEvent(ev_t ev) {
|
||||
strcpy_P(buff, PSTR("JOINED"));
|
||||
sprintf(display_line6, " "); // clear previous lmic status
|
||||
|
||||
// set data rate adaptation
|
||||
// set data rate adaptation according to saved setting
|
||||
LMIC_setAdrMode(cfg.adrmode);
|
||||
|
||||
// set cyclic lmic link check to off if no ADR because is not supported by
|
||||
// ttn (but enabled by lmic after join)
|
||||
LMIC_setLinkCheckMode(cfg.adrmode);
|
||||
|
||||
// Set data rate and transmit power (note: txpower seems to be ignored by
|
||||
// the library)
|
||||
switch_lora(cfg.lorasf, cfg.txpower);
|
||||
@ -240,4 +233,51 @@ void lorawan_loop(void *pvParameters) {
|
||||
}
|
||||
}
|
||||
|
||||
// helper function to assign LoRa datarates to numeric spreadfactor values
|
||||
void switch_lora(uint8_t sf, uint8_t tx) {
|
||||
if (tx > 20)
|
||||
return;
|
||||
cfg.txpower = tx;
|
||||
switch (sf) {
|
||||
case 7:
|
||||
LMIC_setDrTxpow(DR_SF7, tx);
|
||||
cfg.lorasf = sf;
|
||||
break;
|
||||
case 8:
|
||||
LMIC_setDrTxpow(DR_SF8, tx);
|
||||
cfg.lorasf = sf;
|
||||
break;
|
||||
case 9:
|
||||
LMIC_setDrTxpow(DR_SF9, tx);
|
||||
cfg.lorasf = sf;
|
||||
break;
|
||||
case 10:
|
||||
LMIC_setDrTxpow(DR_SF10, tx);
|
||||
cfg.lorasf = sf;
|
||||
break;
|
||||
case 11:
|
||||
#if defined(CFG_eu868)
|
||||
LMIC_setDrTxpow(DR_SF11, tx);
|
||||
cfg.lorasf = sf;
|
||||
break;
|
||||
#elif defined(CFG_us915)
|
||||
LMIC_setDrTxpow(DR_SF11CR, tx);
|
||||
cfg.lorasf = sf;
|
||||
break;
|
||||
#endif
|
||||
case 12:
|
||||
#if defined(CFG_eu868)
|
||||
LMIC_setDrTxpow(DR_SF12, tx);
|
||||
cfg.lorasf = sf;
|
||||
break;
|
||||
#elif defined(CFG_us915)
|
||||
LMIC_setDrTxpow(DR_SF12CR, tx);
|
||||
cfg.lorasf = sf;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAS_LORA
|
@ -13,8 +13,8 @@ void get_hard_deveui(uint8_t *pdeveui);
|
||||
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 showLoraKeys(void);
|
||||
void lorawan_loop(void *pvParameters);
|
||||
void switch_lora(uint8_t sf, uint8_t tx);
|
||||
|
||||
#endif
|
@ -24,6 +24,18 @@ int8_t isBeacon(uint64_t mac) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Display a key
|
||||
void printKey(const char *name, const uint8_t *key, uint8_t len, bool lsb) {
|
||||
const uint8_t *p;
|
||||
char keystring[len + 1] = "", keybyte[3];
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
p = lsb ? key + len - i - 1 : key + i;
|
||||
sprintf(keybyte, "%02X", *p);
|
||||
strncat(keystring, keybyte, 2);
|
||||
}
|
||||
ESP_LOGI(TAG, "%s: %s", name, keystring);
|
||||
}
|
||||
|
||||
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) |
|
||||
@ -34,10 +46,12 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) {
|
||||
|
||||
char buff[16]; // temporary buffer for printf
|
||||
bool added = false;
|
||||
int8_t beaconID; // beacon number in test monitor mode
|
||||
int8_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
|
||||
uint32_t addr2int; // temporary buffer for shortened MAC
|
||||
#ifdef VENDORFILTER
|
||||
uint32_t vendor2int; // temporary buffer for Vendor OUI
|
||||
#endif
|
||||
|
||||
// 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
|
||||
@ -88,13 +102,13 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) {
|
||||
if (cfg.monitormode) {
|
||||
beaconID = isBeacon(macConvert(paddr));
|
||||
if (beaconID >= 0) {
|
||||
ESP_LOGI(TAG, "Beacon ID#d detected", beaconID);
|
||||
ESP_LOGI(TAG, "Beacon ID#%d detected", beaconID);
|
||||
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
||||
blink_LED(COLOR_WHITE, 2000);
|
||||
#endif
|
||||
payload.reset();
|
||||
payload.addAlarm(rssi, beaconID);
|
||||
senddata(BEACONPORT);
|
||||
SendData(BEACONPORT);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -15,5 +15,6 @@
|
||||
uint16_t reset_salt(void);
|
||||
uint64_t macConvert(uint8_t *paddr);
|
||||
bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type);
|
||||
void printKey(const char *name, const uint8_t *key, uint8_t len, bool lsb);
|
||||
|
||||
#endif
|
71
src/main.cpp
71
src/main.cpp
@ -27,7 +27,6 @@ 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
|
||||
@ -42,12 +41,20 @@ hw_timer_t *channelSwitch = NULL, *displaytimer = NULL, *sendCycle = NULL,
|
||||
volatile int ButtonPressedIRQ = 0, ChannelTimerIRQ = 0, SendCycleTimerIRQ = 0,
|
||||
DisplayTimerIRQ = 0, HomeCycleIRQ = 0;
|
||||
|
||||
// RTos send queues for payload transmit
|
||||
#ifdef HAS_LORA
|
||||
QueueHandle_t LoraSendQueue;
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SPI
|
||||
QueueHandle_t SPISendQueue;
|
||||
#endif
|
||||
|
||||
portMUX_TYPE timerMux =
|
||||
portMUX_INITIALIZER_UNLOCKED; // sync main loop and ISR when modifying IRQ
|
||||
// handler shared variables
|
||||
|
||||
std::set<uint16_t> macs; // associative container holding unique MAC
|
||||
// adress hashes (Wifi + BLE)
|
||||
std::set<uint16_t> macs; // container holding unique MAC adress hashes
|
||||
|
||||
// initialize payload encoder
|
||||
PayloadConvert payload(PAYLOAD_BUFFER_SIZE);
|
||||
@ -60,7 +67,7 @@ static const char TAG[] = "main";
|
||||
|
||||
void setup() {
|
||||
|
||||
char features[64] = "";
|
||||
char features[100] = "";
|
||||
|
||||
// disable brownout detection
|
||||
#ifdef DISABLE_BROWNOUT
|
||||
@ -108,12 +115,35 @@ void setup() {
|
||||
// read settings from NVRAM
|
||||
loadConfig(); // includes initialize if necessary
|
||||
|
||||
// initialize LoRa
|
||||
#if HAS_LORA
|
||||
strcat_P(features, " LORA");
|
||||
#ifdef VENDORFILTER
|
||||
strcat_P(features, " OUIFLT");
|
||||
#endif
|
||||
|
||||
// initialize led
|
||||
// initialize LoRa
|
||||
#ifdef HAS_LORA
|
||||
strcat_P(features, " LORA");
|
||||
LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
|
||||
if (LoraSendQueue == 0) {
|
||||
ESP_LOGE(TAG, "Could not create LORA send queue. Aborting.");
|
||||
exit(0);
|
||||
} else
|
||||
ESP_LOGI(TAG, "LORA send queue created, size %d Bytes",
|
||||
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
|
||||
#endif
|
||||
|
||||
// initialize SPI
|
||||
#ifdef HAS_SPI
|
||||
strcat_P(features, " SPI");
|
||||
SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
|
||||
if (SPISendQueue == 0) {
|
||||
ESP_LOGE(TAG, "Could not create SPI send queue. Aborting.");
|
||||
exit(0);
|
||||
} else
|
||||
ESP_LOGI(TAG, "SPI send queue created, size %d Bytes",
|
||||
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
|
||||
#endif
|
||||
|
||||
// initialize led
|
||||
#if (HAS_LED != NOT_A_PIN)
|
||||
pinMode(HAS_LED, OUTPUT);
|
||||
strcat_P(features, " LED");
|
||||
@ -205,13 +235,13 @@ void setup() {
|
||||
|
||||
// show payload encoder
|
||||
#if PAYLOAD_ENCODER == 1
|
||||
strcat_P(features, " PAYLOAD_PLAIN");
|
||||
strcat_P(features, " PLAIN");
|
||||
#elif PAYLOAD_ENCODER == 2
|
||||
strcat_P(features, " PAYLOAD_PACKED");
|
||||
strcat_P(features, " PACKED");
|
||||
#elif PAYLOAD_ENCODER == 3
|
||||
strcat_P(features, " PAYLOAD_LPP_DYN");
|
||||
strcat_P(features, " LPPDYN");
|
||||
#elif PAYLOAD_ENCODER == 4
|
||||
strcat_P(features, " PAYLOAD_LPP_PKD");
|
||||
strcat_P(features, " LPPPKD");
|
||||
#endif
|
||||
|
||||
// show compiled features
|
||||
@ -220,7 +250,7 @@ void setup() {
|
||||
#ifdef HAS_LORA
|
||||
// output LoRaWAN keys to console
|
||||
#ifdef VERBOSE
|
||||
printKeys();
|
||||
showLoraKeys();
|
||||
#endif
|
||||
|
||||
// initialize LoRaWAN LMIC run-time environment
|
||||
@ -264,12 +294,12 @@ void setup() {
|
||||
ESP_LOGI(TAG, "Starting Wifi task on core 0");
|
||||
wifi_sniffer_init();
|
||||
// initialize salt value using esp_random() called by random() in
|
||||
// arduino-esp32 core note: do this *after* wifi has started, since function
|
||||
// arduino-esp32 core. Note: do this *after* wifi has started, since function
|
||||
// gets it's seed from RF noise
|
||||
reset_salt(); // get new 16bit for salting hashes
|
||||
xTaskCreatePinnedToCore(wifi_channel_loop, "wifiloop", 2048, (void *)1, 1,
|
||||
NULL, 0);
|
||||
} // setup
|
||||
} // setup()
|
||||
|
||||
/* end Arduino SETUP
|
||||
* ------------------------------------------------------------ */
|
||||
@ -294,13 +324,16 @@ void loop() {
|
||||
updateDisplay();
|
||||
#endif
|
||||
|
||||
// check housekeeping cycle and to homework if expired
|
||||
// check housekeeping cycle and if expired do homework
|
||||
checkHousekeeping();
|
||||
// check send cycle and send payload if cycle is expired
|
||||
// check send queue and process it
|
||||
processSendBuffer();
|
||||
// check send cycle and enqueue payload if cycle is expired
|
||||
sendPayload();
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS); // reset watchdog
|
||||
// reset watchdog
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||
|
||||
} // end of infinite main loop
|
||||
} // loop()
|
||||
}
|
||||
|
||||
/* end Arduino main loop
|
||||
|
@ -45,6 +45,7 @@
|
||||
#define PAYLOAD_BUFFER_SIZE 51 // maximum size of payload block per transmit
|
||||
#define LORASFDEFAULT 9 // 7 ... 12 SF, according to LoRaWAN specs
|
||||
#define MAXLORARETRY 500 // maximum count of TX retries if LoRa busy
|
||||
#define SEND_QUEUE_SIZE 10 // maximum number of messages in payload send queue
|
||||
|
||||
// Ports on which the device sends and listenes on LoRaWAN and SPI
|
||||
#define COUNTERPORT 1 // Port on which device sends counts
|
||||
|
@ -53,7 +53,7 @@ void PayloadConvert::addConfig(configData_t value) {
|
||||
}
|
||||
|
||||
void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime,
|
||||
float cputemp) {
|
||||
float cputemp, uint32_t mem) {
|
||||
uint32_t temp = (uint32_t)cputemp;
|
||||
buffer[cursor++] = highByte(voltage);
|
||||
buffer[cursor++] = lowByte(voltage);
|
||||
@ -69,6 +69,10 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime,
|
||||
buffer[cursor++] = (byte)((temp & 0x00FF0000) >> 16);
|
||||
buffer[cursor++] = (byte)((temp & 0x0000FF00) >> 8);
|
||||
buffer[cursor++] = (byte)((temp & 0x000000FF));
|
||||
buffer[cursor++] = (byte)((mem & 0xFF000000) >> 24);
|
||||
buffer[cursor++] = (byte)((mem & 0x00FF0000) >> 16);
|
||||
buffer[cursor++] = (byte)((mem & 0x0000FF00) >> 8);
|
||||
buffer[cursor++] = (byte)((mem & 0x000000FF));
|
||||
}
|
||||
|
||||
#ifdef HAS_GPS
|
||||
@ -124,10 +128,11 @@ void PayloadConvert::addConfig(configData_t value) {
|
||||
}
|
||||
|
||||
void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime,
|
||||
float cputemp) {
|
||||
float cputemp, uint32_t mem) {
|
||||
writeUint16(voltage);
|
||||
writeUptime(uptime);
|
||||
writeTemperature(cputemp);
|
||||
writeUint32(mem);
|
||||
}
|
||||
|
||||
#ifdef HAS_GPS
|
||||
@ -159,6 +164,8 @@ void PayloadConvert::writeLatLng(double latitude, double longitude) {
|
||||
intToBytes(cursor, longitude, 4);
|
||||
}
|
||||
|
||||
void PayloadConvert::writeUint32(uint32_t i) { intToBytes(cursor, i, 4); }
|
||||
|
||||
void PayloadConvert::writeUint16(uint16_t i) { intToBytes(cursor, i, 2); }
|
||||
|
||||
void PayloadConvert::writeUint8(uint8_t i) { intToBytes(cursor, i, 1); }
|
||||
@ -239,7 +246,7 @@ void PayloadConvert::addConfig(configData_t value) {
|
||||
}
|
||||
|
||||
void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime,
|
||||
float celsius) {
|
||||
float celsius, uint32_t mem) {
|
||||
uint16_t temp = celsius * 10;
|
||||
uint16_t volt = voltage / 10;
|
||||
#ifdef HAS_BATTERY_PROBE
|
||||
|
@ -22,7 +22,7 @@
|
||||
#define LPP_DIGITAL_OUTPUT 1 // 1 byte
|
||||
#define LPP_ANALOG_INPUT 2 // 2 bytes, 0.01 signed
|
||||
#define LPP_LUMINOSITY 101 // 2 bytes, 1 lux unsigned
|
||||
#define LPP_Presence 102 // 1 byte
|
||||
#define LPP_PRESENCE 102 // 1 byte
|
||||
|
||||
class PayloadConvert {
|
||||
|
||||
@ -35,7 +35,7 @@ public:
|
||||
uint8_t *getBuffer(void);
|
||||
void addCount(uint16_t value1, uint16_t value2);
|
||||
void addConfig(configData_t value);
|
||||
void addStatus(uint16_t voltage, uint64_t uptime, float cputemp);
|
||||
void addStatus(uint16_t voltage, uint64_t uptime, float cputemp, uint32_t mem);
|
||||
void addAlarm(int8_t rssi, uint8_t message);
|
||||
#ifdef HAS_GPS
|
||||
void addGPS(gpsStatus_t value);
|
||||
@ -59,6 +59,7 @@ private:
|
||||
void intToBytes(uint8_t pos, int32_t i, uint8_t byteSize);
|
||||
void writeUptime(uint64_t unixtime);
|
||||
void writeLatLng(double latitude, double longitude);
|
||||
void writeUint32(uint32_t i);
|
||||
void writeUint16(uint16_t i);
|
||||
void writeUint8(uint8_t i);
|
||||
void writeHumidity(float humidity);
|
||||
|
288
src/rcommand.cpp
288
src/rcommand.cpp
@ -1,6 +1,3 @@
|
||||
// remote command interpreter, parses and executes commands with arguments in
|
||||
// array
|
||||
|
||||
// Basic Config
|
||||
#include "globals.h"
|
||||
#include "rcommand.h"
|
||||
@ -8,64 +5,14 @@
|
||||
// Local logging tag
|
||||
static const char TAG[] = "main";
|
||||
|
||||
#ifdef HAS_LORA
|
||||
// helper function to assign LoRa datarates to numeric spreadfactor values
|
||||
void switch_lora(uint8_t sf, uint8_t tx) {
|
||||
if (tx > 20)
|
||||
return;
|
||||
cfg.txpower = tx;
|
||||
switch (sf) {
|
||||
case 7:
|
||||
LMIC_setDrTxpow(DR_SF7, tx);
|
||||
cfg.lorasf = sf;
|
||||
break;
|
||||
case 8:
|
||||
LMIC_setDrTxpow(DR_SF8, tx);
|
||||
cfg.lorasf = sf;
|
||||
break;
|
||||
case 9:
|
||||
LMIC_setDrTxpow(DR_SF9, tx);
|
||||
cfg.lorasf = sf;
|
||||
break;
|
||||
case 10:
|
||||
LMIC_setDrTxpow(DR_SF10, tx);
|
||||
cfg.lorasf = sf;
|
||||
break;
|
||||
case 11:
|
||||
#if defined(CFG_eu868)
|
||||
LMIC_setDrTxpow(DR_SF11, tx);
|
||||
cfg.lorasf = sf;
|
||||
break;
|
||||
#elif defined(CFG_us915)
|
||||
LMIC_setDrTxpow(DR_SF11CR, tx);
|
||||
cfg.lorasf = sf;
|
||||
break;
|
||||
#endif
|
||||
case 12:
|
||||
#if defined(CFG_eu868)
|
||||
LMIC_setDrTxpow(DR_SF12, tx);
|
||||
cfg.lorasf = sf;
|
||||
break;
|
||||
#elif defined(CFG_us915)
|
||||
LMIC_setDrTxpow(DR_SF12CR, tx);
|
||||
cfg.lorasf = sf;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif // HAS_LORA
|
||||
|
||||
// set of functions that can be triggered by remote commands
|
||||
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");
|
||||
vTaskDelay(
|
||||
10000 /
|
||||
portTICK_PERIOD_MS); // wait for LMIC to confirm LoRa downlink to server
|
||||
vTaskDelay(10000 / portTICK_PERIOD_MS); // wait for LMIC to confirm LoRa
|
||||
// downlink to server
|
||||
esp_restart();
|
||||
break;
|
||||
case 1: // reset MAC counter
|
||||
@ -79,22 +26,29 @@ void set_reset(uint8_t val[]) {
|
||||
sprintf(display_line6, "Factory reset");
|
||||
eraseConfig();
|
||||
break;
|
||||
case 3: // reset send queues
|
||||
ESP_LOGI(TAG, "Remote command: flush send queue");
|
||||
sprintf(display_line6, "Queue reset");
|
||||
flushQueues();
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "Remote command: reset called with invalid parameter(s)");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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[0];
|
||||
// update send cycle interrupt
|
||||
timerAlarmWrite(sendCycle, cfg.sendcycle * 2 * 10000, true);
|
||||
// reload interrupt after each trigger of channel switch cycle
|
||||
ESP_LOGI(TAG, "Remote command: set payload send cycle to %d seconds",
|
||||
ESP_LOGI(TAG, "Remote command: set send cycle to %d seconds",
|
||||
cfg.sendcycle * 2);
|
||||
};
|
||||
}
|
||||
|
||||
void set_wifichancycle(uint8_t val[]) {
|
||||
cfg.wifichancycle = val[0];
|
||||
@ -104,7 +58,7 @@ void set_wifichancycle(uint8_t val[]) {
|
||||
ESP_LOGI(TAG,
|
||||
"Remote command: set Wifi channel switch interval to %.1f seconds",
|
||||
cfg.wifichancycle / float(100));
|
||||
};
|
||||
}
|
||||
|
||||
void set_blescantime(uint8_t val[]) {
|
||||
cfg.blescantime = val[0];
|
||||
@ -117,7 +71,7 @@ void set_blescantime(uint8_t val[]) {
|
||||
start_BLEscan();
|
||||
}
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
void set_countmode(uint8_t val[]) {
|
||||
switch (val[0]) {
|
||||
@ -129,73 +83,46 @@ void set_countmode(uint8_t val[]) {
|
||||
cfg.countermode = 1;
|
||||
ESP_LOGI(TAG, "Remote command: set counter mode to cumulative");
|
||||
break;
|
||||
default: // cyclic confirmed
|
||||
case 2: // cyclic confirmed
|
||||
cfg.countermode = 2;
|
||||
ESP_LOGI(TAG, "Remote command: set counter mode to cyclic confirmed");
|
||||
break;
|
||||
default: // invalid parameter
|
||||
ESP_LOGW(
|
||||
TAG,
|
||||
"Remote command: set counter mode called with invalid parameter(s)");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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 = 1;
|
||||
break;
|
||||
default:
|
||||
cfg.screensaver = 0;
|
||||
break;
|
||||
}
|
||||
};
|
||||
cfg.screensaver = val[0] ? 1 : 0;
|
||||
}
|
||||
|
||||
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 = 1;
|
||||
break;
|
||||
default:
|
||||
cfg.screenon = 0;
|
||||
break;
|
||||
}
|
||||
};
|
||||
cfg.screenon = val[0] ? 1 : 0;
|
||||
}
|
||||
|
||||
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 = 1;
|
||||
break;
|
||||
default:
|
||||
cfg.gpsmode = 0;
|
||||
break;
|
||||
}
|
||||
};
|
||||
cfg.gpsmode = val[0] ? 1 : 0;
|
||||
}
|
||||
|
||||
void set_beacon(uint8_t val[]) {
|
||||
if (sizeof(*val) / sizeof(val[0]) == 7) {
|
||||
uint8_t id = val[0]; // use first parameter as beacon storage id
|
||||
memmove(val, val + 1, 6); // 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, 6, false); // show beacon MAC
|
||||
} else
|
||||
ESP_LOGW(TAG, "Remote command: set beacon called with invalid parameters");
|
||||
};
|
||||
uint8_t id = val[0]; // use first parameter as beacon storage id
|
||||
memmove(val, val + 1, 6); // 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, 6, false); // show beacon MAC
|
||||
}
|
||||
|
||||
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 = 1;
|
||||
break;
|
||||
default:
|
||||
cfg.monitormode = 0;
|
||||
break;
|
||||
}
|
||||
};
|
||||
cfg.monitormode = val[0] ? 1 : 0;
|
||||
}
|
||||
|
||||
void set_lorasf(uint8_t val[]) {
|
||||
#ifdef HAS_LORA
|
||||
@ -204,73 +131,46 @@ void set_lorasf(uint8_t val[]) {
|
||||
#else
|
||||
ESP_LOGW(TAG, "Remote command: LoRa not implemented");
|
||||
#endif // HAS_LORA
|
||||
};
|
||||
}
|
||||
|
||||
void set_loraadr(uint8_t val[]) {
|
||||
#ifdef HAS_LORA
|
||||
ESP_LOGI(TAG, "Remote command: set LoRa ADR mode to %s",
|
||||
val[0] ? "on" : "off");
|
||||
switch (val[0]) {
|
||||
case 1:
|
||||
cfg.adrmode = 1;
|
||||
break;
|
||||
default:
|
||||
cfg.adrmode = 0;
|
||||
break;
|
||||
}
|
||||
LMIC_setAdrMode(cfg.adrmode);
|
||||
cfg.adrmode = val[0] ? 1 : 0;
|
||||
LMIC_setAdrMode(cfg.adrmode);
|
||||
#else
|
||||
ESP_LOGW(TAG, "Remote command: LoRa not implemented");
|
||||
#endif // HAS_LORA
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
#ifdef BLECOUNTER
|
||||
stop_BLEscan();
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
cfg.blescan = 1;
|
||||
cfg.blescan = val[0] ? 1 : 0;
|
||||
#ifdef BLECOUNTER
|
||||
if (cfg.blescan)
|
||||
start_BLEscan();
|
||||
#endif
|
||||
break;
|
||||
else {
|
||||
macs_ble = 0; // clear BLE counter
|
||||
stop_BLEscan();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
void set_wifiant(uint8_t val[]) {
|
||||
ESP_LOGI(TAG, "Remote command: set Wifi antenna to %s",
|
||||
val[0] ? "external" : "internal");
|
||||
switch (val[0]) {
|
||||
case 1:
|
||||
cfg.wifiant = 1;
|
||||
break;
|
||||
default:
|
||||
cfg.wifiant = 0;
|
||||
break;
|
||||
}
|
||||
cfg.wifiant = val[0] ? 1 : 0;
|
||||
#ifdef HAS_ANTENNA_SWITCH
|
||||
antenna_select(cfg.wifiant);
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
void set_vendorfilter(uint8_t val[]) {
|
||||
ESP_LOGI(TAG, "Remote command: set vendorfilter mode to %s",
|
||||
val[0] ? "on" : "off");
|
||||
switch (val[0]) {
|
||||
case 1:
|
||||
cfg.vendorfilter = 1;
|
||||
break;
|
||||
default:
|
||||
cfg.vendorfilter = 0;
|
||||
break;
|
||||
}
|
||||
};
|
||||
cfg.vendorfilter = val[0] ? 1 : 0;
|
||||
}
|
||||
|
||||
void set_rgblum(uint8_t val[]) {
|
||||
// Avoid wrong parameters
|
||||
@ -291,7 +191,7 @@ void get_config(uint8_t val[]) {
|
||||
ESP_LOGI(TAG, "Remote command: get device configuration");
|
||||
payload.reset();
|
||||
payload.addConfig(cfg);
|
||||
senddata(CONFIGPORT);
|
||||
SendData(CONFIGPORT);
|
||||
};
|
||||
|
||||
void get_status(uint8_t val[]) {
|
||||
@ -302,8 +202,9 @@ void get_status(uint8_t val[]) {
|
||||
uint16_t voltage = 0;
|
||||
#endif
|
||||
payload.reset();
|
||||
payload.addStatus(voltage, uptime() / 1000, temperatureRead());
|
||||
senddata(STATUSPORT);
|
||||
payload.addStatus(voltage, uptime() / 1000, temperatureRead(),
|
||||
ESP.getFreeHeap());
|
||||
SendData(STATUSPORT);
|
||||
};
|
||||
|
||||
void get_gps(uint8_t val[]) {
|
||||
@ -312,53 +213,66 @@ void get_gps(uint8_t val[]) {
|
||||
gps_read();
|
||||
payload.reset();
|
||||
payload.addGPS(gps_status);
|
||||
senddata(GPSPORT);
|
||||
SendData(GPSPORT);
|
||||
#else
|
||||
ESP_LOGW(TAG, "GPS function not supported");
|
||||
#endif
|
||||
};
|
||||
|
||||
// assign previously defined functions to set of numeric remote commands
|
||||
// format: opcode, function, flag (1 = do make settings persistent / 0 = don't)
|
||||
// format: opcode, function, #bytes params,
|
||||
// flag (1 = do make settings persistent / 0 = don't)
|
||||
//
|
||||
cmd_t table[] = {{0x01, set_rssi, true}, {0x02, set_countmode, true},
|
||||
{0x03, set_gps, true}, {0x04, set_display, true},
|
||||
{0x05, set_lorasf, true}, {0x06, set_lorapower, true},
|
||||
{0x07, set_loraadr, true}, {0x08, set_screensaver, true},
|
||||
{0x09, set_reset, false}, {0x0a, set_sendcycle, 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}, {0x12, set_beacon, false},
|
||||
{0x80, get_config, false}, {0x81, get_status, false},
|
||||
{0x84, get_gps, false}};
|
||||
cmd_t table[] = {
|
||||
{0x01, set_rssi, 1, true}, {0x02, set_countmode, 1, true},
|
||||
{0x03, set_gps, 1, true}, {0x04, set_display, 1, true},
|
||||
{0x05, set_lorasf, 1, true}, {0x06, set_lorapower, 1, true},
|
||||
{0x07, set_loraadr, 1, true}, {0x08, set_screensaver, 1, true},
|
||||
{0x09, set_reset, 1, false}, {0x0a, set_sendcycle, 1, true},
|
||||
{0x0b, set_wifichancycle, 1, true}, {0x0c, set_blescantime, 1, true},
|
||||
{0x0d, set_vendorfilter, 1, false}, {0x0e, set_blescan, 1, true},
|
||||
{0x0f, set_wifiant, 1, true}, {0x10, set_rgblum, 1, true},
|
||||
{0x11, set_monitor, 1, true}, {0x12, set_beacon, 7, false},
|
||||
{0x80, get_config, 0, false}, {0x81, get_status, 0, false},
|
||||
{0x84, get_gps, 0, false}};
|
||||
|
||||
const uint8_t cmdtablesize =
|
||||
sizeof(table) / sizeof(table[0]); // number of commands in command table
|
||||
|
||||
// check and execute remote command
|
||||
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[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
|
||||
}
|
||||
}
|
||||
uint8_t foundcmd[cmdlength], cursor = 0;
|
||||
bool storeflag = false;
|
||||
|
||||
if (store_flag)
|
||||
saveConfig(); // if save flag is set: store new configuration in NVS to make
|
||||
// it persistent
|
||||
while (cursor < cmdlength) {
|
||||
|
||||
int i = cmdtablesize;
|
||||
while (i--) {
|
||||
if (cmd[cursor] == table[i].opcode) { // lookup command in opcode table
|
||||
cursor++; // strip 1 byte opcode
|
||||
if ((cursor + table[i].params) <= cmdlength) {
|
||||
memmove(foundcmd, cmd + cursor,
|
||||
table[i].params); // strip opcode from cmd array
|
||||
cursor += table[i].params;
|
||||
if (table[i].store) // ceck if function needs to store configuration
|
||||
storeflag = true;
|
||||
table[i].func(
|
||||
foundcmd); // execute assigned function with given parameters
|
||||
} else
|
||||
ESP_LOGI(
|
||||
TAG,
|
||||
"Remote command x%02X called with missing parameter(s), skipped",
|
||||
table[i].opcode);
|
||||
break; // exit table lookup loop, command was found
|
||||
} // command validation
|
||||
} // command table lookup loop
|
||||
|
||||
} // command parsing loop
|
||||
|
||||
if (storeflag)
|
||||
saveConfig();
|
||||
} // rcommand()
|
@ -10,10 +10,10 @@
|
||||
typedef struct {
|
||||
const uint8_t opcode;
|
||||
void (*func)(uint8_t []);
|
||||
uint8_t params;
|
||||
const bool store;
|
||||
} cmd_t;
|
||||
|
||||
void rcommand(uint8_t cmd[], uint8_t cmdlength);
|
||||
void switch_lora(uint8_t sf, uint8_t tx);
|
||||
|
||||
#endif
|
@ -1,28 +1,29 @@
|
||||
// Basic Config
|
||||
#include "globals.h"
|
||||
|
||||
void senddata(uint8_t port) {
|
||||
// put data to send in RTos Queues used for transmit over channels Lora and SPI
|
||||
void SendData(uint8_t port) {
|
||||
|
||||
MessageBuffer_t SendBuffer;
|
||||
|
||||
SendBuffer.MessageSize = payload.getSize();
|
||||
SendBuffer.MessagePort = PAYLOAD_ENCODER <= 2
|
||||
? port
|
||||
: (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT);
|
||||
memcpy(SendBuffer.Message, payload.getBuffer(), payload.getSize());
|
||||
|
||||
// enqueue message in LoRa send queue
|
||||
#ifdef HAS_LORA
|
||||
// Check if there is a pending TX/RX job running
|
||||
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
|
||||
: (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT),
|
||||
payload.getBuffer(), payload.getSize(), (cfg.countermode & 0x02));
|
||||
|
||||
ESP_LOGI(TAG, "%d bytes queued to send on LoRa", payload.getSize());
|
||||
sprintf(display_line7, "PACKET QUEUED");
|
||||
}
|
||||
if (xQueueSendToBack(LoraSendQueue, (void *)&SendBuffer, (TickType_t)0) ==
|
||||
pdTRUE)
|
||||
ESP_LOGI(TAG, "%d bytes enqueued to send on LoRa", payload.getSize());
|
||||
#endif
|
||||
|
||||
// enqueue message in SPI send queue
|
||||
#ifdef HAS_SPI
|
||||
// to come here: code for sending payload to a local master via SPI
|
||||
ESP_LOGI(TAG, "%d bytes sent on SPI", payload.getSize());
|
||||
if (xQueueSendToBack(SPISendQueue, (void *)&SendBuffer, (TickType_t)0) ==
|
||||
pdTRUE)
|
||||
ESP_LOGI(TAG, "%d bytes enqueued to send on SPI", payload.getSize());
|
||||
#endif
|
||||
|
||||
// clear counter if not in cumulative counter mode
|
||||
@ -31,9 +32,9 @@ void senddata(uint8_t port) {
|
||||
reset_salt(); // get new salt for salting hashes
|
||||
ESP_LOGI(TAG, "Counter cleared");
|
||||
}
|
||||
} // SendData
|
||||
|
||||
} // senddata
|
||||
|
||||
// cyclic called function to prepare payload to send
|
||||
void sendPayload() {
|
||||
|
||||
if (SendCycleTimerIRQ) {
|
||||
@ -64,12 +65,50 @@ void sendPayload() {
|
||||
ESP_LOGD(TAG, "No valid GPS position or GPS data mode disabled");
|
||||
}
|
||||
#endif
|
||||
senddata(COUNTERPORT);
|
||||
SendData(COUNTERPORT);
|
||||
}
|
||||
} // sendpayload();
|
||||
} // sendpayload()
|
||||
|
||||
// interrupt handler used for payload send cycle timer
|
||||
void IRAM_ATTR SendCycleIRQ() {
|
||||
portENTER_CRITICAL(&timerMux);
|
||||
SendCycleTimerIRQ++;
|
||||
portEXIT_CRITICAL(&timerMux);
|
||||
}
|
||||
|
||||
// cyclic called function to eat data from RTos send queues and transmit it
|
||||
void processSendBuffer() {
|
||||
|
||||
MessageBuffer_t SendBuffer;
|
||||
|
||||
#ifdef HAS_LORA
|
||||
// Check if there is a pending TX/RX job running
|
||||
if ((LMIC.opmode & (OP_JOINING | OP_REJOIN | OP_TXDATA | OP_POLL)) != 0) {
|
||||
// LoRa Busy -> don't eat data from queue, since it cannot be sent
|
||||
} else {
|
||||
if (xQueueReceive(LoraSendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
|
||||
// SendBuffer gets struct MessageBuffer with next payload from queue
|
||||
LMIC_setTxData2(SendBuffer.MessagePort, SendBuffer.Message,
|
||||
SendBuffer.MessageSize, (cfg.countermode & 0x02));
|
||||
ESP_LOGI(TAG, "%d bytes sent to LoRa", SendBuffer.MessageSize);
|
||||
sprintf(display_line7, "PACKET QUEUED");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SPI
|
||||
if (xQueueReceive(SPISendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
|
||||
ESP_LOGI(TAG, "%d bytes sent to SPI", SendBuffer.MessageSize);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // processSendBuffer
|
||||
|
||||
void flushQueues() {
|
||||
#ifdef HAS_LORA
|
||||
xQueueReset(LoraSendQueue);
|
||||
#endif
|
||||
#ifdef HAS_SPI
|
||||
xQueueReset(SPISendQueue);
|
||||
#endif
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
#ifndef _SENDDATA_H
|
||||
#define _SENDDATA_H
|
||||
|
||||
void senddata(uint8_t port);
|
||||
void SendData(uint8_t port);
|
||||
void sendPayload(void);
|
||||
void SendCycleIRQ(void);
|
||||
void processSendBuffer(void);
|
||||
void flushQueues();
|
||||
|
||||
#endif // _SENDDATA_H_
|
Loading…
Reference in New Issue
Block a user