/* Copyright 2018 Oliver Brandmueller Copyright 2018 Klaus Wilting Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. NOTICE: Parts of the source files in this repository are made available under different licenses. Refer to LICENSE.txt file in repository for more details. */ // Basic Config #include "main.h" #include "globals.h" // std::set for unified array functions #include // OLED driver #include // LMIC-Arduino LoRaWAN Stack #include "loraconf.h" #include #include // ESP32 Functions #include // needed for Wifi event handler #include // needed for reading ESP32 chip attributes #include // needed for ESP_LOGx on arduino framework configData_t cfg; // struct holds current device configuration osjob_t sendjob, initjob; // LMIC // Initialize global variables int macnum = 0, salt; uint64_t uptimecounter = 0; bool joinstate = false; std::set macs; // associative container holds filtered Total differents MAC adresses (Wifi + BLE) std::set wifis; // associative container holds filtered Wifi MAC adresses #ifdef BLECOUNTER std::set bles; // associative container holds filtered BLE MAC adresses int scanTime; #endif // this variable will be changed in the ISR, and read in main loop static volatile bool ButtonTriggered = false; // local Tag for logging static const char *TAG = "paxcnt"; // Note: Log level control seems not working during runtime, // so we need to switch loglevel by compiler build option in platformio.ini #ifndef VERBOSE int redirect_log(const char * fmt, va_list args) { //do nothing return 0; } #endif // defined in configmanager.cpp void eraseConfig(void); void saveConfig(void); void loadConfig(void); #ifdef HAS_LED void set_onboard_led(int st); #endif /* begin LMIC specific parts ------------------------------------------------------------ */ // defined in lorawan.cpp void gen_lora_deveui(uint8_t * pdeveui); void RevBytes(unsigned char* b, size_t c); #ifdef VERBOSE void printKeys(void); #endif // VERBOSE // LMIC callback functions void os_getDevKey (u1_t *buf) { memcpy(buf, APPKEY, 16); } void os_getArtEui (u1_t *buf) { memcpy(buf, APPEUI, 8); RevBytes(buf, 8); // TTN requires it in LSB First order, so we swap bytes } void os_getDevEui (u1_t* buf) { int i=0, k=0; memcpy(buf, DEVEUI, 8); // get fixed DEVEUI from loraconf.h for (i=0; i<8 ; i++) k += buf[i]; if (k) RevBytes(buf, 8); // use fixed DEVEUI and swap bytes to LSB format else gen_lora_deveui(buf); // generate DEVEUI from device's MAC } // LMIC enhanced Pin mapping const lmic_pinmap lmic_pins = { .mosi = PIN_SPI_MOSI, .miso = PIN_SPI_MISO, .sck = PIN_SPI_SCK, .nss = PIN_SPI_SS, .rxtx = LMIC_UNUSED_PIN, .rst = RST, .dio = {DIO0, DIO1, DIO2} }; // LMIC functions void onEvent(ev_t ev); void do_send(osjob_t* j); // LoRaWAN Initjob static void lora_init (osjob_t* j) { // reset MAC state LMIC_reset(); // This tells LMIC to make the receive windows bigger, in case your clock is 1% faster or slower. LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100); // start joining LMIC_startJoining(); } // LMIC Task void lorawan_loop(void * pvParameters) { configASSERT( ( ( uint32_t ) pvParameters ) == 1 ); // FreeRTOS check static bool led_state ; bool new_led_state ; while(1) { uint16_t color; os_runloop_once(); // All follow is Led management // Let join at the begining of if sequence, // is prior to send because joining state send data if ( LMIC.opmode & (OP_JOINING | OP_REJOIN) ) { color = COLOR_YELLOW; // Joining Quick blink 20ms on each 1/5 second new_led_state = ((millis() % 200) < 20) ? HIGH : LOW; // Small blink 10ms on each 1/2sec (not when joining) } else if (LMIC.opmode & (OP_TXDATA | OP_TXRXPEND)) { color = COLOR_BLUE; new_led_state = ((millis() % 500) < 20) ? HIGH : LOW; } // This should not happen so indicate a pb if ( LMIC.opmode & (OP_TXDATA | OP_TXRXPEND | OP_JOINING | OP_REJOIN) == 0 ) { color = COLOR_RED; // Heartbeat long blink 200ms on each 2 seconds new_led_state = ((millis() % 2000) < 200) ? HIGH : LOW; } // led need to change state ? // avoid digitalWrite() for nothing if (led_state != new_led_state) { if (new_led_state == HIGH) { set_onboard_led(1); rgb_set_color(color); } else { set_onboard_led(0); rgb_set_color(COLOR_NONE); } led_state = new_led_state; } vTaskDelay(10/portTICK_PERIOD_MS); yield(); } } /* end LMIC specific parts --------------------------------------------------------------- */ /* beginn hardware specific parts -------------------------------------------------------- */ #ifdef HAS_DISPLAY HAS_DISPLAY u8x8(OLED_RST, OLED_SCL, OLED_SDA); #else U8X8_NULL u8x8; #endif #ifdef HAS_ANTENNA_SWITCH // defined in antenna.cpp void antenna_init(); void antenna_select(const int8_t _ant); #endif #ifdef BLECOUNTER void BLECount(void); #else btStop(); #endif void set_onboard_led(int st){ #ifdef HAS_LED switch (st) { #ifdef LED_ACTIVE_LOW case 1: digitalWrite(HAS_LED, LOW); break; case 0: digitalWrite(HAS_LED, HIGH); break; #else case 1: digitalWrite(HAS_LED, HIGH); break; case 0: digitalWrite(HAS_LED, LOW); break; #endif } #endif }; #ifdef HAS_BUTTON // Button Handling, board dependent -> perhaps to be moved to hal/<$board.h> // IRAM_ATTR necessary here, see https://github.com/espressif/arduino-esp32/issues/855 void IRAM_ATTR isr_button_pressed(void) { ButtonTriggered = true; } #endif /* end hardware specific parts -------------------------------------------------------- */ /* begin wifi specific parts ---------------------------------------------------------- */ // defined in wifisniffer.cpp void wifi_sniffer_init(void); void wifi_sniffer_set_channel(uint8_t channel); void wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type); //WiFi Sniffer Task void wifi_sniffer_loop(void * pvParameters) { configASSERT( ( ( uint32_t ) pvParameters ) == 1 ); // FreeRTOS check uint8_t channel = 1; srand((uint32_t) temperatureRead()); // use chip temperature for pseudorandom generator init int nloop=0, lorawait=0, salt=rand() % 256; // random int between 0 and 255 used for salting MAC hashes ESP_LOGI(TAG, "Scan initialized, salt value: %i", salt); while (true) { nloop++; vTaskDelay(cfg.wifichancycle*10 / portTICK_PERIOD_MS); yield(); wifi_sniffer_set_channel(channel); channel = (channel % WIFI_CHANNEL_MAX) + 1; ESP_LOGI(TAG, "Wifi set channel %d", channel); u8x8.setCursor(0,5); u8x8.printf(!cfg.rssilimit ? "RLIM: off" : "RLIM: %4i", cfg.rssilimit); u8x8.setCursor(11,5); u8x8.printf("ch:%02i", channel); u8x8.setCursor(0,4); u8x8.printf("MAC#: %-5i", wifis.size()); // execute BLE count if BLE function is enabled #ifdef BLECOUNTER // Once 2 full Wifi Channels scan, do a BLE scan if (nloop % (WIFI_CHANNEL_MAX*2) == 0 ) { // execute BLE count if BLE function is enabled if (cfg.blescan) BLECount(); } #endif // duration of one wifi scan loop reached? then send data and begin new scan cycle if( nloop >= ((100 / cfg.wifichancycle) * (cfg.wifiscancycle * 2)) ) { u8x8.setPowerSave(!cfg.screenon); // set display on if enabled nloop = 0; // reset wlan sniffing loop counter // Prepare and execute LoRaWAN data upload do_send(&sendjob); // send payload vTaskDelay(500/portTICK_PERIOD_MS); yield(); // clear counter if not in cumulative counter mode if (cfg.countermode != 1) { macs.clear(); // clear all macs container wifis.clear(); // clear Wifi macs couner #ifdef BLECOUNTER bles.clear(); // clear BLE macs counter #endif srand((uint32_t) temperatureRead()); // use chip temperature for pseudorandom generator init salt = rand() % 256; // get new random int between 0 and 255 for salting MAC hashes u8x8.clearLine(0); u8x8.clearLine(1); // clear Display counter ESP_LOGI(TAG, "Scan cycle completed, new salt value: %i", salt); } // wait until payload is sent, while wifi scanning and mac counting task continues lorawait = 0; while(LMIC.opmode & OP_TXRXPEND) { if(!lorawait) u8x8.drawString(0,6,"LoRa wait "); lorawait++; // in case sending really fails: reset and rejoin network if( (lorawait % MAXLORARETRY ) == 0) { ESP_LOGI(TAG, "Payload not sent, trying reset and rejoin"); esp_restart(); }; vTaskDelay(1000/portTICK_PERIOD_MS); yield(); } u8x8.clearLine(6); if (cfg.screenon && cfg.screensaver) { vTaskDelay(2000/portTICK_PERIOD_MS); // pause for displaying results } yield(); u8x8.setPowerSave(1 && cfg.screensaver); // set display off if screensaver is enabled } } } /* end wifi specific parts ------------------------------------------------------------ */ // uptime counter 64bit to prevent millis() rollover after 49 days uint64_t uptime() { static uint32_t low32, high32; uint32_t new_low32 = millis(); if (new_low32 < low32) high32++; low32 = new_low32; return (uint64_t) high32 << 32 | low32; } // Print a key on display void DisplayKey(const uint8_t * key, uint8_t len, bool lsb) { uint8_t start=lsb?len:0; uint8_t end = lsb?0:len; const uint8_t * p ; for (uint8_t i=0; i