diff --git a/include/BT_WIFI_scan.h b/include/BT_WIFI_scan.h new file mode 100644 index 0000000..5a4722b --- /dev/null +++ b/include/BT_WIFI_scan.h @@ -0,0 +1,4 @@ +#pragma once + +extern void scanWiFiWithOSDOut(); +extern void scanBTWithOSDOut(); diff --git a/include/global_config.h b/include/global_config.h index b2bff67..115bf4a 100644 --- a/include/global_config.h +++ b/include/global_config.h @@ -12,7 +12,7 @@ #define BANDWIDTH 467.0 // Detection level from the 33 levels. The higher number is more sensitive -#define DEFAULT_DRONE_DETECTION_LEVEL 21 +#define DEFAULT_DRONE_DETECTION_LEVEL 20 #define BUZZER_PIN 41 diff --git a/include/ui.h b/include/ui.h index 055b8ed..2dcaa96 100644 --- a/include/ui.h +++ b/include/ui.h @@ -1,6 +1,5 @@ -#ifndef __UI_H__ -#define __UI_H__ +#pragma once #include "OLEDDisplayUi.h" #include "SSD1306Wire.h" @@ -13,10 +12,8 @@ #define MINOR_TICKS 5 #define ONE_MILLISEC 1 - -// Prints debug information and the scan measurement bins from the SX1262 in -// hex Change spectrum plot values at once or by line -#define ANIMATED_RELOAD false +#define ONE_SEC_MIL 1000 +#define ONE_MINUTE_MIL 60 * 1000 #define MAJOR_TICK_LENGTH 2 #define MINOR_TICK_LENGTH 1 @@ -37,5 +34,3 @@ extern void UI_displayDecorate(int, int, bool); extern void UI_setLedFlag(bool); extern void UI_clearPlotter(void); extern void UI_drawCursor(int16_t); - -#endif // __UI_H__ diff --git a/src/main.cpp b/src/main.cpp index 456892e..6d3d619 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,15 +25,29 @@ #include // This file contains a binary patch for the SX1262 #include "modules/SX126x/patches/SX126x_patch_scan.h" -#define OSD_ENABLED true +// #define OSD_ENABLED true +// #define WIFI_SCANNING_ENABLED true +// #define BT_SCANNING_ENABLED true -#ifdef OSD_ENABLED +#define BT_SCAN_DELAY 60 * 1 * 1000 +#define WF_SCAN_DELAY 60 * 2 * 1000 +long noDevicesMillis = 0, cycleCnt = 0; +bool present = false; +bool scanFinished = true; + +// time to scan BT +#define BT_SCAN_TIME 10 + +uint64_t wf_start = 0; +uint64_t bt_start = 0; + +#ifndef OSD_ENABLED #include "DFRobot_OSD.h" #define MAX_POWER_LEVELS 33 #define OSD_SIDE_BAR true -static const uint16_t power_level[MAX_POWER_LEVELS] = { - 0x10E, // 0 +static constexpr uint16_t levels[10] = { + 0x105, // 0 0x10E, // 1 0x10D, // 2 0x10C, // 3 @@ -43,6 +57,19 @@ static const uint16_t power_level[MAX_POWER_LEVELS] = { 0x108, // 7 0x107, // 8 0x106, // 9 +}; + +static constexpr uint16_t power_level[MAX_POWER_LEVELS + 1] = { + 0x10E, // 0 + 0x10E, // 1 + 0x10D, // 2 + 0x10C, // 3 + 0x10B, // 4 + 0x10A, // 5 + 0x109, // 6 + 0x108, // 7 + 0x107, // 8 + 0x106, // 9 not using 106 to accent rise // new line 0x10E, // 10 0x10D, // 11 @@ -52,7 +79,7 @@ static const uint16_t power_level[MAX_POWER_LEVELS] = { 0x109, // 15 0x108, // 16 0x107, // 17 - 0x106, // 18 + 0x106, // 18 not using 106 // new line 0x10E, // 19 0x10D, // 20 @@ -63,18 +90,20 @@ static const uint16_t power_level[MAX_POWER_LEVELS] = { 0x108, // 25 0x107, // 26 0x106, // 27 - 0x105, // 28 + 0x105, // 28 --- 0x105, // 29 0x105, // 30 0x105, // 31 - 0x105 // 32 + 0x105, // 32 + 0x105 // 33 }; -#endif + // SPI pins -#define CS 47 +#define OSD_CS 47 #define OSD_MISO 33 #define OSD_MOSI 34 #define OSD_SCK 26 +#endif #define OSD_WIDTH 30 #define OSD_HEIGHT 16 @@ -90,7 +119,7 @@ int osd_steps = 12; int global_counter = 0; #ifdef OSD_ENABLED -DFRobot_OSD osd(CS); +DFRobot_OSD osd(OSD_CS); #endif /*Define Custom characters Example*/ @@ -99,6 +128,16 @@ static const int buf0[36] = {0x02, 0x80, 0x02, 0x40, 0x7F, 0xE0, 0x42, 0x00, 0x49, 0x20, 0x5A, 0xA0, 0x44, 0x60, 0x88, 0x20}; // project components +#ifdef WIFI_SCANNING_ENABLED +#include "WiFi.h" +#endif +#ifdef BT_SCANNING_ENABLED +#include +#include +#include +#include +#endif + #include "global_config.h" #include "ui.h" @@ -125,9 +164,9 @@ int SCAN_RANGES[] = {}; uint64_t RANGE_PER_PAGE = FREQ_END - FREQ_BEGIN; // FREQ_END - FREQ_BEGIN // multiplies STEPS * N to increase scan resolution. -#define SCAN_RBW_RFACTOR 2 +#define SCAN_RBW_FACTOR 2 -int OSD_PIXELS_PER_CHAR = (STEPS * SCAN_RBW_RFACTOR) / OSD_CHART_WIDTH; +constexpr int OSD_PIXELS_PER_CHAR = (STEPS * SCAN_RBW_FACTOR) / OSD_CHART_WIDTH; // To Enable Multi Screen scan // uint64_t RANGE_PER_PAGE = 50; @@ -135,22 +174,26 @@ int OSD_PIXELS_PER_CHAR = (STEPS * SCAN_RBW_RFACTOR) / OSD_CHART_WIDTH; #define DEFAULT_RANGE_PER_PAGE 50 +// Print spectrum values pixels at once or by line +bool ANIMATED_RELOAD = false; + // TODO: Ignore power lines #define UP_FILTER 5 #define LOW_FILTER 3 // Remove reading without neighbors #define FILTER_SPECTRUM_RESULTS true -#define DRAW_DETECTION_TICKS true +#define FILTER_SAMPLES_MIN +constexpr bool DRAW_DETECTION_TICKS = true; // Number of samples for each frequency scan. Fewer samples = better temporal resolution. // if more than 100 it can freez -#define SAMPLES 100 //(scan time = 1294) +#define SAMPLES 35 //(scan time = 1294) // number of samples for RSSI method -#define SAMPLES_RSSI 21 // 21 // +#define SAMPLES_RSSI 20 // 21 // #define RANGE (int)(FREQ_END - FREQ_BEGIN) -#define SINGLE_STEP (float)(RANGE / (STEPS * SCAN_RBW_RFACTOR)) +#define SINGLE_STEP (float)(RANGE / (STEPS * SCAN_RBW_FACTOR)) uint64_t range = (int)(FREQ_END - FREQ_BEGIN); uint64_t fr_begin = FREQ_BEGIN; @@ -168,8 +211,9 @@ uint16_t result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; uint16_t result_display_set[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; uint16_t result_detections[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; uint16_t filtered_result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; -uint16_t max_bins_array[OSD_WIDTH]; -int max_bins_array_value[OSD_WIDTH]; + +int max_bins_array_value[MAX_POWER_LEVELS]; +int max_step_range = 32; // Waterfall array bool waterfall[STEPS], detected_y[STEPS]; // 20 - ??? steps of the waterfall @@ -198,30 +242,245 @@ uint64_t scan_start_time = 0; #endif uint64_t x, y, range_item, w, i = 0; -int osd_x = 1, osd_y = 2; +int osd_x = 1, osd_y = 2, col = 0, max_bin = 32; uint64_t ranges_count = 0; float freq = 0; int rssi = 0; int state = 0; + +#ifdef METHOD_SPECTRAL +constexpr int samples = SAMPLES; +#endif +#ifdef METHOD_RSSI +constexpr int samples = SAMPLES_RSSI; +#endif + uint8_t result_index = 0; - uint8_t button_pressed_counter = 0; - uint64_t loop_cnt = 0; -unsigned short selectFreqChar(int bin) +#ifdef WIFI_SCANNING_ENABLED +// WiFi Scan +// TODO: Make Async Scan +// https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/scan-examples.html#async-scan +void scanWiFi() { - if (bin >= 0 && bin < MAX_POWER_LEVELS) + osd.clear(); + osd.displayString(14, 2, "Scanning WiFi.."); + int n = WiFi.scanNetworks(); +#ifdef PRINT_DEBUG + Serial.println("scan done"); + if (n == 0) + { + Serial.println("no networks found"); + } +#endif + if (n > 0) + { +#ifdef PRINT_DEBUG + Serial.print(n); + Serial.println(" networks found"); +#endif + for (int i = 0; i < n; ++i) + { +// Print SSID and RSSI for each network found +#ifdef PRINT_DEBUG + Serial.print(i + 1); + Serial.print(": "); + Serial.print(WiFi.SSID(i)); + Serial.print(" ("); + Serial.print(WiFi.RSSI(i)); + Serial.print(")"); + Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " " : "*"); +#endif + osd.displayString(i + 1, 1, + "WF:" + String((WiFi.SSID(i) + ":" + WiFi.RSSI(i)))); + } + } + osd.displayChar(14, 1, 0x10f); +} +#endif + +#ifdef BT_SCANNING_ENABLED +//********************** +// BLE devices scan. +//*********************** +// TODO: Make Async Scan +// https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLETests/SampleAsyncScan.cpp +void scanBT() +{ + osd.clear(); + osd.displayString(14, 2, "Scanning BT..."); + cycleCnt++; + + BLEDevice::init(""); + BLEScan *pBLEScan = BLEDevice::getScan(); + // active scan uses more power, but get results faster + pBLEScan->setActiveScan(true); + // TODO: adjust interval and window + pBLEScan->setInterval(0x50); + pBLEScan->setWindow(0x30); + +#ifdef SERIAL_PRINT + Serial.printf("Start BLE scan for %d seconds...\n", BT_SCAN_TIME); +#endif + + BLEScanResults foundDevices = pBLEScan->start(BT_SCAN_TIME); + int count = foundDevices.getCount(); +#ifdef PRINT_DEBUG + Serial.printf("Found devices: %d \n", count); +#endif + present = false; + for (int i = 0; i < count; i++) + { + BLEAdvertisedDevice device = foundDevices.getDevice(i); + String currDevAddr = device.getAddress().toString().c_str(); + String deviceName; + if (device.haveName()) + { + deviceName = device.getName().c_str(); + } + else + { + deviceName = currDevAddr; + } + +#ifdef PRINT_DEBUG + Serial.printf("Found device #%s/%s with RSSI: %d \n", currDevAddr, deviceName, + device.getRSSI()); +#endif + osd.displayString(i + 1, 1, + "BT:" + deviceName + ":" + String(device.getRSSI()) + " \n"); + } +#ifdef PRINT_DEBUG + Serial.println("Scan done!"); + Serial.printf("Cycle counter: %d, Free heap: %d \n", cycleCnt, ESP.getFreeHeap()); +#endif + osd.displayChar(14, 1, 0x10f); + scanFinished = true; +} +#endif + +#ifdef OSD_ENABLED +unsigned short selectFreqChar(int bin, int start_level = 0) +{ + if (bin >= start_level) + { + // level when we are starting show levels symbols + // you can override with your own character for example 0x100 = " " empty char + return power_level[33]; + } + else if (bin >= 0 && bin < MAX_POWER_LEVELS) return power_level[bin]; + // when wrong bin number or noc har assigned we are showing "!" char return 0x121; } +void osdPrintSignalLevelChart(int col, int signal_value) +{ + // Third line + if (signal_value <= 9) + { + osd.displayChar(13, col + 2, 0x100); + osd.displayChar(14, col + 2, 0x100); + osd.displayChar(12, col + 2, selectFreqChar(signal_value, drone_detection_level)); + } + // Second line + else if (signal_value < 19) + { + osd.displayChar(12, col + 2, 0x100); + osd.displayChar(14, col + 2, 0x100); + osd.displayChar(13, col + 2, selectFreqChar(signal_value, drone_detection_level)); + } + // First line + else + { + // Clean Up symbol + osd.displayChar(12, col + 2, 0x100); + osd.displayChar(13, col + 2, 0x100); + osd.displayChar(14, col + 2, selectFreqChar(signal_value, drone_detection_level)); + } +} + +void osdProcess() +{ // OSD enabled + + // memset(max_step_range, 33, 30); + max_bin = 32; + + osd.displayString(12, 1, String(FREQ_BEGIN)); + osd.displayString(12, OSD_WIDTH - 8, String(FREQ_END)); + // Finding biggest in result + // Skiping 0 and 32 31 to avoid overflow + for (int i = 1; i < MAX_POWER_LEVELS - 3; i++) + { + + // filter + if (result[i] > 0 +#if FILTER_SPECTRUM_RESULTS + && ((result[i + 1] != 0 && result[i + 2] != 0) || result[i - 1] != 0) +#endif + ) + { + max_bin = i; +#ifdef PRINT_DEBUG + Serial.print("MAX in bin:" + String(max_bin)); + Serial.println(); +#endif + break; + } + } + // max_bin contains fist not 0 index of the bin + if (max_step_range > max_bin && max_bin != 0) + { + max_step_range = max_bin; +// Store RSSI value for RSSI Method +#ifdef METHOD_RSSI + max_bins_array_value[col] = result[max_bin]; +#endif + } + // Going to the next OSD step + if (x % osd_steps == 0 && col < OSD_WIDTH) + { + // some issue when median = 0 + if (max_step_range == 0) + { + max_step_range = OSD_WIDTH - 1; + } + // OSD SIDE BAR with frequency log +#ifdef OSD_SIDE_BAR + { + osd.displayString(col, OSD_WIDTH - 7, + String(FREQ_BEGIN + (col * osd_mhz_in_bin)) + "-" + + String(max_step_range) + " "); + } +#endif + // Test with Random Result... + // max_step_range = rand() % 32; +#ifdef METHOD_RSSI + // With THe RSSI method we can get real RSSI value not just a bin +#endif + // PRINT SIGNAL CHAR ROW, COL, VALUE + osdPrintSignalLevelChart(col, max_step_range); + +#ifdef PRINT_DEBUG + Serial.println("MAX:" + String(max_step_range)); +#endif + max_step_range = 32; + col++; + } +} +#endif + void setup(void) { + // LED brightness + heltec_led(25); #ifdef OSD_ENABLED osd.init(OSD_SCK, OSD_MISO, OSD_MOSI); osd.clear(); + /* Write the custom character to the OSD, replacing the original character*/ /* Expand 0xe0 to 0x0e0, the high 8 bits indicate page number and the low 8 bits * indicate the inpage address.*/ @@ -236,6 +495,8 @@ void setup(void) float vbat; float resolution; loop_cnt = 0; + bt_start = millis(); + wf_start = millis(); pinMode(LED, OUTPUT); pinMode(BUZZER_PIN, OUTPUT); @@ -278,10 +539,17 @@ void setup(void) both.println("Starting scanning..."); vbat = heltec_vbat(); both.printf("V battery: %.2fV (%d%%)\n", vbat, heltec_battery_percent(vbat)); +#ifdef WIFI_SCANNING_ENABLED + WiFi.mode(WIFI_STA); + WiFi.disconnect(); +#endif +#ifdef BT_SCANNING_ENABLED + +#endif delay(400); display.clear(); - resolution = RANGE / (STEPS * SCAN_RBW_RFACTOR); + resolution = RANGE / (STEPS * SCAN_RBW_FACTOR); single_page_scan = (RANGE_PER_PAGE == range); @@ -299,12 +567,12 @@ void setup(void) both.println("Multi Screen View Press P - button"); both.println("Multi Screan Res: " + String(resolution) + "Mhz/tick"); both.println( - "Resolution: " + String((float)RANGE_PER_PAGE / (STEPS * SCAN_RBW_RFACTOR)) + - "Mhz/tick"); + "Resolution: " + String((float)RANGE_PER_PAGE / (STEPS * SCAN_RBW_FACTOR)) + + "MHz/tick"); for (int i = 0; i < 500; i++) { button.update(); - delay(10); + delay(5); both.print("."); if (button.pressed()) { @@ -323,7 +591,7 @@ void setup(void) both.println("Single screen View Press P - button"); both.println("Single screen Resol: " + String(resolution) + "Mhz/tick"); both.println( - "Resolution: " + String((float)RANGE_PER_PAGE / (STEPS * SCAN_RBW_RFACTOR)) + + "Resolution: " + String((float)RANGE_PER_PAGE / (STEPS * SCAN_RBW_FACTOR)) + "Mhz/tick"); for (int i = 0; i < 500; i++) { @@ -345,8 +613,10 @@ void setup(void) // calibrate only once ,,, at startup // TODO: check documentation (9.2.1) if we must calibrate in certain ranges radio.setFrequency(FREQ_BEGIN, true); + delay(50); #ifdef METHOD_RSSI + // TODO: try RADIOLIB_SX126X_RX_TIMEOUT_INF state = radio.startReceive(RADIOLIB_SX126X_RX_TIMEOUT_NONE); if (state != RADIOLIB_ERR_NONE) { @@ -356,36 +626,17 @@ void setup(void) #endif // waterfall start line y-axis w = WATERFALL_START; -} - -void osdPrintSignalLevelChart(int col, int signal_value) -{ - // Third line - if (signal_value <= 7) - { - osd.displayChar(13, col + 2, 0x100); - osd.displayChar(14, col + 2, 0x100); - osd.displayChar(12, col + 2, selectFreqChar(signal_value)); - } - // Second line - else if (max_bins_array[col] < 17) - { - osd.displayChar(12, col + 2, 0x100); - osd.displayChar(14, col + 2, 0x100); - osd.displayChar(13, col + 2, selectFreqChar(signal_value)); - } - // First line - else - { - // Clean Up symbol - osd.displayChar(12, col + 2, 0x100); - osd.displayChar(13, col + 2, 0x100); - osd.displayChar(14, col + 2, selectFreqChar(signal_value)); - } +#ifdef OSD_ENABLED + osd.clear(); +#endif } // Formula to translate 33 bin to aproximate RSSI value -int binToRSSI(int bin) { return bin * 4; } +int binToRSSI(int bin) +{ + // the first the strongest RSSI in bin value is 0 + return 11 + (bin * 4); +} void loop(void) { @@ -468,11 +719,13 @@ void loop(void) range = fr_end - fr_begin; } +#ifdef DISABLED_CODE if (!ANIMATED_RELOAD || !single_page_scan) { // clear the scan plot rectangle UI_clearPlotter(); } +#endif if (single_page_scan == false) { @@ -482,29 +735,39 @@ void loop(void) drone_detected_frequency_start = 0; display.setTextAlignment(TEXT_ALIGN_RIGHT); + for (int i = 0; i < MAX_POWER_LEVELS; i++) + { + max_bins_array_value[i] = 0; + } + // horizontal (x axis) Frequency loop - int osd_x = 1, osd_y = 1, col = 0, max_bin = 0; + osd_x = 1, osd_y = 2, col = 0, max_bin = 0; // x loop - for (x = 0; x < STEPS * SCAN_RBW_RFACTOR; x++) + for (x = 0; x < STEPS * SCAN_RBW_FACTOR; x++) { - if (x % SCAN_RBW_RFACTOR == 0) + if (x % SCAN_RBW_FACTOR == 0) new_pixel = true; else new_pixel = false; -#if ANIMATED_RELOAD - UI_drawCursor(x); -#endif + if (ANIMATED_RELOAD && SCAN_RBW_FACTOR == 1) + { + UI_drawCursor(x); + } + if (new_pixel && ANIMATED_RELOAD && SCAN_RBW_FACTOR > 1) + { + UI_drawCursor((int)(x / SCAN_RBW_FACTOR)); + } #ifdef PRINT_PROFILE_TIME scan_start_time = millis(); #endif // Real display pixel x - axis. - // Because of the SCAN_RBW_RFACTOR x is not a display coordinate anymore - // x > STEPS on SCAN_RBW_RFACTOR - int dispaly_x = x / SCAN_RBW_RFACTOR; + // Because of the SCAN_RBW_FACTOR x is not a display coordinate anymore + // x > STEPS on SCAN_RBW_FACTOR + int dispaly_x = x / SCAN_RBW_FACTOR; waterfall[dispaly_x] = false; - float step = (range * ((float)x / (STEPS * SCAN_RBW_RFACTOR))); + float step = (range * ((float)x / (STEPS * SCAN_RBW_FACTOR))); freq = fr_begin + step; @@ -523,19 +786,18 @@ void loop(void) { Serial.print("radio.spectralScanGetStatus ERROR: "); Serial.println(radio.spectralScanGetStatus()); - heltec_delay(ONE_MILLISEC); + heltec_delay(ONE_MILLISEC * 10); } // read the results Array to which the results will be saved radio.spectralScanGetResult(result); } #endif #ifdef METHOD_RSSI -// Spectrum analyzer using getRSSI -#ifdef PRINT_DEBUG - Serial.println("METHOD RSSI"); -#endif + // Spectrum analyzer using getRSSI { - +#ifdef PRINT_DEBUG + Serial.println("METHOD RSSI"); +#endif // memset // memset(result, 0, RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE); // Some issues with memset function @@ -572,63 +834,15 @@ void loop(void) } #endif // SCAN_METHOD == METHOD_RSSI + // if this code is not executed LORA radio doesn't work + // basicaly SX1262 requers delay + // osd.displayString(12, 1, String(FREQ_BEGIN)); + // osd.displayString(12, 30 - 8, String(FREQ_END)); + // delay(2); + #ifdef OSD_ENABLED - { // OSD enabled - - for (int i = 0; i < OSD_WIDTH; i++) - { - max_bins_array[i] = 33; - max_bins_array_value[i] = 0; - } - // memset(max_bins_array, 33, 30); - max_bin = 0; - - osd.displayString(12, 1, String(FREQ_BEGIN)); - osd.displayString(12, 30 - 8, String(FREQ_END)); - for (int i = 1; i < 32; i++) - { - if (result[i] > 0 && (result[i + 1] > 0)) - { - max_bin = i; -#ifdef PRINT_DEBUG - Serial.print("MAX in bin:" + String(max_bin)); - Serial.println(); + osdProcess(); #endif - break; - } - } - if (max_bins_array[col] > max_bin) - { - max_bins_array[col] = max_bin; - // Store RSSI value for RSSI Method - max_bins_array_value[col] = result[max_bin]; - } - // Going to the next OSD step - if (x % osd_steps == 0 && col < 30) - { - // OSD SIDE BAR with frequency log -#ifdef OSD_SIDE_BAR - { - osd.displayString(col, 30 - 7, - String(FREQ_BEGIN + (col * osd_mhz_in_bin)) + - ":" + String(max_bins_array[col])); - } -#endif - // Test with Random Result... - // max_bins_array[s] = rand() % 32; -#ifdef METHOD_RSSI - // With THe RSSI method we can get real RSSI value not just a bin -#endif - // PRINT SIGNAL CHAR ROW, COL, VALUE - osdPrintSignalLevelChart(col, max_bins_array[col]); - -#ifdef PRINT_DEBUG - Serial.println("MAX:" + String(max_bins_array[s])); -#endif - col++; - } - } -#endif // END OSD ENABLED detected = false; detected_y[dispaly_x] = false; @@ -640,7 +854,7 @@ void loop(void) Serial.print(String(result[y]) + ","); #endif -#if FILTER_SPECTRUM_RESULTS == false +#if !defined(FILTER_SPECTRUM_RESULTS) || FILTER_SPECTRUM_RESULTS == false if (result[y] && result[y] != 0) { filtered_result[y] = 1; @@ -651,83 +865,91 @@ void loop(void) } #endif +// if samples low ~1 filter removes all values #if FILTER_SPECTRUM_RESULTS - filtered_result[y] = 0; // Filter Elements without neighbors // if RSSI method actual value is -xxx dB - if (result[y]) + if (result[y] > 0 && samples > 1) { // do not process 'first' and 'last' row to avoid out of index - // access - if ((y != 0) && (y != (RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE - 1))) + // access. + if ((y > 0) && (y != (RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE - 3))) { - if ((result[y + 1] != 0) || (result[y - 1] != 0)) + if (((result[y + 1] != 0) && (result[y + 2] != 0)) || + (result[y - 1] != 0)) { filtered_result[y] = 1; } - } - } -#endif - - // if (result[y] || y == drone_detection_level) - { - // check if we should alarm about a drone presence - if ((filtered_result[y] == 1) // we have some data and - && (y <= drone_detection_level) && - detected_y[dispaly_x] == false) // detection threshold match - { - - // Set LED to ON (filtered in UI component) - UI_setLedFlag(true); - -#if (WATERFALL_ENABLED == true) - if (single_page_scan) - { - // Drone detection true for waterfall - if (!waterfall[dispaly_x]) - { - waterfall[dispaly_x] = true; - display.setColor(WHITE); - display.setPixel(dispaly_x, w); - } - } -#endif - if (drone_detected_frequency_start == 0) - { - // mark freq start - drone_detected_frequency_start = freq; - } - - // mark freq end ... will shift right to last detected range - drone_detected_frequency_end = freq; - - // If level is set to sensitive, - // start beeping every 10th frequency and shorter - // it improves performance less short beep delays... - if (drone_detection_level <= 25) - { - if (detection_count == 1 && SOUND_ON) - { - tone(BUZZER_PIN, 205, - 10); // same action ??? but first time - } - if (detection_count % 5 == 0 && SOUND_ON) - { - tone(BUZZER_PIN, 205, - 10); // same action ??? but everey 5th time - } - } else { - if (detection_count % 20 == 0 && SOUND_ON) - { - tone(BUZZER_PIN, 205, - 10); // same action ??? but everey 20th detection - } +#ifdef PRINT_DEBUG + Serial.print("Filtered:" + String(x) + ":" + String(y) + ","); +#endif } + } + } // not filtering if samples == 1 + else if (result[y] > 0 && samples == 1) + { + filtered_result[y] = 1; + } +#endif + // check if we should alarm about a drone presence + if ((filtered_result[y] == 1) // we have some data and + && (y <= drone_detection_level) && + detected_y[dispaly_x] == false) // detection threshold match + { + // Set LED to ON (filtered in UI component) + UI_setLedFlag(true); -#if (DRAW_DETECTION_TICKS == true) +#if (WATERFALL_ENABLED == true) + if (single_page_scan) + { + // Drone detection true for waterfall + if (!waterfall[dispaly_x]) + { + waterfall[dispaly_x] = true; + display.setColor(WHITE); + display.setPixel(dispaly_x, w); + } + } +#endif + if (drone_detected_frequency_start == 0) + { + // mark freq start + drone_detected_frequency_start = freq; + } + + // mark freq end ... will shift right to last detected range + drone_detected_frequency_end = freq; + + // If level is set to sensitive, + // start beeping every 10th frequency and shorter + // it improves performance less short beep delays... + if (drone_detection_level <= 25) + { + if (detection_count == 1 && SOUND_ON) + { + tone(BUZZER_PIN, 205, + 10); // same action ??? but first time + } + if (detection_count % 5 == 0 && SOUND_ON) + { + tone(BUZZER_PIN, 205, + 10); // same action ??? but everey 5th time + } + } + else + { + if (detection_count % 20 == 0 && SOUND_ON) + { + tone(BUZZER_PIN, 205, + 10); // same action ??? but everey 20th detection + } + } + + if (DRAW_DETECTION_TICKS == true) + { // draw vertical line on top of display for "drone detected" // frequencies if (!detected_y[dispaly_x]) @@ -735,52 +957,55 @@ void loop(void) display.drawLine(dispaly_x, 1, dispaly_x, 6); detected_y[dispaly_x] = true; } -#endif } + } #if (WATERFALL_ENABLED == true) - if ((filtered_result[y] == 1) && (y < drone_detection_level) && - (single_page_scan) && (waterfall[dispaly_x] != true) && new_pixel) - { - // If drone not found set dark pixel on the waterfall - // TODO: make something like scrolling up if possible - waterfall[dispaly_x] = false; - display.setColor(BLACK); - display.setPixel(dispaly_x, w); - display.setColor(WHITE); - } + if ((filtered_result[y] == 1) && (y <= drone_detection_level) && + (single_page_scan) && (waterfall[dispaly_x] != true) && new_pixel) + { + // If drone not found set dark pixel on the waterfall + // TODO: make something like scrolling up if possible + waterfall[dispaly_x] = false; + display.setColor(BLACK); + display.setPixel(dispaly_x, w); + display.setColor(WHITE); + } #endif #if 0 #endif // If 0 - // next 2 If's ... adds !!!! 10ms of runtime ......tfk ??? + // next 2 If's ... adds !!!! 10ms of runtime ......tfk ??? + if (filtered_result[y] == 1) + { +#ifdef PRINT_DEBUG + Serial.print("Pixel:" + String(dispaly_x) + "(" + String(x) + ")" + + ":" + String(y) + ","); +#endif + // Set signal level pixel + display.setPixel(dispaly_x, y); + if (!detected) + { + detected = true; + } + } + + // ------------------------------------------------------------- + // Draw "Detection Level line" every 2 pixel + // ------------------------------------------------------------- + if ((y == drone_detection_level) && (dispaly_x % 2 == 0)) + { + display.setColor(WHITE); if (filtered_result[y] == 1) { - // Set signal level pixel - display.setPixel(dispaly_x, y); - if (!detected) - { - detected = true; - } + display.setColor(INVERSE); } + display.setPixel(dispaly_x, y); + display.setPixel(dispaly_x, y - 1); // 2 px wide - // ------------------------------------------------------------- - // Draw "Detection Level line" every 2 pixel - // ------------------------------------------------------------- - if ((y == drone_detection_level) && (dispaly_x % 2 == 0)) - { - display.setColor(WHITE); - if (filtered_result[y] == 1) - { - display.setColor(INVERSE); - } - display.setPixel(dispaly_x, y); - display.setPixel(dispaly_x, y - 1); // 2 px wide - - display.setColor(WHITE); - } + display.setColor(WHITE); } } @@ -859,7 +1084,28 @@ void loop(void) // wait a little bit before the next scan, // otherwise the SX1262 hangs // Add more logic before insead of long delay... - // heltec_delay(1); + int delay_cnt = 1; + while (radio.spectralScanGetStatus() != RADIOLIB_ERR_NONE) + { + if (delay_cnt == 1) + { + // trying to use display as delay.. + display.display(); + } + else + { + heltec_delay(ONE_MILLISEC * 2); + } + + Serial.println("spectralScanGetStatus ERROR(" + + String(radio.spectralScanGetStatus()) + + ") hard delay(2) - " + String(delay_cnt)); + // if error than speed is slow animating chart + ANIMATED_RELOAD = true; + + delay_cnt++; + } + // TODO: move osd logic here as a dalay ;) // Loop is needed if heltec_delay(1) not used heltec_loop(); } @@ -877,16 +1123,21 @@ void loop(void) display.setColor(WHITE); } #endif - // Render display data here display.display(); #ifdef OSD_ENABLED - if (global_counter != 0 && global_counter % 50 == 0) + // Sometimes OSD prints entire screan with the digits. + // We need clean the screan to fix it. + // We can do it every time but to optimise doing every N times + if (global_counter != 0 && global_counter % 10 == 0) { +#if !defined(BT_SCANNING_ENABLED) && !defined(WIFI_SCANNING_ENABLED) osd.clear(); osd.displayChar(14, 1, 0x10f); global_counter = 0; +#endif } + ANIMATED_RELOAD = false; global_counter++; #endif } @@ -900,4 +1151,24 @@ void loop(void) #ifdef PRINT_PROFILE_TIME Serial.printf("LOOP: %lld ms; SCAN: %lld ms;\n ", loop_time, scan_time); #endif +// No WiFi and BT Scan Without OSD +#ifdef OSD_ENABLED +#ifdef WIFI_SCANNING_ENABLED + if ((millis() - wf_start) > WF_SCAN_DELAY) + { + scanWiFi(); + wf_start = millis(); + // prevent BT scanning after scanning WF + bt_start = millis(); + } +#endif +#ifdef BT_SCANNING_ENABLED + if ((millis() - bt_start) > BT_SCAN_DELAY) + { + + scanBT(); + bt_start = millis(); + } +#endif +#endif } diff --git a/src/ui.cpp b/src/ui.cpp index ada8a0b..2760253 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -143,10 +143,11 @@ void drawTicks(float every, int length) void UI_drawCursor(int16_t possition) { - // Draw animated cursor on reload process + // Draw animated vertical cursor on reload process display_instance->setColor(BLACK); display_instance->drawVerticalLine(possition, 0, HEIGHT); display_instance->drawVerticalLine(possition + 1, 0, HEIGHT); + display_instance->drawVerticalLine(possition + 2, 0, HEIGHT); display_instance->setColor(WHITE); } diff --git a/trans_src/main.cpp b/trans_src/main.cpp new file mode 100644 index 0000000..2010f48 --- /dev/null +++ b/trans_src/main.cpp @@ -0,0 +1,120 @@ +/** + * Send and receive LoRa-modulation packets with a sequence number, showing RSSI + * and SNR for received packets on the little display. + * + * Note that while this send and received using LoRa modulation, it does not do + * LoRaWAN. For that, see the LoRaWAN_TTN example. + * + * This works on the stick, but the output on the screen gets cut off. + */ + +// Turns the 'PRG' button into the power button, long press is off +#define HELTEC_POWER_BUTTON // must be before "#include " +#include + +// Pause between transmited packets in mseconds. +// Set to zero to only transmit a packet when pressing the user button +// Will not exceed 1% duty cycle, even if you set a lower value. +#define PAUSE 20 + +// Frequency in MHz. Keep the decimal point to designate float. +// Check your own rules and regulations to see what is legal where you are. +#define FREQUENCY 866.3 // for Europe +// #define FREQUENCY 905.2 // for US + +// LoRa bandwidth. Keep the decimal point to designate float. +// Allowed values are 7.8, 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125.0, 250.0 and 500.0 +// kHz. +#define BANDWIDTH 250.0 + +// Number from 5 to 12. Higher means slower but higher "processor gain", +// meaning (in nutshell) longer range and more robust against interference. +#define SPREADING_FACTOR 9 + +// Transmit power in dBm. 0 dBm = 1 mW, enough for tabletop-testing. This value can be +// set anywhere between -9 dBm (0.125 mW) to 22 dBm (158 mW). Note that the maximum ERP +// (which is what your antenna maximally radiates) on the EU ISM band is 25 mW, and that +// transmissting without an antenna can damage your hardware. +#define TRANSMIT_POWER -9 + +String rxdata; +volatile bool rxFlag = false; +long counter = 0; +uint64_t last_tx = 0; +uint64_t tx_time; +uint64_t minimum_pause; + +// Can't do Serial or display things here, takes too much time for the interrupt +void rx() { rxFlag = true; } + +void setup() +{ + heltec_setup(); + both.println("Radio init"); + RADIOLIB_OR_HALT(radio.begin()); + // Set the callback function for received packets + radio.setDio1Action(rx); + // Set radio parameters + both.printf("Frequency: %.2f MHz\n", FREQUENCY); + RADIOLIB_OR_HALT(radio.setFrequency(FREQUENCY)); + both.printf("Bandwidth: %.1f kHz\n", BANDWIDTH); + RADIOLIB_OR_HALT(radio.setBandwidth(BANDWIDTH)); + both.printf("Spreading Factor: %i\n", SPREADING_FACTOR); + RADIOLIB_OR_HALT(radio.setSpreadingFactor(SPREADING_FACTOR)); + both.printf("TX power: %i dBm\n", TRANSMIT_POWER); + RADIOLIB_OR_HALT(radio.setOutputPower(TRANSMIT_POWER)); + // Start receiving + RADIOLIB_OR_HALT(radio.startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF)); +} + +void loop() +{ + heltec_loop(); + + bool tx_legal = millis() > last_tx + minimum_pause; + // Transmit a packet every PAUSE seconds or when the button is pressed + if ((PAUSE && tx_legal && millis() - last_tx > (PAUSE)) || button.isSingleClick()) + { + // In case of button click, tell user to wait + if (!tx_legal) + { + both.printf("Legal limit, wait %i sec.\n", + (int)((minimum_pause - (millis() - last_tx)) / 1000) + 1); + return; + } + both.printf("TX [%s] ", String(counter).c_str()); + radio.clearDio1Action(); + heltec_led(50); // 50% brightness is plenty for this LED + tx_time = millis(); + RADIOLIB(radio.transmit(String(counter++).c_str())); + tx_time = millis() - tx_time; + heltec_led(0); + if (_radiolib_status == RADIOLIB_ERR_NONE) + { + both.printf("OK (%i ms)\n", (int)tx_time); + } + else + { + both.printf("fail (%i)\n", _radiolib_status); + } + // Maximum 1% duty cycle + minimum_pause = tx_time * 100; + last_tx = millis(); + radio.setDio1Action(rx); + RADIOLIB_OR_HALT(radio.startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF)); + } + + // If a packet was received, display it and the RSSI and SNR + if (rxFlag) + { + rxFlag = false; + radio.readData(rxdata); + if (_radiolib_status == RADIOLIB_ERR_NONE) + { + both.printf("RX [%s]\n", rxdata.c_str()); + both.printf(" RSSI: %.2f dBm\n", radio.getRSSI()); + both.printf(" SNR: %.2f dB\n", radio.getSNR()); + } + RADIOLIB_OR_HALT(radio.startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF)); + } +}