From b02293ad0fb24236e383444739bbcb2584c5bf64 Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Wed, 1 Aug 2018 20:20:31 +0200 Subject: [PATCH 01/28] Update platformio.ini --- platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index 579cd667..9c53b380 100644 --- a/platformio.ini +++ b/platformio.ini @@ -53,7 +53,7 @@ build_flags = -D_lmic_config_h_ -include "src/paxcounter.conf" -include "src/hal/${PIOENV}.h" -; -w + -w [env:test] platform = ${common_env_data.platform_espressif32} @@ -227,4 +227,4 @@ lib_deps = ${common_env_data.lib_deps_gps} ${common_env_data.lib_deps_display} build_flags = - ${common_env_data.build_flags} \ No newline at end of file + ${common_env_data.build_flags} From d7a1922b95d78d8d8727c9c24e5dd1e480a35bde Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Thu, 2 Aug 2018 11:33:02 +0200 Subject: [PATCH 02/28] bugfix rcommand.cpp --- src/globals.h | 2 +- src/main.cpp | 28 ++++++++------- src/rcommand.cpp | 89 ++++++++++++++++++++++++------------------------ src/rcommand.h | 1 + 4 files changed, 62 insertions(+), 58 deletions(-) diff --git a/src/globals.h b/src/globals.h index ba75f1c4..ba6b053e 100644 --- a/src/globals.h +++ b/src/globals.h @@ -8,7 +8,7 @@ #include // attn: increment version after modifications to configData_t truct! -#define PROGVERSION "1.4.0" // use max 10 chars here! +#define PROGVERSION "1.4.1" // use max 10 chars here! #define PROGNAME "PAXCNT" // std::set for unified array functions diff --git a/src/main.cpp b/src/main.cpp index 34948b17..0105ab47 100644 --- a/src/main.cpp +++ b/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 @@ -60,7 +59,7 @@ static const char TAG[] = "main"; void setup() { - char features[64] = ""; + char features[100] = ""; // disable brownout detection #ifdef DISABLE_BROWNOUT @@ -108,8 +107,12 @@ void setup() { // read settings from NVRAM loadConfig(); // includes initialize if necessary +#ifdef VENDORFILTER + strcat_P(features, " OUIFLT"); +#endif + // initialize LoRa -#if HAS_LORA +#ifdef HAS_LORA strcat_P(features, " LORA"); #endif @@ -205,13 +208,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 @@ -264,12 +267,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 +297,14 @@ 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 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 diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 0c294b84..59c58f89 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -63,9 +63,8 @@ void set_reset(uint8_t val[]) { 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,6 +78,8 @@ void set_reset(uint8_t val[]) { sprintf(display_line6, "Factory reset"); eraseConfig(); break; + default: + ESP_LOGW(TAG, "Remote command: reset called with invalid parameter(s)"); } }; @@ -92,7 +93,7 @@ void set_sendcycle(uint8_t val[]) { // 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); }; @@ -129,10 +130,14 @@ 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)"); } }; @@ -170,18 +175,15 @@ void set_gps(uint8_t val[]) { default: cfg.gpsmode = 0; break; - } -}; + }; +} 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[]) { @@ -319,46 +321,43 @@ void get_gps(uint8_t val[]) { }; // 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}}; // check and execute remote command void rcommand(uint8_t cmd[], uint8_t cmdlength) { if (cmdlength == 0) return; + else + cmdlength--; // minus 1 byte for opcode + 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 - } - } + if ((cmd[0] == table[i].opcode) && + (table[i].params == cmdlength)) { // lookup command in opcode table + memmove(cmd, cmd + 1, + cmdlength); // strip opcode + table[i].func(cmd); // execute assigned function with given parameters + if (table[i].store) // ceck if function needs to store configuration + saveConfig(); + break; // exit while loop, command was found + } // lookup command + } // while - if (store_flag) - saveConfig(); // if save flag is set: store new configuration in NVS to make - // it persistent } // rcommand() \ No newline at end of file diff --git a/src/rcommand.h b/src/rcommand.h index 87fe7082..c6d9bdbd 100644 --- a/src/rcommand.h +++ b/src/rcommand.h @@ -10,6 +10,7 @@ typedef struct { const uint8_t opcode; void (*func)(uint8_t []); + uint8_t params; const bool store; } cmd_t; From c322eb5a835758f01f200b3060b6cfc585f9c4c4 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Thu, 2 Aug 2018 12:16:40 +0200 Subject: [PATCH 03/28] bugfix rcommand.cpp --- src/rcommand.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 59c58f89..59a8a24b 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -183,7 +183,7 @@ void set_beacon(uint8_t val[]) { 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 + //printKey("MAC", val, 6, false); // show beacon MAC }; void set_monitor(uint8_t val[]) { From 952ebe77f94f9f472ee5c21e558b677711e45504 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Thu, 2 Aug 2018 12:48:27 +0200 Subject: [PATCH 04/28] printKey() moved --- src/lorawan.cpp | 12 ------------ src/lorawan.h | 1 - src/macsniff.cpp | 12 ++++++++++++ src/macsniff.h | 1 + src/rcommand.cpp | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 943df011..b6c9b2aa 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -107,18 +107,6 @@ 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) { // LMIC may not have used callback to fill diff --git a/src/lorawan.h b/src/lorawan.h index 1c8645d4..45123d9e 100644 --- a/src/lorawan.h +++ b/src/lorawan.h @@ -14,7 +14,6 @@ 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 \ No newline at end of file diff --git a/src/macsniff.cpp b/src/macsniff.cpp index 8bc7addd..b11f8785 100644 --- a/src/macsniff.cpp +++ b/src/macsniff.cpp @@ -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) | diff --git a/src/macsniff.h b/src/macsniff.h index 1f2c2adb..40adb6cf 100644 --- a/src/macsniff.h +++ b/src/macsniff.h @@ -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 \ No newline at end of file diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 59a8a24b..59c58f89 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -183,7 +183,7 @@ void set_beacon(uint8_t val[]) { 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 + printKey("MAC", val, 6, false); // show beacon MAC }; void set_monitor(uint8_t val[]) { From 7b35c6a539369279e069882cd4d5730cb2eae8a5 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Fri, 3 Aug 2018 23:50:04 +0200 Subject: [PATCH 05/28] Send Queues (experimental) --- README.md | 2 +- src/button.cpp | 2 +- src/cyclic.cpp | 2 +- src/globals.h | 2 +- src/lorawan.cpp | 2 +- src/lorawan.h | 2 +- src/macsniff.cpp | 2 +- src/main.cpp | 32 +++++++++++++++++-- src/paxcounter.conf | 1 + src/rcommand.cpp | 13 ++++---- src/senddata.cpp | 75 ++++++++++++++++++++++++++++++++------------- src/senddata.h | 13 +++++++- 12 files changed, 110 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 85d75b7e..f7c38bc7 100644 --- a/README.md +++ b/README.md @@ -227,7 +227,7 @@ function Converter(decoded, port) { The device listenes for remote control commands on LoRaWAN Port 2. -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 diff --git a/src/button.cpp b/src/button.cpp index e9b0fe0e..4ca25638 100644 --- a/src/button.cpp +++ b/src/button.cpp @@ -16,7 +16,7 @@ void readButton() { ESP_LOGI(TAG, "Button pressed"); payload.reset(); payload.addButton(0x01); - senddata(BUTTONPORT); + EnqueueSendData(BUTTONPORT, payload.getBuffer(), payload.getSize()); } } #endif \ No newline at end of file diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 85b170db..a2d9143f 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -37,7 +37,7 @@ void doHomework() { "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 + EnqueueSendData(COUNTERPORT, payload.getBuffer(), payload.getSize()); // send data before clearing counters reset_counters(); // clear macs container and reset all counters reset_salt(); // get new salt for salting hashes } diff --git a/src/globals.h b/src/globals.h index ba6b053e..48505e2c 100644 --- a/src/globals.h +++ b/src/globals.h @@ -8,7 +8,7 @@ #include // attn: increment version after modifications to configData_t truct! -#define PROGVERSION "1.4.1" // use max 10 chars here! +#define PROGVERSION "1.4.2" // use max 10 chars here! #define PROGNAME "PAXCNT" // std::set for unified array functions diff --git a/src/lorawan.cpp b/src/lorawan.cpp index b6c9b2aa..41c386e9 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -108,7 +108,7 @@ void get_hard_deveui(uint8_t *pdeveui) { #ifdef VERBOSE // 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 diff --git a/src/lorawan.h b/src/lorawan.h index 45123d9e..b9abb8c1 100644 --- a/src/lorawan.h +++ b/src/lorawan.h @@ -13,7 +13,7 @@ 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 showLoraKeys(void); void lorawan_loop(void *pvParameters); #endif \ No newline at end of file diff --git a/src/macsniff.cpp b/src/macsniff.cpp index b11f8785..4cf43a56 100644 --- a/src/macsniff.cpp +++ b/src/macsniff.cpp @@ -106,7 +106,7 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) { #endif payload.reset(); payload.addAlarm(rssi, beaconID); - senddata(BEACONPORT); + EnqueueSendData(BEACONPORT, payload.getBuffer(), payload.getSize()); } }; diff --git a/src/main.cpp b/src/main.cpp index 0105ab47..f413220d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,6 +41,10 @@ hw_timer_t *channelSwitch = NULL, *displaytimer = NULL, *sendCycle = NULL, volatile int ButtonPressedIRQ = 0, ChannelTimerIRQ = 0, SendCycleTimerIRQ = 0, DisplayTimerIRQ = 0, HomeCycleIRQ = 0; +// send queues +QueueHandle_t LoraSendQueue, SPISendQueue; +MessageBuffer_t SendBuffer; + portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; // sync main loop and ISR when modifying IRQ // handler shared variables @@ -104,10 +108,30 @@ void setup() { #endif // verbose +// initialize send queues for transmit channels +#ifdef HAS_LORA + LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(struct SendBuffer *)); + 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 +#ifdef HAS_SPI + SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(struct SendBuffer *)); + 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 + // read settings from NVRAM loadConfig(); // includes initialize if necessary -#ifdef VENDORFILTER +#ifdef VENDORFILTER strcat_P(features, " OUIFLT"); #endif @@ -223,7 +247,7 @@ void setup() { #ifdef HAS_LORA // output LoRaWAN keys to console #ifdef VERBOSE - printKeys(); + showLoraKeys(); #endif // initialize LoRaWAN LMIC run-time environment @@ -299,7 +323,9 @@ void loop() { // 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(); // reset watchdog vTaskDelay(1 / portTICK_PERIOD_MS); diff --git a/src/paxcounter.conf b/src/paxcounter.conf index 799c55d4..7cf76677 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -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 diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 59c58f89..74608501 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -293,7 +293,7 @@ void get_config(uint8_t val[]) { ESP_LOGI(TAG, "Remote command: get device configuration"); payload.reset(); payload.addConfig(cfg); - senddata(CONFIGPORT); + EnqueueSendData(CONFIGPORT, payload.getBuffer(), payload.getSize()); }; void get_status(uint8_t val[]) { @@ -305,7 +305,7 @@ void get_status(uint8_t val[]) { #endif payload.reset(); payload.addStatus(voltage, uptime() / 1000, temperatureRead()); - senddata(STATUSPORT); + EnqueueSendData(STATUSPORT, payload.getBuffer(), payload.getSize()); }; void get_gps(uint8_t val[]) { @@ -314,7 +314,7 @@ void get_gps(uint8_t val[]) { gps_read(); payload.reset(); payload.addGPS(gps_status); - senddata(GPSPORT); + EnqueueSendData(GPSPORT, payload.getBuffer(), payload.getSize()); #else ESP_LOGW(TAG, "GPS function not supported"); #endif @@ -337,6 +337,8 @@ cmd_t table[] = { {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) { @@ -345,14 +347,13 @@ void rcommand(uint8_t cmd[], uint8_t cmdlength) { else cmdlength--; // minus 1 byte for opcode - int i = - sizeof(table) / sizeof(table[0]); // number of commands in command table + int i = cmdtablesize; while (i--) { if ((cmd[0] == table[i].opcode) && (table[i].params == cmdlength)) { // lookup command in opcode table memmove(cmd, cmd + 1, - cmdlength); // strip opcode + cmdlength); // strip opcode from cmd array table[i].func(cmd); // execute assigned function with given parameters if (table[i].store) // ceck if function needs to store configuration saveConfig(); diff --git a/src/senddata.cpp b/src/senddata.cpp index dbd6adc4..ba0a7148 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -1,28 +1,27 @@ // 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 EnqueueSendData(uint8_t port, uint8_t data[], uint8_t size) { + MessageBuffer_t *xMsg; // create pointer to struct which holds the sendbuffer + SendBuffer.MessageSize = size; + SendBuffer.MessagePort = PAYLOAD_ENCODER <= 2 + ? port + : (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT); + memcpy(SendBuffer.Message, data, size); + xMsg = &SendBuffer; + + // 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"); - } + xQueueSend(LoraSendQueue, (void *)&xMsg, (TickType_t)0); + ESP_LOGI(TAG, "%d bytes enqueued to send on LoRa", size); #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()); + xQueueSend(SPISendQueue, (void *)&xMsg, (TickType_t)0); + ESP_LOGI(TAG, "%d bytes enqueued to send on SPI", size); #endif // clear counter if not in cumulative counter mode @@ -34,8 +33,8 @@ void senddata(uint8_t port) { } // senddata +// cyclic called function to prepare payload to send void sendPayload() { - if (SendCycleTimerIRQ) { portENTER_CRITICAL(&timerMux); SendCycleTimerIRQ = 0; @@ -64,12 +63,46 @@ void sendPayload() { ESP_LOGD(TAG, "No valid GPS position or GPS data mode disabled"); } #endif - senddata(COUNTERPORT); + EnqueueSendData(COUNTERPORT, payload.getBuffer(), payload.getSize()); } -} // sendpayload(); +} // sendpayload() +// interrupt handler used for payload send cycle timer void IRAM_ATTR SendCycleIRQ() { portENTER_CRITICAL(&timerMux); SendCycleTimerIRQ++; portEXIT_CRITICAL(&timerMux); -} \ No newline at end of file +} + +// cyclic called function to eat data from RTos send queues and transmit it +void processSendBuffer() { + MessageBuffer_t *xMsg; + +#ifdef HAS_LORA + // Check if there is a pending TX/RX job running + if (LMIC.opmode & OP_TXRXPEND) { + sprintf(display_line7, "LORA BUSY"); + } else { + if (xQueueReceive(LoraSendQueue, &(xMsg), (TickType_t)10)) { + // xMsg now holds the struct MessageBuffer from queue + LMIC_setTxData2(xMsg->MessagePort, xMsg->Message, xMsg->MessageSize, + (cfg.countermode & 0x02)); + ESP_LOGI(TAG, "%d bytes sent to LORA", xMsg->MessageSize); + sprintf(display_line7, "PACKET QUEUED"); + } + } +#endif + +#ifdef HAS_SPI + if (xQueueReceive(SPISendQueue, &(xMsg), (TickType_t)10)) { + + // to come here: send data over SPI + // use these pointers to the payload: + // xMsg->MessagePort + // xMsg->MessageSize + // xMsg->Message + + ESP_LOGI(TAG, "%d bytes sent to SPI", xMsg->MessageSize); + } +#endif +} // processSendBuffer \ No newline at end of file diff --git a/src/senddata.h b/src/senddata.h index 00ff6d05..aa14c165 100644 --- a/src/senddata.h +++ b/src/senddata.h @@ -1,8 +1,19 @@ #ifndef _SENDDATA_H #define _SENDDATA_H -void senddata(uint8_t port); +// Struct holding payload for data send queue +typedef struct { + uint8_t MessageSize; + uint8_t MessagePort; + uint8_t Message[PAYLOAD_BUFFER_SIZE]; +} MessageBuffer_t; + +extern QueueHandle_t LoraSendQueue, SPISendQueue; +extern MessageBuffer_t SendBuffer; + +void EnqueueSendData(uint8_t port, uint8_t data[], uint8_t size); void sendPayload(void); void SendCycleIRQ(void); +void processSendBuffer(void); #endif // _SENDDATA_H_ \ No newline at end of file From 03ca7d4a37e93c6846b1e5439cbf90052c5414f5 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 4 Aug 2018 14:37:41 +0200 Subject: [PATCH 06/28] Send Queues (testing) --- README.md | 1 + img/Paxcounter-Screen.png | Bin 0 -> 48012 bytes platformio.ini | 4 +- src/cyclic.cpp | 2 +- src/display.cpp | 20 ++++- src/globals.h | 1 + src/macsniff.cpp | 2 +- src/main.cpp | 13 ++- src/paxcounter.conf | 4 +- src/senddata.cpp | 178 ++++++++++++++++++++++++++++++++------ src/senddata.h | 3 - 11 files changed, 185 insertions(+), 43 deletions(-) create mode 100644 img/Paxcounter-Screen.png diff --git a/README.md b/README.md index f7c38bc7..0becf496 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ + # Use case diff --git a/img/Paxcounter-Screen.png b/img/Paxcounter-Screen.png new file mode 100644 index 0000000000000000000000000000000000000000..0cb1eb47277d47e9603792a838550e914059730a GIT binary patch literal 48012 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 azJHW=*1G0yBX#@$UXr46BGp0$0sjk#SQ 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 diff --git a/src/cyclic.cpp b/src/cyclic.cpp index a2d9143f..b182a2c5 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -33,7 +33,7 @@ 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()); diff --git a/src/display.cpp b/src/display.cpp index d8999bb0..498e38d8 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -91,6 +91,9 @@ void init_display(const char *Productname, const char *Version) { void refreshtheDisplay() { + uint8_t msgWaiting = 0; + char buff[16]; + // set display on/off according to current device configuration if (DisplayState != cfg.screenon) { DisplayState = cfg.screenon; @@ -102,7 +105,6 @@ void refreshtheDisplay() { return; // 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 @@ -162,11 +164,23 @@ void refreshtheDisplay() { #ifdef HAS_LORA // update LoRa status display (line 6) u8x8.setCursor(0, 6); - u8x8.printf("%-16s", display_line6); + u8x8.printf("%-14s", display_line6); // 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.print(" "); // clear queue display + #endif // HAS_LORA } // refreshDisplay() diff --git a/src/globals.h b/src/globals.h index 48505e2c..cd45c995 100644 --- a/src/globals.h +++ b/src/globals.h @@ -47,6 +47,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::iterator it; extern std::array beacons; diff --git a/src/macsniff.cpp b/src/macsniff.cpp index 4cf43a56..73d606b1 100644 --- a/src/macsniff.cpp +++ b/src/macsniff.cpp @@ -100,7 +100,7 @@ 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 diff --git a/src/main.cpp b/src/main.cpp index f413220d..2ce4b6ef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,9 +41,8 @@ hw_timer_t *channelSwitch = NULL, *displaytimer = NULL, *sendCycle = NULL, volatile int ButtonPressedIRQ = 0, ChannelTimerIRQ = 0, SendCycleTimerIRQ = 0, DisplayTimerIRQ = 0, HomeCycleIRQ = 0; -// send queues +// RTos send queues for payload transmit QueueHandle_t LoraSendQueue, SPISendQueue; -MessageBuffer_t SendBuffer; portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; // sync main loop and ISR when modifying IRQ @@ -110,7 +109,9 @@ void setup() { // initialize send queues for transmit channels #ifdef HAS_LORA - LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(struct SendBuffer *)); + //--> LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(struct SendBuffer + //*)); + LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t)); if (LoraSendQueue == 0) { ESP_LOGE(TAG, "Could not create LORA send queue. Aborting."); exit(0); @@ -119,7 +120,9 @@ void setup() { SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE); #endif #ifdef HAS_SPI - SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(struct SendBuffer *)); + //--> SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(struct SendBuffer + //*)); + SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t)); if (SPISendQueue == 0) { ESP_LOGE(TAG, "Could not create SPI send queue. Aborting."); exit(0); @@ -330,6 +333,8 @@ void loop() { // reset watchdog vTaskDelay(1 / portTICK_PERIOD_MS); + //ESP_LOGI(TAG, "%d Bytes left", ESP.getFreeHeap()); + } // loop() } diff --git a/src/paxcounter.conf b/src/paxcounter.conf index 7cf76677..aef4c46b 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -9,10 +9,10 @@ // 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 +//#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, saves power & memory // BLE scan parameters diff --git a/src/senddata.cpp b/src/senddata.cpp index ba0a7148..84fd90ad 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -3,25 +3,25 @@ // put data to send in RTos Queues used for transmit over channels Lora and SPI void EnqueueSendData(uint8_t port, uint8_t data[], uint8_t size) { - MessageBuffer_t *xMsg; // create pointer to struct which holds the sendbuffer - SendBuffer.MessageSize = size; - SendBuffer.MessagePort = PAYLOAD_ENCODER <= 2 - ? port - : (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT); - memcpy(SendBuffer.Message, data, size); - xMsg = &SendBuffer; + MessageBuffer_t MySendBuffer; + + MySendBuffer.MessageSize = size; + MySendBuffer.MessagePort = PAYLOAD_ENCODER <= 2 + ? port + : (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT); + memcpy(MySendBuffer.Message, data, size); // enqueue message in LoRa send queue #ifdef HAS_LORA - xQueueSend(LoraSendQueue, (void *)&xMsg, (TickType_t)0); - ESP_LOGI(TAG, "%d bytes enqueued to send on LoRa", size); + if (xQueueSendToBack(LoraSendQueue, (void *)&MySendBuffer, (TickType_t)0)) + ESP_LOGI(TAG, "%d bytes enqueued to send on LoRa", size); #endif // enqueue message in SPI send queue #ifdef HAS_SPI - xQueueSend(SPISendQueue, (void *)&xMsg, (TickType_t)0); - ESP_LOGI(TAG, "%d bytes enqueued to send on SPI", size); + if (xQueueSendToBack(SPISendQueue, (void *)&MySendBuffer, (TickType_t)0)) + ESP_LOGI(TAG, "%d bytes enqueued to send on SPI", size); #endif // clear counter if not in cumulative counter mode @@ -31,6 +31,8 @@ void EnqueueSendData(uint8_t port, uint8_t data[], uint8_t size) { ESP_LOGI(TAG, "Counter cleared"); } + ESP_LOGI(TAG, "%d Bytes left", ESP.getFreeHeap()); + } // senddata // cyclic called function to prepare payload to send @@ -76,33 +78,155 @@ void IRAM_ATTR SendCycleIRQ() { // cyclic called function to eat data from RTos send queues and transmit it void processSendBuffer() { - MessageBuffer_t *xMsg; + + MessageBuffer_t RcvBuf; #ifdef HAS_LORA // Check if there is a pending TX/RX job running - if (LMIC.opmode & OP_TXRXPEND) { - sprintf(display_line7, "LORA BUSY"); + 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, &(xMsg), (TickType_t)10)) { + if (xQueueReceive(LoraSendQueue, &(RcvBuf), (TickType_t)10)) { // xMsg now holds the struct MessageBuffer from queue - LMIC_setTxData2(xMsg->MessagePort, xMsg->Message, xMsg->MessageSize, + LMIC_setTxData2(RcvBuf.MessagePort, RcvBuf.Message, RcvBuf.MessageSize, (cfg.countermode & 0x02)); - ESP_LOGI(TAG, "%d bytes sent to LORA", xMsg->MessageSize); + ESP_LOGI(TAG, "%d bytes sent to LORA", RcvBuf.MessageSize); sprintf(display_line7, "PACKET QUEUED"); } } #endif #ifdef HAS_SPI - if (xQueueReceive(SPISendQueue, &(xMsg), (TickType_t)10)) { - - // to come here: send data over SPI - // use these pointers to the payload: - // xMsg->MessagePort - // xMsg->MessageSize - // xMsg->Message - - ESP_LOGI(TAG, "%d bytes sent to SPI", xMsg->MessageSize); + if (xQueueReceive(SPISendQueue, &(RcvBuf), (TickType_t)10)) { + ESP_LOGI(TAG, "%d bytes sent to SPI", RcvBuf.MessageSize); } #endif -} // processSendBuffer \ No newline at end of file + + ESP_LOGI(TAG, "%d Bytes left", ESP.getFreeHeap()); + +} // processSendBuffer + +/* old version with pointers + +// Basic Config +#include "globals.h" + +// put data to send in RTos Queues used for transmit over channels Lora and SPI +void EnqueueSendData(uint8_t port, uint8_t data[], uint8_t size) { + + MessageBuffer_t *xMsg = &SendBuffer; + + SendBuffer.MessageSize = size; + SendBuffer.MessagePort = PAYLOAD_ENCODER <= 2 + ? port + : (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT); + memcpy(SendBuffer.Message, data, size); + + // enqueue message in LoRa send queue +#ifdef HAS_LORA + if (uxQueueSpacesAvailable(LoraSendQueue)) { + xQueueSend(LoraSendQueue, (void *)&xMsg, (TickType_t)0); + ESP_LOGI(TAG, "%d bytes enqueued to send on LoRa", size); + }; +#endif + +// enqueue message in SPI send queue +#ifdef HAS_SPI + if (uxQueueSpacesAvailable(SPISendQueue)) { + xQueueSend(SPISendQueue, (void *)&xMsg, (TickType_t)0); + ESP_LOGI(TAG, "%d bytes enqueued to send on SPI", size); + }; +#endif + + // clear counter if not in cumulative counter mode + if ((port == COUNTERPORT) && (cfg.countermode != 1)) { + reset_counters(); // clear macs container and reset all counters + reset_salt(); // get new salt for salting hashes + ESP_LOGI(TAG, "Counter cleared"); + } + + ESP_LOGI(TAG, "%d Bytes left", ESP.getFreeHeap()); + +} // senddata + +// cyclic called function to prepare payload to send +void sendPayload() { + if (SendCycleTimerIRQ) { + portENTER_CRITICAL(&timerMux); + SendCycleTimerIRQ = 0; + portEXIT_CRITICAL(&timerMux); + + // append counter data to payload + payload.reset(); + payload.addCount(macs_wifi, cfg.blescan ? macs_ble : 0); + // append GPS data, if present + +#ifdef HAS_GPS + // show NMEA data in debug mode, useful for debugging GPS on board + // connection + ESP_LOGD(TAG, "GPS NMEA data: passed %d / failed: %d / with fix: %d", + gps.passedChecksum(), gps.failedChecksum(), + gps.sentencesWithFix()); + // log GPS position if we have a fix and gps data mode is enabled + if ((cfg.gpsmode) && (gps.location.isValid())) { + gps_read(); + payload.addGPS(gps_status); + ESP_LOGD(TAG, "lat=%.6f | lon=%.6f | %u Sats | HDOP=%.1f | Altitude=%um", + gps_status.latitude / (float)1e6, + gps_status.longitude / (float)1e6, gps_status.satellites, + gps_status.hdop / (float)100, gps_status.altitude); + } else { + ESP_LOGD(TAG, "No valid GPS position or GPS data mode disabled"); + } +#endif + EnqueueSendData(COUNTERPORT, payload.getBuffer(), payload.getSize()); + } +} // 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 *xMsg; + +#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 (uxQueueMessagesWaiting(LoraSendQueue)) // check if msg are waiting on +queue if (xQueueReceive(LoraSendQueue, &xMsg, (TickType_t)10)) { + // xMsg now holds the struct MessageBuffer from queue + LMIC_setTxData2(xMsg->MessagePort, xMsg->Message, xMsg->MessageSize, + (cfg.countermode & 0x02)); + ESP_LOGI(TAG, "%d bytes sent to LORA", xMsg->MessageSize); + sprintf(display_line7, "PACKET QUEUED"); + } + } +#endif + +#ifdef HAS_SPI + if (uxQueueMessagesWaiting(SPISendQueue)) // check if msg are waiting on queue + if (xQueueReceive(SPISendQueue, &xMsg, (TickType_t)10)) { + + // to come here: send data over SPI + // use these pointers to the payload: + // xMsg->MessagePort + // xMsg->MessageSize + // xMsg->Message + + ESP_LOGI(TAG, "%d bytes sent to SPI", xMsg->MessageSize); + } +#endif + + ESP_LOGI(TAG, "%d Bytes left", ESP.getFreeHeap()); + +} // processSendBuffer + +*/ \ No newline at end of file diff --git a/src/senddata.h b/src/senddata.h index aa14c165..18dbc427 100644 --- a/src/senddata.h +++ b/src/senddata.h @@ -8,9 +8,6 @@ typedef struct { uint8_t Message[PAYLOAD_BUFFER_SIZE]; } MessageBuffer_t; -extern QueueHandle_t LoraSendQueue, SPISendQueue; -extern MessageBuffer_t SendBuffer; - void EnqueueSendData(uint8_t port, uint8_t data[], uint8_t size); void sendPayload(void); void SendCycleIRQ(void); From 88ab6251f61dfa413a462f601e87bf8f46ae4a5e Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 4 Aug 2018 15:27:58 +0200 Subject: [PATCH 07/28] Send Queues (testing) --- platformio.ini | 4 ++-- src/button.cpp | 2 +- src/cyclic.cpp | 2 +- src/display.cpp | 16 ++++++++-------- src/macsniff.cpp | 2 +- src/main.cpp | 2 -- src/rcommand.cpp | 6 +++--- src/senddata.cpp | 22 +++++++++------------- src/senddata.h | 2 +- 9 files changed, 26 insertions(+), 32 deletions(-) diff --git a/platformio.ini b/platformio.ini index 54991dc8..5ac72033 100644 --- a/platformio.ini +++ b/platformio.ini @@ -44,8 +44,8 @@ build_flags = ; ---> NOTE: For production run set DEBUG_LEVEL level to NONE! <--- ; otherwise device may crash in dense environments due to serial buffer overflow ; -; -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE - -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_INFO + -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 ; diff --git a/src/button.cpp b/src/button.cpp index 4ca25638..3f75674a 100644 --- a/src/button.cpp +++ b/src/button.cpp @@ -16,7 +16,7 @@ void readButton() { ESP_LOGI(TAG, "Button pressed"); payload.reset(); payload.addButton(0x01); - EnqueueSendData(BUTTONPORT, payload.getBuffer(), payload.getSize()); + SendData(BUTTONPORT); } } #endif \ No newline at end of file diff --git a/src/cyclic.cpp b/src/cyclic.cpp index b182a2c5..2fdbee03 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -37,7 +37,7 @@ void doHomework() { "Memory full, counter cleared (heap low water mark = %d Bytes / " "free heap = %d bytes)", esp_get_minimum_free_heap_size(), ESP.getFreeHeap()); - EnqueueSendData(COUNTERPORT, payload.getBuffer(), payload.getSize()); // 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 } diff --git a/src/display.cpp b/src/display.cpp index 498e38d8..e43efcd5 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -91,19 +91,19 @@ void init_display(const char *Productname, const char *Version) { void refreshtheDisplay() { - uint8_t msgWaiting = 0; - char buff[16]; - // set display on/off according to current device configuration if (DisplayState != cfg.screenon) { DisplayState = cfg.screenon; 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) snprintf( buff, sizeof(buff), "PAX:%-4d", @@ -164,11 +164,11 @@ void refreshtheDisplay() { #ifdef HAS_LORA // update LoRa status display (line 6) u8x8.setCursor(0, 6); - u8x8.printf("%-14s", display_line6); + u8x8.printf("%-16s", display_line6); // update LMiC event display (line 7) u8x8.setCursor(0, 7); - u8x8.printf("%-14s", display_line7); + u8x8.printf("%-16s", display_line7); // update LoRa send queue display (line 7) msgWaiting = uxQueueMessagesWaiting(LoraSendQueue); @@ -178,10 +178,10 @@ void refreshtheDisplay() { u8x8.setInverseFont(1); u8x8.printf("%-2s", msgWaiting == SEND_QUEUE_SIZE ? "<>" : buff); u8x8.setInverseFont(0); - } else - u8x8.print(" "); // clear queue display + } #endif // HAS_LORA + } // refreshDisplay() void IRAM_ATTR DisplayIRQ() { diff --git a/src/macsniff.cpp b/src/macsniff.cpp index 73d606b1..29b1f1ad 100644 --- a/src/macsniff.cpp +++ b/src/macsniff.cpp @@ -106,7 +106,7 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) { #endif payload.reset(); payload.addAlarm(rssi, beaconID); - EnqueueSendData(BEACONPORT, payload.getBuffer(), payload.getSize()); + SendData(BEACONPORT); } }; diff --git a/src/main.cpp b/src/main.cpp index 2ce4b6ef..dd826c2e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -333,8 +333,6 @@ void loop() { // reset watchdog vTaskDelay(1 / portTICK_PERIOD_MS); - //ESP_LOGI(TAG, "%d Bytes left", ESP.getFreeHeap()); - } // loop() } diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 74608501..03068848 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -293,7 +293,7 @@ void get_config(uint8_t val[]) { ESP_LOGI(TAG, "Remote command: get device configuration"); payload.reset(); payload.addConfig(cfg); - EnqueueSendData(CONFIGPORT, payload.getBuffer(), payload.getSize()); + SendData(CONFIGPORT); }; void get_status(uint8_t val[]) { @@ -305,7 +305,7 @@ void get_status(uint8_t val[]) { #endif payload.reset(); payload.addStatus(voltage, uptime() / 1000, temperatureRead()); - EnqueueSendData(STATUSPORT, payload.getBuffer(), payload.getSize()); + SendData(STATUSPORT); }; void get_gps(uint8_t val[]) { @@ -314,7 +314,7 @@ void get_gps(uint8_t val[]) { gps_read(); payload.reset(); payload.addGPS(gps_status); - EnqueueSendData(GPSPORT, payload.getBuffer(), payload.getSize()); + SendData(GPSPORT); #else ESP_LOGW(TAG, "GPS function not supported"); #endif diff --git a/src/senddata.cpp b/src/senddata.cpp index 84fd90ad..bf811fab 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -2,26 +2,26 @@ #include "globals.h" // put data to send in RTos Queues used for transmit over channels Lora and SPI -void EnqueueSendData(uint8_t port, uint8_t data[], uint8_t size) { +void SendData(uint8_t port) { MessageBuffer_t MySendBuffer; - MySendBuffer.MessageSize = size; + MySendBuffer.MessageSize = payload.getSize(); MySendBuffer.MessagePort = PAYLOAD_ENCODER <= 2 ? port : (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT); - memcpy(MySendBuffer.Message, data, size); + memcpy(MySendBuffer.Message, payload.getBuffer(), payload.getSize()); // enqueue message in LoRa send queue #ifdef HAS_LORA if (xQueueSendToBack(LoraSendQueue, (void *)&MySendBuffer, (TickType_t)0)) - ESP_LOGI(TAG, "%d bytes enqueued to send on LoRa", size); + ESP_LOGI(TAG, "%d bytes enqueued to send on LoRa", payload.getSize()); #endif // enqueue message in SPI send queue #ifdef HAS_SPI if (xQueueSendToBack(SPISendQueue, (void *)&MySendBuffer, (TickType_t)0)) - ESP_LOGI(TAG, "%d bytes enqueued to send on SPI", size); + ESP_LOGI(TAG, "%d bytes enqueued to send on SPI", payload.getSize()); #endif // clear counter if not in cumulative counter mode @@ -31,9 +31,7 @@ void EnqueueSendData(uint8_t port, uint8_t data[], uint8_t size) { ESP_LOGI(TAG, "Counter cleared"); } - ESP_LOGI(TAG, "%d Bytes left", ESP.getFreeHeap()); - -} // senddata +} // SendData // cyclic called function to prepare payload to send void sendPayload() { @@ -65,7 +63,7 @@ void sendPayload() { ESP_LOGD(TAG, "No valid GPS position or GPS data mode disabled"); } #endif - EnqueueSendData(COUNTERPORT, payload.getBuffer(), payload.getSize()); + SendData(COUNTERPORT); } } // sendpayload() @@ -102,8 +100,6 @@ void processSendBuffer() { } #endif - ESP_LOGI(TAG, "%d Bytes left", ESP.getFreeHeap()); - } // processSendBuffer /* old version with pointers @@ -112,7 +108,7 @@ void processSendBuffer() { #include "globals.h" // put data to send in RTos Queues used for transmit over channels Lora and SPI -void EnqueueSendData(uint8_t port, uint8_t data[], uint8_t size) { +void SendData(uint8_t port, uint8_t data[], uint8_t size) { MessageBuffer_t *xMsg = &SendBuffer; @@ -179,7 +175,7 @@ void sendPayload() { ESP_LOGD(TAG, "No valid GPS position or GPS data mode disabled"); } #endif - EnqueueSendData(COUNTERPORT, payload.getBuffer(), payload.getSize()); + SendData(COUNTERPORT, payload.getBuffer(), payload.getSize()); } } // sendpayload() diff --git a/src/senddata.h b/src/senddata.h index 18dbc427..07fc477d 100644 --- a/src/senddata.h +++ b/src/senddata.h @@ -8,7 +8,7 @@ typedef struct { uint8_t Message[PAYLOAD_BUFFER_SIZE]; } MessageBuffer_t; -void EnqueueSendData(uint8_t port, uint8_t data[], uint8_t size); +void SendData(uint8_t port); void sendPayload(void); void SendCycleIRQ(void); void processSendBuffer(void); From c623de9a399eb0aa6bf25d1ef0ef03f5769d0076 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 4 Aug 2018 15:40:28 +0200 Subject: [PATCH 08/28] Send Queues (testing) --- src/display.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/display.cpp b/src/display.cpp index e43efcd5..c3617538 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -168,7 +168,7 @@ 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); @@ -178,7 +178,8 @@ void refreshtheDisplay() { u8x8.setInverseFont(1); u8x8.printf("%-2s", msgWaiting == SEND_QUEUE_SIZE ? "<>" : buff); u8x8.setInverseFont(0); - } + } else + u8x8.printf(" "); #endif // HAS_LORA From 0a63b444de78121705a58134c3f9fa9712d3a301 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 4 Aug 2018 18:09:25 +0200 Subject: [PATCH 09/28] rcommand.cpp improved --- README.md | 2 +- platformio.ini | 3 +-- src/cyclic.cpp | 3 +++ src/rcommand.cpp | 45 ++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 47 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0becf496..7d9dd168 100644 --- a/README.md +++ b/README.md @@ -226,7 +226,7 @@ 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 send remote command 09 02 09 00 unconfirmed(!) once. diff --git a/platformio.ini b/platformio.ini index 5ac72033..0cd42208 100644 --- a/platformio.ini +++ b/platformio.ini @@ -40,9 +40,8 @@ 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 diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 2fdbee03..adbadda4 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -40,6 +40,9 @@ void doHomework() { 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() diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 03068848..f0c53173 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -337,7 +337,46 @@ cmd_t table[] = { {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 +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; + + uint8_t foundcmd[cmdlength], cursor = 0; + bool storeflag = false; + + 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 + } // lookup command + } // while loop table lookup + } // while loop parsing cmd + + if (storeflag) + saveConfig(); +} // rcommand() + +/* // check and execute remote command void rcommand(uint8_t cmd[], uint8_t cmdlength) { @@ -348,7 +387,6 @@ void rcommand(uint8_t cmd[], uint8_t cmdlength) { cmdlength--; // minus 1 byte for opcode int i = cmdtablesize; - while (i--) { if ((cmd[0] == table[i].opcode) && (table[i].params == cmdlength)) { // lookup command in opcode table @@ -361,4 +399,5 @@ void rcommand(uint8_t cmd[], uint8_t cmdlength) { } // lookup command } // while -} // rcommand() \ No newline at end of file +} // rcommand() +*/ \ No newline at end of file From 2df6700b2dfa4d46b2afe13e527d63c0be5824f3 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 4 Aug 2018 18:13:42 +0200 Subject: [PATCH 10/28] macsniff.cpp fixed compiler warning --- platformio.ini | 2 +- src/macsniff.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/platformio.ini b/platformio.ini index 0cd42208..834e9770 100644 --- a/platformio.ini +++ b/platformio.ini @@ -52,7 +52,7 @@ build_flags = -D_lmic_config_h_ -include "src/paxcounter.conf" -include "src/hal/${PIOENV}.h" - -w +; -w [env:test] platform = ${common_env_data.platform_espressif32} diff --git a/src/macsniff.cpp b/src/macsniff.cpp index 29b1f1ad..faf5f583 100644 --- a/src/macsniff.cpp +++ b/src/macsniff.cpp @@ -46,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 + uinit32_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 From 1ba84814747db98df9f11e0a49affe4145930dd5 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 4 Aug 2018 18:29:44 +0200 Subject: [PATCH 11/28] macsniff.cpp bugfix --- src/macsniff.cpp | 2 +- src/paxcounter.conf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/macsniff.cpp b/src/macsniff.cpp index faf5f583..ac037e28 100644 --- a/src/macsniff.cpp +++ b/src/macsniff.cpp @@ -50,7 +50,7 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) { uint16_t hashedmac; // temporary buffer for generated hash value uint32_t addr2int; // temporary buffer for shortened MAC #ifdef VENDORFILTER - uinit32_t vendor2int; // temporary buffer for Vendor OUI + uint32_t vendor2int; // temporary buffer for Vendor OUI #endif // only last 3 MAC Address bytes are used for MAC address anonymization diff --git a/src/paxcounter.conf b/src/paxcounter.conf index aef4c46b..bcecc56d 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -12,7 +12,7 @@ #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 +#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, saves power & memory // BLE scan parameters From 9b472780dadfbee396fd3cf387317ed1250a1606 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 4 Aug 2018 18:58:20 +0200 Subject: [PATCH 12/28] src/hal/ebox.h added --- platformio.ini | 8 ++++---- src/hal/{test.h => ebox.h} | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) rename src/hal/{test.h => ebox.h} (93%) diff --git a/platformio.ini b/platformio.ini index 834e9770..f48e0050 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,12 +11,12 @@ ; ---> SELECT TARGET PLATFORM HERE! <--- [platformio] -;env_default = test +env_default = ebox ;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 @@ -54,10 +54,10 @@ build_flags = -include "src/hal/${PIOENV}.h" ; -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 diff --git a/src/hal/test.h b/src/hal/ebox.h similarity index 93% rename from src/hal/test.h rename to src/hal/ebox.h index 5c9ea1aa..ff622bea 100644 --- a/src/hal/test.h +++ b/src/hal/ebox.h @@ -1,4 +1,4 @@ -// 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 From 0a52b16c780663b8db1bb98ca6ceb8063d585ed7 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 4 Aug 2018 22:49:12 +0200 Subject: [PATCH 13/28] added free memory to device status query --- README.md | 3 ++- src/TTN/packed_decoder.js | 14 +++++++++++--- src/payload.cpp | 13 ++++++++++--- src/payload.h | 3 ++- src/rcommand.cpp | 38 +++++++------------------------------- 5 files changed, 32 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 7d9dd168..c4f3fe02 100644 --- a/README.md +++ b/README.md @@ -138,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 diff --git a/src/TTN/packed_decoder.js b/src/TTN/packed_decoder.js index 5ed8ab54..549a5ae7 100644 --- a/src/TTN/packed_decoder.js +++ b/src/TTN/packed_decoder.js @@ -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']); } @@ -66,7 +66,7 @@ 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 +74,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'); diff --git a/src/payload.cpp b/src/payload.cpp index 192e4af5..ab3d2c91 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -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 diff --git a/src/payload.h b/src/payload.h index c51ebb3c..ba564ec7 100644 --- a/src/payload.h +++ b/src/payload.h @@ -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); diff --git a/src/rcommand.cpp b/src/rcommand.cpp index f0c53173..7c9d7d40 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -304,7 +304,7 @@ void get_status(uint8_t val[]) { uint16_t voltage = 0; #endif payload.reset(); - payload.addStatus(voltage, uptime() / 1000, temperatureRead()); + payload.addStatus(voltage, uptime() / 1000, temperatureRead(), ESP.getFreeHeap()); SendData(STATUSPORT); }; @@ -350,6 +350,7 @@ void rcommand(uint8_t cmd[], uint8_t cmdlength) { bool storeflag = false; while (cursor < cmdlength) { + int i = cmdtablesize; while (i--) { if (cmd[cursor] == table[i].opcode) { // lookup command in opcode table @@ -368,36 +369,11 @@ void rcommand(uint8_t cmd[], uint8_t cmdlength) { "Remote command x%02X called with missing parameter(s), skipped", table[i].opcode); break; // exit table lookup loop, command was found - } // lookup command - } // while loop table lookup - } // while loop parsing cmd + } // command validation + } // command table lookup loop + + } // command parsing loop if (storeflag) saveConfig(); -} // rcommand() - -/* - -// check and execute remote command -void rcommand(uint8_t cmd[], uint8_t cmdlength) { - - if (cmdlength == 0) - return; - else - cmdlength--; // minus 1 byte for opcode - - int i = cmdtablesize; - while (i--) { - if ((cmd[0] == table[i].opcode) && - (table[i].params == cmdlength)) { // lookup command in opcode table - memmove(cmd, cmd + 1, - cmdlength); // strip opcode from cmd array - table[i].func(cmd); // execute assigned function with given parameters - if (table[i].store) // ceck if function needs to store configuration - saveConfig(); - break; // exit while loop, command was found - } // lookup command - } // while - -} // rcommand() -*/ \ No newline at end of file +} // rcommand() \ No newline at end of file From 95983bd3fcb2c3ac2c678bb4cd9930d41d11ff2f Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 5 Aug 2018 12:16:54 +0200 Subject: [PATCH 14/28] rcommand flush send queue added --- README.md | 1 + src/lorawan.cpp | 47 ++++++++++++++++++ src/lorawan.h | 1 + src/rcommand.cpp | 57 +++------------------- src/senddata.cpp | 124 ++--------------------------------------------- src/senddata.h | 1 + 6 files changed, 61 insertions(+), 170 deletions(-) diff --git a/README.md b/README.md index c4f3fe02..92375698 100644 --- a/README.md +++ b/README.md @@ -277,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 diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 41c386e9..2337ec1f 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -228,4 +228,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 \ No newline at end of file diff --git a/src/lorawan.h b/src/lorawan.h index b9abb8c1..d88062e7 100644 --- a/src/lorawan.h +++ b/src/lorawan.h @@ -15,5 +15,6 @@ void os_getArtEui(u1_t *buf); void os_getDevEui(u1_t *buf); void showLoraKeys(void); void lorawan_loop(void *pvParameters); +void switch_lora(uint8_t sf, uint8_t tx); #endif \ No newline at end of file diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 7c9d7d40..f925f1da 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -8,55 +8,6 @@ // 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]) { @@ -78,6 +29,11 @@ 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, "Flush queues"); + flushQueues(); + break; default: ESP_LOGW(TAG, "Remote command: reset called with invalid parameter(s)"); } @@ -304,7 +260,8 @@ void get_status(uint8_t val[]) { uint16_t voltage = 0; #endif payload.reset(); - payload.addStatus(voltage, uptime() / 1000, temperatureRead(), ESP.getFreeHeap()); + payload.addStatus(voltage, uptime() / 1000, temperatureRead(), + ESP.getFreeHeap()); SendData(STATUSPORT); }; diff --git a/src/senddata.cpp b/src/senddata.cpp index bf811fab..d4bced41 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -102,127 +102,11 @@ void processSendBuffer() { } // processSendBuffer -/* old version with pointers - -// Basic Config -#include "globals.h" - -// put data to send in RTos Queues used for transmit over channels Lora and SPI -void SendData(uint8_t port, uint8_t data[], uint8_t size) { - - MessageBuffer_t *xMsg = &SendBuffer; - - SendBuffer.MessageSize = size; - SendBuffer.MessagePort = PAYLOAD_ENCODER <= 2 - ? port - : (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT); - memcpy(SendBuffer.Message, data, size); - - // enqueue message in LoRa send queue +void flushQueues() { #ifdef HAS_LORA - if (uxQueueSpacesAvailable(LoraSendQueue)) { - xQueueSend(LoraSendQueue, (void *)&xMsg, (TickType_t)0); - ESP_LOGI(TAG, "%d bytes enqueued to send on LoRa", size); - }; + xQueueReset(LoraSendQueue); #endif - -// enqueue message in SPI send queue #ifdef HAS_SPI - if (uxQueueSpacesAvailable(SPISendQueue)) { - xQueueSend(SPISendQueue, (void *)&xMsg, (TickType_t)0); - ESP_LOGI(TAG, "%d bytes enqueued to send on SPI", size); - }; + xQueueReset(SPISendQueue); #endif - - // clear counter if not in cumulative counter mode - if ((port == COUNTERPORT) && (cfg.countermode != 1)) { - reset_counters(); // clear macs container and reset all counters - reset_salt(); // get new salt for salting hashes - ESP_LOGI(TAG, "Counter cleared"); - } - - ESP_LOGI(TAG, "%d Bytes left", ESP.getFreeHeap()); - -} // senddata - -// cyclic called function to prepare payload to send -void sendPayload() { - if (SendCycleTimerIRQ) { - portENTER_CRITICAL(&timerMux); - SendCycleTimerIRQ = 0; - portEXIT_CRITICAL(&timerMux); - - // append counter data to payload - payload.reset(); - payload.addCount(macs_wifi, cfg.blescan ? macs_ble : 0); - // append GPS data, if present - -#ifdef HAS_GPS - // show NMEA data in debug mode, useful for debugging GPS on board - // connection - ESP_LOGD(TAG, "GPS NMEA data: passed %d / failed: %d / with fix: %d", - gps.passedChecksum(), gps.failedChecksum(), - gps.sentencesWithFix()); - // log GPS position if we have a fix and gps data mode is enabled - if ((cfg.gpsmode) && (gps.location.isValid())) { - gps_read(); - payload.addGPS(gps_status); - ESP_LOGD(TAG, "lat=%.6f | lon=%.6f | %u Sats | HDOP=%.1f | Altitude=%um", - gps_status.latitude / (float)1e6, - gps_status.longitude / (float)1e6, gps_status.satellites, - gps_status.hdop / (float)100, gps_status.altitude); - } else { - ESP_LOGD(TAG, "No valid GPS position or GPS data mode disabled"); - } -#endif - SendData(COUNTERPORT, payload.getBuffer(), payload.getSize()); - } -} // 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 *xMsg; - -#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 (uxQueueMessagesWaiting(LoraSendQueue)) // check if msg are waiting on -queue if (xQueueReceive(LoraSendQueue, &xMsg, (TickType_t)10)) { - // xMsg now holds the struct MessageBuffer from queue - LMIC_setTxData2(xMsg->MessagePort, xMsg->Message, xMsg->MessageSize, - (cfg.countermode & 0x02)); - ESP_LOGI(TAG, "%d bytes sent to LORA", xMsg->MessageSize); - sprintf(display_line7, "PACKET QUEUED"); - } - } -#endif - -#ifdef HAS_SPI - if (uxQueueMessagesWaiting(SPISendQueue)) // check if msg are waiting on queue - if (xQueueReceive(SPISendQueue, &xMsg, (TickType_t)10)) { - - // to come here: send data over SPI - // use these pointers to the payload: - // xMsg->MessagePort - // xMsg->MessageSize - // xMsg->Message - - ESP_LOGI(TAG, "%d bytes sent to SPI", xMsg->MessageSize); - } -#endif - - ESP_LOGI(TAG, "%d Bytes left", ESP.getFreeHeap()); - -} // processSendBuffer - -*/ \ No newline at end of file +} \ No newline at end of file diff --git a/src/senddata.h b/src/senddata.h index 07fc477d..e709cedc 100644 --- a/src/senddata.h +++ b/src/senddata.h @@ -12,5 +12,6 @@ void SendData(uint8_t port); void sendPayload(void); void SendCycleIRQ(void); void processSendBuffer(void); +void flushQueues(); #endif // _SENDDATA_H_ \ No newline at end of file From 63a063f9bcc7a512dac8795663503171030964bc Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 5 Aug 2018 17:49:53 +0200 Subject: [PATCH 15/28] v1.4.2 --- platformio.ini | 10 +++++----- src/TTN/packed_decoder.js | 19 ++++++++++--------- src/paxcounter.conf | 2 +- src/rcommand.cpp | 5 +---- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/platformio.ini b/platformio.ini index f48e0050..211a3e88 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,8 +11,8 @@ ; ---> SELECT TARGET PLATFORM HERE! <--- [platformio] -env_default = ebox -;env_default = generic +env_default = generic +;env_default = ebox ;env_default = heltec ;env_default = ttgov1 ;env_default = ttgov2 @@ -43,8 +43,8 @@ build_flags = ; ---> NOTE: For production run set DEBUG_LEVEL level to NONE! <--- ; 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_NONE + -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_INFO ; -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG ; -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE ; @@ -52,7 +52,7 @@ build_flags = -D_lmic_config_h_ -include "src/paxcounter.conf" -include "src/hal/${PIOENV}.h" -; -w + -w [env:ebox] platform = ${common_env_data.platform_espressif32} diff --git a/src/TTN/packed_decoder.js b/src/TTN/packed_decoder.js index 549a5ae7..6bf29f09 100644 --- a/src/TTN/packed_decoder.js +++ b/src/TTN/packed_decoder.js @@ -56,14 +56,6 @@ 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('uint8 must have exactly 1 byte'); @@ -96,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'); @@ -177,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, diff --git a/src/paxcounter.conf b/src/paxcounter.conf index bcecc56d..7cf76677 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -9,7 +9,7 @@ // Payload send cycle and encoding #define SEND_SECS 30 // payload send cycle [seconds/2] -> 60 sec. -#define PAYLOAD_ENCODER 2 // payload encoder: 1=Plain, 2=Packed, 3=CayenneLPP dynamic, 4=CayenneLPP packed +#define PAYLOAD_ENCODER 1 // 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 diff --git a/src/rcommand.cpp b/src/rcommand.cpp index f925f1da..60df3f4c 100644 --- a/src/rcommand.cpp +++ b/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" @@ -31,7 +28,7 @@ void set_reset(uint8_t val[]) { break; case 3: // reset send queues ESP_LOGI(TAG, "Remote command: flush send queue"); - sprintf(display_line6, "Flush queues"); + sprintf(display_line6, "Queue reset"); flushQueues(); break; default: From 459f4c1b29216e634ece296b21033a83f30ddfc5 Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sun, 5 Aug 2018 19:39:28 +0200 Subject: [PATCH 16/28] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 92375698..5025c31d 100644 --- a/README.md +++ b/README.md @@ -116,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 From 5413347e35a92d0e06003f833c8925a617779bc4 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 5 Aug 2018 23:07:13 +0200 Subject: [PATCH 17/28] src\hal\ebox.h edit --- src/hal/ebox.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hal/ebox.h b/src/hal/ebox.h index ff622bea..5d42df3a 100644 --- a/src/hal/ebox.h +++ b/src/hal/ebox.h @@ -6,6 +6,7 @@ #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 From cd63d4164b01ef5782f4b88c44e7f0f2b31e2214 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Mon, 6 Aug 2018 08:19:19 +0200 Subject: [PATCH 18/28] rcommand.h edited --- src/rcommand.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rcommand.h b/src/rcommand.h index c6d9bdbd..6afb905f 100644 --- a/src/rcommand.h +++ b/src/rcommand.h @@ -15,6 +15,5 @@ typedef struct { } cmd_t; void rcommand(uint8_t cmd[], uint8_t cmdlength); -void switch_lora(uint8_t sf, uint8_t tx); #endif \ No newline at end of file From 482066eac9efd543fa6716556b22cde54c6d17f5 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Mon, 6 Aug 2018 08:22:04 +0200 Subject: [PATCH 19/28] cleanups main.cpp --- src/main.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index dd826c2e..4f41ee09 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -109,8 +109,6 @@ void setup() { // initialize send queues for transmit channels #ifdef HAS_LORA - //--> LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(struct SendBuffer - //*)); LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t)); if (LoraSendQueue == 0) { ESP_LOGE(TAG, "Could not create LORA send queue. Aborting."); @@ -120,8 +118,6 @@ void setup() { SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE); #endif #ifdef HAS_SPI - //--> SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(struct SendBuffer - //*)); SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t)); if (SPISendQueue == 0) { ESP_LOGE(TAG, "Could not create SPI send queue. Aborting."); From af62c8fb118f73158c358877b4dbdc9fecc7d5a2 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Tue, 7 Aug 2018 21:10:34 +0200 Subject: [PATCH 20/28] Link check validation disabled after join --- src/globals.h | 2 +- src/lorawan.cpp | 7 ++++++- src/senddata.cpp | 28 +++++++++++++--------------- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/globals.h b/src/globals.h index cd45c995..1111476f 100644 --- a/src/globals.h +++ b/src/globals.h @@ -8,7 +8,7 @@ #include // attn: increment version after modifications to configData_t truct! -#define PROGVERSION "1.4.2" // use max 10 chars here! +#define PROGVERSION "1.4.21" // use max 10 chars here! #define PROGNAME "PAXCNT" // std::set for unified array functions diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 2337ec1f..7b6b3a74 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -172,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 cyclic lmic link check to off because is not supported by ttn + // (but enabled by lmic after join) + LMIC_setLinkCheckMode(0); + + // set data rate adaptation according to saved setting LMIC_setAdrMode(cfg.adrmode); + // Set data rate and transmit power (note: txpower seems to be ignored by // the library) switch_lora(cfg.lorasf, cfg.txpower); diff --git a/src/senddata.cpp b/src/senddata.cpp index d4bced41..0536a172 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -1,26 +1,26 @@ // Basic Config #include "globals.h" + MessageBuffer_t SendBuffer; + // put data to send in RTos Queues used for transmit over channels Lora and SPI void SendData(uint8_t port) { - MessageBuffer_t MySendBuffer; - - MySendBuffer.MessageSize = payload.getSize(); - MySendBuffer.MessagePort = PAYLOAD_ENCODER <= 2 + SendBuffer.MessageSize = payload.getSize(); + SendBuffer.MessagePort = PAYLOAD_ENCODER <= 2 ? port : (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT); - memcpy(MySendBuffer.Message, payload.getBuffer(), payload.getSize()); + memcpy(SendBuffer.Message, payload.getBuffer(), payload.getSize()); // enqueue message in LoRa send queue #ifdef HAS_LORA - if (xQueueSendToBack(LoraSendQueue, (void *)&MySendBuffer, (TickType_t)0)) + if (xQueueSendToBack(LoraSendQueue, (void *)&SendBuffer, (TickType_t)0)) ESP_LOGI(TAG, "%d bytes enqueued to send on LoRa", payload.getSize()); #endif // enqueue message in SPI send queue #ifdef HAS_SPI - if (xQueueSendToBack(SPISendQueue, (void *)&MySendBuffer, (TickType_t)0)) + if (xQueueSendToBack(SPISendQueue, (void *)&SendBuffer, (TickType_t)0)) ESP_LOGI(TAG, "%d bytes enqueued to send on SPI", payload.getSize()); #endif @@ -77,26 +77,24 @@ void IRAM_ATTR SendCycleIRQ() { // cyclic called function to eat data from RTos send queues and transmit it void processSendBuffer() { - MessageBuffer_t RcvBuf; - #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, &(RcvBuf), (TickType_t)10)) { - // xMsg now holds the struct MessageBuffer from queue - LMIC_setTxData2(RcvBuf.MessagePort, RcvBuf.Message, RcvBuf.MessageSize, + if (xQueueReceive(LoraSendQueue, &(SendBuffer), (TickType_t)10)) { + // SendBuffer now holds the 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", RcvBuf.MessageSize); + ESP_LOGI(TAG, "%d bytes sent to LORA", SendBuffer.MessageSize); sprintf(display_line7, "PACKET QUEUED"); } } #endif #ifdef HAS_SPI - if (xQueueReceive(SPISendQueue, &(RcvBuf), (TickType_t)10)) { - ESP_LOGI(TAG, "%d bytes sent to SPI", RcvBuf.MessageSize); + if (xQueueReceive(SPISendQueue, &(SendBuffer), (TickType_t)10)) { + ESP_LOGI(TAG, "%d bytes sent to SPI", SendBuffer.MessageSize); } #endif From 0e6b6fb02519ee93c6f7813c1b240fa6cecee3aa Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Tue, 7 Aug 2018 22:39:49 +0200 Subject: [PATCH 21/28] rcommand.cpp: code sanitization --- src/globals.h | 2 +- src/rcommand.cpp | 113 ++++++++++++----------------------------------- 2 files changed, 30 insertions(+), 85 deletions(-) diff --git a/src/globals.h b/src/globals.h index 1111476f..33548607 100644 --- a/src/globals.h +++ b/src/globals.h @@ -8,7 +8,7 @@ #include // attn: increment version after modifications to configData_t truct! -#define PROGVERSION "1.4.21" // use max 10 chars here! +#define PROGVERSION "1.4.22" // use max 10 chars here! #define PROGNAME "PAXCNT" // std::set for unified array functions diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 60df3f4c..8899a805 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -34,12 +34,12 @@ void set_reset(uint8_t val[]) { 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]; @@ -48,7 +48,7 @@ void set_sendcycle(uint8_t val[]) { // reload interrupt after each trigger of channel switch cycle ESP_LOGI(TAG, "Remote command: set send cycle to %d seconds", cfg.sendcycle * 2); -}; +} void set_wifichancycle(uint8_t val[]) { cfg.wifichancycle = val[0]; @@ -58,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]; @@ -71,7 +71,7 @@ void set_blescantime(uint8_t val[]) { start_BLEscan(); } #endif -}; +} void set_countmode(uint8_t val[]) { switch (val[0]) { @@ -92,43 +92,22 @@ void set_countmode(uint8_t val[]) { 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[]) { @@ -137,20 +116,13 @@ void set_beacon(uint8_t val[]) { 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 @@ -159,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 From 5fbcd0aebbb3f87b37965a9b0f19a30670a83bfa Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Wed, 8 Aug 2018 00:05:38 +0200 Subject: [PATCH 22/28] Queue wait time --- src/senddata.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/senddata.cpp b/src/senddata.cpp index 0536a172..748ae07d 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -1,26 +1,26 @@ // Basic Config #include "globals.h" - MessageBuffer_t SendBuffer; +MessageBuffer_t SendBuffer; // put data to send in RTos Queues used for transmit over channels Lora and SPI void SendData(uint8_t port) { SendBuffer.MessageSize = payload.getSize(); SendBuffer.MessagePort = PAYLOAD_ENCODER <= 2 - ? port - : (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT); + ? port + : (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT); memcpy(SendBuffer.Message, payload.getBuffer(), payload.getSize()); // enqueue message in LoRa send queue #ifdef HAS_LORA - if (xQueueSendToBack(LoraSendQueue, (void *)&SendBuffer, (TickType_t)0)) + 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 - if (xQueueSendToBack(SPISendQueue, (void *)&SendBuffer, (TickType_t)0)) + if (xQueueSendToBack(SPISendQueue, (void *)&SendBuffer, (TickType_t)0) == pdTRUE) ESP_LOGI(TAG, "%d bytes enqueued to send on SPI", payload.getSize()); #endif @@ -82,10 +82,10 @@ void processSendBuffer() { 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)10)) { - // SendBuffer now holds the struct MessageBuffer with next payload from queue - LMIC_setTxData2(SendBuffer.MessagePort, SendBuffer.Message, SendBuffer.MessageSize, - (cfg.countermode & 0x02)); + 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"); } @@ -93,7 +93,7 @@ void processSendBuffer() { #endif #ifdef HAS_SPI - if (xQueueReceive(SPISendQueue, &(SendBuffer), (TickType_t)10)) { + if (xQueueReceive(SPISendQueue, &(SendBuffer), (TickType_t)0) == pdTRUE) { ESP_LOGI(TAG, "%d bytes sent to SPI", SendBuffer.MessageSize); } #endif @@ -102,9 +102,9 @@ void processSendBuffer() { void flushQueues() { #ifdef HAS_LORA - xQueueReset(LoraSendQueue); + xQueueReset(LoraSendQueue); #endif #ifdef HAS_SPI - xQueueReset(SPISendQueue); + xQueueReset(SPISendQueue); #endif } \ No newline at end of file From d704acc3e5563fc73f9dba06dc75d2d40b8fb326 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Fri, 10 Aug 2018 16:33:47 +0200 Subject: [PATCH 23/28] link check disabled; SendBuffer no more global (does not solve mem leak) --- src/display.cpp | 2 +- src/globals.h | 7 +++++++ src/lorawan.cpp | 10 +++++----- src/main.cpp | 4 ---- src/senddata.cpp | 44 +++++++++++++++++++++++++++++++++++--------- src/senddata.h | 7 ------- 6 files changed, 48 insertions(+), 26 deletions(-) diff --git a/src/display.cpp b/src/display.cpp index c3617538..b2fecf09 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -114,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) diff --git a/src/globals.h b/src/globals.h index 33548607..f064b258 100644 --- a/src/globals.h +++ b/src/globals.h @@ -37,6 +37,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 diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 7b6b3a74..ea0709ff 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -172,13 +172,13 @@ void onEvent(ev_t ev) { strcpy_P(buff, PSTR("JOINED")); sprintf(display_line6, " "); // clear previous lmic status - // set cyclic lmic link check to off because is not supported by ttn - // (but enabled by lmic after join) - LMIC_setLinkCheckMode(0); - // 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); diff --git a/src/main.cpp b/src/main.cpp index dd826c2e..4f41ee09 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -109,8 +109,6 @@ void setup() { // initialize send queues for transmit channels #ifdef HAS_LORA - //--> LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(struct SendBuffer - //*)); LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t)); if (LoraSendQueue == 0) { ESP_LOGE(TAG, "Could not create LORA send queue. Aborting."); @@ -120,8 +118,6 @@ void setup() { SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE); #endif #ifdef HAS_SPI - //--> SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(struct SendBuffer - //*)); SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t)); if (SPISendQueue == 0) { ESP_LOGE(TAG, "Could not create SPI send queue. Aborting."); diff --git a/src/senddata.cpp b/src/senddata.cpp index 748ae07d..767aa84b 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -1,26 +1,31 @@ // Basic Config #include "globals.h" -MessageBuffer_t SendBuffer; - // put data to send in RTos Queues used for transmit over channels Lora and SPI void SendData(uint8_t port) { + int m1 = 0, m2 = 0; + m1 = ESP.getFreeHeap(); + + MessageBuffer_t SendBuffer; + SendBuffer.MessageSize = payload.getSize(); SendBuffer.MessagePort = PAYLOAD_ENCODER <= 2 - ? port - : (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT); + ? port + : (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT); memcpy(SendBuffer.Message, payload.getBuffer(), payload.getSize()); // enqueue message in LoRa send queue #ifdef HAS_LORA - if (xQueueSendToBack(LoraSendQueue, (void *)&SendBuffer, (TickType_t)0) == pdTRUE) + 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 - if (xQueueSendToBack(SPISendQueue, (void *)&SendBuffer, (TickType_t)0) == pdTRUE) + if (xQueueSendToBack(SPISendQueue, (void *)&SendBuffer, (TickType_t)0) == + pdTRUE) ESP_LOGI(TAG, "%d bytes enqueued to send on SPI", payload.getSize()); #endif @@ -31,10 +36,17 @@ void SendData(uint8_t port) { ESP_LOGI(TAG, "Counter cleared"); } + m2 = ESP.getFreeHeap(); + if (m2 - m1) + ESP_LOGI(TAG, "SendData %d bytes", m2 - m1); + } // SendData // cyclic called function to prepare payload to send void sendPayload() { + + int m1 = 0, m2 = 0; + m1 = ESP.getFreeHeap(); if (SendCycleTimerIRQ) { portENTER_CRITICAL(&timerMux); SendCycleTimerIRQ = 0; @@ -65,6 +77,11 @@ void sendPayload() { #endif SendData(COUNTERPORT); } + + m2 = ESP.getFreeHeap(); + if (m2 - m1) + ESP_LOGI(TAG, "sendpayload %d bytes", m2 - m1); + } // sendpayload() // interrupt handler used for payload send cycle timer @@ -77,27 +94,36 @@ void IRAM_ATTR SendCycleIRQ() { // cyclic called function to eat data from RTos send queues and transmit it void processSendBuffer() { + int m1 = 0, m2 = 0; + m1 = ESP.getFreeHeap(); + + 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) { + 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); + 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) { + if (xQueueReceive(SPISendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) { ESP_LOGI(TAG, "%d bytes sent to SPI", SendBuffer.MessageSize); } #endif + m2 = ESP.getFreeHeap(); + if (m2 - m1) + ESP_LOGI(TAG, "processSendBuffer %d bytes", m2 - m1); + } // processSendBuffer void flushQueues() { diff --git a/src/senddata.h b/src/senddata.h index e709cedc..806899d6 100644 --- a/src/senddata.h +++ b/src/senddata.h @@ -1,13 +1,6 @@ #ifndef _SENDDATA_H #define _SENDDATA_H -// Struct holding payload for data send queue -typedef struct { - uint8_t MessageSize; - uint8_t MessagePort; - uint8_t Message[PAYLOAD_BUFFER_SIZE]; -} MessageBuffer_t; - void SendData(uint8_t port); void sendPayload(void); void SendCycleIRQ(void); From fcd9357116aa9ca65617c6fe7d0362c190149359 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 11 Aug 2018 19:12:04 +0200 Subject: [PATCH 24/28] small code sanitizations --- src/hal/ebox.h | 4 ++-- src/hal/generic.h | 2 +- src/hal/heltec.h | 4 ++-- src/hal/lolin32lite.h | 2 +- src/hal/lopy.h | 4 ++-- src/hal/lopy4.h | 4 ++-- src/hal/ttgobeam.h | 4 ++-- src/hal/ttgov1.h | 4 ++-- src/hal/ttgov2.h | 4 ++-- src/hal/ttgov21.h | 4 ++-- src/main.cpp | 54 ++++++++++++++++++++++--------------------- src/senddata.cpp | 21 +++-------------- 12 files changed, 49 insertions(+), 62 deletions(-) diff --git a/src/hal/ebox.h b/src/hal/ebox.h index ff622bea..121fea58 100644 --- a/src/hal/ebox.h +++ b/src/hal/ebox.h @@ -1,7 +1,7 @@ // 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 diff --git a/src/hal/generic.h b/src/hal/generic.h index 52fa279c..bedff5e8 100644 --- a/src/hal/generic.h +++ b/src/hal/generic.h @@ -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 diff --git a/src/hal/heltec.h b/src/hal/heltec.h index 95ffe469..a860df50 100644 --- a/src/hal/heltec.h +++ b/src/hal/heltec.h @@ -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 diff --git a/src/hal/lolin32lite.h b/src/hal/lolin32lite.h index e1947d3c..7b400cf5 100644 --- a/src/hal/lolin32lite.h +++ b/src/hal/lolin32lite.h @@ -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 \ No newline at end of file +#define HAS_SPI 1 // comment out if device shall not send data via SPI \ No newline at end of file diff --git a/src/hal/lopy.h b/src/hal/lopy.h index bacbfcfe..0e414326 100644 --- a/src/hal/lopy.h +++ b/src/hal/lopy.h @@ -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 diff --git a/src/hal/lopy4.h b/src/hal/lopy4.h index 38f89e10..9e1169ab 100644 --- a/src/hal/lopy4.h +++ b/src/hal/lopy4.h @@ -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 diff --git a/src/hal/ttgobeam.h b/src/hal/ttgobeam.h index 8c3b38d7..ac5b1574 100644 --- a/src/hal/ttgobeam.h +++ b/src/hal/ttgobeam.h @@ -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 diff --git a/src/hal/ttgov1.h b/src/hal/ttgov1.h index f25bda03..fb04cbb0 100644 --- a/src/hal/ttgov1.h +++ b/src/hal/ttgov1.h @@ -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 diff --git a/src/hal/ttgov2.h b/src/hal/ttgov2.h index 240b687d..e622af02 100644 --- a/src/hal/ttgov2.h +++ b/src/hal/ttgov2.h @@ -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 diff --git a/src/hal/ttgov21.h b/src/hal/ttgov21.h index ec58f82f..06cda59f 100644 --- a/src/hal/ttgov21.h +++ b/src/hal/ttgov21.h @@ -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 diff --git a/src/main.cpp b/src/main.cpp index 4f41ee09..1b258f14 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -42,14 +42,19 @@ volatile int ButtonPressedIRQ = 0, ChannelTimerIRQ = 0, SendCycleTimerIRQ = 0, DisplayTimerIRQ = 0, HomeCycleIRQ = 0; // RTos send queues for payload transmit -QueueHandle_t LoraSendQueue, SPISendQueue; +#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 macs; // associative container holding unique MAC -// adress hashes (Wifi + BLE) +std::set macs; // container holding unique MAC adress hashes // initialize payload encoder PayloadConvert payload(PAYLOAD_BUFFER_SIZE); @@ -107,26 +112,6 @@ void setup() { #endif // verbose -// initialize send queues for transmit channels -#ifdef HAS_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 -#ifdef HAS_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 - // read settings from NVRAM loadConfig(); // includes initialize if necessary @@ -137,9 +122,28 @@ void setup() { // 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 led +// 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"); @@ -326,8 +330,6 @@ void loop() { processSendBuffer(); // check send cycle and enqueue payload if cycle is expired sendPayload(); - // reset watchdog - vTaskDelay(1 / portTICK_PERIOD_MS); } // loop() } diff --git a/src/senddata.cpp b/src/senddata.cpp index 767aa84b..aa8308ff 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -4,9 +4,6 @@ // put data to send in RTos Queues used for transmit over channels Lora and SPI void SendData(uint8_t port) { - int m1 = 0, m2 = 0; - m1 = ESP.getFreeHeap(); - MessageBuffer_t SendBuffer; SendBuffer.MessageSize = payload.getSize(); @@ -35,18 +32,11 @@ void SendData(uint8_t port) { reset_salt(); // get new salt for salting hashes ESP_LOGI(TAG, "Counter cleared"); } - - m2 = ESP.getFreeHeap(); - if (m2 - m1) - ESP_LOGI(TAG, "SendData %d bytes", m2 - m1); - } // SendData // cyclic called function to prepare payload to send void sendPayload() { - int m1 = 0, m2 = 0; - m1 = ESP.getFreeHeap(); if (SendCycleTimerIRQ) { portENTER_CRITICAL(&timerMux); SendCycleTimerIRQ = 0; @@ -77,11 +67,6 @@ void sendPayload() { #endif SendData(COUNTERPORT); } - - m2 = ESP.getFreeHeap(); - if (m2 - m1) - ESP_LOGI(TAG, "sendpayload %d bytes", m2 - m1); - } // sendpayload() // interrupt handler used for payload send cycle timer @@ -108,7 +93,7 @@ void processSendBuffer() { // 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); + //ESP_LOGI(TAG, "%d bytes sent to LoRa", SendBuffer.MessageSize); sprintf(display_line7, "PACKET QUEUED"); } } @@ -116,13 +101,13 @@ void processSendBuffer() { #ifdef HAS_SPI if (xQueueReceive(SPISendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) { - ESP_LOGI(TAG, "%d bytes sent to SPI", SendBuffer.MessageSize); + //ESP_LOGI(TAG, "%d bytes sent to SPI", SendBuffer.MessageSize); } #endif m2 = ESP.getFreeHeap(); if (m2 - m1) - ESP_LOGI(TAG, "processSendBuffer %d bytes", m2 - m1); + ESP_LOGI(TAG, "processSendBuffer consumed %d bytes", m2 - m1); } // processSendBuffer From 5880b7e9ff1b8a109882e82210a4fe49adb28def Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 11 Aug 2018 19:31:42 +0200 Subject: [PATCH 25/28] v1.4.23 --- platformio.ini | 16 ++++++++++++---- src/globals.h | 5 +---- src/senddata.cpp | 15 ++++----------- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/platformio.ini b/platformio.ini index 211a3e88..98296872 100644 --- a/platformio.ini +++ b/platformio.ini @@ -43,10 +43,18 @@ build_flags = ; ---> NOTE: For production run set DEBUG_LEVEL level to NONE! <--- ; 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_ diff --git a/src/globals.h b/src/globals.h index f064b258..a34851a7 100644 --- a/src/globals.h +++ b/src/globals.h @@ -4,11 +4,8 @@ // The mother of all embedded development... #include -// needed for ESP_LOGx on arduino framework -#include - // attn: increment version after modifications to configData_t truct! -#define PROGVERSION "1.4.22" // use max 10 chars here! +#define PROGVERSION "1.4.23" // use max 10 chars here! #define PROGNAME "PAXCNT" // std::set for unified array functions diff --git a/src/senddata.cpp b/src/senddata.cpp index aa8308ff..f7e3824c 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -8,8 +8,8 @@ void SendData(uint8_t port) { SendBuffer.MessageSize = payload.getSize(); SendBuffer.MessagePort = PAYLOAD_ENCODER <= 2 - ? port - : (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT); + ? port + : (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT); memcpy(SendBuffer.Message, payload.getBuffer(), payload.getSize()); // enqueue message in LoRa send queue @@ -79,9 +79,6 @@ void IRAM_ATTR SendCycleIRQ() { // cyclic called function to eat data from RTos send queues and transmit it void processSendBuffer() { - int m1 = 0, m2 = 0; - m1 = ESP.getFreeHeap(); - MessageBuffer_t SendBuffer; #ifdef HAS_LORA @@ -93,7 +90,7 @@ void processSendBuffer() { // 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); + ESP_LOGI(TAG, "%d bytes sent to LoRa", SendBuffer.MessageSize); sprintf(display_line7, "PACKET QUEUED"); } } @@ -101,14 +98,10 @@ void processSendBuffer() { #ifdef HAS_SPI if (xQueueReceive(SPISendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) { - //ESP_LOGI(TAG, "%d bytes sent to SPI", SendBuffer.MessageSize); + ESP_LOGI(TAG, "%d bytes sent to SPI", SendBuffer.MessageSize); } #endif - m2 = ESP.getFreeHeap(); - if (m2 - m1) - ESP_LOGI(TAG, "processSendBuffer consumed %d bytes", m2 - m1); - } // processSendBuffer void flushQueues() { From 475a38999afe96685c3f22c61f59899aaf7c07cc Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sat, 11 Aug 2018 20:01:07 +0200 Subject: [PATCH 26/28] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5025c31d..6d5b64d5 100644 --- a/README.md +++ b/README.md @@ -50,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.
3D printable cases can be found (and, if wanted so, ordered) on Thingiverse, see -Heltec, TTGOv2, TTGOv2.1 for example.
+Heltec, TTGOv2, TTGOv2.1, T-BEAM for example.
Power consumption 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). From 0318987a4e4b4a307b96874368ab70498ee7b74b Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sun, 12 Aug 2018 13:44:29 +0200 Subject: [PATCH 27/28] Bugfix main.cpp introduced with last commit --- src/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 1b258f14..bd6d04f1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -330,6 +330,8 @@ void loop() { processSendBuffer(); // check send cycle and enqueue payload if cycle is expired sendPayload(); + // reset watchdog + vTaskDelay(1 / portTICK_PERIOD_MS); } // loop() } From 83a86fdf153e956d20ed65a31ab49dd0de87c221 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Mon, 13 Aug 2018 18:42:48 +0200 Subject: [PATCH 28/28] bug fixed in payload.h (issue #138) --- src/payload.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/payload.h b/src/payload.h index ba564ec7..65aff816 100644 --- a/src/payload.h +++ b/src/payload.h @@ -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 {