From 30d2b72d2c81668f1361541a6dba7bab275d8161 Mon Sep 17 00:00:00 2001 From: "ionsurdu@github.com" Date: Tue, 6 Aug 2024 18:20:00 +0300 Subject: [PATCH 1/8] Minor code cleanup and optimizations. --- .vscode/settings.json | 5 +++++ include/images.h | 10 +++++++++ src/{images.h => images.c} | 11 +++++----- src/main.cpp | 43 ++++++++++++++++++++++++++++---------- 4 files changed, 53 insertions(+), 16 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 include/images.h rename src/{images.h => images.c} (97%) diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..46b53b9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "images.h": "c" + } +} \ No newline at end of file diff --git a/include/images.h b/include/images.h new file mode 100644 index 0000000..d159401 --- /dev/null +++ b/include/images.h @@ -0,0 +1,10 @@ + +#ifndef __IMAGES_H__ +#define __IMAGES_H__ + +// Array of all bitmaps for convenience. (Total bytes used to store images in PROGMEM = 1040) + +extern const unsigned char epd_bitmap_ucog []; + +#endif // __IMAGES_H__ + diff --git a/src/images.h b/src/images.c similarity index 97% rename from src/images.h rename to src/images.c index 7461691..99431cd 100644 --- a/src/images.h +++ b/src/images.c @@ -1,11 +1,12 @@ +#include "images.h" //'Logo_UCOG', 128x64px //https://www.online-utility.org/image/convert/to/XBM //https://javl.github.io/image2cpp/ //#define 1721604660673_width 128 //#define 1721604660673_height 64 -const unsigned char epd_bitmap_ucog [] PROGMEM = { +const unsigned char epd_bitmap_ucog[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -95,7 +96,7 @@ const unsigned char epd_bitmap_ucog [] PROGMEM = { }; // Array of all bitmaps for convenience. (Total bytes used to store images in PROGMEM = 1040) -const int epd_bitmap_allArray_LEN = 1; -const unsigned char* epd_bitmap_allArray[1] = { - epd_bitmap_ucog -}; +// const int epd_bitmap_allArray_LEN = 1; +// const unsigned char* epd_bitmap_allArray[1] = { +// epd_bitmap_ucog +// }; diff --git a/src/main.cpp b/src/main.cpp index e28582a..c098dce 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,13 +21,34 @@ https://jgromes.github.io/RadioLib/ */ +// Turns the 'PRG' button into the power button, long press is off +// TODO add it to compiller options using -DHELTEC_POWER_BUTTON +#define HELTEC_POWER_BUTTON // must be before "#include " + +#include +#include +#include + +// This file contains a binary patch for the SX1262 +#include "modules/SX126x/patches/SX126x_patch_scan.h" + + +// ----------------------------------------------------------------- +// CONFIGURATION OPTIONS +// ----------------------------------------------------------------- + // frequency range in MHz to scan #define FREQ_BEGIN 850 // TODO: if % RANGE_PER_PAGE != 0 #define FREQ_END 950 -// true for RSSI and flase for default spectralScan method -#define RSSI_METHOD false + +typedef enum { + METHOD_RSSI = 0u, + METHOD_SPECTRAL +} TSCAN_METOD_ENUM; + +#define SCAN_METHOD METHOD_SPECTRAL // Feature to scan diapazones. Other frequency settings will be ignored. int SCAN_RANGES[] = {}; @@ -39,6 +60,7 @@ unsigned int RANGE_PER_PAGE = FREQ_END - FREQ_BEGIN; // FREQ_END - FREQ_BEGIN // To Enable Multi Screen scan // unsigned int RANGE_PER_PAGE = 50; // Default Range on Menu Button Switch + #define DEFAULT_RANGE_PER_PAGE 50 // TODO: Ignore power lines @@ -81,14 +103,7 @@ unsigned int median_frequency = FREQ_BEGIN + range_frequency / 2; #define ONE_MILLISEC 1 -// Turns the 'PRG' button into the power button, long press is off -#define HELTEC_POWER_BUTTON // must be before "#include " -#include -#include -#include -// This file contains a binary patch for the SX1262 -#include "modules/SX126x/patches/SX126x_patch_scan.h" // Prints debug information and the scan measurement bins from the SX1262 in hex //#define PRINT_DEBUG @@ -602,8 +617,10 @@ void loop() Serial.print(freq); Serial.println(); #endif + + // SpectralScan Method - if (!RSSI_METHOD) +#if SCAN_METHOD == METHOD_SPECTRAL { // start spectral scan third parameter is a sleep interval radio.spectralScanStart(SAMPLES, 1); @@ -617,9 +634,10 @@ void loop() // read the results Array to which the results will be saved radio.spectralScanGetResult(result); } +#endif +#if SCAN_METHOD == METHOD_RSSI // Spectrum analyzer using getRSSI - if (RSSI_METHOD) { state = radio.startReceive(0); if (state == RADIOLIB_ERR_NONE) @@ -661,6 +679,9 @@ void loop() } } } +#endif + + detected = false; #ifdef FILTER_SPECTRUM_RESULTS // Filter Elements without neighbors From 6521a067ac7cfd6fe3662d8d76e77e91227d323c Mon Sep 17 00:00:00 2001 From: "ionsurdu@github.com" Date: Tue, 6 Aug 2024 19:50:45 +0300 Subject: [PATCH 2/8] move ui fucntions to separate file + cleanup --- .vscode/settings.json | 9 +- include/global_config.h | 25 +++ include/ui.h | 45 +++++ src/{images.c => images.cpp} | 0 src/main.cpp | 318 ++++------------------------------- src/ui.cpp | 270 +++++++++++++++++++++++++++++ 6 files changed, 385 insertions(+), 282 deletions(-) create mode 100644 include/global_config.h create mode 100644 include/ui.h rename src/{images.c => images.cpp} (100%) create mode 100644 src/ui.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 46b53b9..6e493b5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,12 @@ { "files.associations": { - "images.h": "c" + "images.h": "c", + "*.tcc": "cpp", + "deque": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp", + "system_error": "cpp" } } \ No newline at end of file diff --git a/include/global_config.h b/include/global_config.h new file mode 100644 index 0000000..d429eae --- /dev/null +++ b/include/global_config.h @@ -0,0 +1,25 @@ +#ifndef __GLOBAL_CONFIG_H__ +#define __GLOBAL_CONFIG_H__ + +// frequency range in MHz to scan +#define FREQ_BEGIN 850 +// TODO: if % RANGE_PER_PAGE != 0 +#define FREQ_END 950 + +// Measurement bandwidth. Allowed bandwidth values (in kHz) are: +// 4.8, 5.8, 7.3, 9.7, 11.7, 14.6, 19.5, 23.4, 29.3, 39.0, 46.9, 58.6, +// 78.2, 93.8, 117.3, 156.2, 187.2, 234.3, 312.0, 373.6 and 467.0 +#define BANDWIDTH 467.0 + +// Detection level from the 33 levels. The higher number is more sensitive +#define DEFAULT_DRONE_DETECTION_LEVEL 21 + + +#define BUZZER_PIN 41 +// REB trigger PIN +#define REB_PIN 42 + +#define WATERFALL_ENABLED true +#define WATERFALL_START 37 + +#endif \ No newline at end of file diff --git a/include/ui.h b/include/ui.h new file mode 100644 index 0000000..776bc35 --- /dev/null +++ b/include/ui.h @@ -0,0 +1,45 @@ + +#ifndef __UI_H__ +#define __UI_H__ + +#include +#include "SSD1306Wire.h" +#include "OLEDDisplayUi.h" + +// #include + +// (optional) major and minor tickmarks at x MHz +#define MAJOR_TICKS 10 +#define MINOR_TICKS 5 + +#define ONE_MILLISEC 1 + +// Prints debug information and the scan measurement bins from the SX1262 in hex +//#define PRINT_DEBUG +// Change spectrum plot values at once or by line +#define ANIMATED_RELOAD true + +#define MAJOR_TICK_LENGTH 2 +#define MINOR_TICK_LENGTH 1 +// WEIGHT of the x-asix line +#define X_AXIS_WEIGHT 1 + +#define STATUS_TEXT_TOP (64 - 10) + + +// The number of the spectrum screen lines = width of screen +// Resolution of the scan is limited by 128-pixel screen +#define STEPS 128 + + +#define SCREAN_HEIGHT 64 + +// publish functions +extern void UI_Init(SSD1306Wire*); +extern void UI_displayDecorate(int , int , bool ); +extern void UI_setLedFlag( bool); +extern void UI_clearPlotter(void); +extern void UI_drawCurrsor(int16_t); + +#endif // __UI_H__ + diff --git a/src/images.c b/src/images.cpp similarity index 100% rename from src/images.c rename to src/images.cpp diff --git a/src/main.cpp b/src/main.cpp index c098dce..27ac455 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,20 +27,20 @@ #include #include -#include - // This file contains a binary patch for the SX1262 #include "modules/SX126x/patches/SX126x_patch_scan.h" +// project components +#include "global_config.h" +#include "images.h" +#include "ui.h" + + // ----------------------------------------------------------------- // CONFIGURATION OPTIONS // ----------------------------------------------------------------- -// frequency range in MHz to scan -#define FREQ_BEGIN 850 -// TODO: if % RANGE_PER_PAGE != 0 -#define FREQ_END 950 typedef enum { @@ -68,9 +68,6 @@ unsigned int RANGE_PER_PAGE = FREQ_END - FREQ_BEGIN; // FREQ_END - FREQ_BEGIN #define LOW_FILTER 3 #define FILTER_SPECTRUM_RESULTS true -// The number of the spectrum screen lines = width of screen -// Resolution of the scan is limited by 128-pixel screen -#define STEPS 128 // 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) @@ -89,64 +86,23 @@ unsigned int fr_end = FREQ_BEGIN; unsigned int iterations = RANGE / RANGE_PER_PAGE; -unsigned int range_frequency = FREQ_END - FREQ_BEGIN; -unsigned int median_frequency = FREQ_BEGIN + range_frequency / 2; - -// Measurement bandwidth. Allowed bandwidth values (in kHz) are: -// 4.8, 5.8, 7.3, 9.7, 11.7, 14.6, 19.5, 23.4, 29.3, 39.0, 46.9, 58.6, -// 78.2, 93.8, 117.3, 156.2, 187.2, 234.3, 312.0, 373.6 and 467.0 -#define BANDWIDTH 467.0 - -// (optional) major and minor tickmarks at x MHz -#define MAJOR_TICKS 10 -#define MINOR_TICKS 5 - -#define ONE_MILLISEC 1 +// unsigned int range_frequency = FREQ_END - FREQ_BEGIN; +unsigned int median_frequency = FREQ_BEGIN + FREQ_END - FREQ_BEGIN / 2; -// Prints debug information and the scan measurement bins from the SX1262 in hex -//#define PRINT_DEBUG -// Change spectrum plot values at once or by line -#define ANIMATED_RELOAD true - -#define MAJOR_TICK_LENGTH 2 -#define MINOR_TICK_LENGTH 1 -// WEIGHT of the x-asix line -#define X_AXIS_WEIGHT 1 -// Height of the plotter area -#define HEIGHT RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE -// -#define SCALE_TEXT_TOP (HEIGHT + X_AXIS_WEIGHT + MAJOR_TICK_LENGTH) -#define STATUS_TEXT_TOP (64 - 10) - -// Detection level from the 33 levels. The higher number is more sensitive -#define DEFAULT_DRONE_DETECTION_LEVEL 21 - -#define BUZZER_PIN 41 -// REB trigger PIN -#define REB_PIN 42 - -#define SCREAN_HEIGHT 64 - -#define WATERFALL_ENABLED true -#define WATERFALL_START 37 -#define OSD_ENABLED true - -#define DISABLE_PLOT_CHART false +// #define OSD_ENABLED true // unused +// #define DISABLE_PLOT_CHART false // unused // Array to store the scan results uint16_t result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; uint16_t filtered_result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; // Waterfall array -bool waterfall[10][STEPS][10]; +bool waterfall[10][STEPS][10]; // 10 - ??? // global variable -unsigned short int scan_var = 0; -// initialized flag -bool initialized = false; + // Used as a Led Light and Buzzer/count trigger -bool led_flag = false; bool first_run = false; // drone detection flag bool drone_detected = false; @@ -158,14 +114,13 @@ unsigned int detection_count = 0; bool single_page_scan = false; bool SOUND_ON = true; -unsigned int start_scan_text = (128 / 2) - 3; unsigned int scan_time = 0; unsigned int scan_start_time = 0; uint64_t start = 0; -unsigned int x, y, i, w = 0; +unsigned int x, y, scan_iterration, w = 0; unsigned int ranges_count = 0; float freq = 0; @@ -175,203 +130,6 @@ int result_index = 0; unsigned int button_pressed_counter = 0; -void clearStatus() -{ - // clear status line - display.setColor(BLACK); - display.fillRect(0, STATUS_TEXT_TOP + 2, 128, 13); - display.setColor(WHITE); -} - -void clearPlotter() -{ - // clear the scan plot rectangle - display.setColor(BLACK); - display.fillRect(0, 0, STEPS, HEIGHT); - display.setColor(WHITE); -} - -/** - * @brief Draws ticks on the display at regular whole intervals. - * - * @param every The interval between ticks in MHz. - * @param length The length of each tick in pixels. - */ -void drawTicks(float every, int length) -{ - int first_tick = 0; - //+ (every - (fr_begin - (int)(fr_begin / every) * every)); - /*if (first_tick < fr_begin) - { - first_tick += every; - }*/ - bool correction = false; - int pixels_per_step = STEPS / (RANGE_PER_PAGE / every); - if (STEPS / RANGE_PER_PAGE != 0) - { - correction = true; - } - int correction_number = STEPS - (int)(pixels_per_step * (RANGE_PER_PAGE / every)); - int tick = 0; - int tick_minor = 0; - int median = (RANGE_PER_PAGE / every) / 2; - // TODO: (RANGE_PER_PAGE / every) * 2 has twice extra steps we need to figureout correct logic or minor ticks is not showing to the end - for (int t = 0; t <= (RANGE_PER_PAGE / every) * 2; t++) - { - // fix if pixels per step is not int and we have shift - if (correction && t % 2 != 0 && correction_number > 1) - { - // pixels_per_step++; - correction_number--; - } - - tick += pixels_per_step; - tick_minor = tick / 2; - - if (tick <= 128 - 3) - { - display.drawLine(tick, HEIGHT + X_AXIS_WEIGHT, tick, HEIGHT + X_AXIS_WEIGHT + length); - // Central tick - if (tick > (128 / 2) - 3 && tick < (128 / 2) + 3) - { - display.drawLine(tick + 1, HEIGHT + X_AXIS_WEIGHT, tick + 1, HEIGHT + X_AXIS_WEIGHT + length); - } - } - -#ifdef MINOR_TICKS - // Fix two ticks together - if (tick_minor + 1 != tick && tick_minor - 1 != tick && tick_minor + 2 != tick && tick_minor - 2 != tick) - { - display.drawLine(tick_minor, HEIGHT + X_AXIS_WEIGHT, tick_minor, HEIGHT + X_AXIS_WEIGHT + MINOR_TICK_LENGTH); - } - // Central tick - if (tick_minor > (128 / 2) - 3 && tick_minor < (128 / 2) + 3) - { - display.drawLine(tick_minor + 1, HEIGHT + X_AXIS_WEIGHT, tick_minor + 1, HEIGHT + X_AXIS_WEIGHT + MINOR_TICK_LENGTH); - } -#endif - } -} - -/** - * @brief Decorates the display: everything but the plot itself. - */ -void displayDecorate(int begin = 0, int end = 0, bool redraw = false) -{ - if (!initialized) - { - // Start and end ticks - display.fillRect(0, HEIGHT + X_AXIS_WEIGHT, 2, MAJOR_TICK_LENGTH + 1); - display.fillRect(126, HEIGHT + X_AXIS_WEIGHT, 2, MAJOR_TICK_LENGTH + 1); - - // Drone detection level - display.setTextAlignment(TEXT_ALIGN_RIGHT); - display.drawString(128, 0, String(drone_detection_level)); - } - - if (!initialized || redraw) - { - // Clear something - display.setColor(BLACK); - display.fillRect(0, SCALE_TEXT_TOP + 1, 128, 12); - display.setColor(WHITE); - - // Drone detection level - display.setTextAlignment(TEXT_ALIGN_RIGHT); - display.drawString(128, 0, String(drone_detection_level)); - - // Frequency start - display.setTextAlignment(TEXT_ALIGN_LEFT); - display.drawString(0, SCALE_TEXT_TOP, (begin == 0) ? String(FREQ_BEGIN) : String(begin)); - - display.setTextAlignment(TEXT_ALIGN_CENTER); - display.drawString(128 / 2, SCALE_TEXT_TOP, (begin == 0) ? String(median_frequency) : String(begin + ((end - begin) / 2))); - - // Frequency end - display.setTextAlignment(TEXT_ALIGN_RIGHT); - display.drawString(128, SCALE_TEXT_TOP, (end == 0) ? String(FREQ_END) : String(end)); - } - - if (led_flag == true && detection_count >= 5) - { - digitalWrite(LED, HIGH); - if (SOUND_ON) - { - tone(BUZZER_PIN, 104, 100); - } - digitalWrite(REB_PIN, HIGH); - led_flag = false; - } - - else if (!redraw) - { - digitalWrite(LED, LOW); - } - // Status text block - if (!drone_detected) - { - // "Scanning" - display.setTextAlignment(TEXT_ALIGN_CENTER); - // clear status line - clearStatus(); - if (scan_var == 0) - { - display.drawString(start_scan_text, STATUS_TEXT_TOP, "Scan. "); - } - else if (scan_var == 1) - { - display.drawString(start_scan_text, STATUS_TEXT_TOP, "Scan.. "); - } - else if (scan_var == 2) - { - display.drawString(start_scan_text, STATUS_TEXT_TOP, "Scan..."); - } - scan_var++; - if (scan_var == 3) - { - scan_var = 0; - } - } - - if (drone_detected) - { - display.setTextAlignment(TEXT_ALIGN_CENTER); - // clear status line - clearStatus(); - - display.drawString(start_scan_text, STATUS_TEXT_TOP, String(drone_detected_frequency_start) + ">RF<" + String(drone_detected_frequency_end)); - } - - if (ranges_count == 0) - { - display.setTextAlignment(TEXT_ALIGN_LEFT); - display.drawString(0, STATUS_TEXT_TOP, String(FREQ_BEGIN)); - - display.setTextAlignment(TEXT_ALIGN_RIGHT); - display.drawString(128, STATUS_TEXT_TOP, String(FREQ_END)); - } - else if (ranges_count > 0) - { - display.setTextAlignment(TEXT_ALIGN_LEFT); - display.drawString(0, STATUS_TEXT_TOP, String(SCAN_RANGES[i] / 1000) + "-" + String(SCAN_RANGES[i] % 1000)); - if (i + 1 < iterations) - { - display.setTextAlignment(TEXT_ALIGN_RIGHT); - display.drawString(128, STATUS_TEXT_TOP, String(SCAN_RANGES[i + 1] / 1000) + "-" + String(SCAN_RANGES[i + 1] % 1000)); - } - } - - if (!initialized) - { - // X-axis - display.fillRect(0, HEIGHT, STEPS, X_AXIS_WEIGHT); -// ticks -#ifdef MAJOR_TICKS - drawTicks(MAJOR_TICKS, MAJOR_TICK_LENGTH); -#endif - } - initialized = true; -} void setup() { @@ -379,10 +137,8 @@ void setup() pinMode(BUZZER_PIN, OUTPUT); pinMode(REB_PIN, OUTPUT); heltec_setup(); - display.clear(); - // draw the UCOG welcome logo - display.drawXbm(0, 2, 128, 64, epd_bitmap_ucog); - display.display(); + + UI_Init(&display); for (int i = 0; i < 200; i++) { @@ -415,8 +171,8 @@ void setup() both.println("Starting scanning..."); float vbat = heltec_vbat(); both.printf("V battery: %.2fV (%d%%)\n", vbat, heltec_battery_percent(vbat)); + delay(300); - display.clear(); float resolution = RANGE / STEPS; @@ -488,7 +244,7 @@ void setup() void loop() { - displayDecorate(); + UI_displayDecorate(0,0,false); // some default values drone_detected = false; detection_count = 0; drone_detected_frequency_start = 0; @@ -499,7 +255,7 @@ void loop() if (!ANIMATED_RELOAD || !single_page_scan) { // clear the scan plot rectangle - clearPlotter(); + UI_clearPlotter(); } // do the scan @@ -546,31 +302,31 @@ void loop() } // Iterating by small ranges by 50 Mhz each pixel is 0.4 Mhz - for (i = 0; i < iterations; i++) + for (scan_iterration = 0; scan_iterration < iterations; scan_iterration++) { range = RANGE_PER_PAGE; if (ranges_count == 0) { - fr_begin = (i == 0) ? fr_begin : fr_begin += range; + fr_begin = (scan_iterration == 0) ? fr_begin : fr_begin += range; fr_end = fr_begin + RANGE_PER_PAGE; } else { - fr_begin = SCAN_RANGES[i] / 1000; - fr_end = SCAN_RANGES[i] % 1000; + fr_begin = SCAN_RANGES[scan_iterration] / 1000; + fr_end = SCAN_RANGES[scan_iterration] % 1000; range = fr_end - fr_begin; } if (!ANIMATED_RELOAD || !single_page_scan) { // clear the scan plot rectangle - clearPlotter(); + UI_clearPlotter(); } if (single_page_scan == false) { - displayDecorate(fr_begin, fr_end, true); + UI_displayDecorate(fr_begin, fr_end, true); } drone_detected_frequency_start = 0; @@ -580,15 +336,12 @@ void loop() for (x = 0; x < STEPS; x++) { scan_start_time = millis(); - if (ANIMATED_RELOAD) - { - // Draw animated cursor on reload process - display.setColor(BLACK); - display.drawVerticalLine(x, 0, HEIGHT); - display.drawVerticalLine(x + 1, 0, HEIGHT); - display.setColor(WHITE); - } - waterfall[i][x][w] = false; + +#if ANIMATED_RELOAD + UI_drawCurrsor(x); +#endif + + waterfall[scan_iterration][x][w] = false; freq = fr_begin + (range * ((float)x / STEPS)); radio.setFrequency(freq); // TODO: RSSI METHOD @@ -719,7 +472,7 @@ void loop() if (single_page_scan) { // Drone detection true for waterfall - waterfall[i][x][w] = true; + waterfall[scan_iterration][x][w] = true; display.setColor(WHITE); display.setPixel(x, w); } @@ -729,7 +482,9 @@ void loop() drone_detected_frequency_start = freq; } drone_detected_frequency_end = freq; - led_flag = true; + + UI_setLedFlag(true); + // If level is set to sensitive, start beeping every 10th frequency and shorter if (drone_detection_level <= 25) { @@ -748,12 +503,13 @@ void loop() display.setPixel(x, 3); display.setPixel(x, 4); } + #ifdef WATERFALL_ENABLED - if (filtered_result[y] == 1 && y > drone_detection_level && single_page_scan && waterfall[i][x][w] != true) + if (filtered_result[y] == 1 && y > drone_detection_level && single_page_scan && waterfall[scan_iterration][x][w] != true) { // If drone not found set dark pixel on the waterfall // TODO: make something like scrolling up if possible - waterfall[i][x][w] = false; + waterfall[scan_iterration][x][w] = false; display.setColor(BLACK); display.setPixel(x, w); display.setColor(WHITE); diff --git a/src/ui.cpp b/src/ui.cpp new file mode 100644 index 0000000..a28ede3 --- /dev/null +++ b/src/ui.cpp @@ -0,0 +1,270 @@ + + +#include "ui.h" +#include "images.h" +#include "global_config.h" +#include "RadioLib.h" + +// ------------------------------------------------- +// LCOAL DEFINES +// Height of the plotter area +// ------------------------------------------------- + +#define HEIGHT RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE +// +#define SCALE_TEXT_TOP (HEIGHT + X_AXIS_WEIGHT + MAJOR_TICK_LENGTH) + + +static unsigned int start_scan_text = (128 / 2) - 3; +// initialized flag +static bool ui_initialized = false; +static bool led_flag = false; +static unsigned short int scan_progress_count = 0; + +static SSD1306Wire *display_instance; //(0x3c, SDA_OLED, SCL_OLED, DISPLAY_GEOMETRY); +// PrintSplitter both(Serial, display); + + +// temporary dirty import ... to be solved durring upcoming refactoring +extern unsigned int drone_detection_level; +extern unsigned int RANGE_PER_PAGE; +extern unsigned int median_frequency; +extern unsigned int detection_count; +extern bool SOUND_ON; +extern bool drone_detected; +extern unsigned int drone_detected_frequency_start; +extern unsigned int drone_detected_frequency_end ; +extern unsigned int ranges_count; +extern int SCAN_RANGES[]; +extern unsigned int ranges_count; +extern unsigned int iterations ; +extern unsigned int scan_iterration; + +void UI_Init(SSD1306Wire *display_ptr) +{ + // init pointer to display instance. + display_instance = display_ptr; + + // check for null ??? + display_instance->clear(); + // draw the UCOG welcome logo + display_instance->drawXbm(0, 2, 128, 64, epd_bitmap_ucog); + display_instance->display(); +} + +void UI_setLedFlag( bool new_status) +{ + led_flag = new_status; +} + + +void clearStatus(void) +{ + // clear status line + display_instance->setColor(BLACK); + display_instance->fillRect(0, STATUS_TEXT_TOP + 2, 128, 13); + display_instance->setColor(WHITE); +} + +void UI_clearPlotter(void) +{ + // clear the scan plot rectangle + display_instance->setColor(BLACK); + display_instance->fillRect(0, 0, STEPS, HEIGHT); + display_instance->setColor(WHITE); +} + +/** + * @brief Draws ticks on the display at regular whole intervals. + * + * @param every The interval between ticks in MHz. + * @param length The length of each tick in pixels. + */ +void drawTicks(float every, int length) +{ + int first_tick = 0; + //+ (every - (fr_begin - (int)(fr_begin / every) * every)); + /*if (first_tick < fr_begin) + { + first_tick += every; + }*/ + bool correction = false; + int pixels_per_step = STEPS / (RANGE_PER_PAGE / every); + if (STEPS / RANGE_PER_PAGE != 0) + { + correction = true; + } + + int correction_number = STEPS - (int)(pixels_per_step * (RANGE_PER_PAGE / every)); + int tick = 0; + int tick_minor = 0; + int median = (RANGE_PER_PAGE / every) / 2; + // TODO: (RANGE_PER_PAGE / every) * 2 has twice extra steps we need to figureout correct logic or minor ticks is not showing to the end + for (int t = 0; t <= (RANGE_PER_PAGE / every) * 2; t++) + { + // fix if pixels per step is not int and we have shift + if (correction && t % 2 != 0 && correction_number > 1) + { + // pixels_per_step++; + correction_number--; + } + + tick += pixels_per_step; + tick_minor = tick / 2; + + if (tick <= 128 - 3) + { + display_instance->drawLine(tick, HEIGHT + X_AXIS_WEIGHT, tick, HEIGHT + X_AXIS_WEIGHT + length); + // Central tick + if (tick > (128 / 2) - 3 && tick < (128 / 2) + 3) + { + display_instance->drawLine(tick + 1, HEIGHT + X_AXIS_WEIGHT, tick + 1, HEIGHT + X_AXIS_WEIGHT + length); + } + } + +#ifdef MINOR_TICKS + // Fix two ticks together + if (tick_minor + 1 != tick && tick_minor - 1 != tick && tick_minor + 2 != tick && tick_minor - 2 != tick) + { + display_instance->drawLine(tick_minor, HEIGHT + X_AXIS_WEIGHT, tick_minor, HEIGHT + X_AXIS_WEIGHT + MINOR_TICK_LENGTH); + } + // Central tick + if (tick_minor > (128 / 2) - 3 && tick_minor < (128 / 2) + 3) + { + display_instance->drawLine(tick_minor + 1, HEIGHT + X_AXIS_WEIGHT, tick_minor + 1, HEIGHT + X_AXIS_WEIGHT + MINOR_TICK_LENGTH); + } +#endif + } +} + +void UI_drawCurrsor(int16_t possition) +{ + // Draw animated cursor on reload process + display_instance->setColor(BLACK); + display_instance->drawVerticalLine(possition, 0, HEIGHT); + display_instance->drawVerticalLine(possition + 1, 0, HEIGHT); + display_instance->setColor(WHITE); + +} + + +/** + * @brief Decorates the display: everything but the plot itself. + */ +void UI_displayDecorate(int begin = 0, int end = 0, bool redraw = false) +{ + if (!ui_initialized) + { + // Start and end ticks + display_instance->fillRect(0, HEIGHT + X_AXIS_WEIGHT, 2, MAJOR_TICK_LENGTH + 1); + display_instance->fillRect(126, HEIGHT + X_AXIS_WEIGHT, 2, MAJOR_TICK_LENGTH + 1); + + // Drone detection level + display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); + display_instance->drawString(128, 0, String(drone_detection_level)); + } + + if (!ui_initialized || redraw) + { + // Clear something + display_instance->setColor(BLACK); + display_instance->fillRect(0, SCALE_TEXT_TOP + 1, 128, 12); + display_instance->setColor(WHITE); + + // Drone detection level + display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); + display_instance->drawString(128, 0, String(drone_detection_level)); + + // Frequency start + display_instance->setTextAlignment(TEXT_ALIGN_LEFT); + display_instance->drawString(0, SCALE_TEXT_TOP, (begin == 0) ? String(FREQ_BEGIN) : String(begin)); + + display_instance->setTextAlignment(TEXT_ALIGN_CENTER); + display_instance->drawString(128 / 2, SCALE_TEXT_TOP, (begin == 0) ? String(median_frequency) : String(begin + ((end - begin) / 2))); + + // Frequency end + display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); + display_instance->drawString(128, SCALE_TEXT_TOP, (end == 0) ? String(FREQ_END) : String(end)); + } + + if (led_flag == true && detection_count >= 5) + { + digitalWrite(LED, HIGH); + if (SOUND_ON) + { + tone(BUZZER_PIN, 104, 100); + } + digitalWrite(REB_PIN, HIGH); + led_flag = false; + } + + else if (!redraw) + { + digitalWrite(LED, LOW); + } + // Status text block + if (!drone_detected) + { + // "Scanning" + display_instance->setTextAlignment(TEXT_ALIGN_CENTER); + // clear status line + clearStatus(); + if (scan_progress_count == 0) + { + display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, "Scan. "); + } + else if (scan_progress_count == 1) + { + display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, "Scan.. "); + } + else if (scan_progress_count == 2) + { + display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, "Scan..."); + } + scan_progress_count++; + + if (scan_progress_count == 3) + { + scan_progress_count = 0; + } + } + + if (drone_detected) + { + display_instance->setTextAlignment(TEXT_ALIGN_CENTER); + // clear status line + clearStatus(); + + display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, String(drone_detected_frequency_start) + ">RF<" + String(drone_detected_frequency_end)); + } + + if (ranges_count == 0) + { + display_instance->setTextAlignment(TEXT_ALIGN_LEFT); + display_instance->drawString(0, STATUS_TEXT_TOP, String(FREQ_BEGIN)); + + display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); + display_instance->drawString(128, STATUS_TEXT_TOP, String(FREQ_END)); + } + else if (ranges_count > 0) + { + display_instance->setTextAlignment(TEXT_ALIGN_LEFT); + display_instance->drawString(0, STATUS_TEXT_TOP, String(SCAN_RANGES[scan_iterration] / 1000) + "-" + String(SCAN_RANGES[scan_iterration] % 1000)); + if (scan_iterration + 1 < iterations) + { + display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); + display_instance->drawString(128, STATUS_TEXT_TOP, String(SCAN_RANGES[scan_iterration + 1] / 1000) + "-" + String(SCAN_RANGES[scan_iterration + 1] % 1000)); + } + } + + if (!ui_initialized) + { + // X-axis + display_instance->fillRect(0, HEIGHT, STEPS, X_AXIS_WEIGHT); +// ticks +#ifdef MAJOR_TICKS + drawTicks(MAJOR_TICKS, MAJOR_TICK_LENGTH); +#endif + } + ui_initialized = true; +} From 305fe283a6cc346ac58291d926caabd4c207a012 Mon Sep 17 00:00:00 2001 From: "ionsurdu@github.com" Date: Tue, 6 Aug 2024 21:37:11 +0300 Subject: [PATCH 3/8] Fix formatting + spellcheck --- README.md | 215 ++++++------ include/ui.h | 21 +- src/main.cpp | 925 +++++++++++++++++++++++++-------------------------- src/ui.cpp | 364 ++++++++++---------- 4 files changed, 754 insertions(+), 771 deletions(-) diff --git a/README.md b/README.md index a52c95e..767c204 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # Lora SA(Spectrum Analyzer) -RF Spectrum Analyzer using Lora Radio + +## RF Spectrum Analyzer using Lora Radio LORA hardware @@ -10,46 +11,48 @@ The output is in the form of scan lines; each line has 33 power bins. The first power bin corresponds to -11 dBm, the second to -15 dBm, and so on. The higher number of samples in a bin corresponds to more power received at that level. -``` + +```text N in Bin / dBm -1 -11 -2 -15 -3 -19 -4 -23 -5 -27 -6 -31 -7 -35 -8 -39 -9 -43 -10 -47 -11 -51 -12 -55 -13 -59 -14 -63 -15 -67 -16 -71 -17 -75 -18 -79 -19 -83 -20 -87 -21 -91 -22 -95 -23 -99 -24 -103 -25 -107 -26 -111 -27 -115 -28 -119 -29 -123 -30 -127 -31 -131 -32 -135 -33 -139 +1 -11 +2 -15 +3 -19 +4 -23 +5 -27 +6 -31 +7 -35 +8 -39 +9 -43 +10 -47 +11 -51 +12 -55 +13 -59 +14 -63 +15 -67 +16 -71 +17 -75 +18 -79 +19 -83 +20 -87 +21 -91 +22 -95 +23 -99 +24 -103 +25 -107 +26 -111 +27 -115 +28 -119 +29 -123 +30 -127 +31 -131 +32 -135 +33 -139 ``` Example: -``` -step-13 Frequancy:816.25 + +```text +step-13 Frequency:816.25 Power Bins: 0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0400, 0000,0000,0000,0000,0000,0000,0006,001B,000E,0005,0006,0002,0000 ``` @@ -57,47 +60,58 @@ The spectrum analyzer performs power measurements in the configured bandwidth. The X-axis represents frequency in MHz, and the Y-axis displays actually received power. In the example above, the frequency span goes from 850 MHz to 950 MHz (that is a 100MHz range), and -the visual amplitude goes from -11 dBm to -110(-139) according to the datasheet(High sensitivity: down to -148dBm) dBm. +the visual amplitude goes from -11 dBm to -110(-139) according to the datasheet (High sensitivity: down to -148dBm) dBm. To show the results in a plot, run the Python script RadioLib/extras/SX126x_Spectrum_Scan/SpectrumScan.py -# Features -## Multiple Ranges Scan +## Features + +### Multiple Ranges Scan + Disabled By Default -``` + +```c // Feature to scan diapazones. Other frequency settings will be ignored. int SCAN_DIAPAZONES[] = {}; //int SCAN_DIAPAZONES[] = {850890, 920950}; ``` -To Enable Add/ uncomment an array of the frequencies -``` + +To Enable Add/ uncomment an array of the frequencies + +```c int SCAN_DIAPAZONES[] = {850890, 920950}; ``` -where 850890 stands for 950-890Mhz range -920950 - 920-890Mhz -Other settings will be ignored if **Multiple Ranges Scan** is enabled. -## Waterfall -Waterfall showed only on One Page Scan -to disable - uncomment this line -``` +where 850890 stands for 950-890Mhz range +920950 - 920-890Mhz +Other settings will be ignored if **Multiple Ranges Scan** is enabled. + +### Waterfall + +Waterfall showed only on One Page Scan +to disable - uncomment this line + +```c #define WATERFALL_ENABLED true ``` -Waterfall shows the last **N** = SCREAN_HEIGHT (64) - WATERFALL_START(37) - 8 (part of the STATUS_TEXT_TOP) = **19** signal detection that excited set signal level -## RSSI Method of scan -By default, we are using the spectralScan method of the RadioLib Library: https://jgromes.github.io/RadioLib/class_s_x126x.html#a8a3ad4e12df862ab18b326d9dba26d66 -This method works only with Sx1262 modules. -We implemented a scan using the **getRSSI** method, which has more flexibility and supports sx1276 and other modules. -Using this method, we also receive the signal's **dB** values, not just the O-33 number. -To enable this method, set the value of the **RSSI_METHOD** to true. +Waterfall shows the last **N** = SCREEN_HEIGHT (64) - WATERFALL_START(37) - 8 (part of the STATUS_TEXT_TOP) = **19** signal detection that excited set signal level + +### RSSI Method of scan + +By default, we are using the spectralScan method of the RadioLib Library: +This method works only with Sx1262 modules. +We implemented a scan using the **getRSSI** method, which has more flexibility and supports sx1276 and other modules. +Using this method, we also receive the signal's **dB** values, not just the O-33 number. +To enable this method, set the value of the **RSSI_METHOD** to true. + +### Multi Screen Scan -## Multi Screen Scan Single screen scan for now has **RANGE / 128** resolution. -Multi-page scan can be adjusted to how many MHz per page you wanna scan +Multi-page scan can be adjusted to how many MHz per page you wanna scan -``` +```c // frequency range in MHz to scan #define FREQ_BEGIN 850 // TODO: if % RANGE_PER_PAGE != 0 @@ -108,72 +122,81 @@ int SCAN_DIAPAZONES[] = {}; // int SCAN_DIAPAZONES[] = {850890, 920950}; // MHZ per page -//To put everething into one page set RANGE_PER_PAGE = FREQ_END - 800 +//To put everything into one page set RANGE_PER_PAGE = FREQ_END - 800 unsigned int RANGE_PER_PAGE = FREQ_END - FREQ_BEGIN; // FREQ_END - FREQ_BEGIN //To Enable Multi Screen scan // unsigned int RANGE_PER_PAGE = 50; // Default Range on Menu Button Switch #define DEFAULT_RANGE_PER_PAGE = 50; - ``` + To enable Multi-page by default set **RANGE_PER_PAGE** less than **FREQ_END - FREQ_BEGIN**; -Switch to multi-page during regular One Screen application run. Restart the ESP32 on screen after the logo press the P button. +Switch to multi-page during regular One Screen application run. Restart the ESP32 on screen after the logo press the P button. -## Mute Autio Notifications -Restart ESP32, and on the logo display, press the P button. +### Mute Audio Notifications -## Pause Execution -Press P for more than 2 seconds. Execution will pause, and the scan's current Mhz position will be shown on the display. -If less, ESP32 will turn off. Fast pressing(less than 0.5 second) P button changes the notification level +Restart ESP32, and on the logo display, press the P button. + +### Pause Execution + +Press P for more than 2 seconds. Execution will pause, and the scan's current Mhz position will be shown on the display. +If less, ESP32 will turn off. Fast pressing(less than 0.5 second) P button changes the notification level + +## VSCode Platform.IO development env installation -# VSCode Platform.IO development env installation 1. Install VSCode -2. install Platfor.IO extension +2. install Platform.IO extension ![image](https://github.com/user-attachments/assets/00547068-6153-4c78-9f12-54981b013b06) 3. Connect ESP32 to USB. Install USB drivers for Windows 4. Clone this Git Repo or download zip of the sources ![image](https://github.com/user-attachments/assets/971b6592-3b71-414c-971c-2ecd20f0f0b7) - ``` - git clone https://github.com/Genaker/LoraSA.git - ``` -NOTE: in you case name will be Just LoraSA. I have LoraSA2 because I already have LoraSA folder -6. Open the Project with the VS code Platform.IO + + ```bash + git clone https://github.com/Genaker/LoraSA.git + ``` + + NOTE: in you case name will be Just LoraSA. I have LoraSA2 because I already have LoraSA folder + +5. Open the Project with the VS code Platform.IO ![image](https://github.com/user-attachments/assets/5066836b-32ac-4a24-ac03-b5f739a6a658) ![image](https://github.com/user-attachments/assets/da14488d-7a4a-410e-b754-59598578016d) -7. Select Proper Environment +6. Select Proper Environment ![image](https://github.com/user-attachments/assets/a9c6557b-a387-4457-b59b-b3d7242d2826) -8. Select ESP32 USB Device to program +7. Select ESP32 USB Device to program ![image](https://github.com/user-attachments/assets/af76c4b1-7122-45e1-b26b-08b59e03ca3b) Note: It is theoretically possible to program via WiFi and BTH. -9. Programm your ESP32 +8. Program your ESP32 ![image](https://github.com/user-attachments/assets/9e67afd8-0522-4a96-82dc-8e1cdb32add5) -10. Wait until you are done with the compilation and upload. - Usually takes 1 minute. The first run is slower. It needs to compile all libraries. +9. Wait until you are done with the compilation and upload. + Usually takes 1 minute. The first run is slower. It needs to compile all libraries. ![image](https://github.com/user-attachments/assets/6796eb5d-6e3f-45bc-b88c-251499f1ad47) You will have the UCOG SA logo and spectrum analyzing scanning screen when done. ![image](https://github.com/user-attachments/assets/f86ab32a-cab1-461e-ade9-7021136a0af7) - -# Hardware + +## Hardware + Heltec ESP32 Lora V3: -https://www.amazon.com/Heltec-Development-863-870MHz-ESP32-S3FN8-902-928MHz/dp/B0D1H1FN9Y/ -https://heltec.org/project/wifi-lora-32-v3/ -https://www.aliexpress.us/item/3256807037422978.html + + + -Battery with Wire JT connector : -https://www.amazon.com/EEMB-2000mAh-Battery-Rechargeable-Connector/dp/B08214DJLJ +Battery with Wire JT connector : + + +## 3D printed case -# 3D printed case ![image](https://github.com/user-attachments/assets/52ae4e90-5f25-4d72-888e-7586bef9df69) -https://www.printables.com/model/118750-heltec-lora-32-case-for-meshtastic -https://www.thingiverse.com/thing:3125854 -https://thangs.com/designer/Snake0017/3d-model/Heltec%20LoRa%2032%20Desktop%20%26%20Vehicle%20Enclosure-40844 -or buy : -https://www.amazon.com/DIYmalls-ESP32-OLED-WiFi-Type-C/dp/B0BR3MQ9BG + + + +or buy : + -https://www.thingiverse.com/thing:6522462 + + +## Heltec ESP32 Lora v3 Pin Map -# Heltec ESP32 Lora v3 Pin Map ![image](https://github.com/user-attachments/assets/a1e00b51-5566-4ff5-98fe-67eaeb5bc81f) -We are using pin 41 as a Buzzer trigger. Connect buzzer + leg with pin 41 and - leg with the ground (GND). You can change the buzzer pin in the code. +We are using pin 41 as a Buzzer trigger. Connect buzzer + leg with pin 41 and - leg with the ground (GND). You can change the buzzer pin in the code. diff --git a/include/ui.h b/include/ui.h index 776bc35..5fb9ca7 100644 --- a/include/ui.h +++ b/include/ui.h @@ -8,38 +8,35 @@ // #include -// (optional) major and minor tickmarks at x MHz +// (optional) major and minor tick-marks at x MHz #define MAJOR_TICKS 10 #define MINOR_TICKS 5 #define ONE_MILLISEC 1 // Prints debug information and the scan measurement bins from the SX1262 in hex -//#define PRINT_DEBUG +// #define PRINT_DEBUG // Change spectrum plot values at once or by line #define ANIMATED_RELOAD true #define MAJOR_TICK_LENGTH 2 #define MINOR_TICK_LENGTH 1 -// WEIGHT of the x-asix line +// WEIGHT of the x-axis line #define X_AXIS_WEIGHT 1 #define STATUS_TEXT_TOP (64 - 10) - // The number of the spectrum screen lines = width of screen // Resolution of the scan is limited by 128-pixel screen #define STEPS 128 +#define SCREEN_HEIGHT 64 // ???? not used -#define SCREAN_HEIGHT 64 - -// publish functions -extern void UI_Init(SSD1306Wire*); -extern void UI_displayDecorate(int , int , bool ); -extern void UI_setLedFlag( bool); +// publish functions +extern void UI_Init(SSD1306Wire *); +extern void UI_displayDecorate(int, int, bool); +extern void UI_setLedFlag(bool); extern void UI_clearPlotter(void); -extern void UI_drawCurrsor(int16_t); +extern void UI_drawCursor(int16_t); #endif // __UI_H__ - diff --git a/src/main.cpp b/src/main.cpp index 27ac455..81f4462 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,7 +22,7 @@ */ // Turns the 'PRG' button into the power button, long press is off -// TODO add it to compiller options using -DHELTEC_POWER_BUTTON +// TODO add it to compiler options using -DHELTEC_POWER_BUTTON #define HELTEC_POWER_BUTTON // must be before "#include " #include @@ -30,25 +30,21 @@ // This file contains a binary patch for the SX1262 #include "modules/SX126x/patches/SX126x_patch_scan.h" - -// project components +// project components #include "global_config.h" -#include "images.h" #include "ui.h" - // ----------------------------------------------------------------- // CONFIGURATION OPTIONS // ----------------------------------------------------------------- - - -typedef enum { - METHOD_RSSI = 0u, - METHOD_SPECTRAL +typedef enum +{ + METHOD_RSSI = 0u, + METHOD_SPECTRAL } TSCAN_METOD_ENUM; -#define SCAN_METHOD METHOD_SPECTRAL +#define SCAN_METHOD METHOD_SPECTRAL // Feature to scan diapazones. Other frequency settings will be ignored. int SCAN_RANGES[] = {}; @@ -72,7 +68,7 @@ unsigned int RANGE_PER_PAGE = FREQ_END - FREQ_BEGIN; // FREQ_END - FREQ_BEGIN // if more than 100 it can freez #define SAMPLES 100 //(scan time = 1294) // number of samples for RSSI method -#define SAMPLES_RSSI 21// +#define SAMPLES_RSSI 21 // #define RANGE (int)(FREQ_END - FREQ_BEGIN) @@ -89,8 +85,6 @@ unsigned int iterations = RANGE / RANGE_PER_PAGE; // unsigned int range_frequency = FREQ_END - FREQ_BEGIN; unsigned int median_frequency = FREQ_BEGIN + FREQ_END - FREQ_BEGIN / 2; - - // #define OSD_ENABLED true // unused // #define DISABLE_PLOT_CHART false // unused @@ -99,7 +93,7 @@ uint16_t result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; uint16_t filtered_result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; // Waterfall array -bool waterfall[10][STEPS][10]; // 10 - ??? +bool waterfall[10][STEPS][10]; // 10 - ??? // global variable // Used as a Led Light and Buzzer/count trigger @@ -114,13 +108,12 @@ unsigned int detection_count = 0; bool single_page_scan = false; bool SOUND_ON = true; - unsigned int scan_time = 0; unsigned int scan_start_time = 0; uint64_t start = 0; -unsigned int x, y, scan_iterration, w = 0; +unsigned int x, y, scan_iteration, w = 0; unsigned int ranges_count = 0; float freq = 0; @@ -130,510 +123,482 @@ int result_index = 0; unsigned int button_pressed_counter = 0; - -void setup() +void setup(void) { - pinMode(LED, OUTPUT); - pinMode(BUZZER_PIN, OUTPUT); - pinMode(REB_PIN, OUTPUT); - heltec_setup(); + float vbat; + float resolution; - UI_Init(&display); - - for (int i = 0; i < 200; i++) - { - button.update(); - delay(10); - if (button.pressed()) + pinMode(LED, OUTPUT); + pinMode(BUZZER_PIN, OUTPUT); + pinMode(REB_PIN, OUTPUT); + heltec_setup(); + UI_Init(&display); + for (int i = 0; i < 200; i++) { - SOUND_ON = false; - tone(BUZZER_PIN, 205, 100); - delay(50); - tone(BUZZER_PIN, 205, 100); - break; + button.update(); + delay(10); + if (button.pressed()) + { + SOUND_ON = false; + tone(BUZZER_PIN, 205, 100); + delay(50); + tone(BUZZER_PIN, 205, 100); + break; + } } - } - // initialize SX1262 FSK modem at the initial frequency - both.println("Init radio"); - RADIOLIB_OR_HALT(radio.beginFSK(FREQ_BEGIN)); - // upload a patch to the SX1262 to enable spectral scan - // NOTE: this patch is uploaded into volatile memory, - // and must be re-uploaded on every power up - both.println("Upload SX1262 patch"); - // Upload binary patch into the SX126x device RAM. Patch is needed to e.g., enable spectral scan and must be uploaded again on every power cycle. - RADIOLIB_OR_HALT(radio.uploadPatch(sx126x_patch_scan, sizeof(sx126x_patch_scan))); - // configure scan bandwidth and disable the data shaping - both.println("Setting up radio"); - RADIOLIB_OR_HALT(radio.setRxBandwidth(BANDWIDTH)); - // and disable the data shaping - RADIOLIB_OR_HALT(radio.setDataShaping(RADIOLIB_SHAPING_NONE)); - both.println("Starting scanning..."); - float vbat = heltec_vbat(); - both.printf("V battery: %.2fV (%d%%)\n", vbat, heltec_battery_percent(vbat)); - - delay(300); - display.clear(); + // initialize SX1262 FSK modem at the initial frequency + both.println("Init radio"); + RADIOLIB_OR_HALT(radio.beginFSK(FREQ_BEGIN)); - float resolution = RANGE / STEPS; - if (RANGE_PER_PAGE == range) - { - single_page_scan = true; - } - else - { - single_page_scan = false; - } + // upload a patch to the SX1262 to enable spectral scan + // NOTE: this patch is uploaded into volatile memory, + // and must be re-uploaded on every power up + both.println("Upload SX1262 patch"); - // Adjust range if it is not even to RANGE_PER_PAGE - if (!single_page_scan && range % RANGE_PER_PAGE != 0) - { - // range = range + range % RANGE_PER_PAGE; - } + // Upload binary patch into the SX126x device RAM. Patch is needed to e.g., + // enable spectral scan and must be uploaded again on every power cycle. + RADIOLIB_OR_HALT(radio.uploadPatch(sx126x_patch_scan, sizeof(sx126x_patch_scan))); + // configure scan bandwidth and disable the data shaping - if (single_page_scan) - { - both.println("Single Page Screen MODE"); - both.println("Multi Screen View Press P - button"); - both.println("Single Screen Resolution: " + String(resolution) + "Mhz/tick"); - both.println("Curent Resolution: " + String((float)RANGE_PER_PAGE / STEPS) + "Mhz/tick"); + both.println("Setting up radio"); + RADIOLIB_OR_HALT(radio.setRxBandwidth(BANDWIDTH)); - for (int i = 0; i < 500; i++) + // and disable the data shaping + RADIOLIB_OR_HALT(radio.setDataShaping(RADIOLIB_SHAPING_NONE)); + both.println("Starting scanning..."); + vbat = heltec_vbat(); + both.printf("V battery: %.2fV (%d%%)\n", vbat, + heltec_battery_percent(vbat)); + delay(300); + display.clear(); + resolution = RANGE / STEPS; + if (RANGE_PER_PAGE == range) { - button.update(); - delay(10); - both.print("."); - if (button.pressed()) - { - RANGE_PER_PAGE = DEFAULT_RANGE_PER_PAGE; - single_page_scan = false; - tone(BUZZER_PIN, 205, 100); - delay(50); - tone(BUZZER_PIN, 205, 100); - break; - } - } - } - else - { - both.println("Multi Page Screen MODE"); - both.println("Single screen View Press P - button"); - both.println("Single screen Resolution: " + String(resolution) + "Mhz/tick"); - both.println("Curent Resolution: " + String((float)RANGE_PER_PAGE / STEPS) + "Mhz/tick"); - - for (int i = 0; i < 500; i++) - { - button.update(); - delay(10); - both.print("."); - if (button.pressed()) - { - RANGE_PER_PAGE = range; single_page_scan = true; - tone(BUZZER_PIN, 205, 100); - - break; - } - } - } - display.clear(); - - // waterfall start line y-axis - w = WATERFALL_START; -} - -void loop() -{ - UI_displayDecorate(0,0,false); // some default values - drone_detected = false; - detection_count = 0; - drone_detected_frequency_start = 0; -#ifdef PRINT_PROFILE_TIME - start = millis(); -#endif - - if (!ANIMATED_RELOAD || !single_page_scan) - { - // clear the scan plot rectangle - UI_clearPlotter(); - } - - // do the scan - range = FREQ_END - FREQ_BEGIN; - - if (RANGE_PER_PAGE > range) - { - RANGE_PER_PAGE = range; - } - - fr_begin = FREQ_BEGIN; - fr_end = fr_begin; - // 50 is a single-screen range - // TODO: Make 50 a variable with the option to show the full range - iterations = range / RANGE_PER_PAGE; - - single_step = RANGE_PER_PAGE / 128; - if (range % RANGE_PER_PAGE != 0) - { - // add more scan - //++; - } - - if (RANGE_PER_PAGE == range) - { - single_page_scan = true; - } - else - { - single_page_scan = false; - } - - ranges_count = 0; - - for (int range : SCAN_RANGES) - { - ranges_count++; - } - - if (ranges_count > 0) - { - iterations = ranges_count; - single_page_scan = false; - } - - // Iterating by small ranges by 50 Mhz each pixel is 0.4 Mhz - for (scan_iterration = 0; scan_iterration < iterations; scan_iterration++) - { - range = RANGE_PER_PAGE; - - if (ranges_count == 0) - { - fr_begin = (scan_iterration == 0) ? fr_begin : fr_begin += range; - fr_end = fr_begin + RANGE_PER_PAGE; } else { - fr_begin = SCAN_RANGES[scan_iterration] / 1000; - fr_end = SCAN_RANGES[scan_iterration] % 1000; - range = fr_end - fr_begin; + single_page_scan = false; } + // Adjust range if it is not even to RANGE_PER_PAGE + if (!single_page_scan && range % RANGE_PER_PAGE != 0) + { + // range = range + range % RANGE_PER_PAGE; + } + if (single_page_scan) + { + both.println("Single Page Screen MODE"); + both.println("Multi Screen View Press P - button"); + both.println("Single Screen Resolution: " + String(resolution) + "Mhz/tick"); + both.println("Curent Resolution: " + String((float)RANGE_PER_PAGE / STEPS) + "Mhz/tick"); + for (int i = 0; i < 500; i++) + { + button.update(); + delay(10); + both.print("."); + if (button.pressed()) + { + RANGE_PER_PAGE = DEFAULT_RANGE_PER_PAGE; + single_page_scan = false; + tone(BUZZER_PIN, 205, 100); + delay(50); + tone(BUZZER_PIN, 205, 100); + break; + } + } + } + else + { + both.println("Multi Page Screen MODE"); + both.println("Single screen View Press P - button"); + both.println("Single screen Resolution: " + String(resolution) + "Mhz/tick"); + both.println("Curent Resolution: " + String((float)RANGE_PER_PAGE / STEPS) + "Mhz/tick"); + for (int i = 0; i < 500; i++) + { + button.update(); + delay(10); + both.print("."); + if (button.pressed()) + { + RANGE_PER_PAGE = range; + single_page_scan = true; + tone(BUZZER_PIN, 205, 100); + break; + } + } + } + display.clear(); + // waterfall start line y-axis + w = WATERFALL_START; +} +void loop(void) +{ + UI_displayDecorate(0, 0, false); // some default values + drone_detected = false; + detection_count = 0; + drone_detected_frequency_start = 0; +#ifdef PRINT_PROFILE_TIME + start = millis(); +#endif if (!ANIMATED_RELOAD || !single_page_scan) { - // clear the scan plot rectangle - UI_clearPlotter(); + // clear the scan plot rectangle + UI_clearPlotter(); } - - if (single_page_scan == false) + // do the scan + range = FREQ_END - FREQ_BEGIN; + if (RANGE_PER_PAGE > range) { - UI_displayDecorate(fr_begin, fr_end, true); + RANGE_PER_PAGE = range; } - - drone_detected_frequency_start = 0; - - display.setTextAlignment(TEXT_ALIGN_RIGHT); - // horizontal x axis loop - for (x = 0; x < STEPS; x++) + fr_begin = FREQ_BEGIN; + fr_end = fr_begin; + // 50 is a single-screen range + // TODO: Make 50 a variable with the option to show the full range + iterations = range / RANGE_PER_PAGE; + single_step = RANGE_PER_PAGE / 128; + if (range % RANGE_PER_PAGE != 0) { - scan_start_time = millis(); - -#if ANIMATED_RELOAD - UI_drawCurrsor(x); -#endif - - waterfall[scan_iterration][x][w] = false; - freq = fr_begin + (range * ((float)x / STEPS)); - radio.setFrequency(freq); - // TODO: RSSI METHOD - // Gets RSSI (Recorded Signal Strength Indicator) - // Restart continuous receive mode on the new frequency - // state = radio.startReceive(); - // if (state == RADIOLIB_ERR_NONE) { - // Serial.println(F("Started continuous receive mode")); - //} else { - // Serial.print(F("Failed to start receive mode, error code: ")); - // Serial.println(state); - //} - // rssi = radio.getRSSI(false); - // Serial.println(String(rssi) + "db"); - // delay(25); - // This code will iterate over the specified frequencies, changing the frequency every - // second and printing the RSSI value for each frequency to the serial monitor. Adjust the frequencies array - // to include the specific frequencies you're interested in monitoring. - // A short delay after changing the frequency - // ensures the module has time to stabilize and get an accurate RSSI reading. -#ifdef PRINT_DEBUG - Serial.println(); - Serial.print("step-"); - Serial.print(x); - Serial.print(" Frequency:"); - Serial.print(freq); - Serial.println(); -#endif - - - // SpectralScan Method -#if SCAN_METHOD == METHOD_SPECTRAL - { - // start spectral scan third parameter is a sleep interval - radio.spectralScanStart(SAMPLES, 1); - // wait for spectral scan to finish - while (radio.spectralScanGetStatus() != RADIOLIB_ERR_NONE) + // add more scan + //++; + } + if (RANGE_PER_PAGE == range) + { + single_page_scan = true; + } + else + { + single_page_scan = false; + } + ranges_count = 0; + for (int range : SCAN_RANGES) + { + ranges_count++; + } + if (ranges_count > 0) + { + iterations = ranges_count; + single_page_scan = false; + } + // Iterating by small ranges by 50 Mhz each pixel is 0.4 Mhz + for (scan_iteration = 0; scan_iteration < iterations; scan_iteration++) + { + range = RANGE_PER_PAGE; + if (ranges_count == 0) { - Serial.print("radio.spectralScanGetStatus ERROR: "); - Serial.println(radio.spectralScanGetStatus()); - heltec_delay(ONE_MILLISEC); - } - // read the results Array to which the results will be saved - radio.spectralScanGetResult(result); - } -#endif - -#if SCAN_METHOD == METHOD_RSSI - // Spectrum analyzer using getRSSI - { - state = radio.startReceive(0); - if (state == RADIOLIB_ERR_NONE) - { -#ifdef PRINT_DEBUG - Serial.println(F("Started continuous receive mode")); -#endif + fr_begin = (scan_iteration == 0) ? fr_begin : fr_begin += range; + fr_end = fr_begin + RANGE_PER_PAGE; } else { - Serial.print(F("Failed to start receive mode, error code: ")); - Serial.println(state); - } - - for (int r = 1; r < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; r++) - { - result[r] = 0; - } - result_index = 0; - // N of samples - for (int r = 1; r < SAMPLES_RSSI; r++) - { - rssi = radio.getRSSI(false); - // delay(ONE_MILLISEC); - // ToDO: check if 4 is correct value for 33 power bins - result_index = (abs(rssi) / 4); - - // Debug Information -#ifdef PRINT_DEBUG - Serial.print("Frequency: "); - Serial.println(freq); - Serial.println(rssi); - Serial.println(result_index); -#endif - // Saving max value only rss is negative so smaller is bigger - if (result[result_index] > rssi) - { - result[result_index] = rssi; - } - } - } -#endif - - - detected = false; -#ifdef FILTER_SPECTRUM_RESULTS - // Filter Elements without neighbors - for (y = 1; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) - { - // if RSSI method actual value is -xxx dB - if (result[y] && (result[y + 1] != 0 || result[y - 1] != 0)) - { - // Filling the empty pixel between signals int the level < 27 (noise level) - /* if (y < 27 && result[y + 1] == 0 && result[y + 2] > 0) - { - result[y + 1] = 1; - filtered_result[y + 1] = 1; - }*/ - filtered_result[y] = 1; - } - else - { - filtered_result[y] = 0; - } - } -#endif - for (y = 0; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) - { -#ifdef PRINT_DEBUG - Serial.printf("%04X,", result[y]); -#endif - if (result[y] || y == drone_detection_level) - { - // check if we should alarm about a drone presence - if (filtered_result[y] == 1 && y <= drone_detection_level) - { - drone_detected = true; -#ifdef WATERFALL_ENABLED - if (single_page_scan) - { - // Drone detection true for waterfall - waterfall[scan_iterration][x][w] = true; - display.setColor(WHITE); - display.setPixel(x, w); - } -#endif - if (drone_detected_frequency_start == 0) - { - drone_detected_frequency_start = freq; - } - drone_detected_frequency_end = freq; - - UI_setLedFlag(true); - - // If level is set to sensitive, start beeping every 10th frequency and shorter - if (drone_detection_level <= 25) - { - if (detection_count == 1 && SOUND_ON) - tone(BUZZER_PIN, 205, 10); - if (detection_count % 5 == 0 && SOUND_ON) - tone(BUZZER_PIN, 205, 10); - } - else - { - if (detection_count % 20 == 0 && SOUND_ON) - tone(BUZZER_PIN, 205, 10); - } - display.setPixel(x, 1); - display.setPixel(x, 2); - display.setPixel(x, 3); - display.setPixel(x, 4); + fr_begin = SCAN_RANGES[scan_iteration] / 1000; + fr_end = SCAN_RANGES[scan_iteration] % 1000; + range = fr_end - fr_begin; } -#ifdef WATERFALL_ENABLED - if (filtered_result[y] == 1 && y > drone_detection_level && single_page_scan && waterfall[scan_iterration][x][w] != true) + if (!ANIMATED_RELOAD || !single_page_scan) { - // If drone not found set dark pixel on the waterfall - // TODO: make something like scrolling up if possible - waterfall[scan_iterration][x][w] = false; - display.setColor(BLACK); - display.setPixel(x, w); - display.setColor(WHITE); + // clear the scan plot rectangle + UI_clearPlotter(); } + + if (single_page_scan == false) + { + UI_displayDecorate(fr_begin, fr_end, true); + } + + drone_detected_frequency_start = 0; + display.setTextAlignment(TEXT_ALIGN_RIGHT); + + // horizontal x axis loop + for (x = 0; x < STEPS; x++) + { + scan_start_time = millis(); + +#if ANIMATED_RELOAD + UI_drawCursor(x); #endif - if (filtered_result[y] == 1) - { - // Set signal level pixel - display.setPixel(x, y); - detected = true; - } - - // Draw detection Level line evere 2 pixel - if (y == drone_detection_level && x % 2 == 0) - { - display.setPixel(x, y); - } - } - + waterfall[scan_iteration][x][w] = false; + freq = fr_begin + (range * ((float)x / STEPS)); + radio.setFrequency(freq); + // TODO: RSSI METHOD + // Gets RSSI (Recorded Signal Strength Indicator) + // Restart continuous receive mode on the new frequency + // state = radio.startReceive(); + // if (state == RADIOLIB_ERR_NONE) { + // Serial.println(F("Started continuous receive mode")); + //} else { + // Serial.print(F("Failed to start receive mode, error code: ")); + // Serial.println(state); + //} + // rssi = radio.getRSSI(false); + // Serial.println(String(rssi) + "db"); + // delay(25); + // This code will iterate over the specified frequencies, + // changing the frequency every + // second and printing the RSSI value for each frequency to the serial monitor. Adjust the frequencies array + // to include the specific frequencies you're interested in monitoring. + // A short delay after changing the frequency + // ensures the module has time to stabilize and get an accurate RSSI reading. +#ifdef PRINT_DEBUG + Serial.println(); + Serial.print("step-"); + Serial.print(x); + Serial.print(" Frequency:"); + Serial.print(freq); + Serial.println(); +#endif + // SpectralScan Method +#if SCAN_METHOD == METHOD_SPECTRAL + { + // start spectral scan third parameter is a sleep interval + radio.spectralScanStart(SAMPLES, 1); + // wait for spectral scan to finish + while (radio.spectralScanGetStatus() != RADIOLIB_ERR_NONE) + { + Serial.print("radio.spectralScanGetStatus ERROR: "); + Serial.println(radio.spectralScanGetStatus()); + heltec_delay(ONE_MILLISEC); + } + // read the results Array to which the results will be saved + radio.spectralScanGetResult(result); + } +#endif +#if SCAN_METHOD == METHOD_RSSI + // Spectrum analyzer using getRSSI + { + state = radio.startReceive(0); + if (state == RADIOLIB_ERR_NONE) + { +#ifdef PRINT_DEBUG + Serial.println(F("Started continuous receive mode")); +#endif + } + else + { + Serial.print(F("Failed to start receive mode, error code: ")); + Serial.println(state); + } + for (int r = 1; r < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; r++) + { + result[r] = 0; + } + result_index = 0; + // N of samples + for (int r = 1; r < SAMPLES_RSSI; r++) + { + rssi = radio.getRSSI(false); + // delay(ONE_MILLISEC); + // ToDO: check if 4 is correct value for 33 power bins + result_index = (abs(rssi) / 4); + // Debug Information +#ifdef PRINT_DEBUG + Serial.print("Frequency: "); + Serial.println(freq); + Serial.println(rssi); + Serial.println(result_index); +#endif + // Saving max value only rss is negative so smaller is bigger + if (result[result_index] > rssi) + { + result[result_index] = rssi; + } + } + } +#endif + detected = false; +#ifdef FILTER_SPECTRUM_RESULTS + // Filter Elements without neighbors + for (y = 1; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) + { + // if RSSI method actual value is -xxx dB + if (result[y] && (result[y + 1] != 0 || result[y - 1] != 0)) + { + // Filling the empty pixel between signals int the level < 27 (noise level) + /* if (y < 27 && result[y + 1] == 0 && result[y + 2] > 0) + { + result[y + 1] = 1; + filtered_result[y + 1] = 1; + }*/ + filtered_result[y] = 1; + } + else + { + filtered_result[y] = 0; + } + } +#endif + for (y = 0; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) + { +#ifdef PRINT_DEBUG + Serial.printf("%04X,", result[y]); +#endif + if (result[y] || y == drone_detection_level) + { + // check if we should alarm about a drone presence + if (filtered_result[y] == 1 && y <= drone_detection_level) + { + drone_detected = true; +#ifdef WATERFALL_ENABLED + if (single_page_scan) + { + // Drone detection true for waterfall + waterfall[scan_iteration][x][w] = true; + display.setColor(WHITE); + display.setPixel(x, w); + } +#endif + if (drone_detected_frequency_start == 0) + { + drone_detected_frequency_start = freq; + } + drone_detected_frequency_end = freq; + UI_setLedFlag(true); + // If level is set to sensitive, + // start beeping every 10th frequency and shorter + if (drone_detection_level <= 25) + { + if (detection_count == 1 && SOUND_ON) + tone(BUZZER_PIN, 205, 10); + if (detection_count % 5 == 0 && SOUND_ON) + tone(BUZZER_PIN, 205, 10); + } + else + { + if (detection_count % 20 == 0 && SOUND_ON) + tone(BUZZER_PIN, 205, 10); + } + display.setPixel(x, 1); + display.setPixel(x, 2); + display.setPixel(x, 3); + display.setPixel(x, 4); + } +#ifdef WATERFALL_ENABLED + if (filtered_result[y] == 1 && y > drone_detection_level && single_page_scan && waterfall[scan_iteration][x][w] != true) + { + // If drone not found set dark pixel on the waterfall + // TODO: make something like scrolling up if possible + waterfall[scan_iteration][x][w] = false; + display.setColor(BLACK); + display.setPixel(x, w); + display.setColor(WHITE); + } +#endif + if (filtered_result[y] == 1) + { + // Set signal level pixel + display.setPixel(x, y); + detected = true; + } + // Draw detection Level line evere 2 pixel + if (y == drone_detection_level && x % 2 == 0) + { + display.setPixel(x, y); + } + } #ifdef PRINT_PROFILE_TIME - scan_time = millis() - scan_start_time; - // Huge performance issue if enable - // Serial.printf("Single Scan took %lld ms\n", scan_time); + scan_time = millis() - scan_start_time; + // Huge performance issue if enable + // Serial.printf("Single Scan took %lld ms\n", scan_time); #endif - } - if (detected) - { - detection_count++; - } - detected = false; + } + if (detected) + { + detection_count++; + } + detected = false; #ifdef PRINT_DEBUG - Serial.println("...."); + Serial.println("...."); #endif - if (first_run || ANIMATED_RELOAD) - { - display.display(); - } - - // Detection level button short press - if (button.pressedFor(100)) - { - button.update(); - button_pressed_counter = 0; - // if long press stop - while (button.pressedNow()) - { - delay(10); - // Print Curent frequency - display.setTextAlignment(TEXT_ALIGN_CENTER); - display.drawString(128 / 2, 0, String(freq)); - display.display(); - button_pressed_counter++; - if (button_pressed_counter > 150) - { - digitalWrite(LED, HIGH); - delay(150); - digitalWrite(LED, LOW); + if (first_run || ANIMATED_RELOAD) + { + display.display(); + } + // Detection level button short press + if (button.pressedFor(100)) + { + button.update(); + button_pressed_counter = 0; + // if long press stop + while (button.pressedNow()) + { + delay(10); + // Print Curent frequency + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(128 / 2, 0, String(freq)); + display.display(); + button_pressed_counter++; + if (button_pressed_counter > 150) + { + digitalWrite(LED, HIGH); + delay(150); + digitalWrite(LED, LOW); + } + } + if (button_pressed_counter > 150) + { + // Remove Curent Freqancy Text + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.setColor(BLACK); + display.drawString(128 / 2, 0, String(freq)); + display.setColor(WHITE); + display.display(); + break; + } + if (button_pressed_counter > 50 && button_pressed_counter < 150) + { + // Visually confirm it's off so user releases button + display.displayOff(); + // Deep sleep (has wait for release so we don't wake up immediately) + heltec_deep_sleep(); + break; + } + button.update(); + display.setTextAlignment(TEXT_ALIGN_RIGHT); + // erase old value + display.setColor(BLACK); + display.fillRect(128 - 13, 0, 13, 13); + display.setColor(WHITE); + drone_detection_level++; + // print new value + display.drawString(128, 0, String(drone_detection_level)); + tone(BUZZER_PIN, 104, 150); + if (drone_detection_level > 30) + { + drone_detection_level = 1; + } + } + // wait a little bit before the next scan, + // otherwise the SX1262 hangs + // Add more logic before insead of long delay... + // heltec_delay(1); + // Loop is needed if heltec_delay(1) not used + heltec_loop(); + } + w++; + if (w > STATUS_TEXT_TOP + 1) + { + w = WATERFALL_START; } - } - if (button_pressed_counter > 150) - { - // Remove Curent Freqancy Text - display.setTextAlignment(TEXT_ALIGN_CENTER); - display.setColor(BLACK); - display.drawString(128 / 2, 0, String(freq)); - display.setColor(WHITE); - display.display(); - break; - } - if (button_pressed_counter > 50 && button_pressed_counter < 150) - { - // Visually confirm it's off so user releases button - display.displayOff(); - // Deep sleep (has wait for release so we don't wake up immediately) - heltec_deep_sleep(); - break; - } - button.update(); - - display.setTextAlignment(TEXT_ALIGN_RIGHT); - // erase old value - display.setColor(BLACK); - display.fillRect(128 - 13, 0, 13, 13); - display.setColor(WHITE); - drone_detection_level++; - // print new value - display.drawString(128, 0, String(drone_detection_level)); - - tone(BUZZER_PIN, 104, 150); - - if (drone_detection_level > 30) - { - drone_detection_level = 1; - } - } - // wait a little bit before the next scan, otherwise the SX1262 hangs - // Add more logic before insead of long delay... - // heltec_delay(1); - // Loop is needed if heltec_delay(1) not used - heltec_loop(); - } - w++; - if (w > STATUS_TEXT_TOP + 1) - { - w = WATERFALL_START; - } #ifdef WATERFALL_ENABLED - // Draw waterfall position cursor - if (single_page_scan) - { - display.setColor(BLACK); - display.drawHorizontalLine(0, w, STEPS); - display.setColor(WHITE); - } + // Draw waterfall position cursor + if (single_page_scan) + { + display.setColor(BLACK); + display.drawHorizontalLine(0, w, STEPS); + display.setColor(WHITE); + } #endif - display.display(); -} - + display.display(); + } #ifdef PRINT_DEBUG -Serial.println("----"); + Serial.println("----"); #endif // display.display(); #ifdef PRINT_PROFILE_TIME -scan_time = millis() - start; -Serial.printf("Scan took %lld ms\n", scan_time); + scan_time = millis() - start; + Serial.printf("Scan took %lld ms\n", scan_time); #endif } diff --git a/src/ui.cpp b/src/ui.cpp index a28ede3..7b1fd81 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -1,12 +1,10 @@ - - #include "ui.h" #include "images.h" #include "global_config.h" #include "RadioLib.h" // ------------------------------------------------- -// LCOAL DEFINES +// LOCAL DEFINES // Height of the plotter area // ------------------------------------------------- @@ -14,16 +12,14 @@ // #define SCALE_TEXT_TOP (HEIGHT + X_AXIS_WEIGHT + MAJOR_TICK_LENGTH) - static unsigned int start_scan_text = (128 / 2) - 3; // initialized flag static bool ui_initialized = false; static bool led_flag = false; static unsigned short int scan_progress_count = 0; - -static SSD1306Wire *display_instance; //(0x3c, SDA_OLED, SCL_OLED, DISPLAY_GEOMETRY); -// PrintSplitter both(Serial, display); +//(0x3c, SDA_OLED, SCL_OLED, DISPLAY_GEOMETRY); +static SSD1306Wire *display_instance; // temporary dirty import ... to be solved durring upcoming refactoring extern unsigned int drone_detection_level; @@ -33,45 +29,43 @@ extern unsigned int detection_count; extern bool SOUND_ON; extern bool drone_detected; extern unsigned int drone_detected_frequency_start; -extern unsigned int drone_detected_frequency_end ; +extern unsigned int drone_detected_frequency_end; extern unsigned int ranges_count; extern int SCAN_RANGES[]; extern unsigned int ranges_count; -extern unsigned int iterations ; -extern unsigned int scan_iterration; +extern unsigned int iterations; +extern unsigned int scan_iteration; void UI_Init(SSD1306Wire *display_ptr) { - // init pointer to display instance. - display_instance = display_ptr; - - // check for null ??? - display_instance->clear(); - // draw the UCOG welcome logo - display_instance->drawXbm(0, 2, 128, 64, epd_bitmap_ucog); - display_instance->display(); + // init pointer to display instance. + display_instance = display_ptr; + // check for null ??? + display_instance->clear(); + // draw the UCOG welcome logo + display_instance->drawXbm(0, 2, 128, 64, epd_bitmap_ucog); + display_instance->display(); } -void UI_setLedFlag( bool new_status) +void UI_setLedFlag(bool new_status) { - led_flag = new_status; + led_flag = new_status; } - void clearStatus(void) { - // clear status line - display_instance->setColor(BLACK); - display_instance->fillRect(0, STATUS_TEXT_TOP + 2, 128, 13); - display_instance->setColor(WHITE); + // clear status line + display_instance->setColor(BLACK); + display_instance->fillRect(0, STATUS_TEXT_TOP + 2, 128, 13); + display_instance->setColor(WHITE); } void UI_clearPlotter(void) { - // clear the scan plot rectangle - display_instance->setColor(BLACK); - display_instance->fillRect(0, 0, STEPS, HEIGHT); - display_instance->setColor(WHITE); + // clear the scan plot rectangle + display_instance->setColor(BLACK); + display_instance->fillRect(0, 0, STEPS, HEIGHT); + display_instance->setColor(WHITE); } /** @@ -82,189 +76,193 @@ void UI_clearPlotter(void) */ void drawTicks(float every, int length) { - int first_tick = 0; - //+ (every - (fr_begin - (int)(fr_begin / every) * every)); - /*if (first_tick < fr_begin) - { - first_tick += every; - }*/ - bool correction = false; - int pixels_per_step = STEPS / (RANGE_PER_PAGE / every); - if (STEPS / RANGE_PER_PAGE != 0) - { - correction = true; - } + int first_tick; + bool correction; + int pixels_per_step; + int correction_number; + int tick; + int tick_minor; + int median; - int correction_number = STEPS - (int)(pixels_per_step * (RANGE_PER_PAGE / every)); - int tick = 0; - int tick_minor = 0; - int median = (RANGE_PER_PAGE / every) / 2; - // TODO: (RANGE_PER_PAGE / every) * 2 has twice extra steps we need to figureout correct logic or minor ticks is not showing to the end - for (int t = 0; t <= (RANGE_PER_PAGE / every) * 2; t++) - { - // fix if pixels per step is not int and we have shift - if (correction && t % 2 != 0 && correction_number > 1) + first_tick = 0; + //+ (every - (fr_begin - (int)(fr_begin / every) * every)); + /*if (first_tick < fr_begin) { - // pixels_per_step++; - correction_number--; - } - - tick += pixels_per_step; - tick_minor = tick / 2; - - if (tick <= 128 - 3) + first_tick += every; + }*/ + correction = false; + pixels_per_step = STEPS / (RANGE_PER_PAGE / every); + if (STEPS / RANGE_PER_PAGE != 0) { - display_instance->drawLine(tick, HEIGHT + X_AXIS_WEIGHT, tick, HEIGHT + X_AXIS_WEIGHT + length); - // Central tick - if (tick > (128 / 2) - 3 && tick < (128 / 2) + 3) - { - display_instance->drawLine(tick + 1, HEIGHT + X_AXIS_WEIGHT, tick + 1, HEIGHT + X_AXIS_WEIGHT + length); - } + correction = true; } - + correction_number = STEPS - (int)(pixels_per_step * (RANGE_PER_PAGE / every)); + tick = 0; + tick_minor = 0; + median = (RANGE_PER_PAGE / every) / 2; + // TODO: (RANGE_PER_PAGE / every) + // * 2 has twice extra steps we need to figureout correct logic or minor ticks is not showing to the end + for (int t = 0; t <= (RANGE_PER_PAGE / every) * 2; t++) + { + // fix if pixels per step is not int and we have shift + if (correction && t % 2 != 0 && correction_number > 1) + { + // pixels_per_step++; + correction_number--; + } + tick += pixels_per_step; + tick_minor = tick / 2; + if (tick <= 128 - 3) + { + display_instance->drawLine(tick, HEIGHT + X_AXIS_WEIGHT, tick, + HEIGHT + X_AXIS_WEIGHT + length); + // Central tick + if (tick > (128 / 2) - 3 && tick < (128 / 2) + 3) + { + display_instance->drawLine(tick + 1, HEIGHT + X_AXIS_WEIGHT, + tick + 1, HEIGHT + X_AXIS_WEIGHT + length); + } + } #ifdef MINOR_TICKS - // Fix two ticks together - if (tick_minor + 1 != tick && tick_minor - 1 != tick && tick_minor + 2 != tick && tick_minor - 2 != tick) - { - display_instance->drawLine(tick_minor, HEIGHT + X_AXIS_WEIGHT, tick_minor, HEIGHT + X_AXIS_WEIGHT + MINOR_TICK_LENGTH); - } - // Central tick - if (tick_minor > (128 / 2) - 3 && tick_minor < (128 / 2) + 3) - { - display_instance->drawLine(tick_minor + 1, HEIGHT + X_AXIS_WEIGHT, tick_minor + 1, HEIGHT + X_AXIS_WEIGHT + MINOR_TICK_LENGTH); - } + // Fix two ticks together + if ((tick_minor + 1 != tick) && (tick_minor - 1 != tick) && (tick_minor + 2 != tick) && (tick_minor - 2 != tick)) + { + display_instance->drawLine(tick_minor, HEIGHT + X_AXIS_WEIGHT, + tick_minor, HEIGHT + X_AXIS_WEIGHT + MINOR_TICK_LENGTH); + } + // Central tick + if (tick_minor > (128 / 2) - 3 && tick_minor < (128 / 2) + 3) + { + display_instance->drawLine(tick_minor + 1, HEIGHT + X_AXIS_WEIGHT, + tick_minor + 1, HEIGHT + X_AXIS_WEIGHT + MINOR_TICK_LENGTH); + } #endif - } + } } -void UI_drawCurrsor(int16_t possition) +void UI_drawCursor(int16_t possition) { - // Draw animated cursor on reload process - display_instance->setColor(BLACK); - display_instance->drawVerticalLine(possition, 0, HEIGHT); - display_instance->drawVerticalLine(possition + 1, 0, HEIGHT); - display_instance->setColor(WHITE); - + // Draw animated cursor on reload process + display_instance->setColor(BLACK); + display_instance->drawVerticalLine(possition, 0, HEIGHT); + display_instance->drawVerticalLine(possition + 1, 0, HEIGHT); + display_instance->setColor(WHITE); } - /** * @brief Decorates the display: everything but the plot itself. */ void UI_displayDecorate(int begin = 0, int end = 0, bool redraw = false) { - if (!ui_initialized) - { - // Start and end ticks - display_instance->fillRect(0, HEIGHT + X_AXIS_WEIGHT, 2, MAJOR_TICK_LENGTH + 1); - display_instance->fillRect(126, HEIGHT + X_AXIS_WEIGHT, 2, MAJOR_TICK_LENGTH + 1); - - // Drone detection level - display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); - display_instance->drawString(128, 0, String(drone_detection_level)); - } - - if (!ui_initialized || redraw) - { - // Clear something - display_instance->setColor(BLACK); - display_instance->fillRect(0, SCALE_TEXT_TOP + 1, 128, 12); - display_instance->setColor(WHITE); - - // Drone detection level - display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); - display_instance->drawString(128, 0, String(drone_detection_level)); - - // Frequency start - display_instance->setTextAlignment(TEXT_ALIGN_LEFT); - display_instance->drawString(0, SCALE_TEXT_TOP, (begin == 0) ? String(FREQ_BEGIN) : String(begin)); - - display_instance->setTextAlignment(TEXT_ALIGN_CENTER); - display_instance->drawString(128 / 2, SCALE_TEXT_TOP, (begin == 0) ? String(median_frequency) : String(begin + ((end - begin) / 2))); - - // Frequency end - display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); - display_instance->drawString(128, SCALE_TEXT_TOP, (end == 0) ? String(FREQ_END) : String(end)); - } - - if (led_flag == true && detection_count >= 5) - { - digitalWrite(LED, HIGH); - if (SOUND_ON) + if (!ui_initialized) { - tone(BUZZER_PIN, 104, 100); + // Start and end ticks + display_instance->fillRect(0, HEIGHT + X_AXIS_WEIGHT, 2, + MAJOR_TICK_LENGTH + 1); + display_instance->fillRect(126, HEIGHT + X_AXIS_WEIGHT, 2, + MAJOR_TICK_LENGTH + 1); + // Drone detection level + display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); + display_instance->drawString(128, 0, String(drone_detection_level)); } - digitalWrite(REB_PIN, HIGH); - led_flag = false; - } - - else if (!redraw) - { - digitalWrite(LED, LOW); - } - // Status text block - if (!drone_detected) - { - // "Scanning" - display_instance->setTextAlignment(TEXT_ALIGN_CENTER); - // clear status line - clearStatus(); - if (scan_progress_count == 0) + if (!ui_initialized || redraw) { - display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, "Scan. "); + // Clear something + display_instance->setColor(BLACK); + display_instance->fillRect(0, SCALE_TEXT_TOP + 1, 128, 12); + display_instance->setColor(WHITE); + // Drone detection level + display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); + display_instance->drawString(128, 0, String(drone_detection_level)); + // Frequency start + display_instance->setTextAlignment(TEXT_ALIGN_LEFT); + display_instance->drawString(0, SCALE_TEXT_TOP, + (begin == 0) ? String(FREQ_BEGIN) : String(begin)); + display_instance->setTextAlignment(TEXT_ALIGN_CENTER); + display_instance->drawString(128 / 2, SCALE_TEXT_TOP, + (begin == 0) ? String(median_frequency) : String(begin + ((end - begin) / 2))); + // Frequency end + display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); + display_instance->drawString(128, SCALE_TEXT_TOP, + (end == 0) ? String(FREQ_END) : String(end)); } - else if (scan_progress_count == 1) + if (led_flag == true && detection_count >= 5) { - display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, "Scan.. "); + digitalWrite(LED, HIGH); + if (SOUND_ON) + { + tone(BUZZER_PIN, 104, 100); + } + digitalWrite(REB_PIN, HIGH); + led_flag = false; } - else if (scan_progress_count == 2) + else if (!redraw) { - display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, "Scan..."); + digitalWrite(LED, LOW); } - scan_progress_count++; - - if (scan_progress_count == 3) + // Status text block + if (!drone_detected) { - scan_progress_count = 0; + // "Scanning" + display_instance->setTextAlignment(TEXT_ALIGN_CENTER); + // clear status line + clearStatus(); + if (scan_progress_count == 0) + { + display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, + "Scan. "); + } + else if (scan_progress_count == 1) + { + display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, + "Scan.. "); + } + else if (scan_progress_count == 2) + { + display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, + "Scan..."); + } + scan_progress_count++; + if (scan_progress_count == 3) + { + scan_progress_count = 0; + } } - } - - if (drone_detected) - { - display_instance->setTextAlignment(TEXT_ALIGN_CENTER); - // clear status line - clearStatus(); - - display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, String(drone_detected_frequency_start) + ">RF<" + String(drone_detected_frequency_end)); - } - - if (ranges_count == 0) - { - display_instance->setTextAlignment(TEXT_ALIGN_LEFT); - display_instance->drawString(0, STATUS_TEXT_TOP, String(FREQ_BEGIN)); - - display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); - display_instance->drawString(128, STATUS_TEXT_TOP, String(FREQ_END)); - } - else if (ranges_count > 0) - { - display_instance->setTextAlignment(TEXT_ALIGN_LEFT); - display_instance->drawString(0, STATUS_TEXT_TOP, String(SCAN_RANGES[scan_iterration] / 1000) + "-" + String(SCAN_RANGES[scan_iterration] % 1000)); - if (scan_iterration + 1 < iterations) + if (drone_detected) { - display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); - display_instance->drawString(128, STATUS_TEXT_TOP, String(SCAN_RANGES[scan_iterration + 1] / 1000) + "-" + String(SCAN_RANGES[scan_iterration + 1] % 1000)); + display_instance->setTextAlignment(TEXT_ALIGN_CENTER); + // clear status line + clearStatus(); + display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, + String(drone_detected_frequency_start) + ">RF<" + String(drone_detected_frequency_end)); } - } - - if (!ui_initialized) - { - // X-axis - display_instance->fillRect(0, HEIGHT, STEPS, X_AXIS_WEIGHT); + if (ranges_count == 0) + { + display_instance->setTextAlignment(TEXT_ALIGN_LEFT); + display_instance->drawString(0, STATUS_TEXT_TOP, String(FREQ_BEGIN)); + display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); + display_instance->drawString(128, STATUS_TEXT_TOP, String(FREQ_END)); + } + else if (ranges_count > 0) + { + display_instance->setTextAlignment(TEXT_ALIGN_LEFT); + display_instance->drawString(0, STATUS_TEXT_TOP, + String(SCAN_RANGES[scan_iteration] / 1000) + "-" + String(SCAN_RANGES[scan_iteration] % 1000)); + if (scan_iteration + 1 < iterations) + { + display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); + display_instance->drawString(128, STATUS_TEXT_TOP, + String(SCAN_RANGES[scan_iteration + 1] / 1000) + "-" + String(SCAN_RANGES[scan_iteration + 1] % 1000)); + } + } + if (!ui_initialized) + { + // X-axis + display_instance->fillRect(0, HEIGHT, STEPS, X_AXIS_WEIGHT); // ticks #ifdef MAJOR_TICKS - drawTicks(MAJOR_TICKS, MAJOR_TICK_LENGTH); + drawTicks(MAJOR_TICKS, MAJOR_TICK_LENGTH); #endif - } - ui_initialized = true; + } + ui_initialized = true; } From ebf9141d7dcc0cc3b3a4fc394d3b6c2f79dce564 Mon Sep 17 00:00:00 2001 From: "ionsurdu@github.com" Date: Wed, 7 Aug 2024 13:28:05 +0300 Subject: [PATCH 4/8] WIP --- include/global_config.h | 8 +++--- src/main.cpp | 62 +++++++++++++++++++++++++++++++++-------- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/include/global_config.h b/include/global_config.h index d429eae..b2bff67 100644 --- a/include/global_config.h +++ b/include/global_config.h @@ -9,17 +9,17 @@ // Measurement bandwidth. Allowed bandwidth values (in kHz) are: // 4.8, 5.8, 7.3, 9.7, 11.7, 14.6, 19.5, 23.4, 29.3, 39.0, 46.9, 58.6, // 78.2, 93.8, 117.3, 156.2, 187.2, 234.3, 312.0, 373.6 and 467.0 -#define BANDWIDTH 467.0 +#define BANDWIDTH 467.0 // Detection level from the 33 levels. The higher number is more sensitive #define DEFAULT_DRONE_DETECTION_LEVEL 21 +#define BUZZER_PIN 41 -#define BUZZER_PIN 41 // REB trigger PIN -#define REB_PIN 42 +#define REB_PIN 42 #define WATERFALL_ENABLED true #define WATERFALL_START 37 -#endif \ No newline at end of file +#endif diff --git a/src/main.cpp b/src/main.cpp index 81f4462..df2d96d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -53,6 +53,7 @@ int SCAN_RANGES[] = {}; // MHZ per page // to put everething into one page set RANGE_PER_PAGE = FREQ_END - 800 unsigned int RANGE_PER_PAGE = FREQ_END - FREQ_BEGIN; // FREQ_END - FREQ_BEGIN + // To Enable Multi Screen scan // unsigned int RANGE_PER_PAGE = 50; // Default Range on Menu Button Switch @@ -74,7 +75,6 @@ unsigned int RANGE_PER_PAGE = FREQ_END - FREQ_BEGIN; // FREQ_END - FREQ_BEGIN #define SINGLE_STEP (float)(RANGE / STEPS) -unsigned int single_step = SINGLE_STEP; unsigned int range = (int)(FREQ_END - FREQ_BEGIN); unsigned int fr_begin = FREQ_BEGIN; @@ -94,6 +94,7 @@ uint16_t filtered_result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; // Waterfall array bool waterfall[10][STEPS][10]; // 10 - ??? + // global variable // Used as a Led Light and Buzzer/count trigger @@ -111,7 +112,10 @@ bool SOUND_ON = true; unsigned int scan_time = 0; unsigned int scan_start_time = 0; -uint64_t start = 0; + +#ifdef PRINT_PROFILE_TIME +uint64_t scan_start = 0; +#endif unsigned int x, y, scan_iteration, w = 0; unsigned int ranges_count = 0; @@ -239,31 +243,41 @@ void loop(void) drone_detected = false; detection_count = 0; drone_detected_frequency_start = 0; + ranges_count = 0; + + #ifdef PRINT_PROFILE_TIME - start = millis(); + scan_start = millis(); #endif + if (!ANIMATED_RELOAD || !single_page_scan) { // clear the scan plot rectangle UI_clearPlotter(); } + // do the scan range = FREQ_END - FREQ_BEGIN; if (RANGE_PER_PAGE > range) { RANGE_PER_PAGE = range; } + fr_begin = FREQ_BEGIN; fr_end = fr_begin; + // 50 is a single-screen range // TODO: Make 50 a variable with the option to show the full range iterations = range / RANGE_PER_PAGE; - single_step = RANGE_PER_PAGE / 128; + +#if 0 // disabled code if (range % RANGE_PER_PAGE != 0) { // add more scan //++; } +#endif + if (RANGE_PER_PAGE == range) { single_page_scan = true; @@ -272,16 +286,18 @@ void loop(void) { single_page_scan = false; } - ranges_count = 0; + for (int range : SCAN_RANGES) { ranges_count++; } + if (ranges_count > 0) { iterations = ranges_count; single_page_scan = false; } + // Iterating by small ranges by 50 Mhz each pixel is 0.4 Mhz for (scan_iteration = 0; scan_iteration < iterations; scan_iteration++) { @@ -323,6 +339,7 @@ void loop(void) waterfall[scan_iteration][x][w] = false; freq = fr_begin + (range * ((float)x / STEPS)); radio.setFrequency(freq); + // TODO: RSSI METHOD // Gets RSSI (Recorded Signal Strength Indicator) // Restart continuous receive mode on the new frequency @@ -407,8 +424,10 @@ void loop(void) } } } -#endif +#endif // SCAN_METHOD == METHOD_RSSI + detected = false; + #ifdef FILTER_SPECTRUM_RESULTS // Filter Elements without neighbors for (y = 1; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) @@ -454,27 +473,39 @@ void loop(void) { drone_detected_frequency_start = freq; } + drone_detected_frequency_end = freq; + UI_setLedFlag(true); + // If level is set to sensitive, // start beeping every 10th frequency and shorter if (drone_detection_level <= 25) { if (detection_count == 1 && SOUND_ON) - tone(BUZZER_PIN, 205, 10); + { + tone(BUZZER_PIN, 205, 10); // same action ??? + } if (detection_count % 5 == 0 && SOUND_ON) - tone(BUZZER_PIN, 205, 10); + { + tone(BUZZER_PIN, 205, 10); // same action ??? + } } else { if (detection_count % 20 == 0 && SOUND_ON) - tone(BUZZER_PIN, 205, 10); + { + tone(BUZZER_PIN, 205, 10); // same action ??? + } } + + // draw ... ??? display.setPixel(x, 1); display.setPixel(x, 2); display.setPixel(x, 3); display.setPixel(x, 4); } + #ifdef WATERFALL_ENABLED if (filtered_result[y] == 1 && y > drone_detection_level && single_page_scan && waterfall[scan_iteration][x][w] != true) { @@ -486,29 +517,35 @@ void loop(void) display.setColor(WHITE); } #endif + + if (filtered_result[y] == 1) { // Set signal level pixel display.setPixel(x, y); detected = true; } - // Draw detection Level line evere 2 pixel + + // Draw detection Level line every 2 pixel if (y == drone_detection_level && x % 2 == 0) { display.setPixel(x, y); } } + #ifdef PRINT_PROFILE_TIME scan_time = millis() - scan_start_time; // Huge performance issue if enable // Serial.printf("Single Scan took %lld ms\n", scan_time); #endif } + + // count detected if (detected) { detection_count++; } - detected = false; + #ifdef PRINT_DEBUG Serial.println("...."); #endif @@ -516,6 +553,7 @@ void loop(void) { display.display(); } + // Detection level button short press if (button.pressedFor(100)) { @@ -598,7 +636,7 @@ void loop(void) #endif // display.display(); #ifdef PRINT_PROFILE_TIME - scan_time = millis() - start; + scan_time = millis() - scan_start; Serial.printf("Scan took %lld ms\n", scan_time); #endif } From 4de5f7f80f74489e4ca48d466df91d7e80d1083d Mon Sep 17 00:00:00 2001 From: "ionsurdu@github.com" Date: Wed, 7 Aug 2024 15:44:26 +0300 Subject: [PATCH 5/8] Fix profiling format. --- platformio.ini | 4 +- src/main.cpp | 124 +++++++++++++++++++++++++------------------------ 2 files changed, 66 insertions(+), 62 deletions(-) diff --git a/platformio.ini b/platformio.ini index 0be4ad5..5c24979 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,5 +15,5 @@ framework = arduino upload_speed = 115200 monitor_speed = 115200 board_build.f_cpu = 240000000 -lib_deps = - ropg/Heltec_ESP32_LoRa_v3@^0.9.1 +lib_deps = ropg/Heltec_ESP32_LoRa_v3@^0.9.1 +build_flags = -DHELTEC_POWER_BUTTON diff --git a/src/main.cpp b/src/main.cpp index df2d96d..072197f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,9 +21,6 @@ https://jgromes.github.io/RadioLib/ */ -// Turns the 'PRG' button into the power button, long press is off -// TODO add it to compiler options using -DHELTEC_POWER_BUTTON -#define HELTEC_POWER_BUTTON // must be before "#include " #include #include @@ -52,10 +49,10 @@ int SCAN_RANGES[] = {}; // MHZ per page // to put everething into one page set RANGE_PER_PAGE = FREQ_END - 800 -unsigned int RANGE_PER_PAGE = FREQ_END - FREQ_BEGIN; // FREQ_END - FREQ_BEGIN +uint64_t RANGE_PER_PAGE = FREQ_END - FREQ_BEGIN; // FREQ_END - FREQ_BEGIN // To Enable Multi Screen scan -// unsigned int RANGE_PER_PAGE = 50; +// uint64_t RANGE_PER_PAGE = 50; // Default Range on Menu Button Switch #define DEFAULT_RANGE_PER_PAGE 50 @@ -69,21 +66,21 @@ unsigned int RANGE_PER_PAGE = FREQ_END - FREQ_BEGIN; // FREQ_END - FREQ_BEGIN // if more than 100 it can freez #define SAMPLES 100 //(scan time = 1294) // number of samples for RSSI method -#define SAMPLES_RSSI 21 // +#define SAMPLES_RSSI RADIOLIB_SX126X_SPECTRAL_SCAN_WINDOW_DEFAULT // 21 // #define RANGE (int)(FREQ_END - FREQ_BEGIN) #define SINGLE_STEP (float)(RANGE / STEPS) -unsigned int range = (int)(FREQ_END - FREQ_BEGIN); -unsigned int fr_begin = FREQ_BEGIN; -unsigned int fr_end = FREQ_BEGIN; +uint64_t range = (int)(FREQ_END - FREQ_BEGIN); +uint64_t fr_begin = FREQ_BEGIN; +uint64_t fr_end = FREQ_BEGIN; -unsigned int iterations = RANGE / RANGE_PER_PAGE; +uint64_t iterations = RANGE / RANGE_PER_PAGE; -// unsigned int range_frequency = FREQ_END - FREQ_BEGIN; -unsigned int median_frequency = FREQ_BEGIN + FREQ_END - FREQ_BEGIN / 2; +// uint64_t range_frequency = FREQ_END - FREQ_BEGIN; +uint64_t median_frequency = FREQ_BEGIN + FREQ_END - FREQ_BEGIN / 2; // #define OSD_ENABLED true // unused // #define DISABLE_PLOT_CHART false // unused @@ -102,30 +99,32 @@ bool first_run = false; // drone detection flag bool drone_detected = false; bool detected = false; -unsigned int drone_detection_level = DEFAULT_DRONE_DETECTION_LEVEL; -unsigned int drone_detected_frequency_start = 0; -unsigned int drone_detected_frequency_end = 0; -unsigned int detection_count = 0; +uint64_t drone_detection_level = DEFAULT_DRONE_DETECTION_LEVEL; +uint64_t drone_detected_frequency_start = 0; +uint64_t drone_detected_frequency_end = 0; +uint64_t detection_count = 0; bool single_page_scan = false; bool SOUND_ON = true; -unsigned int scan_time = 0; -unsigned int scan_start_time = 0; +#define PRINT_PROFILE_TIME #ifdef PRINT_PROFILE_TIME -uint64_t scan_start = 0; +uint64_t loop_start = 0; +uint64_t loop_time = 0; +uint64_t scan_time = 0; +uint64_t scan_start_time = 0; #endif -unsigned int x, y, scan_iteration, w = 0; -unsigned int ranges_count = 0; +uint64_t x, y, scan_iteration, w = 0; +uint64_t ranges_count = 0; float freq = 0; int rssi = 0; int state = 0; int result_index = 0; -unsigned int button_pressed_counter = 0; +uint64_t button_pressed_counter = 0; void setup(void) { @@ -173,9 +172,10 @@ void setup(void) both.println("Starting scanning..."); vbat = heltec_vbat(); both.printf("V battery: %.2fV (%d%%)\n", vbat, - heltec_battery_percent(vbat)); + heltec_battery_percent(vbat)); delay(300); display.clear(); + resolution = RANGE / STEPS; if (RANGE_PER_PAGE == range) { @@ -233,6 +233,8 @@ void setup(void) } } display.clear(); + Serial.println(); + // waterfall start line y-axis w = WATERFALL_START; } @@ -245,9 +247,11 @@ void loop(void) drone_detected_frequency_start = 0; ranges_count = 0; + //reset scan time + scan_time = 0; #ifdef PRINT_PROFILE_TIME - scan_start = millis(); + loop_start = millis(); #endif if (!ANIMATED_RELOAD || !single_page_scan) @@ -331,11 +335,14 @@ void loop(void) // horizontal x axis loop for (x = 0; x < STEPS; x++) { - scan_start_time = millis(); - #if ANIMATED_RELOAD - UI_drawCursor(x); + UI_drawCursor(x); #endif + +#ifdef PRINT_PROFILE_TIME + scan_start_time = millis(); +#endif + waterfall[scan_iteration][x][w] = false; freq = fr_begin + (range * ((float)x / STEPS)); radio.setFrequency(freq); @@ -360,12 +367,11 @@ void loop(void) // A short delay after changing the frequency // ensures the module has time to stabilize and get an accurate RSSI reading. #ifdef PRINT_DEBUG - Serial.println(); - Serial.print("step-"); - Serial.print(x); - Serial.print(" Frequency:"); - Serial.print(freq); - Serial.println(); + Serial.printf("Step:%d\n",x); + // Serial.printf("Step:%d Freq: %f\n",x,freq); + // Serial.print(" Frequency:"); + // Serial.print(freq); + // Serial.println(); #endif // SpectralScan Method #if SCAN_METHOD == METHOD_SPECTRAL @@ -386,11 +392,11 @@ void loop(void) #if SCAN_METHOD == METHOD_RSSI // Spectrum analyzer using getRSSI { - state = radio.startReceive(0); + state = radio.startReceive(RADIOLIB_SX126X_RX_TIMEOUT_NONE); if (state == RADIOLIB_ERR_NONE) { #ifdef PRINT_DEBUG - Serial.println(F("Started continuous receive mode")); + Serial.println(F("Started continuous RX mode")); #endif } else @@ -398,9 +404,10 @@ void loop(void) Serial.print(F("Failed to start receive mode, error code: ")); Serial.println(state); } + for (int r = 1; r < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; r++) { - result[r] = 0; + result[r] = 0; // ????? } result_index = 0; // N of samples @@ -409,13 +416,10 @@ void loop(void) rssi = radio.getRSSI(false); // delay(ONE_MILLISEC); // ToDO: check if 4 is correct value for 33 power bins - result_index = (abs(rssi) / 4); - // Debug Information + result_index = (abs(rssi) / 4); /// WTF ??? + #ifdef PRINT_DEBUG - Serial.print("Frequency: "); - Serial.println(freq); - Serial.println(rssi); - Serial.println(result_index); + Serial.printf("Freq: %.2f RSSI: %d \n",freq,rssi); #endif // Saving max value only rss is negative so smaller is bigger if (result[result_index] > rssi) @@ -430,7 +434,7 @@ void loop(void) #ifdef FILTER_SPECTRUM_RESULTS // Filter Elements without neighbors - for (y = 1; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) + for (y = 0; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) { // if RSSI method actual value is -xxx dB if (result[y] && (result[y + 1] != 0 || result[y - 1] != 0)) @@ -460,7 +464,8 @@ void loop(void) if (filtered_result[y] == 1 && y <= drone_detection_level) { drone_detected = true; -#ifdef WATERFALL_ENABLED + +#if ( WATERFALL_ENABLED == true ) if (single_page_scan) { // Drone detection true for waterfall @@ -499,14 +504,14 @@ void loop(void) } } - // draw ... ??? - display.setPixel(x, 1); - display.setPixel(x, 2); - display.setPixel(x, 3); - display.setPixel(x, 4); + // debug draw + // display.setPixel(x, 1); + // display.setPixel(x, 2); + // display.setPixel(x, 3); + // display.setPixel(x, 4); } -#ifdef WATERFALL_ENABLED +#if ( WATERFALL_ENABLED == true ) if (filtered_result[y] == 1 && y > drone_detection_level && single_page_scan && waterfall[scan_iteration][x][w] != true) { // If drone not found set dark pixel on the waterfall @@ -532,14 +537,12 @@ void loop(void) display.setPixel(x, y); } } - -#ifdef PRINT_PROFILE_TIME - scan_time = millis() - scan_start_time; - // Huge performance issue if enable - // Serial.printf("Single Scan took %lld ms\n", scan_time); -#endif + } - + +#ifdef PRINT_PROFILE_TIME + scan_time += (millis() - scan_start_time); +#endif // count detected if (detected) { @@ -620,7 +623,7 @@ void loop(void) { w = WATERFALL_START; } -#ifdef WATERFALL_ENABLED +#if ( WATERFALL_ENABLED == true ) // Draw waterfall position cursor if (single_page_scan) { @@ -636,7 +639,8 @@ void loop(void) #endif // display.display(); #ifdef PRINT_PROFILE_TIME - scan_time = millis() - scan_start; - Serial.printf("Scan took %lld ms\n", scan_time); + loop_time = millis() - loop_start; + Serial.printf("LOOP: %lld ms; SCAN: %lld ms;\n ", loop_time,scan_time); #endif + } From 2c866ed89e7b4b5d8385582ba0418bb29a0574a0 Mon Sep 17 00:00:00 2001 From: "ionsurdu@github.com" Date: Wed, 7 Aug 2024 17:11:00 +0300 Subject: [PATCH 6/8] improve scanning speed. --- src/main.cpp | 48 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 072197f..870eea5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -235,6 +235,10 @@ void setup(void) display.clear(); Serial.println(); + // calibrate only once ,,, at startup + radio.setFrequency(FREQ_BEGIN,true); + + // waterfall start line y-axis w = WATERFALL_START; } @@ -345,7 +349,8 @@ void loop(void) waterfall[scan_iteration][x][w] = false; freq = fr_begin + (range * ((float)x / STEPS)); - radio.setFrequency(freq); + + radio.setFrequency(freq,false); // TODO: RSSI METHOD // Gets RSSI (Recorded Signal Strength Indicator) @@ -429,15 +434,21 @@ void loop(void) } } #endif // SCAN_METHOD == METHOD_RSSI - + detected = false; -#ifdef FILTER_SPECTRUM_RESULTS - // Filter Elements without neighbors + for (y = 0; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) { +#ifdef PRINT_DEBUG + Serial.printf("%04X,", result[y]); +#endif + +#ifdef FILTER_SPECTRUM_RESULTS + // Filter Elements without neighbors + // if RSSI method actual value is -xxx dB - if (result[y] && (result[y + 1] != 0 || result[y - 1] != 0)) + if (result[y] && ((result[y + 1] != 0) || (result[y - 1] != 0))) { // Filling the empty pixel between signals int the level < 27 (noise level) /* if (y < 27 && result[y + 1] == 0 && result[y + 2] > 0) @@ -451,13 +462,8 @@ void loop(void) { filtered_result[y] = 0; } - } -#endif - for (y = 0; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) - { -#ifdef PRINT_DEBUG - Serial.printf("%04X,", result[y]); #endif + if (result[y] || y == drone_detection_level) { // check if we should alarm about a drone presence @@ -465,6 +471,10 @@ void loop(void) { drone_detected = true; + // Set LED to ON (filtered in UI component) + UI_setLedFlag(drone_detected); + + #if ( WATERFALL_ENABLED == true ) if (single_page_scan) { @@ -476,12 +486,13 @@ void loop(void) #endif if (drone_detected_frequency_start == 0) { + // mark freq start drone_detected_frequency_start = freq; } - + + // mark freq end ... will right last detected range drone_detected_frequency_end = freq; - UI_setLedFlag(true); // If level is set to sensitive, // start beeping every 10th frequency and shorter @@ -512,7 +523,10 @@ void loop(void) } #if ( WATERFALL_ENABLED == true ) - if (filtered_result[y] == 1 && y > drone_detection_level && single_page_scan && waterfall[scan_iteration][x][w] != true) + if ((filtered_result[y] == 1) + && ( y > drone_detection_level) + && ( single_page_scan ) + && ( waterfall[scan_iteration][x][w] != true) ) { // If drone not found set dark pixel on the waterfall // TODO: make something like scrolling up if possible @@ -531,8 +545,10 @@ void loop(void) detected = true; } - // Draw detection Level line every 2 pixel - if (y == drone_detection_level && x % 2 == 0) + // ------------------------------------------------------------- + // Draw "Detection Level line" every 2 pixel + // ------------------------------------------------------------- + if ( ( y == drone_detection_level) && (x % 2 == 0)) { display.setPixel(x, y); } From 51d1d0545889ef3e4fd2d1331b506cf1961b4923 Mon Sep 17 00:00:00 2001 From: "ionsurdu@github.com" Date: Wed, 7 Aug 2024 21:06:21 +0300 Subject: [PATCH 7/8] WIP ... minor changes --- README.md | 2 +- include/ui.h | 5 ++- src/main.cpp | 89 ++++++++++++++++++++++++---------------------------- src/ui.cpp | 66 +++++++++++++++++++++++++------------- 4 files changed, 88 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index 767c204..d0a843e 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ to disable - uncomment this line #define WATERFALL_ENABLED true ``` -Waterfall shows the last **N** = SCREEN_HEIGHT (64) - WATERFALL_START(37) - 8 (part of the STATUS_TEXT_TOP) = **19** signal detection that excited set signal level +Waterfall shows the last **N** = SCREEN_HEIGHT (64) - WATERFALL_START(37) - 8 (part of the ROW_STATUS_TEXT) = **19** signal detection that excited set signal level ### RSSI Method of scan diff --git a/include/ui.h b/include/ui.h index 5fb9ca7..fe7bff6 100644 --- a/include/ui.h +++ b/include/ui.h @@ -15,16 +15,15 @@ #define ONE_MILLISEC 1 // Prints debug information and the scan measurement bins from the SX1262 in hex -// #define PRINT_DEBUG // Change spectrum plot values at once or by line -#define ANIMATED_RELOAD true +#define ANIMATED_RELOAD false #define MAJOR_TICK_LENGTH 2 #define MINOR_TICK_LENGTH 1 // WEIGHT of the x-axis line #define X_AXIS_WEIGHT 1 -#define STATUS_TEXT_TOP (64 - 10) +#define ROW_STATUS_TEXT (64 - 10) // The number of the spectrum screen lines = width of screen // Resolution of the scan is limited by 128-pixel screen diff --git a/src/main.cpp b/src/main.cpp index 870eea5..0a935dc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -44,8 +44,8 @@ typedef enum #define SCAN_METHOD METHOD_SPECTRAL // Feature to scan diapazones. Other frequency settings will be ignored. -int SCAN_RANGES[] = {}; // int SCAN_RANGES[] = {850890, 920950}; +int SCAN_RANGES[] = {}; // MHZ per page // to put everething into one page set RANGE_PER_PAGE = FREQ_END - 800 @@ -116,7 +116,7 @@ uint64_t scan_time = 0; uint64_t scan_start_time = 0; #endif -uint64_t x, y, scan_iteration, w = 0; +uint64_t x, y, range_item, w = 0; uint64_t ranges_count = 0; float freq = 0; @@ -124,12 +124,15 @@ int rssi = 0; int state = 0; int result_index = 0; -uint64_t button_pressed_counter = 0; +uint8_t button_pressed_counter = 0; + +uint64_t loop_cnt = 0; void setup(void) { float vbat; float resolution; + loop_cnt = 0; pinMode(LED, OUTPUT); pinMode(BUZZER_PIN, OUTPUT); @@ -177,19 +180,17 @@ void setup(void) display.clear(); resolution = RANGE / STEPS; - if (RANGE_PER_PAGE == range) - { - single_page_scan = true; - } - else - { - single_page_scan = false; - } + + single_page_scan = (RANGE_PER_PAGE == range); + +#ifdef DISABLED_CODE // Adjust range if it is not even to RANGE_PER_PAGE if (!single_page_scan && range % RANGE_PER_PAGE != 0) { // range = range + range % RANGE_PER_PAGE; } +#endif + if (single_page_scan) { both.println("Single Page Screen MODE"); @@ -254,6 +255,9 @@ void loop(void) //reset scan time scan_time = 0; + // general purpose loop conter + loop_cnt++; + #ifdef PRINT_PROFILE_TIME loop_start = millis(); #endif @@ -307,18 +311,18 @@ void loop(void) } // Iterating by small ranges by 50 Mhz each pixel is 0.4 Mhz - for (scan_iteration = 0; scan_iteration < iterations; scan_iteration++) + for (range_item = 0; range_item < iterations; range_item++) { range = RANGE_PER_PAGE; if (ranges_count == 0) { - fr_begin = (scan_iteration == 0) ? fr_begin : fr_begin += range; + fr_begin = (range_item == 0) ? fr_begin : fr_begin += range; fr_end = fr_begin + RANGE_PER_PAGE; } else { - fr_begin = SCAN_RANGES[scan_iteration] / 1000; - fr_end = SCAN_RANGES[scan_iteration] % 1000; + fr_begin = SCAN_RANGES[range_item] / 1000; + fr_end = SCAN_RANGES[range_item] % 1000; range = fr_end - fr_begin; } @@ -347,30 +351,11 @@ void loop(void) scan_start_time = millis(); #endif - waterfall[scan_iteration][x][w] = false; + waterfall[range_item][x][w] = false; freq = fr_begin + (range * ((float)x / STEPS)); - radio.setFrequency(freq,false); + radio.setFrequency(freq,false); // false = no calibration need here - // TODO: RSSI METHOD - // Gets RSSI (Recorded Signal Strength Indicator) - // Restart continuous receive mode on the new frequency - // state = radio.startReceive(); - // if (state == RADIOLIB_ERR_NONE) { - // Serial.println(F("Started continuous receive mode")); - //} else { - // Serial.print(F("Failed to start receive mode, error code: ")); - // Serial.println(state); - //} - // rssi = radio.getRSSI(false); - // Serial.println(String(rssi) + "db"); - // delay(25); - // This code will iterate over the specified frequencies, - // changing the frequency every - // second and printing the RSSI value for each frequency to the serial monitor. Adjust the frequencies array - // to include the specific frequencies you're interested in monitoring. - // A short delay after changing the frequency - // ensures the module has time to stabilize and get an accurate RSSI reading. #ifdef PRINT_DEBUG Serial.printf("Step:%d\n",x); // Serial.printf("Step:%d Freq: %f\n",x,freq); @@ -410,10 +395,8 @@ void loop(void) Serial.println(state); } - for (int r = 1; r < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; r++) - { - result[r] = 0; // ????? - } + // memset + memset(result,0,RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE); result_index = 0; // N of samples for (int r = 1; r < SAMPLES_RSSI; r++) @@ -421,7 +404,7 @@ void loop(void) rssi = radio.getRSSI(false); // delay(ONE_MILLISEC); // ToDO: check if 4 is correct value for 33 power bins - result_index = (abs(rssi) / 4); /// WTF ??? + result_index = (abs(rssi) / 4); /// still not clear formula #ifdef PRINT_DEBUG Serial.printf("Freq: %.2f RSSI: %d \n",freq,rssi); @@ -479,7 +462,7 @@ void loop(void) if (single_page_scan) { // Drone detection true for waterfall - waterfall[scan_iteration][x][w] = true; + waterfall[range_item][x][w] = true; display.setColor(WHITE); display.setPixel(x, w); } @@ -520,17 +503,20 @@ void loop(void) // display.setPixel(x, 2); // display.setPixel(x, 3); // display.setPixel(x, 4); + + // draw vertical line on top of display for "drone detected" frequencies + display.drawLine(x , 1, x, 6 ); } #if ( WATERFALL_ENABLED == true ) if ((filtered_result[y] == 1) && ( y > drone_detection_level) && ( single_page_scan ) - && ( waterfall[scan_iteration][x][w] != true) ) + && ( waterfall[range_item][x][w] != true) ) { // If drone not found set dark pixel on the waterfall // TODO: make something like scrolling up if possible - waterfall[scan_iteration][x][w] = false; + waterfall[range_item][x][w] = false; display.setColor(BLACK); display.setPixel(x, w); display.setColor(WHITE); @@ -635,7 +621,7 @@ void loop(void) heltec_loop(); } w++; - if (w > STATUS_TEXT_TOP + 1) + if (w > ROW_STATUS_TEXT + 1) { w = WATERFALL_START; } @@ -648,15 +634,22 @@ void loop(void) display.setColor(WHITE); } #endif - display.display(); + + // Render display data here + display.display(); } + #ifdef PRINT_DEBUG Serial.println("----"); #endif -// display.display(); -#ifdef PRINT_PROFILE_TIME + loop_time = millis() - loop_start; + + +#ifdef PRINT_PROFILE_TIME +#ifdef PRINT_DEBUG Serial.printf("LOOP: %lld ms; SCAN: %lld ms;\n ", loop_time,scan_time); #endif +#endif } diff --git a/src/ui.cpp b/src/ui.cpp index 7b1fd81..6359786 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -34,7 +34,10 @@ extern unsigned int ranges_count; extern int SCAN_RANGES[]; extern unsigned int ranges_count; extern unsigned int iterations; -extern unsigned int scan_iteration; +extern unsigned int range_item; + +extern uint64_t loop_time; + void UI_Init(SSD1306Wire *display_ptr) { @@ -56,13 +59,13 @@ void clearStatus(void) { // clear status line display_instance->setColor(BLACK); - display_instance->fillRect(0, STATUS_TEXT_TOP + 2, 128, 13); + display_instance->fillRect(0, ROW_STATUS_TEXT + 2, 128, 13); display_instance->setColor(WHITE); } void UI_clearPlotter(void) { - // clear the scan plot rectangle + // clear the scan plot rectangle (top part) display_instance->setColor(BLACK); display_instance->fillRect(0, 0, STEPS, HEIGHT); display_instance->setColor(WHITE); @@ -171,21 +174,25 @@ void UI_displayDecorate(int begin = 0, int end = 0, bool redraw = false) display_instance->setColor(BLACK); display_instance->fillRect(0, SCALE_TEXT_TOP + 1, 128, 12); display_instance->setColor(WHITE); + // Drone detection level display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); display_instance->drawString(128, 0, String(drone_detection_level)); // Frequency start display_instance->setTextAlignment(TEXT_ALIGN_LEFT); - display_instance->drawString(0, SCALE_TEXT_TOP, + display_instance->drawString(0, ROW_STATUS_TEXT, (begin == 0) ? String(FREQ_BEGIN) : String(begin)); + + // Frequency detected display_instance->setTextAlignment(TEXT_ALIGN_CENTER); - display_instance->drawString(128 / 2, SCALE_TEXT_TOP, + display_instance->drawString(128 / 2, ROW_STATUS_TEXT, (begin == 0) ? String(median_frequency) : String(begin + ((end - begin) / 2))); // Frequency end display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); - display_instance->drawString(128, SCALE_TEXT_TOP, + display_instance->drawString(128, ROW_STATUS_TEXT, (end == 0) ? String(FREQ_END) : String(end)); } + if (led_flag == true && detection_count >= 5) { digitalWrite(LED, HIGH); @@ -200,6 +207,7 @@ void UI_displayDecorate(int begin = 0, int end = 0, bool redraw = false) { digitalWrite(LED, LOW); } + // Status text block if (!drone_detected) { @@ -209,21 +217,26 @@ void UI_displayDecorate(int begin = 0, int end = 0, bool redraw = false) clearStatus(); if (scan_progress_count == 0) { - display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, - "Scan. "); + display_instance->drawString(start_scan_text, ROW_STATUS_TEXT, + "Scan \\"); } else if (scan_progress_count == 1) { - display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, - "Scan.. "); + display_instance->drawString(start_scan_text, ROW_STATUS_TEXT, + "Scan |"); } else if (scan_progress_count == 2) { - display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, - "Scan..."); + display_instance->drawString(start_scan_text, ROW_STATUS_TEXT, + "Scan /"); + } + else if (scan_progress_count == 3) + { + display_instance->drawString(start_scan_text, ROW_STATUS_TEXT, + "Scan -"); } scan_progress_count++; - if (scan_progress_count == 3) + if (scan_progress_count >= 4) { scan_progress_count = 0; } @@ -233,29 +246,38 @@ void UI_displayDecorate(int begin = 0, int end = 0, bool redraw = false) display_instance->setTextAlignment(TEXT_ALIGN_CENTER); // clear status line clearStatus(); - display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, + display_instance->drawString(start_scan_text, ROW_STATUS_TEXT, String(drone_detected_frequency_start) + ">RF<" + String(drone_detected_frequency_end)); + + } if (ranges_count == 0) { +#ifdef DEBUG display_instance->setTextAlignment(TEXT_ALIGN_LEFT); - display_instance->drawString(0, STATUS_TEXT_TOP, String(FREQ_BEGIN)); + display_instance->drawString(0, ROW_STATUS_TEXT,String(loop_time)); +#else + display_instance->setTextAlignment(TEXT_ALIGN_LEFT); + display_instance->drawString(0, ROW_STATUS_TEXT, String(FREQ_BEGIN)); + +#endif display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); - display_instance->drawString(128, STATUS_TEXT_TOP, String(FREQ_END)); + display_instance->drawString(128, ROW_STATUS_TEXT, String(FREQ_END)); + } else if (ranges_count > 0) { display_instance->setTextAlignment(TEXT_ALIGN_LEFT); - display_instance->drawString(0, STATUS_TEXT_TOP, - String(SCAN_RANGES[scan_iteration] / 1000) + "-" + String(SCAN_RANGES[scan_iteration] % 1000)); - if (scan_iteration + 1 < iterations) + display_instance->drawString(0, ROW_STATUS_TEXT, + String(SCAN_RANGES[range_item] / 1000) + "-" + String(SCAN_RANGES[range_item] % 1000)); + if (range_item + 1 < iterations) { display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); - display_instance->drawString(128, STATUS_TEXT_TOP, - String(SCAN_RANGES[scan_iteration + 1] / 1000) + "-" + String(SCAN_RANGES[scan_iteration + 1] % 1000)); + display_instance->drawString(128, ROW_STATUS_TEXT, + String(SCAN_RANGES[range_item + 1] / 1000) + "-" + String(SCAN_RANGES[range_item + 1] % 1000)); } } - if (!ui_initialized) + if (ui_initialized == false) { // X-axis display_instance->fillRect(0, HEIGHT, STEPS, X_AXIS_WEIGHT); From b4139be5a04fd5ff10b4d66c57129efaa1f7ff27 Mon Sep 17 00:00:00 2001 From: "ionsurdu@github.com" Date: Thu, 8 Aug 2024 09:30:38 +0300 Subject: [PATCH 8/8] code improvements. --- src/main.cpp | 102 ++++++++++++++++++++++++++++----------------------- src/ui.cpp | 49 ++++++++++++------------- 2 files changed, 81 insertions(+), 70 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 0a935dc..514c0f8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -97,7 +97,6 @@ bool waterfall[10][STEPS][10]; // 10 - ??? // Used as a Led Light and Buzzer/count trigger bool first_run = false; // drone detection flag -bool drone_detected = false; bool detected = false; uint64_t drone_detection_level = DEFAULT_DRONE_DETECTION_LEVEL; uint64_t drone_detected_frequency_start = 0; @@ -122,7 +121,7 @@ uint64_t ranges_count = 0; float freq = 0; int rssi = 0; int state = 0; -int result_index = 0; +uint8_t result_index = 0; uint8_t button_pressed_counter = 0; @@ -247,7 +246,6 @@ void setup(void) void loop(void) { UI_displayDecorate(0, 0, false); // some default values - drone_detected = false; detection_count = 0; drone_detected_frequency_start = 0; ranges_count = 0; @@ -357,11 +355,7 @@ void loop(void) radio.setFrequency(freq,false); // false = no calibration need here #ifdef PRINT_DEBUG - Serial.printf("Step:%d\n",x); // Serial.printf("Step:%d Freq: %f\n",x,freq); - // Serial.print(" Frequency:"); - // Serial.print(freq); - // Serial.println(); #endif // SpectralScan Method #if SCAN_METHOD == METHOD_SPECTRAL @@ -383,13 +377,7 @@ void loop(void) // Spectrum analyzer using getRSSI { state = radio.startReceive(RADIOLIB_SX126X_RX_TIMEOUT_NONE); - if (state == RADIOLIB_ERR_NONE) - { -#ifdef PRINT_DEBUG - Serial.println(F("Started continuous RX mode")); -#endif - } - else + if (state != RADIOLIB_ERR_NONE) { Serial.print(F("Failed to start receive mode, error code: ")); Serial.println(state); @@ -397,23 +385,34 @@ void loop(void) // memset memset(result,0,RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE); - result_index = 0; + result_index = 0u; // N of samples - for (int r = 1; r < SAMPLES_RSSI; r++) + for (int r = 0; r < SAMPLES_RSSI; r++) { rssi = radio.getRSSI(false); // delay(ONE_MILLISEC); // ToDO: check if 4 is correct value for 33 power bins - result_index = (abs(rssi) / 4); /// still not clear formula + result_index = uint8_t(abs(rssi) / 4); /// still not clear formula #ifdef PRINT_DEBUG - Serial.printf("Freq: %.2f RSSI: %d \n",freq,rssi); + // Serial.printf("RSSI: %d IDX: %d\n",rssi,result_index); #endif - // Saving max value only rss is negative so smaller is bigger - if (result[result_index] > rssi) + // avoid buffer overflow + if (result_index < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE) { - result[result_index] = rssi; + // Saving max value only rss is negative so smaller is bigger + if (result[result_index] > rssi) + { + result[result_index] = rssi; + } } +#ifdef PRINT_DEBUG + else + { + Serial.print("Out-of-Range: result_index %d\n"); + } +#endif + } } #endif // SCAN_METHOD == METHOD_RSSI @@ -424,39 +423,43 @@ void loop(void) for (y = 0; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) { #ifdef PRINT_DEBUG - Serial.printf("%04X,", result[y]); + // Serial.printf("%04X,", result[y]); #endif #ifdef FILTER_SPECTRUM_RESULTS - // Filter Elements without neighbors + filtered_result[y] = 0; + // Filter Elements without neighbors // if RSSI method actual value is -xxx dB - if (result[y] && ((result[y + 1] != 0) || (result[y - 1] != 0))) + if (result[y]) { - // Filling the empty pixel between signals int the level < 27 (noise level) - /* if (y < 27 && result[y + 1] == 0 && result[y + 2] > 0) + // do not process 'first' and 'last' row to avoid out of index access + if ((y!=0) && (y != (RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE - 1))) + { + if ((result[y + 1] != 0) || (result[y - 1] != 0)) { - result[y + 1] = 1; - filtered_result[y + 1] = 1; - }*/ - filtered_result[y] = 1; - } - else - { - filtered_result[y] = 0; + // Filling the empty pixel between signals int the level < 27 (noise level) + /* if (y < 27 && result[y + 1] == 0 && result[y + 2] > 0) + { + result[y + 1] = 1; + filtered_result[y + 1] = 1; + }*/ + filtered_result[y] = 1; + } + } } #endif - if (result[y] || y == drone_detection_level) + + // if (result[y] || y == drone_detection_level) { // check if we should alarm about a drone presence - if (filtered_result[y] == 1 && y <= drone_detection_level) + if ((filtered_result[y] == 1) // we have some data and + && (y <= drone_detection_level)) // detection threshold match { - drone_detected = true; - + // Set LED to ON (filtered in UI component) - UI_setLedFlag(drone_detected); - + UI_setLedFlag(true); #if ( WATERFALL_ENABLED == true ) if (single_page_scan) @@ -473,7 +476,7 @@ void loop(void) drone_detected_frequency_start = freq; } - // mark freq end ... will right last detected range + // mark freq end ... will shift right to last detected range drone_detected_frequency_end = freq; @@ -498,6 +501,7 @@ void loop(void) } } + // debug draw // display.setPixel(x, 1); // display.setPixel(x, 2); @@ -523,7 +527,12 @@ void loop(void) } #endif +#if 0 +#endif // If 0 + + + // next 2 If's ... adds !!!! 10ms of runtime ......tfk ??? if (filtered_result[y] == 1) { // Set signal level pixel @@ -535,15 +544,18 @@ void loop(void) // Draw "Detection Level line" every 2 pixel // ------------------------------------------------------------- if ( ( y == drone_detection_level) && (x % 2 == 0)) - { + { + display.setColor(INVERSE); display.setPixel(x, y); + display.setPixel(x, y+1); // 2 px wide + display.setColor(WHITE); } } } #ifdef PRINT_PROFILE_TIME - scan_time += (millis() - scan_start_time); + scan_time += (millis() - scan_start_time); #endif // count detected if (detected) @@ -552,7 +564,7 @@ void loop(void) } #ifdef PRINT_DEBUG - Serial.println("...."); + // Serial.println("...."); #endif if (first_run || ANIMATED_RELOAD) { @@ -640,7 +652,7 @@ void loop(void) } #ifdef PRINT_DEBUG - Serial.println("----"); + //Serial.println("----"); #endif loop_time = millis() - loop_start; diff --git a/src/ui.cpp b/src/ui.cpp index 6359786..f4f8ef2 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -27,7 +27,6 @@ extern unsigned int RANGE_PER_PAGE; extern unsigned int median_frequency; extern unsigned int detection_count; extern bool SOUND_ON; -extern bool drone_detected; extern unsigned int drone_detected_frequency_start; extern unsigned int drone_detected_frequency_end; extern unsigned int ranges_count; @@ -193,23 +192,15 @@ void UI_displayDecorate(int begin = 0, int end = 0, bool redraw = false) (end == 0) ? String(FREQ_END) : String(end)); } - if (led_flag == true && detection_count >= 5) - { - digitalWrite(LED, HIGH); - if (SOUND_ON) - { - tone(BUZZER_PIN, 104, 100); - } - digitalWrite(REB_PIN, HIGH); - led_flag = false; - } - else if (!redraw) - { - digitalWrite(LED, LOW); - } - // Status text block - if (!drone_detected) + if (led_flag) // 'drone' detected + { + display_instance->setTextAlignment(TEXT_ALIGN_CENTER); + // clear status line + clearStatus(); + display_instance->drawString(start_scan_text, ROW_STATUS_TEXT, + String(drone_detected_frequency_start) + ">RF<" + String(drone_detected_frequency_end)); + } else { // "Scanning" display_instance->setTextAlignment(TEXT_ALIGN_CENTER); @@ -241,16 +232,24 @@ void UI_displayDecorate(int begin = 0, int end = 0, bool redraw = false) scan_progress_count = 0; } } - if (drone_detected) - { - display_instance->setTextAlignment(TEXT_ALIGN_CENTER); - // clear status line - clearStatus(); - display_instance->drawString(start_scan_text, ROW_STATUS_TEXT, - String(drone_detected_frequency_start) + ">RF<" + String(drone_detected_frequency_end)); - + + if (led_flag == true && detection_count >= 5) + { + digitalWrite(LED, HIGH); + if (SOUND_ON) + { + tone(BUZZER_PIN, 104, 100); + } + digitalWrite(REB_PIN, HIGH); + led_flag = false; } + else if (!redraw) + { + digitalWrite(LED, LOW); + } + + if (ranges_count == 0) { #ifdef DEBUG