From e15e345e51c05d1f8cb005f8ad5b905df5a4a39c Mon Sep 17 00:00:00 2001 From: Egor Shitikov Date: Thu, 12 Sep 2024 14:43:05 -0700 Subject: [PATCH 01/80] Update README.md --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index 957a5a0..c07cced 100644 --- a/README.md +++ b/README.md @@ -222,6 +222,24 @@ or buy : ![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. + +## Analog FPV OSD (ON SCREEN DISPLAY) +To Enable OSD, Uncomment these lines
+``` +// #define OSD_ENABLED true +``` +**OSD sidebar enabled/disable** +comment or uncomment this line +``` +#define OSD_SIDE_BAR true +``` + +Or you can set this and other variables as a build parameter: +``` +build_flags = + -DOSD_ENABLED +``` + ## DFRobot OSD Wiring **Heltec V3 -> DFRobot OSD**
GND -> GND
@@ -287,3 +305,11 @@ Edit **paltformio.io** uncommenting/selecting your sources ``` for LilyGo use env:heltec_wifi_lora_32_V3 +# WiFi and Bluetooth BT Scanning +Works only with OSD enabled
+Uncomment this lines +``` +// #define OSD_ENABLED true +// #define WIFI_SCANNING_ENABLED true +// #define BT_SCANNING_ENABLED true +``` From 87938c3ad11173a7f9ae54502ee5896a530a415d Mon Sep 17 00:00:00 2001 From: Egor Shitikov Date: Thu, 12 Sep 2024 22:25:55 -0700 Subject: [PATCH 02/80] add e-ink waterfall --- eink_src/main.cpp | 88 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 11 deletions(-) diff --git a/eink_src/main.cpp b/eink_src/main.cpp index a87ece8..5f04822 100644 --- a/eink_src/main.cpp +++ b/eink_src/main.cpp @@ -23,6 +23,7 @@ #define DISPLAY_WIDTH 296 #define DISPLAY_HEIGHT 128 // Without this line Lora Radio doesn't work with heltec lib +#define BUTTON 21 #define ARDUINO_heltec_wifi_32_lora_V3 #include "heltec_unofficial.h" @@ -250,18 +251,21 @@ void VextOFF(void) // Vext default OFF constexpr int lower_level = 108; constexpr int up_level = 40; +constexpr int start_pixel = 80; + int rssiToPix(int rssi) { // Bigger is lower signal if (abs(rssi) >= lower_level) { - return lower_level - 1; + return start_pixel - 1; } if (abs(rssi) <= up_level) { - return up_level; + return start_pixel - up_level; } - return abs(rssi); + + return start_pixel - (lower_level - abs(rssi)); } long timeSinceLastModeSwitch = 0; @@ -287,10 +291,42 @@ long display_scan_start = 0; long display_scan_end = 0; long display_scan_i_end = 0; int scan_iterations = 0; +bool waterfall_values[DISPLAY_HEIGHT][DISPLAY_WIDTH] = {false}; constexpr unsigned int SCANS_PER_DISPLAY = 5; constexpr unsigned int STATUS_BAR_HEIGHT = 5; +void button_logic(void) +{ + heltec_loop(); + button_pressed_counter = 0; + if (button.pressed()) + { + drone_detection_level++; + if (drone_detection_level > 107) + drone_detection_level = DEFAULT_DRONE_DETECTION_LEVEL - 20; + while (button.pressedNow()) + { + delay(100); + display.display(); + button_pressed_counter++; + // button.update(); + + if (button_pressed_counter > 18) + { + // Some sign there + } + } + } + + if (button_pressed_counter < 9 && button_pressed_counter > 5) + { + display.clear(); + display.display(); + heltec_deep_sleep(); + } +} + void loop() { if (screen_update_loop_counter == 0) @@ -307,7 +343,6 @@ void loop() int u = 0; for (int i = 0; i < SAMPLES_RSSI; i++) { - radio.setFrequency((float)fr + (float)(rssi_mhz_step * u), false); // false = no calibration need here u++; @@ -319,10 +354,14 @@ void loop() rssi2 = radio.getRSSI(false); scan_iterations++; if (rssi2 > lower_level) + { + max_scan_rssi[x1] = rssi2; continue; + } #ifdef PRINT_DEBUG Serial.println(String(fr) + ":" + String(rssi2)); #endif + button_logic(); // display.drawString(x1, (int)y2, String(fr) + ":" + String(rssi2)); display.setPixel(x1, rssiToPix(rssi2)); @@ -332,6 +371,16 @@ void loop() } } + // Waterfall per scan not per screen + if (abs(max_scan_rssi[x1]) <= drone_detection_level) + { + waterfall_values[w][x1] = true; + } + else + { + waterfall_values[w][x1] = false; + } + // drone detection level line if (x1 % 2 == 0) { @@ -343,9 +392,15 @@ void loop() { display_scan_i_end = millis(); } + button_logic(); // Main N x-axis full loop end logic if (x1 >= STEPS) { + w++; + if (w >= DISPLAY_HEIGHT - start_pixel - 13) + { + w = 0; + } if (screen_update_loop_counter == SCANS_PER_DISPLAY) { // max Mhz and dB in window @@ -372,14 +427,23 @@ void loop() display.drawString(i - rssi_window_size + 5, y2 + 10, String(window_max_fr)); // Vertical lines between windows - for (int l = y2; l < 100; l += 4) + for (int l = y2; l < start_pixel; l += 4) { display.setPixel(i, l); } } window_max_rssi = -999; } + + // Draw Waterfall + for (int y = 0; y < DISPLAY_HEIGHT; y++) + if (waterfall_values[y][i] == true) + { + display.setPixel(i, start_pixel + 5 + y); + } } + // Draw Waterfall cursor + display.drawHorizontalLine(0, start_pixel + 5 + w, DISPLAY_WIDTH); display_scan_end = millis(); @@ -396,6 +460,7 @@ void loop() esp_restart(); } + // ToDo: it doesn't work battery(); // iteration full scan / samples pixel step / numbers of scan per display display.drawString(DISPLAY_WIDTH - ((DISPLAY_WIDTH / 6) * 2) - 5, 0, @@ -409,21 +474,21 @@ void loop() "s:" + String(mhz_step)); // Draw a line horizontally - display.drawHorizontalLine(0, lower_level + 1, DISPLAY_WIDTH); + display.drawHorizontalLine(0, 1 + start_pixel, DISPLAY_WIDTH); // Generate Ticks for (int x = 0; x < DISPLAY_WIDTH; x++) { if (x % (DISPLAY_WIDTH / 2) == 0 && x > 5) { - display.drawVerticalLine(x, lower_level + 1, 11); + display.drawVerticalLine(x, 1 + start_pixel, 11); // central tick width // display.drawVerticalLine(x - 1, lower_level + 1, 8); // display.drawVerticalLine(x + 1, lower_level + 1, 8); } if (x % 10 == 0 || x == 0) - display.drawVerticalLine(x, lower_level + 1, 6); + display.drawVerticalLine(x, 1 + start_pixel, 6); if (x % 5 == 0) - display.drawVerticalLine(x, lower_level + 1, 3); + display.drawVerticalLine(x, 1 + start_pixel, 3); } display.setFont(ArialMT_Plain_10); @@ -441,13 +506,14 @@ void loop() String(FREQ_BEGIN + (((int)fr - FREQ_BEGIN) - ((int)fr - FREQ_BEGIN) / 4))); // End Mhz - display.drawString(DISPLAY_WIDTH - 24, DISPLAY_HEIGHT - 10, String((int)fr)); + display.drawString(DISPLAY_WIDTH - 20, DISPLAY_HEIGHT - 10, String((int)fr)); display.display(); // display will be cleared next scan iteration. it is just buffer clear // memset(buffer, 0, displayBufferSize); display.clear(); screen_update_loop_counter = 0; + scan_iterations = 0; display_scan_i_end = 0; } @@ -480,7 +546,7 @@ void setup() delay(1000); display.clear(); Serial.begin(115200); - w = WATERFALL_START; + w = 0; // WATERFALL_START; init_radio(); state = radio.startReceive(RADIOLIB_SX126X_RX_TIMEOUT_NONE); if (state != RADIOLIB_ERR_NONE) From 7bfa5aba9ac84092db1c14b6b615f4be888c51e1 Mon Sep 17 00:00:00 2001 From: Egor Shitikov Date: Fri, 13 Sep 2024 15:15:47 -0700 Subject: [PATCH 03/80] add some button fixes --- eink_src/main.cpp | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/eink_src/main.cpp b/eink_src/main.cpp index 5f04822..71a35a0 100644 --- a/eink_src/main.cpp +++ b/eink_src/main.cpp @@ -97,6 +97,7 @@ bool first_run, new_pixel, detected_x = false; // drone detection flag bool detected = false; uint64_t drone_detection_level = 90; +bool detection_level_changed = false; uint64_t drone_detected_frequency_start = 0; uint64_t drone_detected_frequency_end = 0; uint64_t detection_count = 0; @@ -296,6 +297,17 @@ bool waterfall_values[DISPLAY_HEIGHT][DISPLAY_WIDTH] = {false}; constexpr unsigned int SCANS_PER_DISPLAY = 5; constexpr unsigned int STATUS_BAR_HEIGHT = 5; +void clear_rectangle(int x, int y, int width, int height) +{ + for (int x = 0; x < width; x++) + { + for (int y = 0; y < height; y++) + { + display.clearPixel(x, y); + } + } +} + void button_logic(void) { heltec_loop(); @@ -303,11 +315,19 @@ void button_logic(void) if (button.pressed()) { drone_detection_level++; + detection_level_changed = true; if (drone_detection_level > 107) drone_detection_level = DEFAULT_DRONE_DETECTION_LEVEL - 20; + clear_rectangle(0, 0, 10, 10); + display.setFont(ArialMT_Plain_10); + + display.drawString(0, 0, + "T:" + String(display_scan_end - display_scan_start) + "/" + + String(display_scan_i_end - display_scan_start) + " L:-" + + String(drone_detection_level) + "dB"); while (button.pressedNow()) { - delay(100); + display.display(); button_pressed_counter++; // button.update(); @@ -343,8 +363,13 @@ void loop() int u = 0; for (int i = 0; i < SAMPLES_RSSI; i++) { - radio.setFrequency((float)fr + (float)(rssi_mhz_step * u), - false); // false = no calibration need here + state = radio.setFrequency((float)fr + (float)(rssi_mhz_step * u), + false); // false = no calibration need here + int radio_error_count = 0; + if (state != RADIOLIB_ERR_NONE) + { + Serial.println("E:setFrequency:" + String(freq)); + } u++; if (rssi_mhz_step * u >= mhz_step) { @@ -446,7 +471,10 @@ void loop() display.drawHorizontalLine(0, start_pixel + 5 + w, DISPLAY_WIDTH); display_scan_end = millis(); - + if (detection_level_changed == true) + { + clear_rectangle(0, 0, 100, 10); + } display.setFont(ArialMT_Plain_10); display.drawString(0, 0, "T:" + String(display_scan_end - display_scan_start) + From 109b71739f1dd7d65cbc94b21983f4823c17698a Mon Sep 17 00:00:00 2001 From: Egor Shitikov Date: Fri, 13 Sep 2024 16:49:18 -0700 Subject: [PATCH 04/80] Fix result index --- platformio.ini | 4 ---- src/main.cpp | 5 +++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/platformio.ini b/platformio.ini index 0b7648d..24ece4d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -25,7 +25,6 @@ monitor_speed = 115200 board_build.f_cpu = 240000000 lib_deps = ropg/Heltec_ESP32_LoRa_v3@^0.9.1 - adafruit/Adafruit ST7735 and ST7789 Library@^1.10.4 build_flags = -DHELTEC_POWER_BUTTON [env:lilygo-T3S3-v1-2] @@ -38,7 +37,6 @@ board_build.f_cpu = 240000000 lib_deps = ropg/Heltec_ESP32_LoRa_v3@^0.9.1 RadioLib - adafruit/Adafruit ST7735 and ST7789 Library@^1.10.4 build_flags = -DLILYGO -DT3_S3_V1_2_SX1262 @@ -61,7 +59,6 @@ board_build.f_cpu = 240000000 board_build.flash_size = 80000000L lib_deps = ropg/Heltec_ESP32_LoRa_v3@^0.9.1 - adafruit/Adafruit ST7735 and ST7789 Library@^1.10.4 build_flags = -DLILYGO [env:vision-master-e290] @@ -95,7 +92,6 @@ lib_deps = https://github.com/HelTecAutomation/Heltec_ESP32/ adafruit/Adafruit GFX Library@^1.11.10 ropg/Heltec_ESP32_LoRa_v3@^0.9.1 - adafruit/Adafruit ST7735 and ST7789 Library@^1.10.4 [env:vision-master-t190] platform = espressif32 diff --git a/src/main.cpp b/src/main.cpp index acfc13b..cb51aeb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -948,6 +948,11 @@ void loop(void) // I like this formula better result_index = uint8_t(abs(rssi) / 2) - 22; } + if (result_index >= RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE) + { + // Maximum index possible + result_index = RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE - 1; + } #ifdef PRINT_DEBUG Serial.printf("RSSI: %d IDX: %d\n", rssi, result_index); From f703abce20a7a75cf98e40cc25f0c9cf2bb6136f Mon Sep 17 00:00:00 2001 From: Egor Shitikov Date: Sat, 14 Sep 2024 18:21:22 -0700 Subject: [PATCH 05/80] fix issues described by Olexii --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index cb51aeb..3852959 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -960,8 +960,8 @@ void loop(void) // avoid buffer overflow if (result_index < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE) { - // Saving max value only rss is negative so smaller is bigger - if (result[result_index] > rssi) + // Saving max value of the rssi. + if (result[result_index] < rssi) { result[result_index] = rssi; } @@ -1023,7 +1023,7 @@ void loop(void) { // do not process 'first' and 'last' row to avoid out of index // access. - if ((y > 0) && (y != (RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE - 3))) + if ((y > 0) && (y < (RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE - 3))) { if (((result[y + 1] != 0) && (result[y + 2] != 0)) || (result[y - 1] != 0)) From bd852bda3368b2951d1a4855b8f0634fc99d3851 Mon Sep 17 00:00:00 2001 From: Egor Shitikov Date: Sat, 14 Sep 2024 19:11:46 -0700 Subject: [PATCH 06/80] add some fixes --- src/main.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 3852959..88f76bc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -750,7 +750,7 @@ void check_ranges() single_page_scan = false; } } -// MAX Frequency RSSI value of the samples +// MAX Frequency RSSI BIN value of the samples int max_rssi_x = 999; void loop(void) @@ -960,10 +960,10 @@ void loop(void) // avoid buffer overflow if (result_index < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE) { - // Saving max value of the rssi. - if (result[result_index] < rssi) + // Saving max value only rss is negative so smaller is bigger + if (result[result_index] == 0 || result[result_index] > abs(rssi)) { - result[result_index] = rssi; + result[result_index] = abs(rssi); } } else @@ -1023,12 +1023,14 @@ void loop(void) { // do not process 'first' and 'last' row to avoid out of index // access. - if ((y > 0) && (y < (RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE - 3))) + if ((y > 0) && (y < (RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE - 1))) { if (((result[y + 1] != 0) && (result[y + 2] != 0)) || (result[y - 1] != 0)) { filtered_result[y] = 1; + // Fill empty pixel + result[y + 1] = 1; } else { @@ -1108,6 +1110,7 @@ void loop(void) #endif if (max_rssi_x > y) { + // MAx bin Value not RSSI max_rssi_x = y; } // Set signal level pixel From 6203bdc33f12a3b469b5107c9a0a788555286ef0 Mon Sep 17 00:00:00 2001 From: Egor Shitikov Date: Sat, 14 Sep 2024 19:30:51 -0700 Subject: [PATCH 07/80] add comment --- src/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 88f76bc..45c1419 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -960,7 +960,8 @@ void loop(void) // avoid buffer overflow if (result_index < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE) { - // Saving max value only rss is negative so smaller is bigger + // Saving max ABS value of rssi. dB is negative so smaller is + // bigger if (result[result_index] == 0 || result[result_index] > abs(rssi)) { result[result_index] = abs(rssi); From 151ebe661f199ae7f9f52d8042d34d05a5ae08c6 Mon Sep 17 00:00:00 2001 From: Christian Ruiz Date: Mon, 16 Sep 2024 21:46:54 +0200 Subject: [PATCH 08/80] overview ML datasets --- ml_research/datasets.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 ml_research/datasets.md diff --git a/ml_research/datasets.md b/ml_research/datasets.md new file mode 100644 index 0000000..60d95f0 --- /dev/null +++ b/ml_research/datasets.md @@ -0,0 +1,7 @@ +| dataset name | data content | data format | frequencies / Sampling rate | other infos | source paper/website | source data | open questions | +|----------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------| +| Noisy Drone RF Signal Classification (Glüge et al.) | Drones: DJI, FutabaT14, FutabaT7, Graupner, Taranis, Turnigy | I/Q Data as well as generated Spectrograms available | The device can scan a range of 6Ghz. But the newest plot is very weird. +/- 7 Mhz, can this really be? "non-overlapping signal vectors of length of 1048576 samples, which corresponds to approx. 74.9ms at 14MHz" | mixed with either Labnoise (50%) or Gaussian noise (50%). The noise class was created by mixing Labnoise and Gaussian noise in all possible combinations. Several levels of SNR were used over the entire dataset. | https://www.scitepress.org/Link.aspx?doi=10.5220/0012176800003595 https://github.com/sgluege/Noisy-Drone-RF-Signal-Classification https://github.com/sgluege/Noisy-Drone-RF-Signal-Classification-v2/tree/main | https://www.kaggle.com/datasets/sgluege/noisy-drone-rf-signal-classification-v2/data https://www.kaggle.com/datasets/sgluege/noisy-drone-rf-signal-classification | Not sure about the frequencies. | +| DroneDataset (Swinney and Woods) | Drones: new DJI Mavic 2 Air S, DJI Mavic Pro, DJI Mavic Pro 2, DJI Inspire 2, DJI Mavic Mini, DJI Phantom 4 and the Parrot Disco. | Raw I/Q Data | TODO; Recordings were collected using a Nuand BladeRF SDR and using open source software GNURadio | There are 4 subsets of data included in this dataset, the UAS signals in the presence of Bluetooth interference, in the presence of Wi-Fi signals, in the presence of both and with no interference. 3 flight modes are captured - switched on, hovering and flying. | No paper seen | https://ieee-dataport.org/open-access/dronedetect-dataset-radio-frequency-dataset-unmanned-aerial-system-uas-signals-machine | Sampling rate? | +| DroneRF (Allahham et al.) | Drones: Bepop; AR; Phantom | "the dataset contains only time series data, and not the complex IQ signals" (From Glüge and not from the authors) - but what does this exactly mean? | capture the whole 2.4GHz bandwidth, we have used 2 RF receivers. Each RF receiver has a maximum instantaneous bandwidth of 40 MHz, so both receivers must be operating simultaneously to at least capture a technology spectrum such as WiFi (i.e. 80 MHz). Recorded using universal soft- ware radio peripheral (USRP) software-defined radio (SDR) transceivers. Signals that could be considered noise in the 2.4 GHz band (Bluetooth, Wi-Fi) were not recorded. | modes, including off, on and connected, hovering, flying, and video recording. | https://www.sciencedirect.com/science/article/pii/S2352340919306675?ref=pdf_download&fr=RR-2&rr=8bf5de727fa35d7f | https://data.mendeley.com/datasets/f4c2b4n755/1 | Apparently time versus db? Not very clear what is in the data | +| Radio-Frequency Control and Video Signal Recordings of Drones (Vuorenmaa et al.) | Drones: DJI Inspire 2 (2.44 and 5.8 GHz), DJI Matrice 100 (2.44 GHz), DJI Matrice 210 (2.44 and 5.8 GHz), DJI Mavic Mini (2.44 GHz), DJI Mavic Pro (2.44 GHz), DJI Phantom 4 (2.44 GHz), DJI Phantom 4 Pro Plus (2.44 and 5.8 GHz), Parrot Disco (2.44 GHz), Parrot Mambo (2.44 GHz), Yuneec Typhoon H (2.44 and 5.7 GHz) | I/Q Data (And Video data) | TODO: not clear yet | | https://zenodo.org/records/4264467 | | | +| Spectrogram Dataset (Wicht et al.) | Wi-Fi and Bluetooth signals (NOT DRONES) | Sepctrograms | | | https://www.mdpi.com/2306-5729/7/12/168 | | | From 7e63d9787b99714d03e839c65531f6cba6ca25bf Mon Sep 17 00:00:00 2001 From: Christian Ruiz Date: Mon, 16 Sep 2024 22:10:43 +0200 Subject: [PATCH 09/80] added ML diagram --- ml_research/Diagram_ML.svg | 4 ++++ ml_research/datasets.md | 6 ++++++ 2 files changed, 10 insertions(+) create mode 100644 ml_research/Diagram_ML.svg diff --git a/ml_research/Diagram_ML.svg b/ml_research/Diagram_ML.svg new file mode 100644 index 0000000..f96baa4 --- /dev/null +++ b/ml_research/Diagram_ML.svg @@ -0,0 +1,4 @@ + + + +
Device produces a scan of N samples in Frequency F and loops over the entire Frequency band

Device cannot produce I/Q data
Device produces a sc...
Datasets contain I/Q data or Spectrograms (that can be built out of I/Q data)
Datasets contain I/Q...
SAME FORMAT











SAME FORMAT...
Generation of pseudo spectrogram with frequency bins and time length similar to train dataset
Generation of pseudo...
Spectrograms are cut so that the shown frequencies and time length is congruent with device data
Spectrograms are cut...
Train data
Convert to TF-Lite
(Model: MobileNetV3?)
Train data...
Prediction on device using TF-Lite library and saved model weights
Prediction on device...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/ml_research/datasets.md b/ml_research/datasets.md index 60d95f0..ebf796a 100644 --- a/ml_research/datasets.md +++ b/ml_research/datasets.md @@ -1,3 +1,5 @@ +## Overview of ML Dataets + | dataset name | data content | data format | frequencies / Sampling rate | other infos | source paper/website | source data | open questions | |----------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------| | Noisy Drone RF Signal Classification (Glüge et al.) | Drones: DJI, FutabaT14, FutabaT7, Graupner, Taranis, Turnigy | I/Q Data as well as generated Spectrograms available | The device can scan a range of 6Ghz. But the newest plot is very weird. +/- 7 Mhz, can this really be? "non-overlapping signal vectors of length of 1048576 samples, which corresponds to approx. 74.9ms at 14MHz" | mixed with either Labnoise (50%) or Gaussian noise (50%). The noise class was created by mixing Labnoise and Gaussian noise in all possible combinations. Several levels of SNR were used over the entire dataset. | https://www.scitepress.org/Link.aspx?doi=10.5220/0012176800003595 https://github.com/sgluege/Noisy-Drone-RF-Signal-Classification https://github.com/sgluege/Noisy-Drone-RF-Signal-Classification-v2/tree/main | https://www.kaggle.com/datasets/sgluege/noisy-drone-rf-signal-classification-v2/data https://www.kaggle.com/datasets/sgluege/noisy-drone-rf-signal-classification | Not sure about the frequencies. | @@ -5,3 +7,7 @@ | DroneRF (Allahham et al.) | Drones: Bepop; AR; Phantom | "the dataset contains only time series data, and not the complex IQ signals" (From Glüge and not from the authors) - but what does this exactly mean? | capture the whole 2.4GHz bandwidth, we have used 2 RF receivers. Each RF receiver has a maximum instantaneous bandwidth of 40 MHz, so both receivers must be operating simultaneously to at least capture a technology spectrum such as WiFi (i.e. 80 MHz). Recorded using universal soft- ware radio peripheral (USRP) software-defined radio (SDR) transceivers. Signals that could be considered noise in the 2.4 GHz band (Bluetooth, Wi-Fi) were not recorded. | modes, including off, on and connected, hovering, flying, and video recording. | https://www.sciencedirect.com/science/article/pii/S2352340919306675?ref=pdf_download&fr=RR-2&rr=8bf5de727fa35d7f | https://data.mendeley.com/datasets/f4c2b4n755/1 | Apparently time versus db? Not very clear what is in the data | | Radio-Frequency Control and Video Signal Recordings of Drones (Vuorenmaa et al.) | Drones: DJI Inspire 2 (2.44 and 5.8 GHz), DJI Matrice 100 (2.44 GHz), DJI Matrice 210 (2.44 and 5.8 GHz), DJI Mavic Mini (2.44 GHz), DJI Mavic Pro (2.44 GHz), DJI Phantom 4 (2.44 GHz), DJI Phantom 4 Pro Plus (2.44 and 5.8 GHz), Parrot Disco (2.44 GHz), Parrot Mambo (2.44 GHz), Yuneec Typhoon H (2.44 and 5.7 GHz) | I/Q Data (And Video data) | TODO: not clear yet | | https://zenodo.org/records/4264467 | | | | Spectrogram Dataset (Wicht et al.) | Wi-Fi and Bluetooth signals (NOT DRONES) | Sepctrograms | | | https://www.mdpi.com/2306-5729/7/12/168 | | | + + +## Current Proposal for ML +![Diagram ML](./Diagram_ML.svg) From fa55cb2fd09bb35ec980849e7afcd53f41ab0ea8 Mon Sep 17 00:00:00 2001 From: Egor Shitikov Date: Tue, 17 Sep 2024 10:17:25 -0700 Subject: [PATCH 10/80] dB output --- gps_src/main.cpp | 126 +++++++++++++++++++++++++++++++++++++++++++++ include/ui.h | 1 + platformio.ini | 2 + src/main.cpp | 100 +++++++++++++++++++++++++---------- src/ui.cpp | 10 +++- trans_src/main.cpp | 47 +++++++++++------ 6 files changed, 241 insertions(+), 45 deletions(-) create mode 100644 gps_src/main.cpp diff --git a/gps_src/main.cpp b/gps_src/main.cpp new file mode 100644 index 0000000..0ea23d7 --- /dev/null +++ b/gps_src/main.cpp @@ -0,0 +1,126 @@ +#include +#include + +// Objects for GNSS parsing and serial communication +TinyGPSPlus gps; +HardwareSerial GNSSSerial(2); + +// Pin definitions for GNSS module communication +const int GNSS_RXPin = 34; +const int GNSS_TXPin = 33; +const int GNSS_RSTPin = 35; // There is a function built for this in the example below- + // currently it isn't used +const int GNSS_PPS_Pin = 36; + +// Flags for PPS handling and synchronization status +volatile bool ppsFlag = false; +volatile bool initialSyncDone = false; + +// Timestamp for the last valid GNSS data received +unsigned long lastGNSSDataMillis = 0; + +void setup() +{ + // Initialize serial communication for debugging + USBSerial.begin(115200); + while (!USBSerial) + ; + + // Start GNSS module communication + GNSSSerial.begin(115200, SERIAL_8N1, GNSS_TXPin, GNSS_RXPin); + while (!GNSSSerial) + ; + + // Configure GNSS reset pin + pinMode(GNSS_RSTPin, OUTPUT); + digitalWrite(GNSS_RSTPin, HIGH); + + // Set up PPS pin and attach an interrupt handler + pinMode(GNSS_PPS_Pin, INPUT); + attachInterrupt(digitalPinToInterrupt(GNSS_PPS_Pin), ppsInterrupt, RISING); + + // Short delay for GNSS module initialization + delay(1000); +} + +void loop() +{ + // Process incoming GNSS data + while (GNSSSerial.available()) + { + if (gps.encode(GNSSSerial.read())) + { + // Update the timestamp when valid GNSS data is received + lastGNSSDataMillis = millis(); + displayGNSSData(); // Display GNSS data for debugging + } + } + + // Perform initial synchronization using NMEA time data + if (!initialSyncDone && gps.date.isValid() && gps.time.isValid()) + { + setSystemTime(); + initialSyncDone = true; + USBSerial.println("Initial time synchronization done using NMEA data."); + } + + // Disable interrupts to safely check and reset the PPS flag + noInterrupts(); + if (ppsFlag) + { + fineTuneSystemTime(); // Adjust system time based on the PPS pulse + ppsFlag = false; + } + // Re-enable interrupts + interrupts(); + + // Check if GNSS data has been absent for more than a minute + if (millis() - lastGNSSDataMillis > 60000) + { + USBSerial.println("Warning: Haven't received GNSS data for more than 1 minute!"); + // Additional actions can be added here, like alerts or module resets. + } +} + +// Interrupt handler for the PPS signal +void ppsInterrupt() { ppsFlag = true; } + +// Function to set system time using GNSS data +void setSystemTime() +{ + struct tm timeinfo; + timeinfo.tm_year = gps.date.year() - 1900; + timeinfo.tm_mon = gps.date.month() - 1; + timeinfo.tm_mday = gps.date.day(); + timeinfo.tm_hour = gps.time.hour(); + timeinfo.tm_min = gps.time.minute(); + timeinfo.tm_sec = gps.time.second(); + time_t t = mktime(&timeinfo); + + timeval tv = {t, 0}; + settimeofday(&tv, NULL); // Update system time +} + +// Function to fine-tune system time using the PPS pulse +void fineTuneSystemTime() +{ + timeval tv; + gettimeofday(&tv, NULL); + tv.tv_usec = 0; // Reset microseconds to zero + settimeofday(&tv, NULL); // Update system time + USBSerial.println("System time fine-tuned using PPS signal."); +} + +// Debugging function to display GNSS data +void displayGNSSData() +{ + USBSerial.print("Latitude: "); + USBSerial.println(gps.location.lat(), 6); + USBSerial.print("Longitude: "); + USBSerial.println(gps.location.lng(), 6); + USBSerial.print("Altitude: "); + USBSerial.println(gps.altitude.meters()); + USBSerial.print("Speed: "); + USBSerial.println(gps.speed.kmph()); + USBSerial.println("-----------------------------"); +} diff --git a/include/ui.h b/include/ui.h index 2ca7525..2f18a7b 100644 --- a/include/ui.h +++ b/include/ui.h @@ -40,4 +40,5 @@ 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_clearTopStatus(void); extern void UI_drawCursor(int16_t); diff --git a/platformio.ini b/platformio.ini index 24ece4d..26992cb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,6 +15,8 @@ ; src_dir = eink_src ; for env:heltec_wifi_lora_32_V3 ; src_dir = src ;;Default +; for heltec_wifi_lora_32_V3-test-signal-generator +; src_dir = trans_src [env:heltec_wifi_lora_32_V3] platform = espressif32 diff --git a/src/main.cpp b/src/main.cpp index 45c1419..36667a6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -194,12 +194,15 @@ bool ANIMATED_RELOAD = false; #define FILTER_SPECTRUM_RESULTS true #define FILTER_SAMPLES_MIN constexpr bool DRAW_DETECTION_TICKS = true; - +int16_t max_x_rssi[STEPS] = {999}; +int16_t max_x_window[STEPS / 14] = {999}; +int x_window = 0; +constexpr int WINDOW_SIZE = 15; // Number of samples for each frequency scan. Fewer samples = better temporal resolution. // if more than 100 it can freeze #define SAMPLES 35 //(scan time = 1294) // number of samples for RSSI method -#define SAMPLES_RSSI 20 // 21 // +#define SAMPLES_RSSI 12 // 21 // #define RANGE (int)(FREQ_END - FREQ_BEGIN) @@ -217,10 +220,9 @@ uint64_t median_frequency = FREQ_BEGIN + FREQ_END - FREQ_BEGIN / 2; // #define DISABLE_PLOT_CHART false // unused // Array to store the scan results -uint16_t result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; -uint16_t result_display_set[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; -uint16_t result_detections[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; -uint16_t filtered_result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; +int16_t result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; + +bool filtered_result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; int max_bins_array_value[MAX_POWER_LEVELS]; int max_step_range = 32; @@ -235,6 +237,7 @@ bool first_run, new_pixel, detected_x = false; // drone detection flag bool detected = false; uint64_t drone_detection_level = DEFAULT_DRONE_DETECTION_LEVEL; +uint64_t show_db_after = 80; uint64_t drone_detected_frequency_start = 0; uint64_t drone_detected_frequency_end = 0; uint64_t detection_count = 0; @@ -610,11 +613,14 @@ bool buttonPressHandler(float freq) #endif ) { + // Print Curent frequency once + if (button_pressed_counter == 0) + { + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(128 / 2, 0, String(freq)); + display.display(); + } 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) { @@ -663,21 +669,28 @@ bool buttonPressHandler(float freq) return true; } -void drone_sound_alarm(int drone_detection_level, int detection_count) +void drone_sound_alarm(int drone_detection_level, int detection_count, + int tone_freq_db = 205) { // If level is set to sensitive, // start beeping every 10th frequency and shorter // it improves performance less short beep delays... if (drone_detection_level <= 25) { + + if (tone_freq_db != 205) + { + tone_freq_db = 285 - tone_freq_db; + } + if (detection_count == 1 && SOUND_ON) { - tone(BUZZER_PIN, 205, + tone(BUZZER_PIN, tone_freq_db, 10); // same action ??? but first time } if (detection_count % 5 == 0 && SOUND_ON) { - tone(BUZZER_PIN, 205, + tone(BUZZER_PIN, tone_freq_db, 10); // same action ??? but every 5th time } } @@ -775,6 +788,7 @@ void loop(void) { // clear the scan plot rectangle UI_clearPlotter(); + UI_clearTopStatus(); } // do the scan @@ -934,6 +948,7 @@ void loop(void) for (int r = 0; r < SAMPLES_RSSI; r++) { rssi = radio.getRSSI(false); + int abs_rssi = abs(rssi); // ToDO: check if 4 is correct value for 33 power bins // Now we have more space because we are ignoring low dB values // we can / 3 default 4 @@ -941,12 +956,12 @@ void loop(void) { result_index = /// still not clear formula but it works - uint8_t(abs(rssi) / 4); + uint8_t(abs_rssi / 4); } else if (RSSI_OUTPUT_FORMULA == 2) { // I like this formula better - result_index = uint8_t(abs(rssi) / 2) - 22; + result_index = uint8_t(abs_rssi / 2) - 22; } if (result_index >= RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE) { @@ -960,11 +975,15 @@ void loop(void) // avoid buffer overflow if (result_index < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE) { - // Saving max ABS value of rssi. dB is negative so smaller is - // bigger - if (result[result_index] == 0 || result[result_index] > abs(rssi)) + // Saving max ABS value of RSSI. dB is negative, so smaller + // absolute value represents stronger signal. + if (result[result_index] == 0 || result[result_index] > abs_rssi) { - result[result_index] = abs(rssi); + result[result_index] = abs_rssi; + } + if (max_x_rssi[display_x] > abs_rssi) + { + max_x_rssi[display_x] = abs_rssi; } } else @@ -1024,7 +1043,7 @@ void loop(void) { // 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 ((y > 0) && (y < (RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE - 2))) { if (((result[y + 1] != 0) && (result[y + 2] != 0)) || (result[y - 1] != 0)) @@ -1040,12 +1059,23 @@ void loop(void) #endif } } - } // not filtering if samples == 1 + } // not filtering if samples == 1 because it will be filtered else if (result[y] > 0 && samples == 1) { filtered_result[y] = 1; } - + // calculating max window x RSSI after filters + x_window = (int)(display_x / WINDOW_SIZE); + int abs_result = abs(result[y]); + if (filtered_result[y] == 1 && result[y] != 0 && result[y] != 1 && + max_x_window[x_window] > abs_result) + { + max_x_window[x_window] = abs_result; +#ifdef PRINT_DEBUG + Serial.println("MAX x window: " + String(x_window) + " " + + String(abs_result)); +#endif + } #endif // check if we should alarm about a drone presence if ((filtered_result[y] == 1) // we have some data and @@ -1076,18 +1106,21 @@ void loop(void) drone_detected_frequency_end = freq; if (SOUND_ON == true) { - drone_sound_alarm(drone_detection_level, detection_count); + drone_sound_alarm(drone_detection_level, detection_count, + max_rssi_x * 2); } if (DRAW_DETECTION_TICKS == true) { - // draw vertical line on top of display for "drone detected" - // frequencies +// draw vertical line on top of display for "drone detected" +// frequencies +#ifdef METHOD_SPECTRAL if (!detected_y[display_x]) { display.drawLine(display_x, 1, display_x, 4); detected_y[display_x] = true; } +#endif } } #if (WATERFALL_ENABLED == true) @@ -1114,7 +1147,7 @@ void loop(void) // MAx bin Value not RSSI max_rssi_x = y; } - // Set signal level pixel + // Set MAIN signal level pixel if (y < MAX_POWER_LEVELS - START_LOW) { display.setPixel(display_x, y + START_LOW); @@ -1232,6 +1265,19 @@ void loop(void) display.setColor(WHITE); } #endif + +#ifdef METHOD_RSSI + // Printing Max Window DB. + for (int x2 = 0; x2 < STEPS / WINDOW_SIZE; x2++) + { + if (max_x_window[x2] < show_db_after && max_x_window[x2] != 0) + { + display.drawString(x2 * WINDOW_SIZE + WINDOW_SIZE, 0, + "-" + String(max_x_window[x2])); + } + max_x_window[x2] = 999; + } +#endif // Render display data here display.display(); #ifdef OSD_ENABLED @@ -1251,7 +1297,7 @@ void loop(void) #endif } #ifdef PRINT_DEBUG - // Serial.println("----"); +// Serial.println("----"); #endif loop_time = millis() - loop_start; diff --git a/src/ui.cpp b/src/ui.cpp index 48a7bf1..94342b7 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -80,7 +80,15 @@ void UI_clearPlotter(void) { // clear the scan plot rectangle (top part) display_instance->setColor(BLACK); - display_instance->fillRect(0, 0, STEPS, HEIGHT); + display_instance->fillRect(0, 10, STEPS, HEIGHT - 10); + display_instance->setColor(WHITE); +} + +void UI_clearTopStatus(void) +{ + // clear the scan plot rectangle (top part) + display_instance->setColor(BLACK); + display_instance->fillRect(0, 0, STEPS, 10); display_instance->setColor(WHITE); } diff --git a/trans_src/main.cpp b/trans_src/main.cpp index 2010f48..e40a4c2 100644 --- a/trans_src/main.cpp +++ b/trans_src/main.cpp @@ -8,6 +8,7 @@ * This works on the stick, but the output on the screen gets cut off. */ +#include // Turns the 'PRG' button into the power button, long press is off #define HELTEC_POWER_BUTTON // must be before "#include " #include @@ -15,11 +16,11 @@ // Pause between transmited packets in mseconds. // Set to zero to only transmit a packet when pressing the user button // Will not exceed 1% duty cycle, even if you set a lower value. -#define PAUSE 20 +#define PAUSE 10 // Frequency in MHz. Keep the decimal point to designate float. // Check your own rules and regulations to see what is legal where you are. -#define FREQUENCY 866.3 // for Europe +#define FREQUENCY 915 // for Europe // #define FREQUENCY 905.2 // for US // LoRa bandwidth. Keep the decimal point to designate float. @@ -29,13 +30,13 @@ // Number from 5 to 12. Higher means slower but higher "processor gain", // meaning (in nutshell) longer range and more robust against interference. -#define SPREADING_FACTOR 9 +#define SPREADING_FACTOR 7 // Transmit power in dBm. 0 dBm = 1 mW, enough for tabletop-testing. This value can be // set anywhere between -9 dBm (0.125 mW) to 22 dBm (158 mW). Note that the maximum ERP // (which is what your antenna maximally radiates) on the EU ISM band is 25 mW, and that // transmissting without an antenna can damage your hardware. -#define TRANSMIT_POWER -9 +#define TRANSMIT_POWER 22 String rxdata; volatile bool rxFlag = false; @@ -67,35 +68,41 @@ void setup() RADIOLIB_OR_HALT(radio.startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF)); } +int FHSS = 0.25; +float FHSS_counter = -10; void loop() { heltec_loop(); - bool tx_legal = millis() > last_tx + minimum_pause; + bool tx_legal = true; // millis() > last_tx + minimum_pause; + // Emulate frequency hopping spread spectrum (FHSS) is a method of transmitting radio + // signals by rapidly switching the carrier between different frequency channels. + float fr = (float)(FREQUENCY + (float)(FHSS_counter)); + RADIOLIB_OR_HALT(radio.setFrequency(fr, false)); // Transmit a packet every PAUSE seconds or when the button is pressed if ((PAUSE && tx_legal && millis() - last_tx > (PAUSE)) || button.isSingleClick()) { - // In case of button click, tell user to wait - if (!tx_legal) + if (button.isSingleClick()) { - both.printf("Legal limit, wait %i sec.\n", - (int)((minimum_pause - (millis() - last_tx)) / 1000) + 1); - return; + fr = 1000; + RADIOLIB_OR_HALT(radio.setFrequency(fr, false)); } - both.printf("TX [%s] ", String(counter).c_str()); + // In case of button click, tell user to wait + display.printf("TX[%s]", String(counter).c_str()); radio.clearDio1Action(); heltec_led(50); // 50% brightness is plenty for this LED tx_time = millis(); - RADIOLIB(radio.transmit(String(counter++).c_str())); + RADIOLIB(radio.transmit(String("Putin Huylo!!! LA-LA-LA-LA").c_str())); tx_time = millis() - tx_time; heltec_led(0); if (_radiolib_status == RADIOLIB_ERR_NONE) { - both.printf("OK (%i ms)\n", (int)tx_time); + display.printf("OK(%ims)/%.2fMhz\n", (int)tx_time, fr); } else { - both.printf("fail (%i)\n", _radiolib_status); + display.printf("fail (%i)\n", _radiolib_status); + heltec_delay(100); } // Maximum 1% duty cycle minimum_pause = tx_time * 100; @@ -111,10 +118,16 @@ void loop() radio.readData(rxdata); if (_radiolib_status == RADIOLIB_ERR_NONE) { - both.printf("RX [%s]\n", rxdata.c_str()); - both.printf(" RSSI: %.2f dBm\n", radio.getRSSI()); - both.printf(" SNR: %.2f dB\n", radio.getSNR()); + display.printf("RX [%s]\n", rxdata.c_str()); + display.printf(" RSSI: %.2f dBm\n", radio.getRSSI()); + display.printf(" SNR: %.2f dB\n", radio.getSNR()); } RADIOLIB_OR_HALT(radio.startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF)); } + FHSS_counter += 0.250; + counter++; + if (FHSS_counter > 20) + { + FHSS_counter = -10; + } } From d36e73a7cc4f267fd745da9d4981af428185e0dd Mon Sep 17 00:00:00 2001 From: Egor Shitikov Date: Tue, 17 Sep 2024 10:33:46 -0700 Subject: [PATCH 11/80] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c07cced..f04980e 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,9 @@ - Heltec Wireless Stick Lite V3 No Display (Not Tested) - Heltec Vision Master E290 - e-Ink 296 x 128 (No OSD) - Heltec Vision MAster T190 - color TFT 320X170 (No OSD) -- LilyGo Radio Lora T3S3 V.2 SX1262 +- LilyGo Radio Lora T3S3 V.2 SX1262 +- LilyGo Radio Lora T3S3 V.2 SX1280 +- LilyGo Radio Lora T3_V1.6.1 SX1276 (Not Tested) ## RF Spectrum Analyzer using Lora Radio From 7b1e8599b9b719a300fe9b0e8583731139b163fb Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Thu, 19 Sep 2024 19:51:43 +0100 Subject: [PATCH 12/80] Testable RSSI method --- platformio.ini | 3 ++ src/core.cpp | 57 ++++++++++++++++++++++++++++++ src/core.h | 45 ++++++++++++++++++++++++ src/main.cpp | 88 +++++++++++----------------------------------- test/test_rssi.cpp | 56 +++++++++++++++++++++++++++++ 5 files changed, 181 insertions(+), 68 deletions(-) create mode 100644 src/core.cpp create mode 100644 src/core.h create mode 100644 test/test_rssi.cpp diff --git a/platformio.ini b/platformio.ini index 26992cb..e8fcfb9 100644 --- a/platformio.ini +++ b/platformio.ini @@ -127,3 +127,6 @@ lib_deps = adafruit/Adafruit GFX Library@^1.11.10 ropg/Heltec_ESP32_LoRa_v3@^0.9.1 adafruit/Adafruit ST7735 and ST7789 Library@^1.10.4 + +[env:native] +platform = native diff --git a/src/core.cpp b/src/core.cpp new file mode 100644 index 0000000..a1c6330 --- /dev/null +++ b/src/core.cpp @@ -0,0 +1,57 @@ +#ifndef LORASA_CORE_CPP +#define LORASA_CORE_CPP + +#include "core.h" +#include +#include +#include + +float Scan::getRSSI() { return 0.1; } + +uint16_t Scan::rssiMethod(uint16_t *result) +{ + memset(result, 0, res_size * sizeof(uint16_t)); + int result_index = 0; + + uint16_t max_signal = 65535; + // N of samples + for (int r = 0; r < SAMPLES_RSSI; r++) + { + float rssi = getRSSI(); + uint16_t abs_rssi = abs(rssi); + if (abs_rssi < max_signal) + { + max_signal = abs_rssi; + } + // ToDO: check if 4 is correct value for 33 power bins + // Now we have more space because we are ignoring low dB values + // we can / 3 default 4 + if (RSSI_OUTPUT_FORMULA == 1) + { + result_index = + /// still not clear formula but it works + uint8_t(abs(rssi) / 4); + } + else if (RSSI_OUTPUT_FORMULA == 2) + { + // I like this formula better + result_index = uint8_t(abs(rssi) / 2) - 22; + } + + if (result_index >= res_size) + { + // Maximum index possible + result_index = res_size - 1; + } + + LOG("RSSI: %f IDX: %d\n", rssi, result_index); + if (result[result_index] == 0 || result[result_index] > abs_rssi) + { + result[result_index] = abs_rssi; + } + } + + return max_signal; +} + +#endif diff --git a/src/core.h b/src/core.h new file mode 100644 index 0000000..bdcaa51 --- /dev/null +++ b/src/core.h @@ -0,0 +1,45 @@ +#include +#include + +#ifndef LORASA_CORE_H + +#define LORASA_CORE_H + +#ifdef PRINT_DEBUG +#define LOG(args...) Serial.printf(args...) +#define LOG_IF(cond, args...) \ + if (cond) \ + LOG(args...) +#elif !defined(LOG) +#define LOG(args...) +#define LOG_IF(cond, args...) +#endif + +// Output Pixel Formula +// 1 = rssi / 4, 2 = (rssi / 2) - 22 or 20 +constexpr int RSSI_OUTPUT_FORMULA = 2; + +// based on the formula for RSSI_OUTPUT_FORMULA == 2 +// -2 * (22 + RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE) < rssi =< -44 +// practice may require a better pair of thresholds +constexpr float HI_RSSI_THRESHOLD = -44.0; +constexpr float LO_RSSI_THRESHOLD = HI_RSSI_THRESHOLD - 66; + +// number of samples for RSSI method +#define SAMPLES_RSSI 12 // 21 // + +struct Scan +{ + Scan(int sz) + : res_size(sz) + { + } + + virtual float getRSSI(); + + uint16_t rssiMethod(uint16_t *result); + + int res_size; +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp index 36667a6..71320a5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,6 +29,8 @@ // #define WIFI_SCANNING_ENABLED true // #define BT_SCANNING_ENABLED true +#include "core.h" + #ifndef LILYGO #include // This file contains a binary patch for the SX1262 @@ -160,10 +162,6 @@ typedef enum // #define METHOD_SPECTRAL // Spectral scan method #define METHOD_RSSI // Uncomment this and comment METHOD_SPECTRAL fot RSSI -// Output Pixel Formula -// 1 = rssi / 4, 2 = (rssi / 2) - 22 or 20 -constexpr int RSSI_OUTPUT_FORMULA = 2; - // Feature to scan diapasones. Other frequency settings will be ignored. // int SCAN_RANGES[] = {850890, 920950}; int SCAN_RANGES[] = {}; @@ -201,8 +199,6 @@ constexpr int WINDOW_SIZE = 15; // Number of samples for each frequency scan. Fewer samples = better temporal resolution. // if more than 100 it can freeze #define SAMPLES 35 //(scan time = 1294) -// number of samples for RSSI method -#define SAMPLES_RSSI 12 // 21 // #define RANGE (int)(FREQ_END - FREQ_BEGIN) @@ -220,7 +216,7 @@ uint64_t median_frequency = FREQ_BEGIN + FREQ_END - FREQ_BEGIN / 2; // #define DISABLE_PLOT_CHART false // unused // Array to store the scan results -int16_t result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; +uint16_t result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; bool filtered_result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; @@ -763,9 +759,21 @@ void check_ranges() single_page_scan = false; } } + +struct RadioScan : Scan +{ + RadioScan() : Scan(RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE) {} + + float getRSSI() override; +}; + +float RadioScan::getRSSI() { return radio.getRSSI(false); } + // MAX Frequency RSSI BIN value of the samples int max_rssi_x = 999; +RadioScan r; + void loop(void) { UI_displayDecorate(0, 0, false); // some default values @@ -854,6 +862,7 @@ void loop(void) // horizontal (x axis) Frequency loop osd_x = 1, osd_y = 2, col = 0, max_bin = 0; + int radio_error_count = 0; // x loop for (x = 0; x < STEPS * SCAN_RBW_FACTOR; x++) { @@ -882,12 +891,7 @@ void loop(void) Serial.println("setFrequency:" + String(freq)); #endif -#ifdef LILYGO state = radio.setFrequency(freq, false); // false = no calibration need here -#else - state = radio.setFrequency(freq, false); // false = no calibration need here -#endif - int radio_error_count = 0; if (state != RADIOLIB_ERR_NONE) { display.drawString(0, 64 - 10, "E:setFrequency:" + String(freq)); @@ -933,63 +937,11 @@ void loop(void) #ifdef METHOD_RSSI // Spectrum analyzer using getRSSI { -#ifdef PRINT_DEBUG - Serial.println("METHOD RSSI"); -#endif - // memset - // memset(result, 0, RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE); - // Some issues with memset function - for (i = 0; i < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; i++) + LOG("METHOD RSSI"); + uint16_t max_rssi = r.rssiMethod(result); + if (max_x_rssi[display_x] > max_rssi) { - result[i] = 0; - } - result_index = 0; - // N of samples - for (int r = 0; r < SAMPLES_RSSI; r++) - { - rssi = radio.getRSSI(false); - int abs_rssi = abs(rssi); - // ToDO: check if 4 is correct value for 33 power bins - // Now we have more space because we are ignoring low dB values - // we can / 3 default 4 - if (RSSI_OUTPUT_FORMULA == 1) - { - result_index = - /// still not clear formula but it works - uint8_t(abs_rssi / 4); - } - else if (RSSI_OUTPUT_FORMULA == 2) - { - // I like this formula better - result_index = uint8_t(abs_rssi / 2) - 22; - } - if (result_index >= RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE) - { - // Maximum index possible - result_index = RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE - 1; - } - -#ifdef PRINT_DEBUG - Serial.printf("RSSI: %d IDX: %d\n", rssi, result_index); -#endif - // avoid buffer overflow - if (result_index < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE) - { - // Saving max ABS value of RSSI. dB is negative, so smaller - // absolute value represents stronger signal. - if (result[result_index] == 0 || result[result_index] > abs_rssi) - { - result[result_index] = abs_rssi; - } - if (max_x_rssi[display_x] > abs_rssi) - { - max_x_rssi[display_x] = abs_rssi; - } - } - else - { - Serial.print("Out-of-Range: result_index %d\n"); - } + max_x_rssi[display_x] = max_rssi; } } #endif // SCAN_METHOD == METHOD_RSSI diff --git a/test/test_rssi.cpp b/test/test_rssi.cpp new file mode 100644 index 0000000..fad8e22 --- /dev/null +++ b/test/test_rssi.cpp @@ -0,0 +1,56 @@ +#include +#define LOG(args...) printf(args) +#include "../src/core.cpp" +#include + +void setUp(void) {} + +void tearDown(void) {} + +struct TestScan : Scan +{ + TestScan(float *ctx, int sz) : Scan(sz), ctx(ctx), sz(sz), idx(0) {} + + float getRSSI() override; + + float *ctx; + int sz; + int idx; +}; + +float TestScan::getRSSI() +{ + if (idx >= sz) + { + return -1000000; + } + + return ctx[idx++]; +} + +constexpr int test_sz = 13; + +void test_rssi(void) +{ + uint16_t samples[test_sz]; + float inputs[] = {-40.0, -100.0, -200.0, -50.0, -400.0, -60.0, -20, + -75.5, -70, -80, -90, -55.9, -110}; + + TestScan t = TestScan(inputs, test_sz); + + uint16_t r = t.rssiMethod(samples); + + uint16_t expect[test_sz] = {20, 50, 55, 60, 0, 70, 75, 80, 0, 90, 0, 100, 200}; + + TEST_ASSERT_EQUAL_INT16(20, r); + TEST_ASSERT_EQUAL_INT16_ARRAY(expect, samples, test_sz); +} + +int main(int argc, char **argv) +{ + UNITY_BEGIN(); + + RUN_TEST(test_rssi); + + UNITY_END(); +} From dbbe3f4e3a02eca9a7b22b4a30b39a68e6a5ce82 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Thu, 19 Sep 2024 19:52:49 +0100 Subject: [PATCH 13/80] Tests pass --- src/core.cpp | 15 +++++++++++++-- src/core.h | 3 ++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/core.cpp b/src/core.cpp index a1c6330..1eca711 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -18,6 +18,9 @@ uint16_t Scan::rssiMethod(uint16_t *result) for (int r = 0; r < SAMPLES_RSSI; r++) { float rssi = getRSSI(); + if (rssi < -65535) + rssi = -65535; + uint16_t abs_rssi = abs(rssi); if (abs_rssi < max_signal) { @@ -34,8 +37,16 @@ uint16_t Scan::rssiMethod(uint16_t *result) } else if (RSSI_OUTPUT_FORMULA == 2) { - // I like this formula better - result_index = uint8_t(abs(rssi) / 2) - 22; + if (rssi > HI_RSSI_THRESHOLD) + { + rssi = HI_RSSI_THRESHOLD; + } + else if (rssi < LO_RSSI_THRESHOLD) + { + rssi = LO_RSSI_THRESHOLD; + } + + result_index = uint8_t((HI_RSSI_THRESHOLD - rssi) * scale); } if (result_index >= res_size) diff --git a/src/core.h b/src/core.h index bdcaa51..36f449c 100644 --- a/src/core.h +++ b/src/core.h @@ -31,7 +31,7 @@ constexpr float LO_RSSI_THRESHOLD = HI_RSSI_THRESHOLD - 66; struct Scan { Scan(int sz) - : res_size(sz) + : res_size(sz), scale((float)sz / (HI_RSSI_THRESHOLD - LO_RSSI_THRESHOLD + 0.1)) { } @@ -40,6 +40,7 @@ struct Scan uint16_t rssiMethod(uint16_t *result); int res_size; + float scale; }; #endif From 02e59cbff2160edd4075c6ea7d561516568e331e Mon Sep 17 00:00:00 2001 From: Egor Shitikov Date: Thu, 19 Sep 2024 13:28:22 -0700 Subject: [PATCH 14/80] add sx1280 --- include/LiLyGo.h | 23 +++++++++++++--- include/global_config.h | 6 +++++ platformio.ini | 26 +++++++++++++++++- src/main.cpp | 60 ++++++++++++++++++++++++++++++++--------- 4 files changed, 98 insertions(+), 17 deletions(-) diff --git a/include/LiLyGo.h b/include/LiLyGo.h index ec8754c..37e02f0 100644 --- a/include/LiLyGo.h +++ b/include/LiLyGo.h @@ -2,6 +2,8 @@ #define UNUSED_PIN (0) // LilyGo defined +// Check this LiLyGo file LoraSA2\include\utilities.h + #define I2C_SDA 18 #define I2C_SCL 17 #define OLED_RST UNUSED_PIN @@ -27,6 +29,14 @@ #define RADIO_DIO1_PIN 33 #define RADIO_BUSY_PIN 34 +#ifdef USING_SX1280PA +#define RADIO_DIO1_PIN 9 // SX1280 DIO1 = IO9 +#define RADIO_BUSY_PIN 36 // SX1280 BUSY = IO36 +#define RADIO_RX_PIN 21 +#define RADIO_TX_PIN 10 +#define BUTTON_PIN 0 +#endif + // Define for our code #define RST_OLED UNUSED_PIN #define LED BOARD_LED @@ -52,11 +62,16 @@ #include SPIClass *hspi = new SPIClass(2); SX1262 radio = new Module(SS, DIO1, RST_LoRa, BUSY_LoRa, *hspi); -#else +#else // ARDUINO_heltec_wifi_32_lora_V3 +#ifdef USING_SX1280PA +SX1280 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN); +#endif // end USING_SX1280PA +#ifdef USING_SX1262 // Default SPI on pins from pins_arduino.h SX1262 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN); -#endif -#endif +#endif // end USING_SX1262 +#endif // end ARDUINO_heltec_wifi_32_lora_V3 +#endif // end HELTEC_NO_RADIO_INSTANCE void heltec_loop() {} @@ -107,7 +122,7 @@ PrintSplitter both(Serial, display); Print &both = Serial; #endif // some fake pin -#define BUTTON 38 +#define BUTTON BUTTON_PIN #include "HotButton.h" HotButton button(BUTTON); diff --git a/include/global_config.h b/include/global_config.h index d169fff..f8205e6 100644 --- a/include/global_config.h +++ b/include/global_config.h @@ -1,15 +1,21 @@ #ifndef __GLOBAL_CONFIG_H__ #define __GLOBAL_CONFIG_H__ +#ifndef FREQ_BEGIN // frequency range in MHz to scan #define FREQ_BEGIN 850 +#endif + +#ifndef FREQ_END // TODO: if % RANGE_PER_PAGE != 0 #define FREQ_END 950 +#endif // 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_SX1280 406. // Detection level from the 33 levels. The higher number is more sensitive #define DEFAULT_DRONE_DETECTION_LEVEL 18 diff --git a/platformio.ini b/platformio.ini index 26992cb..e2a476a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -29,7 +29,7 @@ lib_deps = ropg/Heltec_ESP32_LoRa_v3@^0.9.1 build_flags = -DHELTEC_POWER_BUTTON -[env:lilygo-T3S3-v1-2] +[env:lilygo-T3S3-v1-2-sx1262] platform = espressif32 board = t3_s3_v1_x framework = arduino @@ -51,6 +51,30 @@ build_flags = -DARDUINO_LILYGO_T3_S3_V1_X -DARDUINO_USB_MODE=1 + +[env:lilygo-T3S3-v1-2-xs1280] +platform = espressif32 +board = t3_s3_v1_x +framework = arduino +upload_speed = 921600 +monitor_speed = 115200 +board_build.f_cpu = 240000000 +lib_deps = + ropg/Heltec_ESP32_LoRa_v3@^0.9.1 + RadioLib +build_flags = + -DLILYGO + -DT3_S3_V1_2_SX1280_PA + -DARDUINO_LILYGO_T3S3_SX1280_PA + -DESP32 + -DUSING_SX1280PA + -DFREQ_BEGIN=2400 + -DFREQ_END=2500 + -DARDUINO_ARCH_ESP32 + -DARDUINO_USB_CDC_ON_BOOT=1 + -DARDUINO_LILYGO_T3_S3_V1_X + -DARDUINO_USB_MODE=1 + [env:heltec_wifi_lora_32_V3-test-signal-generator] platform = espressif32 board = heltec_wifi_lora_32_V3 diff --git a/src/main.cpp b/src/main.cpp index 36667a6..a0276d7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,11 +29,18 @@ // #define WIFI_SCANNING_ENABLED true // #define BT_SCANNING_ENABLED true +// Direct access to the low-level SPI communication between RadioLib and the radio module. +#define RADIOLIB_LOW_LEVEL (1) +// In this mode, all methods and member variables of all RadioLib classes will be made +// public and so will be exposed to the user. This allows direct manipulation of the +// library internals. +#define RADIOLIB_GODMODE (1) #ifndef LILYGO #include // This file contains a binary patch for the SX1262 #include "modules/SX126x/patches/SX126x_patch_scan.h" -#elif defined(LILYGO) +#endif // end LILYGO +#if defined(LILYGO) // LiLyGO device does not support the auto download mode, you need to get into the // download mode manually. To do so, press and hold the BOOT button and then press the // RESET button once. After that release the BOOT button. Or OFF->ON together with BOOT @@ -42,7 +49,6 @@ #include "utilities.h" // Our Code #include "LiLyGo.h" - #endif // end LILYGO #define BT_SCAN_DELAY 60 * 1 * 1000 @@ -172,15 +178,15 @@ int SCAN_RANGES[] = {}; // to put everything into one page set RANGE_PER_PAGE = FREQ_END - 800 uint64_t RANGE_PER_PAGE = FREQ_END - FREQ_BEGIN; // FREQ_END - FREQ_BEGIN +// To Enable Multi Screen scan +// uint64_t RANGE_PER_PAGE = 50; +// Default Range on Menu Button Switch + // multiplies STEPS * N to increase scan resolution. #define SCAN_RBW_FACTOR 2 constexpr int OSD_PIXELS_PER_CHAR = (STEPS * SCAN_RBW_FACTOR) / OSD_CHART_WIDTH; -// To Enable Multi Screen scan -// uint64_t RANGE_PER_PAGE = 50; -// Default Range on Menu Button Switch - #define DEFAULT_RANGE_PER_PAGE 50 // Print spectrum values pixels at once or by line @@ -403,14 +409,19 @@ void init_radio() { // initialize SX1262 FSK modem at the initial frequency both.println("Init radio"); +#ifdef USING_SX1280PA + radio.begin(); + state == radio.beginGFSK(FREQ_BEGIN); +#else state == radio.beginFSK(FREQ_BEGIN); - +#endif if (state == RADIOLIB_ERR_NONE) { Serial.println(F("success!")); } else { + display.println("Error:" + String(state)); Serial.print(F("failed, code ")); Serial.println(state); while (true) @@ -432,15 +443,25 @@ void init_radio() #endif both.println("Setting up radio"); +#ifdef USING_SX1280PA + // RADIOLIB_OR_HALT(radio.setBandwidth(RADIOLIB_SX128X_LORA_BW_406_25)); +#else RADIOLIB_OR_HALT(radio.setRxBandwidth(BANDWIDTH)); +#endif // and disable the data shaping RADIOLIB_OR_HALT(radio.setDataShaping(RADIOLIB_SHAPING_NONE)); both.println("Starting scanning..."); - // calibrate only once ,,, at startup - // TODO: check documentation (9.2.1) if we must calibrate in certain ranges +// calibrate only once ,,, at startup +// TODO: check documentation (9.2.1) if we must calibrate in certain ranges +#ifdef USING_SX1280PA + radio.setFrequency(FREQ_BEGIN); + state = radio.startReceive(); +#else radio.setFrequency(FREQ_BEGIN, true); +#endif + delay(50); } @@ -507,7 +528,7 @@ void setup(void) delay(400); display.clear(); - resolution = RANGE / (STEPS * SCAN_RBW_FACTOR); + resolution = (float)RANGE / (STEPS * SCAN_RBW_FACTOR); single_page_scan = (RANGE_PER_PAGE == range); @@ -570,7 +591,12 @@ void setup(void) #ifdef METHOD_RSSI // TODO: try RADIOLIB_SX126X_RX_TIMEOUT_INF +#ifdef USING_SX1280PA + state = radio.startReceive(RADIOLIB_SX128X_RX_TIMEOUT_NONE); +#else state = radio.startReceive(RADIOLIB_SX126X_RX_TIMEOUT_NONE); +#endif + if (state != RADIOLIB_ERR_NONE) { Serial.print(F("Failed to start receive mode, error code: ")); @@ -882,8 +908,9 @@ void loop(void) Serial.println("setFrequency:" + String(freq)); #endif -#ifdef LILYGO - state = radio.setFrequency(freq, false); // false = no calibration need here +#ifdef USING_SX1280PA + state = radio.setFrequency(freq); // 1280 doesn't have calibration + radio.startReceive(RADIOLIB_SX128X_RX_TIMEOUT_INF); #else state = radio.setFrequency(freq, false); // false = no calibration need here #endif @@ -947,7 +974,16 @@ void loop(void) // N of samples for (int r = 0; r < SAMPLES_RSSI; r++) { +#ifdef USING_SX1280PA + // radio.startReceive(); + // get instantaneous RSSI value + // When PR will be merged we can use radi.getRSSI(false); + uint8_t data[3] = {0, 0, 0}; // RssiInst, Status, RFU + radio.mod->SPIreadStream(RADIOLIB_SX128X_CMD_GET_RSSI_INST, data, 3); + rssi = ((float)data[0] / (-2.0)); +#else rssi = radio.getRSSI(false); +#endif int abs_rssi = abs(rssi); // ToDO: check if 4 is correct value for 33 power bins // Now we have more space because we are ignoring low dB values From ea319a6cac8694ca3a2338970afc4e579cae77b9 Mon Sep 17 00:00:00 2001 From: Egor Shitikov Date: Thu, 19 Sep 2024 16:00:38 -0700 Subject: [PATCH 15/80] Refactor RSSI Scan --- include/DFRobot_OSD.h | 59 +++++++++++++++++++++++++++++ src/core.cpp => lib/scan/scan.cpp | 3 +- src/core.h => lib/scan/scan.h | 0 platformio.ini | 1 + src/main.cpp | 62 ++----------------------------- test/test_rssi.cpp | 2 +- 6 files changed, 66 insertions(+), 61 deletions(-) rename src/core.cpp => lib/scan/scan.cpp (98%) rename src/core.h => lib/scan/scan.h (100%) diff --git a/include/DFRobot_OSD.h b/include/DFRobot_OSD.h index d446a86..063753c 100644 --- a/include/DFRobot_OSD.h +++ b/include/DFRobot_OSD.h @@ -13,8 +13,67 @@ #ifndef _DFRobot_OSD_H_ #define _DFRobot_OSD_H_ +#define MAX_POWER_LEVELS 33 + #include +/*Define Custom characters Example*/ +static const int buf0[36] = {0x02, 0x80, 0x02, 0x40, 0x7F, 0xE0, 0x42, 0x00, + 0x42, 0x00, 0x7A, 0x40, 0x4A, 0x40, 0x4A, 0x80, + 0x49, 0x20, 0x5A, 0xA0, 0x44, 0x60, 0x88, 0x20}; + +static constexpr uint16_t levels[10] = { + 0x105, // 0 + 0x10E, // 1 + 0x10D, // 2 + 0x10C, // 3 + 0x10B, // 4 + 0x10A, // 5 + 0x109, // 6 + 0x108, // 7 + 0x107, // 8 + 0x106, // 9 +}; + +static constexpr uint16_t power_level[MAX_POWER_LEVELS + 1] = { + 0x10E, // 0 + 0x10E, // 1 + 0x10D, // 2 + 0x10C, // 3 + 0x10B, // 4 + 0x10A, // 5 + 0x109, // 6 + 0x108, // 7 + 0x107, // 8 + 0x106, // 9 not using 106 to accent rise + // new line + 0x10E, // 10 + 0x10D, // 11 + 0x10C, // 12 + 0x10B, // 13 + 0x10A, // 14 + 0x109, // 15 + 0x108, // 16 + 0x107, // 17 + 0x106, // 18 not using 106 + // new line + 0x10E, // 19 + 0x10D, // 20 + 0x10C, // 21 + 0x10B, // 22 + 0x10A, // 23 + 0x109, // 24 + 0x108, // 25 + 0x107, // 26 + 0x106, // 27 + 0x105, // 28 --- + 0x105, // 29 + 0x105, // 30 + 0x105, // 31 + 0x105, // 32 + 0x105 // 33 +}; + // #define ENABLE_DBG //!< Open this macro and you can see the details of the program #ifdef ENABLE_DBG #define DBG(...) \ diff --git a/src/core.cpp b/lib/scan/scan.cpp similarity index 98% rename from src/core.cpp rename to lib/scan/scan.cpp index 1eca711..bf8b416 100644 --- a/src/core.cpp +++ b/lib/scan/scan.cpp @@ -1,7 +1,7 @@ #ifndef LORASA_CORE_CPP #define LORASA_CORE_CPP -#include "core.h" +#include "scan.h" #include #include #include @@ -13,6 +13,7 @@ uint16_t Scan::rssiMethod(uint16_t *result) memset(result, 0, res_size * sizeof(uint16_t)); int result_index = 0; + // uint16_t max_signal = 65535; // N of samples for (int r = 0; r < SAMPLES_RSSI; r++) diff --git a/src/core.h b/lib/scan/scan.h similarity index 100% rename from src/core.h rename to lib/scan/scan.h diff --git a/platformio.ini b/platformio.ini index e8fcfb9..7145e78 100644 --- a/platformio.ini +++ b/platformio.ini @@ -9,6 +9,7 @@ ; https://docs.platformio.org/page/projectconf.html [platformio] +default_envs = heltec_wifi_lora_32_V3 ; for env:vision-master-e190 ; src_dir = tft_src ; for env:vision-master-e290 diff --git a/src/main.cpp b/src/main.cpp index 71320a5..b6953bc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,11 +25,12 @@ #include -// #define OSD_ENABLED true +#define OSD_ENABLED true // #define WIFI_SCANNING_ENABLED true // #define BT_SCANNING_ENABLED true -#include "core.h" +// RSSI Scan Logic +#include #ifndef LILYGO #include @@ -64,58 +65,6 @@ uint64_t bt_start = 0; #include "DFRobot_OSD.h" #define OSD_SIDE_BAR true -static constexpr uint16_t levels[10] = { - 0x105, // 0 - 0x10E, // 1 - 0x10D, // 2 - 0x10C, // 3 - 0x10B, // 4 - 0x10A, // 5 - 0x109, // 6 - 0x108, // 7 - 0x107, // 8 - 0x106, // 9 -}; - -static constexpr uint16_t power_level[MAX_POWER_LEVELS + 1] = { - 0x10E, // 0 - 0x10E, // 1 - 0x10D, // 2 - 0x10C, // 3 - 0x10B, // 4 - 0x10A, // 5 - 0x109, // 6 - 0x108, // 7 - 0x107, // 8 - 0x106, // 9 not using 106 to accent rise - // new line - 0x10E, // 10 - 0x10D, // 11 - 0x10C, // 12 - 0x10B, // 13 - 0x10A, // 14 - 0x109, // 15 - 0x108, // 16 - 0x107, // 17 - 0x106, // 18 not using 106 - // new line - 0x10E, // 19 - 0x10D, // 20 - 0x10C, // 21 - 0x10B, // 22 - 0x10A, // 23 - 0x109, // 24 - 0x108, // 25 - 0x107, // 26 - 0x106, // 27 - 0x105, // 28 --- - 0x105, // 29 - 0x105, // 30 - 0x105, // 31 - 0x105, // 32 - 0x105 // 33 -}; - // SPI pins #define OSD_CS 47 #define OSD_MISO 33 @@ -140,11 +89,6 @@ int global_counter = 0; DFRobot_OSD osd(OSD_CS); #endif -/*Define Custom characters Example*/ -static const int buf0[36] = {0x02, 0x80, 0x02, 0x40, 0x7F, 0xE0, 0x42, 0x00, - 0x42, 0x00, 0x7A, 0x40, 0x4A, 0x40, 0x4A, 0x80, - 0x49, 0x20, 0x5A, 0xA0, 0x44, 0x60, 0x88, 0x20}; - #include "global_config.h" #include "ui.h" diff --git a/test/test_rssi.cpp b/test/test_rssi.cpp index fad8e22..9c4b023 100644 --- a/test/test_rssi.cpp +++ b/test/test_rssi.cpp @@ -1,6 +1,6 @@ #include #define LOG(args...) printf(args) -#include "../src/core.cpp" +#include #include void setUp(void) {} From d5b79b58edf15aed14a8afce8a2820a24f190721 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Fri, 20 Sep 2024 13:18:01 +0100 Subject: [PATCH 16/80] Undo unintended error count fix --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index b6953bc..300cac3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -806,7 +806,6 @@ void loop(void) // horizontal (x axis) Frequency loop osd_x = 1, osd_y = 2, col = 0, max_bin = 0; - int radio_error_count = 0; // x loop for (x = 0; x < STEPS * SCAN_RBW_FACTOR; x++) { @@ -836,6 +835,7 @@ void loop(void) #endif state = radio.setFrequency(freq, false); // false = no calibration need here + int radio_error_count = 0; if (state != RADIOLIB_ERR_NONE) { display.drawString(0, 64 - 10, "E:setFrequency:" + String(freq)); From 3b4dc0473c7dee4d95e04f20286b74b5ee6dfe75 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Fri, 20 Sep 2024 13:35:22 +0100 Subject: [PATCH 17/80] Report error code --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 300cac3..47a844f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -838,9 +838,9 @@ void loop(void) int radio_error_count = 0; if (state != RADIOLIB_ERR_NONE) { - display.drawString(0, 64 - 10, "E:setFrequency:" + String(freq)); - // display.drawString(0, 64 - 10, "E:setFrequency:" + String(freq)); - Serial.println("E:setFrequency:" + String(freq)); + display.drawString( + 0, 64 - 10, "E(" + String(state) + "):setFrequency:" + String(freq)); + Serial.println("E(" + String(state) + "):setFrequency:" + String(freq)); display.display(); delay(2); radio_error_count++; From d9170811eb940a4c8a9f7929c1f98f9187cce131 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Fri, 20 Sep 2024 19:11:30 +0100 Subject: [PATCH 18/80] Address review comments from PR 40 --- lib/scan/scan.cpp | 8 ++++---- lib/scan/scan.h | 14 ++++---------- src/main.cpp | 5 ++--- test/test_rssi.cpp | 13 +++++++------ 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/lib/scan/scan.cpp b/lib/scan/scan.cpp index bf8b416..c1f777f 100644 --- a/lib/scan/scan.cpp +++ b/lib/scan/scan.cpp @@ -6,17 +6,17 @@ #include #include -float Scan::getRSSI() { return 0.1; } - -uint16_t Scan::rssiMethod(uint16_t *result) +uint16_t Scan::rssiMethod(size_t samples, uint16_t *result, size_t res_size) { + float scale((float)res_size / (HI_RSSI_THRESHOLD - LO_RSSI_THRESHOLD + 0.1)); + memset(result, 0, res_size * sizeof(uint16_t)); int result_index = 0; // uint16_t max_signal = 65535; // N of samples - for (int r = 0; r < SAMPLES_RSSI; r++) + for (int r = 0; r < samples; r++) { float rssi = getRSSI(); if (rssi < -65535) diff --git a/lib/scan/scan.h b/lib/scan/scan.h index 36f449c..1c8ca8e 100644 --- a/lib/scan/scan.h +++ b/lib/scan/scan.h @@ -30,17 +30,11 @@ constexpr float LO_RSSI_THRESHOLD = HI_RSSI_THRESHOLD - 66; struct Scan { - Scan(int sz) - : res_size(sz), scale((float)sz / (HI_RSSI_THRESHOLD - LO_RSSI_THRESHOLD + 0.1)) - { - } + virtual float getRSSI() = 0; - virtual float getRSSI(); - - uint16_t rssiMethod(uint16_t *result); - - int res_size; - float scale; + // rssiMethod gets the data similar to the scan method, + // but uses getRSSI directly. + uint16_t rssiMethod(size_t samples, uint16_t *result, size_t res_size); }; #endif diff --git a/src/main.cpp b/src/main.cpp index 47a844f..6dcb633 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -706,8 +706,6 @@ void check_ranges() struct RadioScan : Scan { - RadioScan() : Scan(RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE) {} - float getRSSI() override; }; @@ -882,7 +880,8 @@ void loop(void) // Spectrum analyzer using getRSSI { LOG("METHOD RSSI"); - uint16_t max_rssi = r.rssiMethod(result); + uint16_t max_rssi = r.rssiMethod(SAMPLES_RSSI, result, + RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE); if (max_x_rssi[display_x] > max_rssi) { max_x_rssi[display_x] = max_rssi; diff --git a/test/test_rssi.cpp b/test/test_rssi.cpp index 9c4b023..b6ed9f3 100644 --- a/test/test_rssi.cpp +++ b/test/test_rssi.cpp @@ -9,7 +9,7 @@ void tearDown(void) {} struct TestScan : Scan { - TestScan(float *ctx, int sz) : Scan(sz), ctx(ctx), sz(sz), idx(0) {} + TestScan(float *ctx, int sz) : ctx(ctx), sz(sz), idx(0) {} float getRSSI() override; @@ -29,18 +29,19 @@ float TestScan::getRSSI() } constexpr int test_sz = 13; +constexpr int inputs_sz = 13; void test_rssi(void) { uint16_t samples[test_sz]; - float inputs[] = {-40.0, -100.0, -200.0, -50.0, -400.0, -60.0, -20, - -75.5, -70, -80, -90, -55.9, -110}; + float inputs[inputs_sz] = {-40.0, -100.0, -200.0, -50.0, -400.0, -60.0, -20, + -75.5, -70, -80, -90, -55.9, -110}; - TestScan t = TestScan(inputs, test_sz); + TestScan t = TestScan(inputs, inputs_sz); - uint16_t r = t.rssiMethod(samples); + uint16_t r = t.rssiMethod(inputs_sz, samples, test_sz); - uint16_t expect[test_sz] = {20, 50, 55, 60, 0, 70, 75, 80, 0, 90, 0, 100, 200}; + uint16_t expect[test_sz] = {20, 50, 55, 60, 0, 70, 75, 80, 0, 90, 0, 100, 110}; TEST_ASSERT_EQUAL_INT16(20, r); TEST_ASSERT_EQUAL_INT16_ARRAY(expect, samples, test_sz); From 8d16e01ce5a6110956def527507b20b8ede45dc9 Mon Sep 17 00:00:00 2001 From: Egor Shitikov Date: Sun, 22 Sep 2024 12:00:28 -0700 Subject: [PATCH 19/80] status debug --- src/main.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index a0276d7..1881d14 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -411,9 +411,9 @@ void init_radio() both.println("Init radio"); #ifdef USING_SX1280PA radio.begin(); - state == radio.beginGFSK(FREQ_BEGIN); + state = radio.beginGFSK(FREQ_BEGIN); #else - state == radio.beginFSK(FREQ_BEGIN); + state = radio.beginFSK(FREQ_BEGIN); #endif if (state == RADIOLIB_ERR_NONE) { @@ -450,14 +450,26 @@ void init_radio() #endif // and disable the data shaping - RADIOLIB_OR_HALT(radio.setDataShaping(RADIOLIB_SHAPING_NONE)); + state = radio.setDataShaping(RADIOLIB_SHAPING_NONE); + if (state != RADIOLIB_ERR_NONE) + { + Serial.println("Error:setDataShaping:" + String(state)); + } both.println("Starting scanning..."); // calibrate only once ,,, at startup // TODO: check documentation (9.2.1) if we must calibrate in certain ranges #ifdef USING_SX1280PA - radio.setFrequency(FREQ_BEGIN); + state = radio.setFrequency(FREQ_BEGIN); + if (state != RADIOLIB_ERR_NONE) + { + Serial.println("Error:setFrequency:" + String(state)); + } state = radio.startReceive(); + if (state != RADIOLIB_ERR_NONE) + { + Serial.println("Error:startReceive:" + String(state)); + } #else radio.setFrequency(FREQ_BEGIN, true); #endif From 655e8caf42b6959062de178a8d6fdba1593e79d9 Mon Sep 17 00:00:00 2001 From: Egor Shitikov Date: Sun, 22 Sep 2024 13:59:13 -0700 Subject: [PATCH 20/80] merge SX1280 with new ScanRSSI method logic --- include/LiLyGo.h | 38 ------------- platformio.ini | 3 ++ src/main.cpp | 103 ++++++++++++------------------------ src/radioScan/radioScan.cpp | 69 ++++++++++++++++++++++++ src/radioScan/radioScan.h | 40 ++++++++++++++ test/test_rssi.cpp | 57 ++++++++++++++++++++ 6 files changed, 204 insertions(+), 106 deletions(-) create mode 100644 src/radioScan/radioScan.cpp create mode 100644 src/radioScan/radioScan.h create mode 100644 test/test_rssi.cpp diff --git a/include/LiLyGo.h b/include/LiLyGo.h index 37e02f0..3c0c262 100644 --- a/include/LiLyGo.h +++ b/include/LiLyGo.h @@ -1,42 +1,4 @@ -#define UNUSED_PIN (0) -// LilyGo defined - -// Check this LiLyGo file LoraSA2\include\utilities.h - -#define I2C_SDA 18 -#define I2C_SCL 17 -#define OLED_RST UNUSED_PIN - -#define RADIO_SCLK_PIN 5 -#define RADIO_MISO_PIN 3 -#define RADIO_MOSI_PIN 6 -#define RADIO_CS_PIN 7 - -#define SDCARD_MOSI 11 -#define SDCARD_MISO 2 -#define SDCARD_SCLK 14 -#define SDCARD_CS 13 - -#define BOARD_LED 37 -#define LED_ON HIGH - -#define BUTTON_PIN 0 -#define ADC_PIN 1 - -#define RADIO_RST_PIN 8 - -#define RADIO_DIO1_PIN 33 -#define RADIO_BUSY_PIN 34 - -#ifdef USING_SX1280PA -#define RADIO_DIO1_PIN 9 // SX1280 DIO1 = IO9 -#define RADIO_BUSY_PIN 36 // SX1280 BUSY = IO36 -#define RADIO_RX_PIN 21 -#define RADIO_TX_PIN 10 -#define BUTTON_PIN 0 -#endif - // Define for our code #define RST_OLED UNUSED_PIN #define LED BOARD_LED diff --git a/platformio.ini b/platformio.ini index e2a476a..4e79c64 100644 --- a/platformio.ini +++ b/platformio.ini @@ -151,3 +151,6 @@ lib_deps = adafruit/Adafruit GFX Library@^1.11.10 ropg/Heltec_ESP32_LoRa_v3@^0.9.1 adafruit/Adafruit ST7735 and ST7789 Library@^1.10.4 + +[env:native] +platform = native diff --git a/src/main.cpp b/src/main.cpp index 1881d14..f83e37b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -35,6 +35,9 @@ // public and so will be exposed to the user. This allows direct manipulation of the // library internals. #define RADIOLIB_GODMODE (1) + +#include "radioScan/radioScan.h" + #ifndef LILYGO #include // This file contains a binary patch for the SX1262 @@ -162,13 +165,15 @@ typedef enum METHOD_SPECTRAL } TSCAN_METOD_ENUM; +// #define SCAN_METHOD METHOD_SPECTRAL + #define SCAN_METHOD // #define METHOD_SPECTRAL // Spectral scan method #define METHOD_RSSI // Uncomment this and comment METHOD_SPECTRAL fot RSSI // Output Pixel Formula // 1 = rssi / 4, 2 = (rssi / 2) - 22 or 20 -constexpr int RSSI_OUTPUT_FORMULA = 2; +// constexpr int RSSI_OUTPUT_FORMULA = 2; // Feature to scan diapasones. Other frequency settings will be ignored. // int SCAN_RANGES[] = {850890, 920950}; @@ -226,7 +231,7 @@ uint64_t median_frequency = FREQ_BEGIN + FREQ_END - FREQ_BEGIN / 2; // #define DISABLE_PLOT_CHART false // unused // Array to store the scan results -int16_t result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; +uint16_t result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; bool filtered_result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; @@ -410,7 +415,7 @@ void init_radio() // initialize SX1262 FSK modem at the initial frequency both.println("Init radio"); #ifdef USING_SX1280PA - radio.begin(); + // radio.begin(); state = radio.beginGFSK(FREQ_BEGIN); #else state = radio.beginFSK(FREQ_BEGIN); @@ -801,9 +806,31 @@ void check_ranges() single_page_scan = false; } } + +struct RadioScan : Scan +{ + float getRSSI() override; +}; + +float RadioScan::getRSSI() +{ +#ifdef USING_SX1280PA + // radio.startReceive(); + // get instantaneous RSSI value + // When PR will be merged we can use radi.getRSSI(false); + uint8_t data[3] = {0, 0, 0}; // RssiInst, Status, RFU + radio.mod->SPIreadStream(RADIOLIB_SX128X_CMD_GET_RSSI_INST, data, 3); + return ((float)data[0] / (-2.0)); +#else + return radio.getRSSI(false); +#endif +} + // MAX Frequency RSSI BIN value of the samples int max_rssi_x = 999; +RadioScan r; + void loop(void) { UI_displayDecorate(0, 0, false); // some default values @@ -972,72 +999,12 @@ void loop(void) #ifdef METHOD_RSSI // Spectrum analyzer using getRSSI { -#ifdef PRINT_DEBUG - Serial.println("METHOD RSSI"); -#endif - // memset - // memset(result, 0, RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE); - // Some issues with memset function - for (i = 0; i < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; i++) + LOG("METHOD RSSI"); + uint16_t max_rssi = r.rssiMethod(SAMPLES_RSSI, result, + RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE); + if (max_x_rssi[display_x] > max_rssi) { - result[i] = 0; - } - result_index = 0; - // N of samples - for (int r = 0; r < SAMPLES_RSSI; r++) - { -#ifdef USING_SX1280PA - // radio.startReceive(); - // get instantaneous RSSI value - // When PR will be merged we can use radi.getRSSI(false); - uint8_t data[3] = {0, 0, 0}; // RssiInst, Status, RFU - radio.mod->SPIreadStream(RADIOLIB_SX128X_CMD_GET_RSSI_INST, data, 3); - rssi = ((float)data[0] / (-2.0)); -#else - rssi = radio.getRSSI(false); -#endif - int abs_rssi = abs(rssi); - // ToDO: check if 4 is correct value for 33 power bins - // Now we have more space because we are ignoring low dB values - // we can / 3 default 4 - if (RSSI_OUTPUT_FORMULA == 1) - { - result_index = - /// still not clear formula but it works - uint8_t(abs_rssi / 4); - } - else if (RSSI_OUTPUT_FORMULA == 2) - { - // I like this formula better - result_index = uint8_t(abs_rssi / 2) - 22; - } - if (result_index >= RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE) - { - // Maximum index possible - result_index = RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE - 1; - } - -#ifdef PRINT_DEBUG - Serial.printf("RSSI: %d IDX: %d\n", rssi, result_index); -#endif - // avoid buffer overflow - if (result_index < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE) - { - // Saving max ABS value of RSSI. dB is negative, so smaller - // absolute value represents stronger signal. - if (result[result_index] == 0 || result[result_index] > abs_rssi) - { - result[result_index] = abs_rssi; - } - if (max_x_rssi[display_x] > abs_rssi) - { - max_x_rssi[display_x] = abs_rssi; - } - } - else - { - Serial.print("Out-of-Range: result_index %d\n"); - } + max_x_rssi[display_x] = max_rssi; } } #endif // SCAN_METHOD == METHOD_RSSI diff --git a/src/radioScan/radioScan.cpp b/src/radioScan/radioScan.cpp new file mode 100644 index 0000000..73150fc --- /dev/null +++ b/src/radioScan/radioScan.cpp @@ -0,0 +1,69 @@ +#ifndef LORASA_CORE_CPP +#define LORASA_CORE_CPP + +#include "radioScan.h" +#include +#include +#include + +uint16_t Scan::rssiMethod(size_t samples, uint16_t *result, size_t res_size) +{ + float scale((float)res_size / (HI_RSSI_THRESHOLD - LO_RSSI_THRESHOLD + 0.1)); + + memset(result, 0, res_size * sizeof(uint16_t)); + int result_index = 0; + + // + uint16_t max_signal = 65535; + // N of samples + for (int r = 0; r < samples; r++) + { + float rssi = getRSSI(); + if (rssi < -65535) + rssi = -65535; + + uint16_t abs_rssi = abs(rssi); + if (abs_rssi < max_signal) + { + max_signal = abs_rssi; + } + // ToDO: check if 4 is correct value for 33 power bins + // Now we have more space because we are ignoring low dB values + // we can / 3 default 4 + if (RSSI_OUTPUT_FORMULA == 1) + { + result_index = + /// still not clear formula but it works + uint8_t(abs(rssi) / 4); + } + else if (RSSI_OUTPUT_FORMULA == 2) + { + if (rssi > HI_RSSI_THRESHOLD) + { + rssi = HI_RSSI_THRESHOLD; + } + else if (rssi < LO_RSSI_THRESHOLD) + { + rssi = LO_RSSI_THRESHOLD; + } + + result_index = uint8_t((HI_RSSI_THRESHOLD - rssi) * scale); + } + + if (result_index >= res_size) + { + // Maximum index possible + result_index = res_size - 1; + } + + LOG("RSSI: %f IDX: %d\n", rssi, result_index); + if (result[result_index] == 0 || result[result_index] > abs_rssi) + { + result[result_index] = abs_rssi; + } + } + + return max_signal; +} + +#endif diff --git a/src/radioScan/radioScan.h b/src/radioScan/radioScan.h new file mode 100644 index 0000000..1c8ca8e --- /dev/null +++ b/src/radioScan/radioScan.h @@ -0,0 +1,40 @@ +#include +#include + +#ifndef LORASA_CORE_H + +#define LORASA_CORE_H + +#ifdef PRINT_DEBUG +#define LOG(args...) Serial.printf(args...) +#define LOG_IF(cond, args...) \ + if (cond) \ + LOG(args...) +#elif !defined(LOG) +#define LOG(args...) +#define LOG_IF(cond, args...) +#endif + +// Output Pixel Formula +// 1 = rssi / 4, 2 = (rssi / 2) - 22 or 20 +constexpr int RSSI_OUTPUT_FORMULA = 2; + +// based on the formula for RSSI_OUTPUT_FORMULA == 2 +// -2 * (22 + RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE) < rssi =< -44 +// practice may require a better pair of thresholds +constexpr float HI_RSSI_THRESHOLD = -44.0; +constexpr float LO_RSSI_THRESHOLD = HI_RSSI_THRESHOLD - 66; + +// number of samples for RSSI method +#define SAMPLES_RSSI 12 // 21 // + +struct Scan +{ + virtual float getRSSI() = 0; + + // rssiMethod gets the data similar to the scan method, + // but uses getRSSI directly. + uint16_t rssiMethod(size_t samples, uint16_t *result, size_t res_size); +}; + +#endif diff --git a/test/test_rssi.cpp b/test/test_rssi.cpp new file mode 100644 index 0000000..0215179 --- /dev/null +++ b/test/test_rssi.cpp @@ -0,0 +1,57 @@ +#include +#define LOG(args...) printf(args) +#include "../src/radioScan/radioScan.cpp" +#include + +void setUp(void) {} + +void tearDown(void) {} + +struct TestScan : Scan +{ + TestScan(float *ctx, int sz) : ctx(ctx), sz(sz), idx(0) {} + + float getRSSI() override; + + float *ctx; + int sz; + int idx; +}; + +float TestScan::getRSSI() +{ + if (idx >= sz) + { + return -1000000; + } + + return ctx[idx++]; +} + +constexpr int test_sz = 13; +constexpr int inputs_sz = 13; + +void test_rssi(void) +{ + uint16_t samples[test_sz]; + float inputs[inputs_sz] = {-40.0, -100.0, -200.0, -50.0, -400.0, -60.0, -20, + -75.5, -70, -80, -90, -55.9, -110}; + + TestScan t = TestScan(inputs, inputs_sz); + + uint16_t r = t.rssiMethod(inputs_sz, samples, test_sz); + + uint16_t expect[test_sz] = {20, 50, 55, 60, 0, 70, 75, 80, 0, 90, 0, 100, 110}; + + TEST_ASSERT_EQUAL_INT16(20, r); + TEST_ASSERT_EQUAL_INT16_ARRAY(expect, samples, test_sz); +} + +int main(int argc, char **argv) +{ + UNITY_BEGIN(); + + RUN_TEST(test_rssi); + + UNITY_END(); +} From c5ac3896fb0f6d669031c3ea1ae99e1f06e23628 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Sat, 21 Sep 2024 16:36:21 +0100 Subject: [PATCH 21/80] Factor out detection + display loop Refactor code that does not depend on y. Essentially, the following logic: Recall that some loops are folds, some are maps. Maps look at only elements with one index. The important property of maps is that map(f . g) = map(f) . map(g) - parts of the loop body can be split into separate loops. Another property of maps is that if map(f) does not depend on the output of map(g), then map(f) . map(g) = map(g) . map(f) - the loops can be reordered. --- lib/scan/scan.cpp | 67 +++++++++++++++- lib/scan/scan.h | 22 +++--- src/main.cpp | 148 +++++++++++++----------------------- src/radioScan/radioScan.cpp | 59 ++++++++++++++ src/radioScan/radioScan.h | 10 +++ 5 files changed, 197 insertions(+), 109 deletions(-) diff --git a/lib/scan/scan.cpp b/lib/scan/scan.cpp index bf8b416..bc58902 100644 --- a/lib/scan/scan.cpp +++ b/lib/scan/scan.cpp @@ -6,17 +6,17 @@ #include #include -float Scan::getRSSI() { return 0.1; } - -uint16_t Scan::rssiMethod(uint16_t *result) +uint16_t Scan::rssiMethod(size_t samples, uint16_t *result, size_t res_size) { + float scale((float)res_size / (HI_RSSI_THRESHOLD - LO_RSSI_THRESHOLD + 0.1)); + memset(result, 0, res_size * sizeof(uint16_t)); int result_index = 0; // uint16_t max_signal = 65535; // N of samples - for (int r = 0; r < SAMPLES_RSSI; r++) + for (int r = 0; r < samples; r++) { float rssi = getRSSI(); if (rssi < -65535) @@ -66,4 +66,63 @@ uint16_t Scan::rssiMethod(uint16_t *result) return max_signal; } +size_t Scan::detect(uint16_t *result, bool *filtered_result, size_t result_size, + int samples) +{ + size_t max_rssi_x = 999; + + for (int y = 0; y < result_size; y++) + { + + LOG("%i:%i,", y, result[y]); +#if !defined(FILTER_SPECTRUM_RESULTS) || FILTER_SPECTRUM_RESULTS == false + if (result[y] && result[y] != 0) + { + filtered_result[y] = 1; + } + else + { + filtered_result[y] = 0; + } +#endif + +// if samples low ~1 filter removes all values +#if FILTER_SPECTRUM_RESULTS + + filtered_result[y] = 0; + // Filter Elements without neighbors + // if RSSI method actual value is -xxx dB + if (result[y] > 0 && samples > 1) + { + // do not process 'first' and 'last' row to avoid out of index + // access. + if ((y > 0) && (y < (result_size - 2))) + { + if (((result[y + 1] != 0) && (result[y + 2] != 0)) || + (result[y - 1] != 0)) + { + filtered_result[y] = 1; + // Fill empty pixel + result[y + 1] = 1; + } + else + { + LOG("Filtered::%i,", y); + } + } + } // not filtering if samples == 1 because it will be filtered + else if (result[y] > 0 && samples == 1) + { + filtered_result[y] = 1; + } +#endif + if (filtered_result[y] && max_rssi_x > y) + { + max_rssi_x = y; + } + } + + return max_rssi_x; +} + #endif diff --git a/lib/scan/scan.h b/lib/scan/scan.h index 36f449c..bfbdcf8 100644 --- a/lib/scan/scan.h +++ b/lib/scan/scan.h @@ -30,17 +30,21 @@ constexpr float LO_RSSI_THRESHOLD = HI_RSSI_THRESHOLD - 66; struct Scan { - Scan(int sz) - : res_size(sz), scale((float)sz / (HI_RSSI_THRESHOLD - LO_RSSI_THRESHOLD + 0.1)) - { - } + virtual float getRSSI() = 0; - virtual float getRSSI(); + // rssiMethod gets the data similar to the scan method, + // but uses getRSSI directly. + uint16_t rssiMethod(size_t samples, uint16_t *result, size_t res_size); - uint16_t rssiMethod(uint16_t *result); - - int res_size; - float scale; + // detect method analyses result, and produces filtered_result, marking + // those values that represent a detection event. + // It returns index that represents strongest signal at which a detection event + // occurred. + static size_t detect(uint16_t *result, bool *filtered_result, size_t result_size, + int samples); }; +// Remove reading without neighbors +#define FILTER_SPECTRUM_RESULTS true + #endif diff --git a/src/main.cpp b/src/main.cpp index a7704e9..6f48c1d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -144,8 +144,6 @@ bool ANIMATED_RELOAD = false; #define UP_FILTER 5 // Trim low signals - nose level #define START_LOW 6 -// Remove reading without neighbors -#define FILTER_SPECTRUM_RESULTS true #define FILTER_SAMPLES_MIN constexpr bool DRAW_DETECTION_TICKS = true; int16_t max_x_rssi[STEPS] = {999}; @@ -888,9 +886,7 @@ void loop(void) float step = (range * ((float)x / (STEPS * SCAN_RBW_FACTOR))); freq = fr_begin + step; -#ifdef PRINT_DEBUG - Serial.println("setFrequency:" + String(freq)); -#endif + LOG("setFrequency:%f\n", freq); #ifdef USING_SX1280PA state = radio.setFrequency(freq); // 1280 doesn't have calibration @@ -911,9 +907,7 @@ void loop(void) continue; } -#ifdef PRINT_DEBUG - Serial.printf("Step:%d Freq: %f\n", x, freq); -#endif + LOG("Step:%d Freq: %f\n", x, freq); // SpectralScan Method #ifdef METHOD_SPECTRAL { @@ -971,59 +965,21 @@ void loop(void) display.setColor(WHITE); } #endif - detected = false; - detected_y[display_x] = false; - max_rssi_x = 999; + size_t detected_at = r.detect( + result, filtered_result, RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE, samples); - for (y = 0; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) + if (max_rssi_x > detected_at) { + // MAx bin Value not RSSI + max_rssi_x = detected_at; + } -#ifdef PRINT_DEBUG - Serial.print(String(y) + ":"); - Serial.print(String(result[y]) + ","); -#endif -#if !defined(FILTER_SPECTRUM_RESULTS) || FILTER_SPECTRUM_RESULTS == false - if (result[y] && result[y] != 0) - { - filtered_result[y] = 1; - } - else - { - filtered_result[y] = 0; - } -#endif + detected = detected_at < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; + detected_y[display_x] = false; -// if samples low ~1 filter removes all values #if FILTER_SPECTRUM_RESULTS - - filtered_result[y] = 0; - // Filter Elements without neighbors - // if RSSI method actual value is -xxx dB - if (result[y] > 0 && samples > 1) - { - // do not process 'first' and 'last' row to avoid out of index - // access. - if ((y > 0) && (y < (RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE - 2))) - { - if (((result[y + 1] != 0) && (result[y + 2] != 0)) || - (result[y - 1] != 0)) - { - filtered_result[y] = 1; - // Fill empty pixel - result[y + 1] = 1; - } - else - { -#ifdef PRINT_DEBUG - Serial.print("Filtered:" + String(x) + ":" + String(y) + ","); -#endif - } - } - } // not filtering if samples == 1 because it will be filtered - else if (result[y] > 0 && samples == 1) - { - filtered_result[y] = 1; - } + for (int y = 0; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) + { // calculating max window x RSSI after filters x_window = (int)(display_x / WINDOW_SIZE); int abs_result = abs(result[y]); @@ -1031,16 +987,15 @@ void loop(void) max_x_window[x_window] > abs_result) { max_x_window[x_window] = abs_result; -#ifdef PRINT_DEBUG - Serial.println("MAX x window: " + String(x_window) + " " + - String(abs_result)); -#endif + LOG("MAX x window: %i %i\n", x_window, abs_result); } + } #endif + + if (detected_at <= drone_detection_level) + { // check if we should alarm about a drone presence - if ((filtered_result[y] == 1) // we have some data and - && (y <= drone_detection_level) && - detected_y[display_x] == false) // detection threshold match + if (detected_y[display_x] == false) // detection threshold match { // Set LED to ON (filtered in UI component) UI_setLedFlag(true); @@ -1084,8 +1039,7 @@ void loop(void) } } #if (WATERFALL_ENABLED == true) - if ((filtered_result[y] == 1) && (y <= drone_detection_level) && - (single_page_scan) && (waterfall[display_x] != true) && new_pixel) + if ((single_page_scan) && (waterfall[display_x] != true) && new_pixel) { // If drone not found set dark pixel on the waterfall // TODO: make something like scrolling up if possible @@ -1095,44 +1049,46 @@ void loop(void) display.setColor(WHITE); } #endif - // next 2 If's ... adds !!!! 10ms of runtime ......tfk ??? + } + +#ifdef PRINT_DEBUG + for (int y = 0; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) + { if (filtered_result[y] == 1) { -#ifdef PRINT_DEBUG - Serial.print("Pixel:" + String(display_x) + "(" + String(x) + ")" + - ":" + String(y) + ","); -#endif - if (max_rssi_x > y) - { - // MAx bin Value not RSSI - max_rssi_x = y; - } - // Set MAIN signal level pixel - if (y < MAX_POWER_LEVELS - START_LOW) - { - display.setPixel(display_x, y + START_LOW); - } - if (!detected) - { - detected = true; - } + LOG("Pixel:%i(%i):%i,", display_x, x, y); } + } +#endif - // ------------------------------------------------------------- - // Draw "Detection Level line" every 2 pixel - // ------------------------------------------------------------- - if ((y == drone_detection_level) && (display_x % 2 == 0)) + for (int y = 0; y < min(RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE, + MAX_POWER_LEVELS - START_LOW); + y++) + { + if (filtered_result[y] == 1) + { + // Set MAIN signal level pixel + display.setPixelColor(display_x, y + START_LOW, WHITE); + } + } + + // ------------------------------------------------------------- + // Draw "Detection Level line" every 2 pixel + // ------------------------------------------------------------- + if (display_x % 2 == 0) + { + if (filtered_result[drone_detection_level] == 1) + { + display.setColor(INVERSE); + } + else { display.setColor(WHITE); - if (filtered_result[y] == 1) - { - display.setColor(INVERSE); - } - display.setPixel(display_x, y + START_LOW); - // display.setPixel(display_x, y + START_LOW - 1); // 2 px wide - - display.setColor(WHITE); } + display.setPixel(display_x, drone_detection_level + START_LOW); + // display.setPixel(display_x, y + START_LOW - 1); // 2 px wide + + display.setColor(WHITE); } #ifdef JOYSTICK_ENABLED diff --git a/src/radioScan/radioScan.cpp b/src/radioScan/radioScan.cpp index 73150fc..a5a2332 100644 --- a/src/radioScan/radioScan.cpp +++ b/src/radioScan/radioScan.cpp @@ -66,4 +66,63 @@ uint16_t Scan::rssiMethod(size_t samples, uint16_t *result, size_t res_size) return max_signal; } +size_t Scan::detect(uint16_t *result, bool *filtered_result, size_t result_size, + int samples) +{ + size_t max_rssi_x = 999; + + for (int y = 0; y < result_size; y++) + { + + LOG("%i:%i,", y, result[y]); +#if !defined(FILTER_SPECTRUM_RESULTS) || FILTER_SPECTRUM_RESULTS == false + if (result[y] && result[y] != 0) + { + filtered_result[y] = 1; + } + else + { + filtered_result[y] = 0; + } +#endif + +// if samples low ~1 filter removes all values +#if FILTER_SPECTRUM_RESULTS + + filtered_result[y] = 0; + // Filter Elements without neighbors + // if RSSI method actual value is -xxx dB + if (result[y] > 0 && samples > 1) + { + // do not process 'first' and 'last' row to avoid out of index + // access. + if ((y > 0) && (y < (result_size - 2))) + { + if (((result[y + 1] != 0) && (result[y + 2] != 0)) || + (result[y - 1] != 0)) + { + filtered_result[y] = 1; + // Fill empty pixel + result[y + 1] = 1; + } + else + { + LOG("Filtered::%i,", y); + } + } + } // not filtering if samples == 1 because it will be filtered + else if (result[y] > 0 && samples == 1) + { + filtered_result[y] = 1; + } +#endif + if (filtered_result[y] && max_rssi_x > y) + { + max_rssi_x = y; + } + } + + return max_rssi_x; +} + #endif diff --git a/src/radioScan/radioScan.h b/src/radioScan/radioScan.h index 1c8ca8e..bfbdcf8 100644 --- a/src/radioScan/radioScan.h +++ b/src/radioScan/radioScan.h @@ -35,6 +35,16 @@ struct Scan // rssiMethod gets the data similar to the scan method, // but uses getRSSI directly. uint16_t rssiMethod(size_t samples, uint16_t *result, size_t res_size); + + // detect method analyses result, and produces filtered_result, marking + // those values that represent a detection event. + // It returns index that represents strongest signal at which a detection event + // occurred. + static size_t detect(uint16_t *result, bool *filtered_result, size_t result_size, + int samples); }; +// Remove reading without neighbors +#define FILTER_SPECTRUM_RESULTS true + #endif From 718ea408599c95e19daa6445319d093e506701fc Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Sun, 22 Sep 2024 09:57:34 +0100 Subject: [PATCH 22/80] Add test for detect() --- test/test_rssi.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/test_rssi.cpp b/test/test_rssi.cpp index 0215179..519f72f 100644 --- a/test/test_rssi.cpp +++ b/test/test_rssi.cpp @@ -47,11 +47,32 @@ void test_rssi(void) TEST_ASSERT_EQUAL_INT16_ARRAY(expect, samples, test_sz); } +void test_detect() +{ + uint16_t samples[test_sz] = {20, 50, 55, 60, 0, 70, 75, 80, 0, 90, 0, 100, 110}; + bool result[test_sz]; + + size_t r = Scan::detect(samples, result, test_sz, 1); + + bool expect[test_sz] = {1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1}; + + TEST_ASSERT_EQUAL_INT16(0, r); + TEST_ASSERT_EQUAL_INT8_ARRAY(expect, result, test_sz); + + r = Scan::detect(samples, result, test_sz, 2); + + bool expect2[test_sz] = {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}; + + TEST_ASSERT_EQUAL_INT16(1, r); + TEST_ASSERT_EQUAL_INT8_ARRAY(expect2, result, test_sz); +} + int main(int argc, char **argv) { UNITY_BEGIN(); RUN_TEST(test_rssi); + RUN_TEST(test_detect); UNITY_END(); } From 94c042a18698706e9a0b4729cd29c3152dbab9e2 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Thu, 26 Sep 2024 21:35:32 +0100 Subject: [PATCH 23/80] LilyGO T3S3 works --- {include => lib/loraboards}/LoRaBoards.cpp | 0 {include => lib/loraboards}/LoRaBoards.h | 0 {include => lib/loraboards}/utilities.h | 0 platformio.ini | 2 + src/main.cpp | 10 +- src/radioScan/radioScan.cpp | 128 --------------------- src/radioScan/radioScan.h | 50 -------- test/test_rssi.cpp | 2 +- 8 files changed, 10 insertions(+), 182 deletions(-) rename {include => lib/loraboards}/LoRaBoards.cpp (100%) rename {include => lib/loraboards}/LoRaBoards.h (100%) rename {include => lib/loraboards}/utilities.h (100%) delete mode 100644 src/radioScan/radioScan.cpp delete mode 100644 src/radioScan/radioScan.h diff --git a/include/LoRaBoards.cpp b/lib/loraboards/LoRaBoards.cpp similarity index 100% rename from include/LoRaBoards.cpp rename to lib/loraboards/LoRaBoards.cpp diff --git a/include/LoRaBoards.h b/lib/loraboards/LoRaBoards.h similarity index 100% rename from include/LoRaBoards.h rename to lib/loraboards/LoRaBoards.h diff --git a/include/utilities.h b/lib/loraboards/utilities.h similarity index 100% rename from include/utilities.h rename to lib/loraboards/utilities.h diff --git a/platformio.ini b/platformio.ini index 5ff09ab..997258b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -63,6 +63,8 @@ board_build.f_cpu = 240000000 lib_deps = ropg/Heltec_ESP32_LoRa_v3@^0.9.1 RadioLib + U8g2 + XPowersLib build_flags = -DLILYGO -DT3_S3_V1_2_SX1280_PA diff --git a/src/main.cpp b/src/main.cpp index c4f3573..fdcd374 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,7 +21,7 @@ https://jgromes.github.io/RadioLib/ */ -// #define HELTEC_NO_DISPLAY +// #define HELTEC_NO_DISPLAY #include @@ -36,7 +36,8 @@ // library internals. #define RADIOLIB_GODMODE (1) -#include "radioScan/radioScan.h" +#include +#include #ifndef LILYGO #include @@ -354,7 +355,6 @@ void init_radio() // initialize SX1262 FSK modem at the initial frequency both.println("Init radio"); #ifdef USING_SX1280PA - // radio.begin(); state = radio.beginGFSK(FREQ_BEGIN); #else state = radio.beginFSK(FREQ_BEGIN); @@ -423,6 +423,10 @@ void init_radio() void setup(void) { + setupBoards(); + delay(5000); + Serial.println("setup is done"); + // LED brightness heltec_led(25); #ifdef OSD_ENABLED diff --git a/src/radioScan/radioScan.cpp b/src/radioScan/radioScan.cpp deleted file mode 100644 index a5a2332..0000000 --- a/src/radioScan/radioScan.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#ifndef LORASA_CORE_CPP -#define LORASA_CORE_CPP - -#include "radioScan.h" -#include -#include -#include - -uint16_t Scan::rssiMethod(size_t samples, uint16_t *result, size_t res_size) -{ - float scale((float)res_size / (HI_RSSI_THRESHOLD - LO_RSSI_THRESHOLD + 0.1)); - - memset(result, 0, res_size * sizeof(uint16_t)); - int result_index = 0; - - // - uint16_t max_signal = 65535; - // N of samples - for (int r = 0; r < samples; r++) - { - float rssi = getRSSI(); - if (rssi < -65535) - rssi = -65535; - - uint16_t abs_rssi = abs(rssi); - if (abs_rssi < max_signal) - { - max_signal = abs_rssi; - } - // ToDO: check if 4 is correct value for 33 power bins - // Now we have more space because we are ignoring low dB values - // we can / 3 default 4 - if (RSSI_OUTPUT_FORMULA == 1) - { - result_index = - /// still not clear formula but it works - uint8_t(abs(rssi) / 4); - } - else if (RSSI_OUTPUT_FORMULA == 2) - { - if (rssi > HI_RSSI_THRESHOLD) - { - rssi = HI_RSSI_THRESHOLD; - } - else if (rssi < LO_RSSI_THRESHOLD) - { - rssi = LO_RSSI_THRESHOLD; - } - - result_index = uint8_t((HI_RSSI_THRESHOLD - rssi) * scale); - } - - if (result_index >= res_size) - { - // Maximum index possible - result_index = res_size - 1; - } - - LOG("RSSI: %f IDX: %d\n", rssi, result_index); - if (result[result_index] == 0 || result[result_index] > abs_rssi) - { - result[result_index] = abs_rssi; - } - } - - return max_signal; -} - -size_t Scan::detect(uint16_t *result, bool *filtered_result, size_t result_size, - int samples) -{ - size_t max_rssi_x = 999; - - for (int y = 0; y < result_size; y++) - { - - LOG("%i:%i,", y, result[y]); -#if !defined(FILTER_SPECTRUM_RESULTS) || FILTER_SPECTRUM_RESULTS == false - if (result[y] && result[y] != 0) - { - filtered_result[y] = 1; - } - else - { - filtered_result[y] = 0; - } -#endif - -// if samples low ~1 filter removes all values -#if FILTER_SPECTRUM_RESULTS - - filtered_result[y] = 0; - // Filter Elements without neighbors - // if RSSI method actual value is -xxx dB - if (result[y] > 0 && samples > 1) - { - // do not process 'first' and 'last' row to avoid out of index - // access. - if ((y > 0) && (y < (result_size - 2))) - { - if (((result[y + 1] != 0) && (result[y + 2] != 0)) || - (result[y - 1] != 0)) - { - filtered_result[y] = 1; - // Fill empty pixel - result[y + 1] = 1; - } - else - { - LOG("Filtered::%i,", y); - } - } - } // not filtering if samples == 1 because it will be filtered - else if (result[y] > 0 && samples == 1) - { - filtered_result[y] = 1; - } -#endif - if (filtered_result[y] && max_rssi_x > y) - { - max_rssi_x = y; - } - } - - return max_rssi_x; -} - -#endif diff --git a/src/radioScan/radioScan.h b/src/radioScan/radioScan.h deleted file mode 100644 index bfbdcf8..0000000 --- a/src/radioScan/radioScan.h +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include - -#ifndef LORASA_CORE_H - -#define LORASA_CORE_H - -#ifdef PRINT_DEBUG -#define LOG(args...) Serial.printf(args...) -#define LOG_IF(cond, args...) \ - if (cond) \ - LOG(args...) -#elif !defined(LOG) -#define LOG(args...) -#define LOG_IF(cond, args...) -#endif - -// Output Pixel Formula -// 1 = rssi / 4, 2 = (rssi / 2) - 22 or 20 -constexpr int RSSI_OUTPUT_FORMULA = 2; - -// based on the formula for RSSI_OUTPUT_FORMULA == 2 -// -2 * (22 + RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE) < rssi =< -44 -// practice may require a better pair of thresholds -constexpr float HI_RSSI_THRESHOLD = -44.0; -constexpr float LO_RSSI_THRESHOLD = HI_RSSI_THRESHOLD - 66; - -// number of samples for RSSI method -#define SAMPLES_RSSI 12 // 21 // - -struct Scan -{ - virtual float getRSSI() = 0; - - // rssiMethod gets the data similar to the scan method, - // but uses getRSSI directly. - uint16_t rssiMethod(size_t samples, uint16_t *result, size_t res_size); - - // detect method analyses result, and produces filtered_result, marking - // those values that represent a detection event. - // It returns index that represents strongest signal at which a detection event - // occurred. - static size_t detect(uint16_t *result, bool *filtered_result, size_t result_size, - int samples); -}; - -// Remove reading without neighbors -#define FILTER_SPECTRUM_RESULTS true - -#endif diff --git a/test/test_rssi.cpp b/test/test_rssi.cpp index 519f72f..8a75b60 100644 --- a/test/test_rssi.cpp +++ b/test/test_rssi.cpp @@ -1,6 +1,6 @@ #include #define LOG(args...) printf(args) -#include "../src/radioScan/radioScan.cpp" +#include "../lib/scan/scan.cpp" #include void setUp(void) {} From 72b5d0b80d38d80dfa4cab8ca2146d30d657c74c Mon Sep 17 00:00:00 2001 From: Egor Date: Fri, 27 Sep 2024 00:57:26 -0700 Subject: [PATCH 24/80] fix Heltec boards --- lib/loraboards/LoRaBoards.cpp | 3 +++ lib/loraboards/utilities.h | 2 ++ lib/scan/scan.h | 3 +++ platformio.ini | 13 ++++++++++--- src/main.cpp | 20 ++++++++++++++------ src/ui.cpp | 2 +- 6 files changed, 33 insertions(+), 10 deletions(-) diff --git a/lib/loraboards/LoRaBoards.cpp b/lib/loraboards/LoRaBoards.cpp index 04c93a6..0331489 100644 --- a/lib/loraboards/LoRaBoards.cpp +++ b/lib/loraboards/LoRaBoards.cpp @@ -8,6 +8,8 @@ * */ +#ifdef LILYGO + #include "LoRaBoards.h" #if defined(HAS_SDCARD) @@ -927,3 +929,4 @@ bool beginGPS() return result; } #endif +#endif // #ifdef LILYGO diff --git a/lib/loraboards/utilities.h b/lib/loraboards/utilities.h index 1d1b3ef..32a804b 100644 --- a/lib/loraboards/utilities.h +++ b/lib/loraboards/utilities.h @@ -486,6 +486,8 @@ #define USING_DIO2_AS_RF_SWITCH +#elif defined(HELTEC) +// just to prevent error #elif defined(T_BEAM_S3_BPF) #ifndef USING_SX1278 diff --git a/lib/scan/scan.h b/lib/scan/scan.h index bfbdcf8..f5af2c6 100644 --- a/lib/scan/scan.h +++ b/lib/scan/scan.h @@ -27,6 +27,9 @@ constexpr float LO_RSSI_THRESHOLD = HI_RSSI_THRESHOLD - 66; // number of samples for RSSI method #define SAMPLES_RSSI 12 // 21 // +#ifdef USING_SX1280PA +#define SAMPLES_RSSI 20 +#endif struct Scan { diff --git a/platformio.ini b/platformio.ini index 997258b..84a0e9e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -28,7 +28,10 @@ monitor_speed = 115200 board_build.f_cpu = 240000000 lib_deps = ropg/Heltec_ESP32_LoRa_v3@^0.9.1 -build_flags = -DHELTEC_POWER_BUTTON +build_flags = + -DHELTEC_POWER_BUTTON + -DHELTEC + [env:lilygo-T3S3-v1-2-sx1262] platform = espressif32 @@ -88,7 +91,9 @@ board_build.f_cpu = 240000000 board_build.flash_size = 80000000L lib_deps = ropg/Heltec_ESP32_LoRa_v3@^0.9.1 -build_flags = -DLILYGO +build_flags = + -DHELTEC + -DHELTEC_POWER_BUTTON [env:vision-master-e290] platform = espressif32 @@ -97,7 +102,8 @@ framework = arduino monitor_speed = 115200 monitor_filters = esp32_exception_decoder board_upload.use_1200bps_touch = true -build_flags = +build_flags = + -DHELTEC -DHELTEC_BOARD=37 -DSLOW_CLK_TPYE=1 -DARDUINO_USB_CDC_ON_BOOT=1 @@ -130,6 +136,7 @@ monitor_speed = 115200 monitor_filters = esp32_exception_decoder board_upload.use_1200bps_touch = true build_flags = + -DHELTEC -DHELTEC_BOARD=38 -DSLOW_CLK_TPYE=1 -DARDUINO_USB_CDC_ON_BOOT=1 diff --git a/src/main.cpp b/src/main.cpp index fdcd374..dd891aa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,22 +36,24 @@ // library internals. #define RADIOLIB_GODMODE (1) -#include #include #ifndef LILYGO #include // This file contains a binary patch for the SX1262 #include "modules/SX126x/patches/SX126x_patch_scan.h" -#endif // end LILYGO +#endif // end ifndef LILYGO + #if defined(LILYGO) // LiLyGO device does not support the auto download mode, you need to get into the // download mode manually. To do so, press and hold the BOOT button and then press the // RESET button once. After that release the BOOT button. Or OFF->ON together with BOOT // Default LilyGO code -#include "utilities.h" -// Our Code +#include + +// #include "utilities.h" +// Our Code #include "LiLyGo.h" #endif // end LILYGO @@ -134,6 +136,10 @@ uint64_t RANGE_PER_PAGE = FREQ_END - FREQ_BEGIN; // FREQ_END - FREQ_BEGIN // multiplies STEPS * N to increase scan resolution. #define SCAN_RBW_FACTOR 2 +#ifdef USING_SX1280PA +#define SCAN_RBW_FACTOR 2 +#endif + constexpr int OSD_PIXELS_PER_CHAR = (STEPS * SCAN_RBW_FACTOR) / OSD_CHART_WIDTH; #define DEFAULT_RANGE_PER_PAGE 50 @@ -423,9 +429,11 @@ void init_radio() void setup(void) { +#ifdef LILYGO setupBoards(); - delay(5000); - Serial.println("setup is done"); + delay(3000); + Serial.println("setup LiLyGO board is done"); +#endif // LED brightness heltec_led(25); diff --git a/src/ui.cpp b/src/ui.cpp index 94342b7..ee8bc92 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -223,7 +223,7 @@ 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, ROW_STATUS_TEXT, + display_instance->drawString(start_scan_text + 2, ROW_STATUS_TEXT, String(drone_detected_frequency_start) + ">RF<" + String(drone_detected_frequency_end)); } From 1c674701a1f395ec3352721fc350fe657468b6f1 Mon Sep 17 00:00:00 2001 From: Egor Date: Fri, 27 Sep 2024 01:19:01 -0700 Subject: [PATCH 25/80] mac os usb --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index f04980e..23c36fe 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,9 @@ If less, ESP32 will turn off. Fast pressing(less than 0.5 second) P button chang 3. Connect ESP32 to USB. Install USB CP2101 drivers for Windows or other OS https://docs.heltec.org/general/establish_serial_connection.html#for-windows https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers?tab=downloads + + ## NOTE: MACOS driver + https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers?tab=downloads 5. Clone this Git Repo or download zip of the sources ![image](https://github.com/user-attachments/assets/971b6592-3b71-414c-971c-2ecd20f0f0b7) From 25053faf72cf1b1422b56be6bd218b3c062de51f Mon Sep 17 00:00:00 2001 From: Egor Date: Fri, 27 Sep 2024 01:20:18 -0700 Subject: [PATCH 26/80] legacy driver --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 23c36fe..6885ced 100644 --- a/README.md +++ b/README.md @@ -161,8 +161,9 @@ If less, ESP32 will turn off. Fast pressing(less than 0.5 second) P button chang https://docs.heltec.org/general/establish_serial_connection.html#for-windows https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers?tab=downloads - ## NOTE: MACOS driver - https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers?tab=downloads + ## NOTE: MACOS Heltec USB driver + https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers?tab=downloads
+ I used legacy driver 5. Clone this Git Repo or download zip of the sources ![image](https://github.com/user-attachments/assets/971b6592-3b71-414c-971c-2ecd20f0f0b7) From c5156373c228818389fa68704bd6230221195b45 Mon Sep 17 00:00:00 2001 From: Egor Date: Fri, 27 Sep 2024 12:03:09 -0700 Subject: [PATCH 27/80] buzzer pin button pin --- include/LiLyGo.h | 4 ++-- include/global_config.h | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/LiLyGo.h b/include/LiLyGo.h index 3c0c262..0cc28df 100644 --- a/include/LiLyGo.h +++ b/include/LiLyGo.h @@ -35,8 +35,6 @@ SX1262 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUS #endif // end ARDUINO_heltec_wifi_32_lora_V3 #endif // end HELTEC_NO_RADIO_INSTANCE -void heltec_loop() {} - void heltec_led(int led) {} void heltec_deep_sleep() {} @@ -88,6 +86,8 @@ Print &both = Serial; #include "HotButton.h" HotButton button(BUTTON); +void heltec_loop() { button.update(); } + // This file contains a binary patch for the SX1262 #include "modules/SX126x/patches/SX126x_patch_scan.h" diff --git a/include/global_config.h b/include/global_config.h index f8205e6..1073b4d 100644 --- a/include/global_config.h +++ b/include/global_config.h @@ -22,6 +22,10 @@ #define BUZZER_PIN 41 +#ifdef LILYGO +#define BUZZER_PIN 45 +#endif + // REB trigger PIN #define REB_PIN 42 From 2c1b2ca56c08c19829bcef90d3450cca9f70c119 Mon Sep 17 00:00:00 2001 From: Egor Date: Fri, 27 Sep 2024 12:10:13 -0700 Subject: [PATCH 28/80] button fix --- src/main.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index dd891aa..18ac922 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1127,12 +1127,8 @@ void loop(void) display.display(); } -// LiLyGo doesn't have button ;( -// ToDO: Check if we use BOOT button -#ifndef LILYGO if (buttonPressHandler(freq) == false) break; -#endif // END LILYGO // wait a little bit before the next scan, // otherwise the SX1262 hangs From 8cd7c7b661486c6bc6b205f436997e8640984b7e Mon Sep 17 00:00:00 2001 From: Egor Date: Fri, 27 Sep 2024 15:54:21 -0700 Subject: [PATCH 29/80] osd disable by default --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 18ac922..d11671c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,9 +25,9 @@ #include -#define OSD_ENABLED true -// #define WIFI_SCANNING_ENABLED true -// #define BT_SCANNING_ENABLED true +// #define OSD_ENABLED true +// #define WIFI_SCANNING_ENABLED true +// #define BT_SCANNING_ENABLED true // Direct access to the low-level SPI communication between RadioLib and the radio module. #define RADIOLIB_LOW_LEVEL (1) From 6539445091f70972b4ec1c5acddeb19e385c477f Mon Sep 17 00:00:00 2001 From: Egor Date: Fri, 27 Sep 2024 17:26:03 -0700 Subject: [PATCH 30/80] disable Lora display library --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index d11671c..f91b577 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -430,9 +430,9 @@ void init_radio() void setup(void) { #ifdef LILYGO - setupBoards(); - delay(3000); - Serial.println("setup LiLyGO board is done"); + setupBoards(true); // true for disable U8g2 display library + delay(500); + Serial.println("Setup LiLyGO board is done"); #endif // LED brightness From 89940b854c4a3977a7ed3bd0113528df6d0d7c35 Mon Sep 17 00:00:00 2001 From: Egor Date: Sun, 29 Sep 2024 01:35:25 -0700 Subject: [PATCH 31/80] SX1262 support --- include/LiLyGo.h | 26 ++++++++++++++++++++++---- include/global_config.h | 14 +++++++++++++- platformio.ini | 25 +++++++++++++++++++++++++ src/main.cpp | 12 +++++++++++- 4 files changed, 71 insertions(+), 6 deletions(-) diff --git a/include/LiLyGo.h b/include/LiLyGo.h index 0cc28df..c9c88ee 100644 --- a/include/LiLyGo.h +++ b/include/LiLyGo.h @@ -13,8 +13,11 @@ #else #define DISPLAY_WIDTH 128 #define DISPLAY_HEIGHT 64 -#include "OLEDDisplayUi.h" +// #include "OLEDDisplayUi.h" +// #include "SH1106Wire.h" +// #include "SSD1306Brzo.h" #include "SSD1306Wire.h" + #endif #define ARDUINO_heltec_wifi_32_lora_V3 #ifndef HELTEC_NO_RADIO_INSTANCE @@ -32,6 +35,10 @@ SX1280 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUS // Default SPI on pins from pins_arduino.h SX1262 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN); #endif // end USING_SX1262 +#ifdef USING_SX1276 +// Default SPI on pins from pins_arduino.h +SX1276 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN); +#endif // end USING_SX1276 #endif // end ARDUINO_heltec_wifi_32_lora_V3 #endif // end HELTEC_NO_RADIO_INSTANCE @@ -76,17 +83,28 @@ class PrintSplitter : public Print #else #define DISPLAY_GEOMETRY GEOMETRY_128_64 #endif -SSD1306Wire display(0x3c, 18, 17, DISPLAY_GEOMETRY); +#define SCREEN_ADDRESS 0x3C + +SSD1306Wire display(SCREEN_ADDRESS, I2C_SDA, I2C_SCL, DISPLAY_GEOMETRY); +// SH1106Wire display(0x3c, I2C_SDA, I2C_SCL, DISPLAY_GEOMETRY); PrintSplitter both(Serial, display); #else Print &both = Serial; #endif // some fake pin +#ifdef T3_V1_6_SX1276 +#define BUTTON_PIN 22 +#endif #define BUTTON BUTTON_PIN #include "HotButton.h" HotButton button(BUTTON); -void heltec_loop() { button.update(); } +void heltec_loop() +{ +#ifndef DT3_V1_6_SX1276 + button.update(); +#endif +} // This file contains a binary patch for the SX1262 #include "modules/SX126x/patches/SX126x_patch_scan.h" @@ -138,7 +156,7 @@ void heltec_setup() #ifndef HELTEC_NO_DISPLAY_INSTANCE heltec_display_power(true); display.init(); - display.setContrast(200); + // display.setContrast(200); display.flipScreenVertically(); #endif } diff --git a/include/global_config.h b/include/global_config.h index 1073b4d..a2014f7 100644 --- a/include/global_config.h +++ b/include/global_config.h @@ -1,6 +1,8 @@ #ifndef __GLOBAL_CONFIG_H__ #define __GLOBAL_CONFIG_H__ +#include "utilities.h" + #ifndef FREQ_BEGIN // frequency range in MHz to scan #define FREQ_BEGIN 850 @@ -25,14 +27,24 @@ #ifdef LILYGO #define BUZZER_PIN 45 #endif +#ifdef T3_V1_6_SX1276 +#define BUZZER_PIN 35 +#endif // REB trigger PIN #define REB_PIN 42 +#ifdef T3_V1_6_SX1276 +#define REB_PIN 35 +#endif #define WATERFALL_ENABLED true #define WATERFALL_START 37 #ifdef LILYGO -#define LED 46 +#define LED BOARD_LED #endif // end not LILYGO +#ifdef T3_V1_6_SX1276 +#define LED BOARD_LED +#endif + #endif diff --git a/platformio.ini b/platformio.ini index 84a0e9e..8cc17d2 100644 --- a/platformio.ini +++ b/platformio.ini @@ -81,6 +81,31 @@ build_flags = -DARDUINO_LILYGO_T3_S3_V1_X -DARDUINO_USB_MODE=1 +[env:lilygo-T3-v1-6-xs1276] +platform = espressif32 +board = esp32dev +framework = arduino +upload_speed = 115200 +monitor_speed = 115200 +lib_deps = + ropg/Heltec_ESP32_LoRa_v3@^0.9.1 + RadioLib + U8g2 + XPowersLib +build_flags = + -DLILYGO + -DT3_V1_6_SX1276 + -DUSING_SX1276 + -DESP32 + -DARDUINO_ARCH_ESP32 + -DARDUINO_USB_CDC_ON_BOOT=0 ;; if not 0 - Serial issue + -DARDUINO_LILYGO_T3_V1_6 + -DARDUINO_USB_MODE=1 + +;; More old lylygo/titygo boeads defenitions you can find here: +;; https://github.com/PTR-projects/PTR_GroundStation_firmware/blob/main/platformio.ini +;; https://github.com/Xinyuan-LilyGO/LilyGo-LoRa-Series/blob/master/platformio.ini + [env:heltec_wifi_lora_32_V3-test-signal-generator] platform = espressif32 board = heltec_wifi_lora_32_V3 diff --git a/src/main.cpp b/src/main.cpp index f91b577..8ecbd0f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -395,6 +395,11 @@ void init_radio() both.println("Setting up radio"); #ifdef USING_SX1280PA // RADIOLIB_OR_HALT(radio.setBandwidth(RADIOLIB_SX128X_LORA_BW_406_25)); +#elif USING_SX1276 + // Receiver bandwidth in kHz. Allowed values + // are 2.6, 3.1, 3.9, 5.2, 6.3, 7.8, 10.4, 12.5, 15.6, 20.8, 25, 31.3, 41.7, + // 50, 62.5, 83.3, 100, 125, 166.7, 200 and 250 kHz. + RADIOLIB_OR_HALT(radio.setRxBandwidth(250)); #else RADIOLIB_OR_HALT(radio.setRxBandwidth(BANDWIDTH)); #endif @@ -420,6 +425,9 @@ void init_radio() { Serial.println("Error:startReceive:" + String(state)); } +#elif USING_SX1276 + // Sets carrier frequency. Allowed values range from 137.0 MHz to 1020.0 MHz. + radio.setFrequency(FREQ_BEGIN); #else radio.setFrequency(FREQ_BEGIN, true); #endif @@ -430,7 +438,7 @@ void init_radio() void setup(void) { #ifdef LILYGO - setupBoards(true); // true for disable U8g2 display library + setupBoards(); // true for disable U8g2 display library delay(500); Serial.println("Setup LiLyGO board is done"); #endif @@ -899,6 +907,8 @@ void loop(void) #ifdef USING_SX1280PA state = radio.setFrequency(freq); // 1280 doesn't have calibration radio.startReceive(RADIOLIB_SX128X_RX_TIMEOUT_INF); +#elif USING_SX1276 + state = radio.setFrequency(freq); #else state = radio.setFrequency(freq, false); // false = no calibration need here #endif From 93dd0e74b38ffbfc89eaf87860ddc55c6b60d57b Mon Sep 17 00:00:00 2001 From: Konrad Iturbe Date: Mon, 30 Sep 2024 13:01:07 +0200 Subject: [PATCH 32/80] [docs] Add mention of expressif esp32 catalog update on install instructions --- README.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6885ced..3e64c2a 100644 --- a/README.md +++ b/README.md @@ -180,13 +180,22 @@ If less, ESP32 will turn off. Fast pressing(less than 0.5 second) P button chang 7. Select Proper Environment ![image](https://github.com/user-attachments/assets/a9c6557b-a387-4457-b59b-b3d7242d2826) -8. Select ESP32 USB Device to program + + --- + + >**Important note:** make sure your ESP32 Expressif catalog is up to date before selecting environment, otherwise you'll get error `Error: Unknown board ID 'heltec_wifi_lora_32_V3'` when trying to select environment. + > + >Open a PlatformIO CLI: https://docs.platformio.org/en/latest/integration/ide/vscode.html#platformio-core-cli + > + >Run: `pio pkg update -g -p espressif32` + +9. 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. Program your ESP32 +10. 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. +11. 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. From c820a0d84c4921eb736b7377189c70ce81f78a2e Mon Sep 17 00:00:00 2001 From: Konrad Iturbe Date: Mon, 30 Sep 2024 13:03:24 +0200 Subject: [PATCH 33/80] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3e64c2a..3e7cc40 100644 --- a/README.md +++ b/README.md @@ -183,19 +183,19 @@ If less, ESP32 will turn off. Fast pressing(less than 0.5 second) P button chang --- - >**Important note:** make sure your ESP32 Expressif catalog is up to date before selecting environment, otherwise you'll get error `Error: Unknown board ID 'heltec_wifi_lora_32_V3'` when trying to select environment. + >**Important note:** If using a Heltec V3 board, make sure your ESP32 Expressif catalog is up to date before selecting environment, otherwise might get a build time error such as: `Error: Unknown board ID 'heltec_wifi_lora_32_V3'` when trying to select the environment. > >Open a PlatformIO CLI: https://docs.platformio.org/en/latest/integration/ide/vscode.html#platformio-core-cli > >Run: `pio pkg update -g -p espressif32` -9. Select ESP32 USB Device to program +8. 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. -10. Program your ESP32 +9. Program your ESP32 ![image](https://github.com/user-attachments/assets/9e67afd8-0522-4a96-82dc-8e1cdb32add5) -11. Wait until you are done with the compilation and upload. +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. ![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. From 207efeea37de0ea2ad01435e4513da5f4f9c2000 Mon Sep 17 00:00:00 2001 From: Egor Date: Tue, 1 Oct 2024 08:54:22 -0700 Subject: [PATCH 34/80] add 433 freq --- platformio.ini | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/platformio.ini b/platformio.ini index 8cc17d2..08579af 100644 --- a/platformio.ini +++ b/platformio.ini @@ -32,6 +32,21 @@ build_flags = -DHELTEC_POWER_BUTTON -DHELTEC +[env:heltec_wifi_lora_32_V3_433] +platform = espressif32 +board = heltec_wifi_lora_32_V3 +framework = arduino +upload_speed = 921600 +monitor_speed = 115200 +board_build.f_cpu = 240000000 +lib_deps = + ropg/Heltec_ESP32_LoRa_v3@^0.9.1 +build_flags = + -DHELTEC_POWER_BUTTON + -DHELTEC + -DFREQ_BEGIN=130 + -DFREQ_END=180 + -DRADIOLIB_CHECK_PARAMS=0 [env:lilygo-T3S3-v1-2-sx1262] platform = espressif32 From 13bdeb3589da4a086ea2d2bd3d4d1bd0506738a2 Mon Sep 17 00:00:00 2001 From: KonradIT Date: Fri, 4 Oct 2024 15:19:01 +0200 Subject: [PATCH 35/80] feat: periodically write to serial the frequency scan results --- platformio.ini | 1 + src/main.cpp | 67 +++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/platformio.ini b/platformio.ini index 08579af..4995061 100644 --- a/platformio.ini +++ b/platformio.ini @@ -28,6 +28,7 @@ monitor_speed = 115200 board_build.f_cpu = 240000000 lib_deps = ropg/Heltec_ESP32_LoRa_v3@^0.9.1 + bblanchon/ArduinoJson@^7.2.0 build_flags = -DHELTEC_POWER_BUTTON -DHELTEC diff --git a/src/main.cpp b/src/main.cpp index 8ecbd0f..2be3d4d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,6 +24,9 @@ // #define HELTEC_NO_DISPLAY #include +#include +#include +#include // #define OSD_ENABLED true // #define WIFI_SCANNING_ENABLED true @@ -202,7 +205,7 @@ bool single_page_scan = false; bool SOUND_ON = false; // #define PRINT_DEBUG -#define PRINT_PROFILE_TIME +// #define PRINT_PROFILE_TIME #ifdef PRINT_PROFILE_TIME uint64_t loop_start = 0; @@ -211,6 +214,10 @@ uint64_t scan_time = 0; uint64_t scan_start_time = 0; #endif +// log data via serial console, JSON format: +#define LOG_DATA_JSON true +int LOG_DATA_JSON_INTERVAL = 100; + uint64_t x, y, range_item, w = WATERFALL_START, i = 0; int osd_x = 1, osd_y = 2, col = 0, max_bin = 32; uint64_t ranges_count = 0; @@ -435,6 +442,49 @@ void init_radio() delay(50); } +struct FrequencyRange +{ + uint64_t begin; + uint64_t end; +} frequencyRange; + +void logToSerialTask(void *parameter) +{ + JsonDocument doc; + char jsonOutput[200]; + + for (;;) + { + if (frequencyRange.begin != frequencyRange.end) + { + String max_result = "-999"; + int highest_value_scanned = 999; + + for (int rssi_item = 0; rssi_item < STEPS / WINDOW_SIZE; rssi_item++) + { + if (max_x_window[rssi_item] != 0 && + max_x_window[rssi_item] < highest_value_scanned) + { + max_result = "-" + String(max_x_window[rssi_item]); + highest_value_scanned = max_x_window[rssi_item]; + } + } + if (max_result == "-999") + { + continue; + } + + doc["low_range_freq"] = frequencyRange.begin; + doc["high_range_freq"] = frequencyRange.end; + doc["value"] = max_result; + + serializeJson(doc, jsonOutput); + Serial.println(jsonOutput); + } + vTaskDelay(LOG_DATA_JSON_INTERVAL / portTICK_PERIOD_MS); + } +} + void setup(void) { #ifdef LILYGO @@ -587,6 +637,10 @@ void setup(void) #ifdef OSD_ENABLED osd.clear(); #endif + +#ifdef LOG_DATA_JSON + xTaskCreate(logToSerialTask, "LOG_DATA_JSON", 2048, NULL, 1, NULL); +#endif } // Formula to translate 33 bin to approximate RSSI value @@ -798,13 +852,11 @@ void loop(void) drone_detected_frequency_start = 0; ranges_count = 0; - // reset scan time +// reset scan time +#ifdef PRINT_PROFILE_TIME scan_time = 0; - // general purpose loop counter loop_cnt++; - -#ifdef PRINT_PROFILE_TIME loop_start = millis(); #endif @@ -1043,6 +1095,9 @@ void loop(void) max_rssi_x * 2); } + frequencyRange.begin = drone_detected_frequency_start; + frequencyRange.end = drone_detected_frequency_end; + if (DRAW_DETECTION_TICKS == true) { // draw vertical line on top of display for "drone detected" @@ -1230,10 +1285,10 @@ void loop(void) // Serial.println("----"); #endif - loop_time = millis() - loop_start; joy_btn_clicked = false; #ifdef PRINT_PROFILE_TIME + loop_time = millis() - loop_start; Serial.printf("LOOP: %lld ms; SCAN: %lld ms;\n ", loop_time, scan_time); #endif // No WiFi and BT Scan Without OSD From 40ceb82431d6aa6afb24a8ab60a504d06f9dd296 Mon Sep 17 00:00:00 2001 From: KonradIT Date: Fri, 4 Oct 2024 15:28:00 +0200 Subject: [PATCH 36/80] naming, use int highest value gotten variable for comparison, only keep track of frequency ranges if json feature is enabled --- src/main.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 2be3d4d..bacbf1e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -442,11 +442,11 @@ void init_radio() delay(50); } -struct FrequencyRange +struct frequency_scan_result { uint64_t begin; uint64_t end; -} frequencyRange; +} frequency_scan_result; void logToSerialTask(void *parameter) { @@ -455,7 +455,7 @@ void logToSerialTask(void *parameter) for (;;) { - if (frequencyRange.begin != frequencyRange.end) + if (frequency_scan_result.begin != frequency_scan_result.end) { String max_result = "-999"; int highest_value_scanned = 999; @@ -469,13 +469,13 @@ void logToSerialTask(void *parameter) highest_value_scanned = max_x_window[rssi_item]; } } - if (max_result == "-999") + if (highest_value_scanned == 999) { continue; } - doc["low_range_freq"] = frequencyRange.begin; - doc["high_range_freq"] = frequencyRange.end; + doc["low_range_freq"] = frequency_scan_result.begin; + doc["high_range_freq"] = frequency_scan_result.end; doc["value"] = max_result; serializeJson(doc, jsonOutput); @@ -1095,9 +1095,10 @@ void loop(void) max_rssi_x * 2); } - frequencyRange.begin = drone_detected_frequency_start; - frequencyRange.end = drone_detected_frequency_end; - +#ifdef LOG_DATA_JSON + frequency_scan_result.begin = drone_detected_frequency_start; + frequency_scan_result.end = drone_detected_frequency_end; +#endif if (DRAW_DETECTION_TICKS == true) { // draw vertical line on top of display for "drone detected" From 6d02c4121757413272be6c6e9f2513daaadaa9b6 Mon Sep 17 00:00:00 2001 From: KonradIT Date: Mon, 7 Oct 2024 19:51:16 +0200 Subject: [PATCH 37/80] Added a delay when frequency scan result is invalid --- src/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.cpp b/src/main.cpp index bacbf1e..480c3b2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -471,6 +471,7 @@ void logToSerialTask(void *parameter) } if (highest_value_scanned == 999) { + vTaskDelay(LOG_DATA_JSON_INTERVAL / portTICK_PERIOD_MS); continue; } From a547e752af4dec9009a9a97a77d216535b136ca0 Mon Sep 17 00:00:00 2001 From: KonradIT Date: Tue, 8 Oct 2024 12:35:53 +0200 Subject: [PATCH 38/80] Set defines to how it was before --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 480c3b2..0e91b25 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -205,7 +205,7 @@ bool single_page_scan = false; bool SOUND_ON = false; // #define PRINT_DEBUG -// #define PRINT_PROFILE_TIME +#define PRINT_PROFILE_TIME #ifdef PRINT_PROFILE_TIME uint64_t loop_start = 0; @@ -215,7 +215,7 @@ uint64_t scan_start_time = 0; #endif // log data via serial console, JSON format: -#define LOG_DATA_JSON true +// #define LOG_DATA_JSON true int LOG_DATA_JSON_INTERVAL = 100; uint64_t x, y, range_item, w = WATERFALL_START, i = 0; From 63458b080e6062c6d40620fee7a23515d7ec1a7f Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Fri, 11 Oct 2024 22:54:17 +0100 Subject: [PATCH 39/80] Fixup OSD output - do not use Json lib for lilygo --- src/main.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 0e91b25..ed469c1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,7 +24,9 @@ // #define HELTEC_NO_DISPLAY #include +#ifdef HELTEC #include +#endif #include #include @@ -450,7 +452,9 @@ struct frequency_scan_result void logToSerialTask(void *parameter) { +#ifdef HELTEC JsonDocument doc; +#endif char jsonOutput[200]; for (;;) @@ -475,12 +479,19 @@ void logToSerialTask(void *parameter) continue; } +#ifdef HELTEC doc["low_range_freq"] = frequency_scan_result.begin; doc["high_range_freq"] = frequency_scan_result.end; doc["value"] = max_result; serializeJson(doc, jsonOutput); Serial.println(jsonOutput); +#else + Serial.printf("{\"low_range_freq\": %ull, \"high_range_freq\": %ull, " + "\"value\": \"%s\"}\n", + frequency_scan_result.begin, frequency_scan_result.end, + max_result); +#endif } vTaskDelay(LOG_DATA_JSON_INTERVAL / portTICK_PERIOD_MS); } From 309c78c0a9e87be9bfb2fa6b1828367d8a5953fd Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Sun, 29 Sep 2024 07:44:14 +0100 Subject: [PATCH 40/80] vscode wants to update settings.json --- .vscode/settings.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index c2f89c3..b5ca201 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,4 +12,7 @@ }, "files.insertFinalNewline": true, "files.autoSave": "onFocusChange", + "files.associations": { + "cstdint": "cpp" + }, } From 51e95485833ba3c24f4cd7c1da0ea4bdcc786ea3 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Sun, 6 Oct 2024 14:50:57 +0100 Subject: [PATCH 41/80] fixup platformio spelling --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 4995061..52cad92 100644 --- a/platformio.ini +++ b/platformio.ini @@ -72,7 +72,7 @@ build_flags = -DARDUINO_USB_MODE=1 -[env:lilygo-T3S3-v1-2-xs1280] +[env:lilygo-T3S3-v1-2-sx1280] platform = espressif32 board = t3_s3_v1_x framework = arduino From 9eaf1437d28dc25c81b818d895c700526a71ac25 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Thu, 10 Oct 2024 22:12:17 +0100 Subject: [PATCH 42/80] Platformio update for SX1262 --- platformio.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platformio.ini b/platformio.ini index 52cad92..32ddad0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -59,6 +59,8 @@ board_build.f_cpu = 240000000 lib_deps = ropg/Heltec_ESP32_LoRa_v3@^0.9.1 RadioLib + U8g2 + XPowersLib build_flags = -DLILYGO -DT3_S3_V1_2_SX1262 From 39073dd026708cf5d4024e02271260c962ca6a58 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Sat, 28 Sep 2024 16:58:29 +0100 Subject: [PATCH 43/80] Filtered_results is not needed --- lib/scan/scan.cpp | 2 +- src/main.cpp | 24 +++++++++--------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/lib/scan/scan.cpp b/lib/scan/scan.cpp index bc58902..5f584b4 100644 --- a/lib/scan/scan.cpp +++ b/lib/scan/scan.cpp @@ -69,7 +69,7 @@ uint16_t Scan::rssiMethod(size_t samples, uint16_t *result, size_t res_size) size_t Scan::detect(uint16_t *result, bool *filtered_result, size_t result_size, int samples) { - size_t max_rssi_x = 999; + size_t max_rssi_x = result_size; for (int y = 0; y < result_size; y++) { diff --git a/src/main.cpp b/src/main.cpp index ed469c1..6fd371a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1060,13 +1060,12 @@ void loop(void) detected_y[display_x] = false; #if FILTER_SPECTRUM_RESULTS - for (int y = 0; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) + if (detected) { // calculating max window x RSSI after filters x_window = (int)(display_x / WINDOW_SIZE); - int abs_result = abs(result[y]); - if (filtered_result[y] == 1 && result[y] != 0 && result[y] != 1 && - max_x_window[x_window] > abs_result) + int abs_result = abs(result[detected_at]); + if (max_x_window[x_window] > abs_result) { max_x_window[x_window] = abs_result; LOG("MAX x window: %i %i\n", x_window, abs_result); @@ -1147,23 +1146,18 @@ void loop(void) } #endif - for (int y = 0; y < min(RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE, - MAX_POWER_LEVELS - START_LOW); - y++) - { - if (filtered_result[y] == 1) - { - // Set MAIN signal level pixel - display.setPixelColor(display_x, y + START_LOW, WHITE); - } - } + display.setColor(WHITE); + display.drawVerticalLine(display_x, START_LOW + detected_at, + min(RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE, + MAX_POWER_LEVELS - START_LOW) - + detected_at); // ------------------------------------------------------------- // Draw "Detection Level line" every 2 pixel // ------------------------------------------------------------- if (display_x % 2 == 0) { - if (filtered_result[drone_detection_level] == 1) + if (detected_at <= drone_detection_level) { display.setColor(INVERSE); } From c78fa6de7aee3d051de662880d82d4160d14c2f0 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Sat, 28 Sep 2024 22:36:48 +0100 Subject: [PATCH 44/80] Factor out drawing into Charts --- lib/charts/BarChart.cpp | 92 +++++++++++++++++++++++++++++++++++++++++ lib/charts/charts.h | 67 ++++++++++++++++++++++++++++++ src/main.cpp | 52 +++++++++++------------ 3 files changed, 185 insertions(+), 26 deletions(-) create mode 100644 lib/charts/BarChart.cpp create mode 100644 lib/charts/charts.h diff --git a/lib/charts/BarChart.cpp b/lib/charts/BarChart.cpp new file mode 100644 index 0000000..eacd78d --- /dev/null +++ b/lib/charts/BarChart.cpp @@ -0,0 +1,92 @@ +#include "charts.h" + +void BarChart::reset() +{ + memset(ys, 0, width * sizeof(float)); + memset(changed, false, width * sizeof(bool)); + redraw_all = true; +} + +int BarChart::updatePoint(float x, float y) +{ + if (x < min_x || x >= max_x) + { + return -1; + } + + size_t idx = width * (x - min_x) / (max_x - min_x); + if (idx >= width) + { + idx = width - 1; + } + + if (!changed[idx] || ys[idx] < y) + { + ys[idx] = y; + changed[idx] = true; + } + + return idx; +} + +void BarChart::draw() +{ + for (int x = 0; x < width; x++) + { + if (!changed[x] && !redraw_all) + continue; + + drawOne(x); + } + + redraw_all = false; +} + +void BarChart::drawOne(int x) +{ + if (x < 0) + return; + + int y = y2pos(ys[x]); + + if (y < height) + { + display.setColor(BLACK); + display.drawVerticalLine(pos_x + x, pos_y, y); + display.setColor(WHITE); + display.drawVerticalLine(pos_x + x, pos_y + y, height - y); + } + else + { + display.setColor(BLACK); + display.drawVerticalLine(pos_x + x, pos_y, height); + } + + if (x % 2 == 0) + { + display.setColor(INVERSE); + display.setPixel(pos_x + x, pos_y + y2pos(level_y)); + } + + changed[x] = false; +} + +int BarChart::x2pos(float x) +{ + if (x < min_x) + x = min_x; + if (x > max_x) + x = max_x; + + return width * (x - min_x) / (max_x - min_x); +} + +int BarChart::y2pos(float y) +{ + if (y < min_y) + y = min_y; + if (y > max_y) + y = max_y; + + return height - height * (y - min_y) / (max_y - min_y); +} diff --git a/lib/charts/charts.h b/lib/charts/charts.h new file mode 100644 index 0000000..5c8a4ce --- /dev/null +++ b/lib/charts/charts.h @@ -0,0 +1,67 @@ +#ifndef CHARTS_H +#define CHARTS_H + +#include +#include +#include + +struct Chart +{ + uint16_t pos_x, pos_y; + uint16_t width, height; + OLEDDisplay &display; + + Chart(OLEDDisplay &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h) + : display(d), pos_x(x), pos_y(y), width(w), height(h) {}; + + /* + * This method resets the state and sets the reference time. + */ + virtual void reset() = 0; + + /* + * Update one data point, and return what column needs redrawing. + */ + virtual int updatePoint(float x, float y) = 0; + + /* + * If you fancy animated progress, then pass the output of updatePoint to here. + */ + virtual void drawOne(int x) = 0; + + /* + * Redraw everything that needs redrawing. + */ + virtual void draw() = 0; +}; + +struct BarChart : Chart +{ + float min_x, max_x, min_y, max_y; + float level_y; + + float *ys; + bool *changed; + bool redraw_all; + + BarChart(OLEDDisplay &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h, float min_x, + float max_x, float min_y, float max_y, float level_y) + : Chart(d, x, y, w, h), min_x(min_x), max_x(max_x), min_y(min_y), max_y(max_y), + level_y(level_y), redraw_all(true) + { + ys = new float[w]; + changed = new bool[w]; + + memset(ys, 0, w * sizeof(float)); + memset(changed, 0, w * sizeof(bool)); + }; + + void reset() override; + int updatePoint(float x, float y) override; + void drawOne(int x) override; + void draw() override; + + int x2pos(float x); + int y2pos(float y); +}; +#endif diff --git a/src/main.cpp b/src/main.cpp index 6fd371a..4e232ab 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,6 +41,7 @@ // library internals. #define RADIOLIB_GODMODE (1) +#include #include #ifndef LILYGO @@ -365,6 +366,8 @@ void osdProcess() } #endif +BarChart *bar; + void init_radio() { // initialize SX1262 FSK modem at the initial frequency @@ -653,6 +656,14 @@ void setup(void) #ifdef LOG_DATA_JSON xTaskCreate(logToSerialTask, "LOG_DATA_JSON", 2048, NULL, 1, NULL); #endif + + bar = new BarChart(display, 0, START_LOW, display.width(), + display.height() / 2 - START_LOW, FREQ_BEGIN, FREQ_END, + LO_RSSI_THRESHOLD, HI_RSSI_THRESHOLD, + -(float)show_db_after); // LO_RSSI_THRESHOLD + (HI_RSSI_THRESHOLD - + // LO_RSSI_THRESHOLD) * 0.3); + + bar->reset(); } // Formula to translate 33 bin to approximate RSSI value @@ -1060,6 +1071,7 @@ void loop(void) detected_y[display_x] = false; #if FILTER_SPECTRUM_RESULTS + float rr; if (detected) { // calculating max window x RSSI after filters @@ -1070,6 +1082,18 @@ void loop(void) max_x_window[x_window] = abs_result; LOG("MAX x window: %i %i\n", x_window, abs_result); } + rr = -(float)result[detected_at]; + } + else + { + rr = LO_RSSI_THRESHOLD; + } + + int updated = bar->updatePoint(freq, rr); + + if (first_run || ANIMATED_RELOAD) + { + bar->drawOne(updated); } #endif @@ -1146,31 +1170,6 @@ void loop(void) } #endif - display.setColor(WHITE); - display.drawVerticalLine(display_x, START_LOW + detected_at, - min(RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE, - MAX_POWER_LEVELS - START_LOW) - - detected_at); - - // ------------------------------------------------------------- - // Draw "Detection Level line" every 2 pixel - // ------------------------------------------------------------- - if (display_x % 2 == 0) - { - if (detected_at <= drone_detection_level) - { - display.setColor(INVERSE); - } - else - { - display.setColor(WHITE); - } - display.setPixel(display_x, drone_detection_level + START_LOW); - // display.setPixel(display_x, y + START_LOW - 1); // 2 px wide - - display.setColor(WHITE); - } - #ifdef JOYSTICK_ENABLED // Draw joystick cursor and Frequency RSSI value if (display_x == cursor_x_position) @@ -1258,6 +1257,7 @@ void loop(void) } #endif + bar->draw(); #ifdef METHOD_RSSI // Printing Max Window DB. for (int x2 = 0; x2 < STEPS / WINDOW_SIZE; x2++) @@ -1270,7 +1270,7 @@ void loop(void) max_x_window[x2] = 999; } #endif - // Render display data here + // Render display data here display.display(); #ifdef OSD_ENABLED // Sometimes OSD prints entire screen with the digits. From 02df8efa1c9b67aac1bf796f9fc39217a00f447b Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Sun, 29 Sep 2024 08:29:40 +0100 Subject: [PATCH 45/80] DecoratedBarChart - labels and axis with ticks --- lib/charts/BarChart.cpp | 59 +++++++++++++++++++++++++++++++++++++++++ lib/charts/charts.h | 21 +++++++++++++++ src/main.cpp | 32 +++------------------- src/ui.cpp | 12 --------- 4 files changed, 84 insertions(+), 40 deletions(-) diff --git a/lib/charts/BarChart.cpp b/lib/charts/BarChart.cpp index eacd78d..cdc04d8 100644 --- a/lib/charts/BarChart.cpp +++ b/lib/charts/BarChart.cpp @@ -90,3 +90,62 @@ int BarChart::y2pos(float y) return height - height * (y - min_y) / (max_y - min_y); } + +void DecoratedBarChart::draw() +{ + bool draw_axis = redraw_all; + + BarChart::draw(); + + display.setColor(BLACK); + display.fillRect(pos_x, text_y, width, pos_y - text_y); + + display.setColor(WHITE); + display.setTextAlignment(TEXT_ALIGN_LEFT); + + for (uint16_t x = 0; x < width; x++) + { + float y = ys[x]; + if (y >= level_y) + { + String s = String(ys[x], 0); + uint16_t w = display.getStringWidth(s); + uint16_t x1 = x; + for (; x < x1 + w; x++) + { + if (ys[x] > y) + { + y = ys[x]; + s = String(y, 0); + w = max(w, display.getStringWidth(s)); + } + } + + display.drawString(x1, text_y, s); + } + } + + if (draw_axis) + { + display.setColor(WHITE); + + uint16_t y = pos_y + height + 1; + display.fillRect(pos_x, y, width, X_AXIS_WEIGHT); + + // Start and end ticks + display.fillRect(pos_x, y + 1, 2, AXIS_HEIGHT); + display.fillRect(pos_x + width - 2, y + 1, 2, AXIS_HEIGHT); + + for (float step = 0; min_x + step * MAJOR_TICKS < max_x; step += 1) + { + int tick_pos = x2pos(min_x + step * MAJOR_TICKS); + display.drawVerticalLine(pos_x + tick_pos, y + 1, MAJOR_TICK_LENGTH); + } + + for (float step = 0; min_x + step * MINOR_TICKS < max_x; step += 1) + { + int tick_pos = x2pos(min_x + step * MINOR_TICKS); + display.drawVerticalLine(pos_x + tick_pos, y + 1, MINOR_TICK_LENGTH); + } + } +} diff --git a/lib/charts/charts.h b/lib/charts/charts.h index 5c8a4ce..83675a8 100644 --- a/lib/charts/charts.h +++ b/lib/charts/charts.h @@ -64,4 +64,25 @@ struct BarChart : Chart int x2pos(float x); int y2pos(float y); }; + +#define LABEL_HEIGHT 6 +#define X_AXIS_WEIGHT 1 +#define MAJOR_TICK_LENGTH 2 +#define MAJOR_TICKS 10 +#define MINOR_TICK_LENGTH 1 +#define MINOR_TICKS 5 +#define AXIS_HEIGHT (X_AXIS_WEIGHT + MAJOR_TICK_LENGTH + 1) +struct DecoratedBarChart : BarChart +{ + int text_y; + + DecoratedBarChart(OLEDDisplay &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h, + float min_x, float max_x, float min_y, float max_y, float level_y) + : BarChart(d, x, y + LABEL_HEIGHT, w, h - LABEL_HEIGHT - AXIS_HEIGHT, min_x, + max_x, min_y, max_y, level_y), + text_y(y) {}; + + void draw() override; +}; + #endif diff --git a/src/main.cpp b/src/main.cpp index 4e232ab..939ffaa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -657,11 +657,9 @@ void setup(void) xTaskCreate(logToSerialTask, "LOG_DATA_JSON", 2048, NULL, 1, NULL); #endif - bar = new BarChart(display, 0, START_LOW, display.width(), - display.height() / 2 - START_LOW, FREQ_BEGIN, FREQ_END, - LO_RSSI_THRESHOLD, HI_RSSI_THRESHOLD, - -(float)show_db_after); // LO_RSSI_THRESHOLD + (HI_RSSI_THRESHOLD - - // LO_RSSI_THRESHOLD) * 0.3); + bar = new DecoratedBarChart( + display, 0, 0, display.width(), display.height() / 2 + AXIS_HEIGHT, FREQ_BEGIN, + FREQ_END, LO_RSSI_THRESHOLD, HI_RSSI_THRESHOLD, -(float)show_db_after); bar->reset(); } @@ -1070,18 +1068,9 @@ void loop(void) detected = detected_at < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; detected_y[display_x] = false; -#if FILTER_SPECTRUM_RESULTS float rr; if (detected) { - // calculating max window x RSSI after filters - x_window = (int)(display_x / WINDOW_SIZE); - int abs_result = abs(result[detected_at]); - if (max_x_window[x_window] > abs_result) - { - max_x_window[x_window] = abs_result; - LOG("MAX x window: %i %i\n", x_window, abs_result); - } rr = -(float)result[detected_at]; } else @@ -1095,7 +1084,6 @@ void loop(void) { bar->drawOne(updated); } -#endif if (detected_at <= drone_detection_level) { @@ -1258,19 +1246,7 @@ void loop(void) #endif bar->draw(); -#ifdef METHOD_RSSI - // Printing Max Window DB. - for (int x2 = 0; x2 < STEPS / WINDOW_SIZE; x2++) - { - if (max_x_window[x2] < show_db_after && max_x_window[x2] != 0) - { - display.drawString(x2 * WINDOW_SIZE + WINDOW_SIZE, 0, - "-" + String(max_x_window[x2])); - } - max_x_window[x2] = 999; - } -#endif - // Render display data here + // Render display data here display.display(); #ifdef OSD_ENABLED // Sometimes OSD prints entire screen with the digits. diff --git a/src/ui.cpp b/src/ui.cpp index ee8bc92..ea376dd 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -184,9 +184,6 @@ 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)); @@ -299,14 +296,5 @@ void UI_displayDecorate(int begin = 0, int end = 0, bool redraw = false) String(SCAN_RANGES[range_item + 1] % 1000)); } } - if (ui_initialized == false) - { - // 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 384743ddb41db961cd9c3ae5ac9096b77525232a Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Sun, 29 Sep 2024 13:09:22 +0100 Subject: [PATCH 46/80] StackedChart support --- lib/charts/BarChart.cpp | 17 +++++++- lib/charts/StackedChart.cpp | 87 +++++++++++++++++++++++++++++++++++++ lib/charts/charts.h | 56 +++++++++++++++++++----- src/main.cpp | 18 +++++--- 4 files changed, 162 insertions(+), 16 deletions(-) create mode 100644 lib/charts/StackedChart.cpp diff --git a/lib/charts/BarChart.cpp b/lib/charts/BarChart.cpp index cdc04d8..c3e8116 100644 --- a/lib/charts/BarChart.cpp +++ b/lib/charts/BarChart.cpp @@ -1,7 +1,22 @@ #include "charts.h" -void BarChart::reset() +void BarChart::reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { + pos_x = x; + pos_y = y; + + if (w != width) + { + delete[] ys; + delete[] changed; + + width = w; + ys = new float[width]; + changed = new bool[width]; + } + + height = h; + memset(ys, 0, width * sizeof(float)); memset(changed, false, width * sizeof(bool)); redraw_all = true; diff --git a/lib/charts/StackedChart.cpp b/lib/charts/StackedChart.cpp new file mode 100644 index 0000000..be602bd --- /dev/null +++ b/lib/charts/StackedChart.cpp @@ -0,0 +1,87 @@ +#include "charts.h" + +uint16_t trim_w(uint16_t pos, uint16_t width, uint16_t w) +{ + return min(width, (uint16_t)(max(w, pos) - pos)); +} + +size_t StackedChart::addChart(Chart *c) +{ + Chart **cc = new Chart *[charts_sz + 1]; + memcpy(cc, charts, charts_sz * sizeof(Chart *)); + cc[charts_sz] = c; + free(charts); + + c->reset(pos_x + c->pos_x, pos_y + c->pos_y, trim_w(c->pos_x, c->width, width), c->height); + charts = cc; + return charts_sz++; +} + +uint16_t StackedChart::setHeight(size_t c, uint16_t h) +{ + if (h < height) + { + charts[c]->reset(charts[c]->pos_x, charts[c]->pos_y, charts[c]->width, h); + + uint16_t used_space = 0; + for (int i = 0; i < charts_sz; i++) + { + used_space += charts[i]->height; + } + + return used_space; + } + // this chart gets special treatment - pack all other charts, + // and make this one as big as possible + uint16_t used_space = 0; + for (int i = 0; i < c; i++) + { + charts[i]->reset(charts[i]->pos_x, pos_y + used_space, charts[i]->width, + charts[i]->height); + used_space += charts[i]->height; + } + + uint16_t more_used_space = used_space; + for (int i = c + 1; i < charts_sz; i++) + { + more_used_space += charts[i]->height; + } + + if (more_used_space < height) + { + charts[c]->reset(charts[c]->pos_x, pos_y + used_space, charts[c]->width, + height - more_used_space); + used_space += charts[c]->height; + } + + for (int i = c + 1; i < charts_sz; i++) + { + charts[i]->reset(charts[i]->pos_x, pos_y + used_space, charts[i]->width, + charts[i]->height); + used_space += charts[i]->height; + } + + return used_space; +} + +void StackedChart::reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) +{ + for (int i = 0; i < charts_sz; i++) + { + uint16_t rel_x = charts[i]->pos_x - pos_x; + uint16_t rel_y = charts[i]->pos_y - pos_y; + charts[i]->reset(x + rel_x, y + rel_y, trim_w(rel_x, charts[i]->width, w), + charts[i]->height); + } + + pos_x = x; + pos_y = y; + width = w; + height = h; +} + +void StackedChart::draw() +{ + for (int i = 0; i < charts_sz; i++) + charts[i]->draw(); +} diff --git a/lib/charts/charts.h b/lib/charts/charts.h index 83675a8..54fc2fa 100644 --- a/lib/charts/charts.h +++ b/lib/charts/charts.h @@ -17,7 +17,21 @@ struct Chart /* * This method resets the state and sets the reference time. */ - virtual void reset() = 0; + virtual void reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {}; + + /* + * Redraw everything that needs redrawing. + */ + virtual void draw() {}; +}; + +/* + * ProgressChart supports updates with progressive redraw of just the affected area. + */ +struct ProgressChart : Chart +{ + ProgressChart(OLEDDisplay &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h) + : Chart(d, x, y, w, h) {}; /* * Update one data point, and return what column needs redrawing. @@ -28,14 +42,9 @@ struct Chart * If you fancy animated progress, then pass the output of updatePoint to here. */ virtual void drawOne(int x) = 0; - - /* - * Redraw everything that needs redrawing. - */ - virtual void draw() = 0; }; -struct BarChart : Chart +struct BarChart : ProgressChart { float min_x, max_x, min_y, max_y; float level_y; @@ -46,8 +55,8 @@ struct BarChart : Chart BarChart(OLEDDisplay &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h, float min_x, float max_x, float min_y, float max_y, float level_y) - : Chart(d, x, y, w, h), min_x(min_x), max_x(max_x), min_y(min_y), max_y(max_y), - level_y(level_y), redraw_all(true) + : ProgressChart(d, x, y, w, h), min_x(min_x), max_x(max_x), min_y(min_y), + max_y(max_y), level_y(level_y), redraw_all(true) { ys = new float[w]; changed = new bool[w]; @@ -56,7 +65,7 @@ struct BarChart : Chart memset(changed, 0, w * sizeof(bool)); }; - void reset() override; + void reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) override; int updatePoint(float x, float y) override; void drawOne(int x) override; void draw() override; @@ -85,4 +94,31 @@ struct DecoratedBarChart : BarChart void draw() override; }; +struct StackedChart : Chart +{ + Chart **charts; + size_t charts_sz; + + StackedChart(OLEDDisplay &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h) + : Chart(d, x, y, w, h), charts(NULL), charts_sz(0) {}; + + /* + * addChart adds c to the StackedChart, treats pos_x and pos_y of the chart + * as relative to this chart's origin, and trims width to fit. Adjust the + * height and pack charts using setHeight. + */ + size_t addChart(Chart *c); + + /* + * Adjust the height of the chart and return the resulting required height. + * If h is >= height, the chart gets a special treatment: packs all other + * charts, and uses up the rest of space. + */ + uint16_t setHeight(size_t c, uint16_t h); + + void reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) override; + + void draw() override; +}; + #endif diff --git a/src/main.cpp b/src/main.cpp index 939ffaa..2e8fbb6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -367,6 +367,7 @@ void osdProcess() #endif BarChart *bar; +StackedChart stacked(display, 0, 0, 0, 0); void init_radio() { @@ -657,11 +658,18 @@ void setup(void) xTaskCreate(logToSerialTask, "LOG_DATA_JSON", 2048, NULL, 1, NULL); #endif - bar = new DecoratedBarChart( - display, 0, 0, display.width(), display.height() / 2 + AXIS_HEIGHT, FREQ_BEGIN, - FREQ_END, LO_RSSI_THRESHOLD, HI_RSSI_THRESHOLD, -(float)show_db_after); + bar = new DecoratedBarChart(display, 0, 0, display.width(), 0, FREQ_BEGIN, FREQ_END, + LO_RSSI_THRESHOLD, HI_RSSI_THRESHOLD, + -(float)show_db_after); - bar->reset(); + stacked.reset(0, 0, display.width(), display.height() - 6); + + size_t b = stacked.addChart(bar); + size_t c = + stacked.addChart(new Chart(display, 0, 0, display.width(), 0)); + + stacked.setHeight(c, stacked.height - WATERFALL_START); + stacked.setHeight(b, stacked.height); } // Formula to translate 33 bin to approximate RSSI value @@ -1245,7 +1253,7 @@ void loop(void) } #endif - bar->draw(); + stacked.draw(); // Render display data here display.display(); #ifdef OSD_ENABLED From 6011c47f81faa958a740cf74820a270ea6706253 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Mon, 30 Sep 2024 07:37:18 +0100 Subject: [PATCH 47/80] Fixup Chart inheritance --- lib/charts/BarChart.cpp | 16 ++++++---------- lib/charts/StackedChart.cpp | 5 +---- lib/charts/charts.h | 8 +++++++- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/lib/charts/BarChart.cpp b/lib/charts/BarChart.cpp index c3e8116..eb58770 100644 --- a/lib/charts/BarChart.cpp +++ b/lib/charts/BarChart.cpp @@ -2,24 +2,20 @@ void BarChart::reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { - pos_x = x; - pos_y = y; - if (w != width) { delete[] ys; delete[] changed; - width = w; - ys = new float[width]; - changed = new bool[width]; + ys = new float[w]; + changed = new bool[w]; } - height = h; - - memset(ys, 0, width * sizeof(float)); - memset(changed, false, width * sizeof(bool)); + memset(ys, 0, w * sizeof(float)); + memset(changed, false, w * sizeof(bool)); redraw_all = true; + + Chart::reset(x, y, w, h); } int BarChart::updatePoint(float x, float y) diff --git a/lib/charts/StackedChart.cpp b/lib/charts/StackedChart.cpp index be602bd..1c879dc 100644 --- a/lib/charts/StackedChart.cpp +++ b/lib/charts/StackedChart.cpp @@ -74,10 +74,7 @@ void StackedChart::reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) charts[i]->height); } - pos_x = x; - pos_y = y; - width = w; - height = h; + Chart::reset(x, y, w, h); } void StackedChart::draw() diff --git a/lib/charts/charts.h b/lib/charts/charts.h index 54fc2fa..59fc29e 100644 --- a/lib/charts/charts.h +++ b/lib/charts/charts.h @@ -17,7 +17,13 @@ struct Chart /* * This method resets the state and sets the reference time. */ - virtual void reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {}; + virtual void reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) + { + pos_x = x; + pos_y = y; + width = w; + height = h; + } /* * Redraw everything that needs redrawing. From 30883386c493c22161156974a85330a4ef6128f2 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Mon, 30 Sep 2024 07:59:34 +0100 Subject: [PATCH 48/80] Fixup DecoratedBarChart - incapsulate, not inherit --- lib/charts/BarChart.cpp | 44 +++++++++++++++++++++++------------------ lib/charts/charts.h | 13 ++++++------ src/main.cpp | 6 +++--- 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/lib/charts/BarChart.cpp b/lib/charts/BarChart.cpp index eb58770..36c17d7 100644 --- a/lib/charts/BarChart.cpp +++ b/lib/charts/BarChart.cpp @@ -102,37 +102,43 @@ int BarChart::y2pos(float y) return height - height * (y - min_y) / (max_y - min_y); } +void DecoratedBarChart::reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) +{ + Chart::reset(x, y, w, h); + bar.reset(x, y + LABEL_HEIGHT, w, h - LABEL_HEIGHT - AXIS_HEIGHT); +} + void DecoratedBarChart::draw() { - bool draw_axis = redraw_all; + bool draw_axis = bar.redraw_all; - BarChart::draw(); + bar.draw(); display.setColor(BLACK); - display.fillRect(pos_x, text_y, width, pos_y - text_y); + display.fillRect(pos_x, pos_y, width, bar.pos_y - pos_y); display.setColor(WHITE); display.setTextAlignment(TEXT_ALIGN_LEFT); for (uint16_t x = 0; x < width; x++) { - float y = ys[x]; - if (y >= level_y) + float y = bar.ys[x]; + if (y >= bar.level_y) { - String s = String(ys[x], 0); + String s = String(bar.ys[x], 0); uint16_t w = display.getStringWidth(s); uint16_t x1 = x; for (; x < x1 + w; x++) { - if (ys[x] > y) + if (bar.ys[x] > y) { - y = ys[x]; + y = bar.ys[x]; s = String(y, 0); w = max(w, display.getStringWidth(s)); } } - display.drawString(x1, text_y, s); + display.drawString(x1, pos_y, s); } } @@ -140,23 +146,23 @@ void DecoratedBarChart::draw() { display.setColor(WHITE); - uint16_t y = pos_y + height + 1; - display.fillRect(pos_x, y, width, X_AXIS_WEIGHT); + uint16_t y = pos_y + height - AXIS_HEIGHT + 2; + display.fillRect(pos_x, y - 1, width, X_AXIS_WEIGHT); // Start and end ticks - display.fillRect(pos_x, y + 1, 2, AXIS_HEIGHT); - display.fillRect(pos_x + width - 2, y + 1, 2, AXIS_HEIGHT); + display.fillRect(pos_x, y - 1, 2, AXIS_HEIGHT); + display.fillRect(pos_x + width - 2, y - 1, 2, AXIS_HEIGHT); - for (float step = 0; min_x + step * MAJOR_TICKS < max_x; step += 1) + for (float step = 0; bar.min_x + step * MAJOR_TICKS < bar.max_x; step += 1) { - int tick_pos = x2pos(min_x + step * MAJOR_TICKS); - display.drawVerticalLine(pos_x + tick_pos, y + 1, MAJOR_TICK_LENGTH); + int tick_pos = bar.x2pos(bar.min_x + step * MAJOR_TICKS); + display.drawVerticalLine(pos_x + tick_pos, y, MAJOR_TICK_LENGTH); } - for (float step = 0; min_x + step * MINOR_TICKS < max_x; step += 1) + for (float step = 0; bar.min_x + step * MINOR_TICKS < bar.max_x; step += 1) { - int tick_pos = x2pos(min_x + step * MINOR_TICKS); - display.drawVerticalLine(pos_x + tick_pos, y + 1, MINOR_TICK_LENGTH); + int tick_pos = bar.x2pos(bar.min_x + step * MINOR_TICKS); + display.drawVerticalLine(pos_x + tick_pos, y, MINOR_TICK_LENGTH); } } } diff --git a/lib/charts/charts.h b/lib/charts/charts.h index 59fc29e..f50df37 100644 --- a/lib/charts/charts.h +++ b/lib/charts/charts.h @@ -86,17 +86,18 @@ struct BarChart : ProgressChart #define MAJOR_TICKS 10 #define MINOR_TICK_LENGTH 1 #define MINOR_TICKS 5 -#define AXIS_HEIGHT (X_AXIS_WEIGHT + MAJOR_TICK_LENGTH + 1) -struct DecoratedBarChart : BarChart +#define AXIS_HEIGHT (X_AXIS_WEIGHT + MAJOR_TICK_LENGTH + 2) +struct DecoratedBarChart : Chart { - int text_y; + BarChart bar; DecoratedBarChart(OLEDDisplay &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h, float min_x, float max_x, float min_y, float max_y, float level_y) - : BarChart(d, x, y + LABEL_HEIGHT, w, h - LABEL_HEIGHT - AXIS_HEIGHT, min_x, - max_x, min_y, max_y, level_y), - text_y(y) {}; + : Chart(d, x, y, w, h), + bar(d, x, y + LABEL_HEIGHT, w, h - LABEL_HEIGHT - AXIS_HEIGHT, min_x, max_x, + min_y, max_y, level_y) {}; + void reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) override; void draw() override; }; diff --git a/src/main.cpp b/src/main.cpp index 2e8fbb6..639f350 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -366,7 +366,7 @@ void osdProcess() } #endif -BarChart *bar; +DecoratedBarChart *bar; StackedChart stacked(display, 0, 0, 0, 0); void init_radio() @@ -1086,11 +1086,11 @@ void loop(void) rr = LO_RSSI_THRESHOLD; } - int updated = bar->updatePoint(freq, rr); + int updated = bar->bar.updatePoint(freq, rr); if (first_run || ANIMATED_RELOAD) { - bar->drawOne(updated); + bar->bar.drawOne(updated); } if (detected_at <= drone_detection_level) From ad7ad3d8afd08b1a85f4d1af09f86ee5a737c16e Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Mon, 30 Sep 2024 07:36:36 +0100 Subject: [PATCH 49/80] Waterfall chart to capture history of detections --- lib/charts/WaterfallChart.cpp | 57 +++++++++ lib/charts/charts.h | 25 ++++ lib/models/WaterfallModel.cpp | 153 +++++++++++++++++++++++ lib/models/models.h | 27 +++++ src/main.cpp | 26 +++- test/test_rssi.cpp | 14 --- test/test_waterfall.cpp | 220 ++++++++++++++++++++++++++++++++++ test/tests.cpp | 21 ++++ 8 files changed, 524 insertions(+), 19 deletions(-) create mode 100644 lib/charts/WaterfallChart.cpp create mode 100644 lib/models/WaterfallModel.cpp create mode 100644 lib/models/models.h create mode 100644 test/test_waterfall.cpp create mode 100644 test/tests.cpp diff --git a/lib/charts/WaterfallChart.cpp b/lib/charts/WaterfallChart.cpp new file mode 100644 index 0000000..af83090 --- /dev/null +++ b/lib/charts/WaterfallChart.cpp @@ -0,0 +1,57 @@ +#include "charts.h" +#include + +void WaterfallChart::reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) +{ + Chart::reset(x, y, w, h); + + model->reset(model->times[0], w); + + update_to = model->buckets; +} + +void WaterfallChart::updatePoint(uint64_t t, float x, float y) +{ + if (x < min_x || x >= max_x) + { + return; + } + + update_to = max(update_to, model->updateModel(t, x2pos(x), y >= level_y)); +} + +void WaterfallChart::draw() +{ + size_t h = min(update_to, (size_t)height); + + for (int y = 0; y < h; y++) + { + for (int x = 0; x < width; x++) + { + bool b = model->counts[y][x] > 0 && + (model->events[y][x] >= model->counts[y][x] * threshold); + if (b) + { + display.setColor(WHITE); + } + else + { + display.setColor(BLACK); + } + + display.setPixel(pos_x + x, pos_y + y); + } + } + + update_to = 0; +} + +int WaterfallChart::x2pos(float x) +{ + if (x < min_x) + x = min_x; + if (x > max_x) + x = max_x; + + return width * (x - min_x) / (max_x - min_x); +} diff --git a/lib/charts/charts.h b/lib/charts/charts.h index f50df37..c2c7dfb 100644 --- a/lib/charts/charts.h +++ b/lib/charts/charts.h @@ -3,6 +3,7 @@ #include #include +#include #include struct Chart @@ -128,4 +129,28 @@ struct StackedChart : Chart void draw() override; }; +struct WaterfallChart : Chart +{ + float min_x, max_x; + float level_y, threshold; + + size_t update_to; + + WaterfallModel *model; + + WaterfallChart(OLEDDisplay &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h, + float min_x, float max_x, float level_y, float threshold, + WaterfallModel *m) + : Chart(d, x, y, w, h), model(m), min_x(min_x), max_x(max_x), level_y(level_y), + threshold(threshold), update_to(m->buckets) {}; + + void updatePoint(uint64_t t, float x, float y); + + void reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) override; + + void draw() override; + + int x2pos(float x); +}; + #endif diff --git a/lib/models/WaterfallModel.cpp b/lib/models/WaterfallModel.cpp new file mode 100644 index 0000000..99f7816 --- /dev/null +++ b/lib/models/WaterfallModel.cpp @@ -0,0 +1,153 @@ +#include "models.h" +#include + +WaterfallModel::WaterfallModel(size_t w, uint64_t base_dt, size_t m_sz, + const size_t *multiples) +{ + width = w; + + size_t dt_sz = 0; + for (int i = 0; i < m_sz; i++) + dt_sz += multiples[i]; + + buckets = dt_sz; + dt = new uint64_t[buckets]; + events = new uint32_t *[buckets]; + counts = new uint32_t *[buckets]; + times = new uint64_t[buckets]; + + uint64_t m = base_dt; + for (int i = 0, j = 0; i < m_sz; i++) + { + for (int k = 0; k < multiples[i]; k++, j++) + { + dt[j] = m; + + events[j] = new uint32_t[width]; + counts[j] = new uint32_t[width]; + } + + m *= multiples[i]; + } +} + +void WaterfallModel::reset(uint64_t t0, size_t w) +{ + if (w != width) + { + width = w; + for (int i = 0; i < buckets; i++) + { + delete[] counts[i]; + delete[] events[i]; + + counts[i] = new uint32_t[w]; + events[i] = new uint32_t[w]; + } + } + + for (int i = 0; i < buckets; i++) + { + memset(counts[i], 0, width * sizeof(uint32_t)); + memset(events[i], 0, width * sizeof(uint32_t)); + times[i] = t0 + dt[i]; + } +} + +/* + * The model is literally a stack of counters: + * - incomplete second + * - n complete seconds + * - incomplete minute + * - n complete minutes + * - ... + * + * updateModel updates incomplete second. When the second becomes complete, it + * gets pushed to complete seconds, and the last complete second is rotated out + * and it gets added to incomplete minute. This gets repeated for incomplete + * minutes, etc. + */ +size_t WaterfallModel::updateModel(uint16_t t, size_t x, uint16_t y) +{ + size_t changed = 1; + + while (t > times[0]) + { + changed = push(); + } + + counts[0][x]++; + events[0][x] += y; + return changed; +} + +size_t WaterfallModel::push() +{ + size_t i = 1; + for (; i < buckets; i++) + { + if (dt[i - 1] == dt[i]) + continue; + if (times[i - 1] <= times[i]) + break; + times[i - 1] = times[i] + dt[i]; + } + + uint64_t t0 = times[0]; + + uint32_t *cc = counts[i - 1]; + uint32_t *ee = events[i - 1]; + memmove(times + 1, times, (i - 1) * sizeof(uint64_t)); + memmove(counts + 1, counts, (i - 1) * sizeof(uint32_t *)); + memmove(events + 1, events, (i - 1) * sizeof(uint32_t *)); + + if (i < buckets) + { + for (int j = 0; j < width; j++) + { + counts[i][j] += cc[j]; + events[i][j] += ee[j]; + } + + i++; + } + + memset(cc, 0, width * sizeof(uint32_t)); + memset(ee, 0, width * sizeof(uint32_t)); + counts[0] = cc; + events[0] = ee; + times[0] = t0 + dt[0]; + + return i; +} + +#ifdef TO_STRING + +#include +#include + +#endif + +char *WaterfallModel::toString() +{ +#ifdef TO_STRING + std::stringstream r; + r << "w:" << width << " b:" << buckets << " ["; + + for (int i = 0; i < buckets; i++) + { + r << "dt:" << dt[i] << " t:" << times[i] << " ["; + for (int j = 0; j < width; j++) + r << " c:" << counts[i][j] << " e:" << events[i][j]; + r << " ]"; + } + + r << " ]"; + + char *ret = new char[r.str().length() + 1]; + strncpy(ret, r.str().c_str(), r.str().length()); +#else + char *ret = NULL; +#endif + return ret; +} diff --git a/lib/models/models.h b/lib/models/models.h new file mode 100644 index 0000000..ce8de8f --- /dev/null +++ b/lib/models/models.h @@ -0,0 +1,27 @@ +#ifndef CHARTS_MODELS_H +#define CHARTS_MODELS_H + +#include +#include +#include + +struct WaterfallModel +{ + uint32_t **events; + uint32_t **counts; + uint64_t *times; + uint64_t *dt; + + size_t buckets; + size_t width; + + WaterfallModel(size_t w, uint64_t base_dt, size_t m_sz, const size_t *multiples); + + void reset(uint64_t t0, size_t width); + size_t updateModel(uint16_t t, size_t x, uint16_t y); + size_t push(); + + char *toString(); +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp index 639f350..d04b6ec 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -366,7 +366,9 @@ void osdProcess() } #endif +#define WATERFALL_SENSITIVITY 0.05 DecoratedBarChart *bar; +WaterfallChart *waterChart; StackedChart stacked(display, 0, 0, 0, 0); void init_radio() @@ -664,9 +666,21 @@ void setup(void) stacked.reset(0, 0, display.width(), display.height() - 6); + size_t *multiples = new size_t[6]{5, 3, 4, 15, 4, 3}; + WaterfallModel *model = + new WaterfallModel((size_t)display.width(), 1000, 6, multiples); + model->reset(millis(), display.width()); + + delete[] multiples; + + waterChart = + new WaterfallChart(display, 0, WATERFALL_START, display.width(), 0, FREQ_BEGIN, + FREQ_END, -(float)show_db_after, WATERFALL_SENSITIVITY, model); + + stacked.reset(0, 0, display.width(), display.height() - 6); + size_t b = stacked.addChart(bar); - size_t c = - stacked.addChart(new Chart(display, 0, 0, display.width(), 0)); + size_t c = stacked.addChart(waterChart); stacked.setHeight(c, stacked.height - WATERFALL_START); stacked.setHeight(b, stacked.height); @@ -1086,6 +1100,8 @@ void loop(void) rr = LO_RSSI_THRESHOLD; } + waterChart->updatePoint(millis(), freq, rr); + int updated = bar->bar.updatePoint(freq, rr); if (first_run || ANIMATED_RELOAD) @@ -1108,7 +1124,7 @@ void loop(void) { waterfall[display_x] = true; display.setColor(WHITE); - display.setPixel(display_x, w); + // display.setPixel(display_x, w); } } #endif @@ -1150,7 +1166,7 @@ void loop(void) // TODO: make something like scrolling up if possible waterfall[display_x] = false; display.setColor(BLACK); - display.setPixel(display_x, w); + // display.setPixel(display_x, w); display.setColor(WHITE); } #endif @@ -1248,7 +1264,7 @@ void loop(void) if (single_page_scan) { display.setColor(BLACK); - display.drawHorizontalLine(0, w, STEPS); + // display.drawHorizontalLine(0, w, STEPS); display.setColor(WHITE); } #endif diff --git a/test/test_rssi.cpp b/test/test_rssi.cpp index 8a75b60..3d82f29 100644 --- a/test/test_rssi.cpp +++ b/test/test_rssi.cpp @@ -3,10 +3,6 @@ #include "../lib/scan/scan.cpp" #include -void setUp(void) {} - -void tearDown(void) {} - struct TestScan : Scan { TestScan(float *ctx, int sz) : ctx(ctx), sz(sz), idx(0) {} @@ -66,13 +62,3 @@ void test_detect() TEST_ASSERT_EQUAL_INT16(1, r); TEST_ASSERT_EQUAL_INT8_ARRAY(expect2, result, test_sz); } - -int main(int argc, char **argv) -{ - UNITY_BEGIN(); - - RUN_TEST(test_rssi); - RUN_TEST(test_detect); - - UNITY_END(); -} diff --git a/test/test_waterfall.cpp b/test/test_waterfall.cpp new file mode 100644 index 0000000..1875065 --- /dev/null +++ b/test/test_waterfall.cpp @@ -0,0 +1,220 @@ +#define TO_STRING +#include "../lib/models/WaterfallModel.cpp" +#include +#include + +void test_push() +{ + size_t *ms = new size_t[6]{5, 3, 4, 15, 4, 3}; + WaterfallModel m(1, 1, 6, ms); + delete ms; + + char *r = m.toString(); + TEST_ASSERT_EQUAL_STRING("w:1 b:34 " + "[dt:1 t:0 [ c:0 e:0 ]" + "dt:1 t:0 [ c:0 e:0 ]" + "dt:1 t:0 [ c:0 e:0 ]" + "dt:1 t:0 [ c:0 e:0 ]" + "dt:1 t:0 [ c:0 e:0 ]" + "dt:5 t:0 [ c:0 e:0 ]" + "dt:5 t:0 [ c:0 e:0 ]" + "dt:5 t:0 [ c:0 e:0 ]" + "dt:15 t:0 [ c:0 e:0 ]" + "dt:15 t:0 [ c:0 e:0 ]" + "dt:15 t:0 [ c:0 e:0 ]" + "dt:15 t:0 [ c:0 e:0 ]" + "dt:60 t:0 [ c:0 e:0 ]" + "dt:60 t:0 [ c:0 e:0 ]" + "dt:60 t:0 [ c:0 e:0 ]" + "dt:60 t:0 [ c:0 e:0 ]" + "dt:60 t:0 [ c:0 e:0 ]" + "dt:60 t:0 [ c:0 e:0 ]" + "dt:60 t:0 [ c:0 e:0 ]" + "dt:60 t:0 [ c:0 e:0 ]" + "dt:60 t:0 [ c:0 e:0 ]" + "dt:60 t:0 [ c:0 e:0 ]" + "dt:60 t:0 [ c:0 e:0 ]" + "dt:60 t:0 [ c:0 e:0 ]" + "dt:60 t:0 [ c:0 e:0 ]" + "dt:60 t:0 [ c:0 e:0 ]" + "dt:60 t:0 [ c:0 e:0 ]" + "dt:900 t:0 [ c:0 e:0 ]" + "dt:900 t:0 [ c:0 e:0 ]" + "dt:900 t:0 [ c:0 e:0 ]" + "dt:900 t:0 [ c:0 e:0 ]" + "dt:3600 t:0 [ c:0 e:0 ]" + "dt:3600 t:0 [ c:0 e:0 ]" + "dt:3600 t:0 [ c:0 e:0 ] ]", + r); + delete r; + + m.reset(0, 1); + uint64_t i = 0; + for (; i < 10; i++) + m.updateModel(i, 0, 1); + + r = m.toString(); + TEST_ASSERT_EQUAL_STRING("w:1 b:34 " + "[dt:1 t:9 [ c:1 e:1 ]" + "dt:1 t:8 [ c:1 e:1 ]" + "dt:1 t:7 [ c:1 e:1 ]" + "dt:1 t:6 [ c:1 e:1 ]" + "dt:1 t:5 [ c:1 e:1 ]" + "dt:5 t:5 [ c:5 e:5 ]" + "dt:5 t:5 [ c:0 e:0 ]" + "dt:5 t:5 [ c:0 e:0 ]" + "dt:15 t:15 [ c:0 e:0 ]" + "dt:15 t:15 [ c:0 e:0 ]" + "dt:15 t:15 [ c:0 e:0 ]" + "dt:15 t:15 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:900 t:900 [ c:0 e:0 ]" + "dt:900 t:900 [ c:0 e:0 ]" + "dt:900 t:900 [ c:0 e:0 ]" + "dt:900 t:900 [ c:0 e:0 ]" + "dt:3600 t:3600 [ c:0 e:0 ]" + "dt:3600 t:3600 [ c:0 e:0 ]" + "dt:3600 t:3600 [ c:0 e:0 ] ]", + r); + delete r; + + for (; i < 100; i += 10) + m.updateModel(i, 0, 1); + + r = m.toString(); + TEST_ASSERT_EQUAL_STRING("w:1 b:34 " + "[dt:1 t:90 [ c:1 e:1 ]" + "dt:1 t:89 [ c:0 e:0 ]" + "dt:1 t:88 [ c:0 e:0 ]" + "dt:1 t:87 [ c:0 e:0 ]" + "dt:1 t:86 [ c:0 e:0 ]" + "dt:5 t:85 [ c:0 e:0 ]" + "dt:5 t:80 [ c:1 e:1 ]" + "dt:5 t:75 [ c:0 e:0 ]" + "dt:15 t:75 [ c:1 e:1 ]" + "dt:15 t:60 [ c:2 e:2 ]" + "dt:15 t:45 [ c:1 e:1 ]" + "dt:15 t:30 [ c:2 e:2 ]" + "dt:60 t:60 [ c:11 e:11 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:60 t:60 [ c:0 e:0 ]" + "dt:900 t:900 [ c:0 e:0 ]" + "dt:900 t:900 [ c:0 e:0 ]" + "dt:900 t:900 [ c:0 e:0 ]" + "dt:900 t:900 [ c:0 e:0 ]" + "dt:3600 t:3600 [ c:0 e:0 ]" + "dt:3600 t:3600 [ c:0 e:0 ]" + "dt:3600 t:3600 [ c:0 e:0 ] ]", + r); + delete r; + + for (; i < 10000; i++) + m.updateModel(i, 0, 1); + + r = m.toString(); + TEST_ASSERT_EQUAL_STRING("w:1 b:34 " + "[dt:1 t:9999 [ c:1 e:1 ]" + "dt:1 t:9998 [ c:1 e:1 ]" + "dt:1 t:9997 [ c:1 e:1 ]" + "dt:1 t:9996 [ c:1 e:1 ]" + "dt:1 t:9995 [ c:1 e:1 ]" + "dt:5 t:9995 [ c:4 e:4 ]" + "dt:5 t:9990 [ c:5 e:5 ]" + "dt:5 t:9985 [ c:5 e:5 ]" + "dt:15 t:9990 [ c:5 e:5 ]" + "dt:15 t:9975 [ c:15 e:15 ]" + "dt:15 t:9960 [ c:15 e:15 ]" + "dt:15 t:9945 [ c:15 e:15 ]" + "dt:60 t:9960 [ c:30 e:30 ]" + "dt:60 t:9900 [ c:60 e:60 ]" + "dt:60 t:9840 [ c:60 e:60 ]" + "dt:60 t:9780 [ c:60 e:60 ]" + "dt:60 t:9720 [ c:60 e:60 ]" + "dt:60 t:9660 [ c:60 e:60 ]" + "dt:60 t:9600 [ c:60 e:60 ]" + "dt:60 t:9540 [ c:60 e:60 ]" + "dt:60 t:9480 [ c:60 e:60 ]" + "dt:60 t:9420 [ c:60 e:60 ]" + "dt:60 t:9360 [ c:60 e:60 ]" + "dt:60 t:9300 [ c:60 e:60 ]" + "dt:60 t:9240 [ c:60 e:60 ]" + "dt:60 t:9180 [ c:60 e:60 ]" + "dt:60 t:9120 [ c:60 e:60 ]" + "dt:900 t:9900 [ c:60 e:60 ]" + "dt:900 t:9000 [ c:900 e:900 ]" + "dt:900 t:8100 [ c:900 e:900 ]" + "dt:900 t:7200 [ c:900 e:900 ]" + "dt:3600 t:7200 [ c:2700 e:2700 ]" + "dt:3600 t:3600 [ c:3520 e:3520 ]" + "dt:3600 t:3600 [ c:0 e:0 ] ]", + r); + delete r; + + for (; i < 5000; i++) + m.updateModel(i, 0, 1); + + r = m.toString(); + TEST_ASSERT_EQUAL_STRING("w:1 b:34 " + "[dt:1 t:9999 [ c:1 e:1 ]" + "dt:1 t:9998 [ c:1 e:1 ]" + "dt:1 t:9997 [ c:1 e:1 ]" + "dt:1 t:9996 [ c:1 e:1 ]" + "dt:1 t:9995 [ c:1 e:1 ]" + "dt:5 t:9995 [ c:4 e:4 ]" + "dt:5 t:9990 [ c:5 e:5 ]" + "dt:5 t:9985 [ c:5 e:5 ]" + "dt:15 t:9990 [ c:5 e:5 ]" + "dt:15 t:9975 [ c:15 e:15 ]" + "dt:15 t:9960 [ c:15 e:15 ]" + "dt:15 t:9945 [ c:15 e:15 ]" + "dt:60 t:9960 [ c:30 e:30 ]" + "dt:60 t:9900 [ c:60 e:60 ]" + "dt:60 t:9840 [ c:60 e:60 ]" + "dt:60 t:9780 [ c:60 e:60 ]" + "dt:60 t:9720 [ c:60 e:60 ]" + "dt:60 t:9660 [ c:60 e:60 ]" + "dt:60 t:9600 [ c:60 e:60 ]" + "dt:60 t:9540 [ c:60 e:60 ]" + "dt:60 t:9480 [ c:60 e:60 ]" + "dt:60 t:9420 [ c:60 e:60 ]" + "dt:60 t:9360 [ c:60 e:60 ]" + "dt:60 t:9300 [ c:60 e:60 ]" + "dt:60 t:9240 [ c:60 e:60 ]" + "dt:60 t:9180 [ c:60 e:60 ]" + "dt:60 t:9120 [ c:60 e:60 ]" + "dt:900 t:9900 [ c:60 e:60 ]" + "dt:900 t:9000 [ c:900 e:900 ]" + "dt:900 t:8100 [ c:900 e:900 ]" + "dt:900 t:7200 [ c:900 e:900 ]" + "dt:3600 t:7200 [ c:2700 e:2700 ]" + "dt:3600 t:3600 [ c:3520 e:3520 ]" + "dt:3600 t:3600 [ c:0 e:0 ] ]", + r); + delete r; +} diff --git a/test/tests.cpp b/test/tests.cpp new file mode 100644 index 0000000..46fa133 --- /dev/null +++ b/test/tests.cpp @@ -0,0 +1,21 @@ +#include + +void test_rssi(); +void test_detect(); +void test_push(); + +void setUp(void) {} + +void tearDown(void) {} + +int main(int argc, char **argv) +{ + UNITY_BEGIN(); + + RUN_TEST(test_rssi); + RUN_TEST(test_detect); + + RUN_TEST(test_push); + + UNITY_END(); +} From 645e9988c8a8934099968a33dea962c3b00ad5f9 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Tue, 1 Oct 2024 19:32:46 +0100 Subject: [PATCH 50/80] Test stability: uptime clock --- lib/charts/UptimeClock.cpp | 28 ++++++++++++++++++++++++++++ lib/charts/charts.h | 10 ++++++++++ src/main.cpp | 10 ++++++++++ 3 files changed, 48 insertions(+) create mode 100644 lib/charts/UptimeClock.cpp diff --git a/lib/charts/UptimeClock.cpp b/lib/charts/UptimeClock.cpp new file mode 100644 index 0000000..f7e229e --- /dev/null +++ b/lib/charts/UptimeClock.cpp @@ -0,0 +1,28 @@ +#include "charts.h" + +void UptimeClock::draw(uint64_t t) +{ + t1 = t; + draw(); +} + +void UptimeClock::draw() +{ + uint64_t uptime = t1 - t0; + int mils = uptime % 1000; + int seconds = (uptime / 1000) % 60; + int minutes = (uptime / 60000) % 60; + int hours = uptime / 3600000; + String s = String(hours) + (minutes < 10 ? ":0" : ":") + String(minutes) + + (seconds < 10 ? ":0" : ":") + String(seconds) + + (mils < 10 ? ".00" + : mils < 100 ? ".0" + : ".") + + String(mils); + int w = display.getStringWidth(s); + display.setColor(BLACK); + display.fillRect((display.width() - w) / 2, display.height() / 2 - 3, w, 7); + display.setColor(WHITE); + display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH); + display.drawString(display.width() / 2, display.height() / 2, s); +} diff --git a/lib/charts/charts.h b/lib/charts/charts.h index c2c7dfb..53b4f31 100644 --- a/lib/charts/charts.h +++ b/lib/charts/charts.h @@ -153,4 +153,14 @@ struct WaterfallChart : Chart int x2pos(float x); }; +struct UptimeClock : Chart +{ + uint64_t t0; + uint64_t t1; + UptimeClock(OLEDDisplay &d, uint64_t t0) : Chart(d, 0, 0, 0, 0), t0(t0), t1(t0) {}; + + void draw(uint64_t t); + virtual void draw() override; +}; + #endif diff --git a/src/main.cpp b/src/main.cpp index d04b6ec..b26b32d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -371,6 +371,8 @@ DecoratedBarChart *bar; WaterfallChart *waterChart; StackedChart stacked(display, 0, 0, 0, 0); +UptimeClock *uptime; + void init_radio() { // initialize SX1262 FSK modem at the initial frequency @@ -684,6 +686,10 @@ void setup(void) stacked.setHeight(c, stacked.height - WATERFALL_START); stacked.setHeight(b, stacked.height); + +#ifdef UPTIME_CLOCK + uptime = new UptimeClock(display, millis()); +#endif } // Formula to translate 33 bin to approximate RSSI value @@ -1271,6 +1277,10 @@ void loop(void) stacked.draw(); // Render display data here + +#ifdef UPTIME_CLOCK + uptime->draw(millis()); +#endif display.display(); #ifdef OSD_ENABLED // Sometimes OSD prints entire screen with the digits. From 0c760610dfd3f37633252b683cdf47a5df4e2f41 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Thu, 3 Oct 2024 07:28:51 +0100 Subject: [PATCH 51/80] Status Bar --- include/ui.h | 26 +++-- lib/charts/charts.h | 25 +++-- lib/scan/scan.h | 14 +++ src/main.cpp | 195 +++++++++++++-------------------- src/ui.cpp | 256 +++++++++++++------------------------------- 5 files changed, 198 insertions(+), 318 deletions(-) diff --git a/include/ui.h b/include/ui.h index 2f18a7b..ddf7a73 100644 --- a/include/ui.h +++ b/include/ui.h @@ -8,6 +8,9 @@ #include #endif +#include +#include + // #include // (optional) major and minor tick-marks at x MHz @@ -31,14 +34,21 @@ #define SCREEN_HEIGHT 64 // ???? not used -// publish functions -#ifdef Vision_Master_E290 -extern void UI_Init(DEPG0290BxS800FxX_BW *); -#else -extern void UI_Init(SSD1306Wire *); -#endif -extern void UI_displayDecorate(int, int, bool); -extern void UI_setLedFlag(bool); +extern void UI_Init(Display_t *); extern void UI_clearPlotter(void); extern void UI_clearTopStatus(void); extern void UI_drawCursor(int16_t); + +struct StatusBar : Chart +{ + Scan &r; + bool ui_initialized; + uint16_t scan_progress_count; + + StatusBar(Display_t &d, uint16_t x, uint16_t y, uint16_t w, Scan &r) + : Chart(d, x, y, w, LABEL_HEIGHT), r(r), ui_initialized(false), + scan_progress_count(0) {}; + + virtual void clearStatus(); + virtual void draw() override; +}; diff --git a/lib/charts/charts.h b/lib/charts/charts.h index 53b4f31..d6d11b4 100644 --- a/lib/charts/charts.h +++ b/lib/charts/charts.h @@ -1,7 +1,14 @@ #ifndef CHARTS_H #define CHARTS_H +#ifdef Vision_Master_E290 +#include "HT_DEPG0290BxS800FxX_BW.h" +typedef DEPG0290BxS800FxX_BW Display_t; +#else #include +typedef OLEDDisplay Display_t; +#endif + #include #include #include @@ -10,9 +17,9 @@ struct Chart { uint16_t pos_x, pos_y; uint16_t width, height; - OLEDDisplay &display; + Display_t &display; - Chart(OLEDDisplay &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h) + Chart(Display_t &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h) : display(d), pos_x(x), pos_y(y), width(w), height(h) {}; /* @@ -37,7 +44,7 @@ struct Chart */ struct ProgressChart : Chart { - ProgressChart(OLEDDisplay &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h) + ProgressChart(Display_t &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h) : Chart(d, x, y, w, h) {}; /* @@ -60,7 +67,7 @@ struct BarChart : ProgressChart bool *changed; bool redraw_all; - BarChart(OLEDDisplay &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h, float min_x, + BarChart(Display_t &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h, float min_x, float max_x, float min_y, float max_y, float level_y) : ProgressChart(d, x, y, w, h), min_x(min_x), max_x(max_x), min_y(min_y), max_y(max_y), level_y(level_y), redraw_all(true) @@ -81,7 +88,7 @@ struct BarChart : ProgressChart int y2pos(float y); }; -#define LABEL_HEIGHT 6 +#define LABEL_HEIGHT 7 #define X_AXIS_WEIGHT 1 #define MAJOR_TICK_LENGTH 2 #define MAJOR_TICKS 10 @@ -92,7 +99,7 @@ struct DecoratedBarChart : Chart { BarChart bar; - DecoratedBarChart(OLEDDisplay &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h, + DecoratedBarChart(Display_t &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h, float min_x, float max_x, float min_y, float max_y, float level_y) : Chart(d, x, y, w, h), bar(d, x, y + LABEL_HEIGHT, w, h - LABEL_HEIGHT - AXIS_HEIGHT, min_x, max_x, @@ -107,7 +114,7 @@ struct StackedChart : Chart Chart **charts; size_t charts_sz; - StackedChart(OLEDDisplay &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h) + StackedChart(Display_t &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h) : Chart(d, x, y, w, h), charts(NULL), charts_sz(0) {}; /* @@ -138,7 +145,7 @@ struct WaterfallChart : Chart WaterfallModel *model; - WaterfallChart(OLEDDisplay &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h, + WaterfallChart(Display_t &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h, float min_x, float max_x, float level_y, float threshold, WaterfallModel *m) : Chart(d, x, y, w, h), model(m), min_x(min_x), max_x(max_x), level_y(level_y), @@ -157,7 +164,7 @@ struct UptimeClock : Chart { uint64_t t0; uint64_t t1; - UptimeClock(OLEDDisplay &d, uint64_t t0) : Chart(d, 0, 0, 0, 0), t0(t0), t1(t0) {}; + UptimeClock(Display_t &d, uint64_t t0) : Chart(d, 0, 0, 0, 0), t0(t0), t1(t0) {}; void draw(uint64_t t); virtual void draw() override; diff --git a/lib/scan/scan.h b/lib/scan/scan.h index f5af2c6..b471e7e 100644 --- a/lib/scan/scan.h +++ b/lib/scan/scan.h @@ -33,6 +33,20 @@ constexpr float LO_RSSI_THRESHOLD = HI_RSSI_THRESHOLD - 66; struct Scan { + uint64_t epoch; + float current_frequency; + uint64_t fr_begin; + uint64_t fr_end; + uint64_t drone_detection_level; + bool sound_on; + bool led_flag; + uint64_t detection_count; + + Scan() + : epoch(0), current_frequency(0), fr_begin(0), fr_end(0), + drone_detection_level(0), sound_on(false), led_flag(false), + detection_count(0) {}; + virtual float getRSSI() = 0; // rssiMethod gets the data similar to the scan method, diff --git a/src/main.cpp b/src/main.cpp index b26b32d..1a6590a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -172,13 +172,11 @@ constexpr int WINDOW_SIZE = 15; #define SINGLE_STEP (float)(RANGE / (STEPS * SCAN_RBW_FACTOR)) uint64_t range = (int)(FREQ_END - FREQ_BEGIN); -uint64_t fr_begin = FREQ_BEGIN; -uint64_t fr_end = FREQ_BEGIN; uint64_t iterations = RANGE / RANGE_PER_PAGE; // uint64_t range_frequency = FREQ_END - FREQ_BEGIN; -uint64_t median_frequency = FREQ_BEGIN + FREQ_END - FREQ_BEGIN / 2; +uint64_t median_frequency = (FREQ_BEGIN + FREQ_END) / 2; // #define DISABLE_PLOT_CHART false // unused @@ -190,8 +188,7 @@ bool filtered_result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; int max_bins_array_value[MAX_POWER_LEVELS]; int max_step_range = 32; -// Waterfall array -bool waterfall[STEPS], detected_y[STEPS]; // 20 - ??? steps of the waterfall +bool detected_y[STEPS]; // 20 - ??? steps // global variable @@ -203,9 +200,7 @@ uint64_t drone_detection_level = DEFAULT_DRONE_DETECTION_LEVEL; uint64_t show_db_after = 80; 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 = false; // #define PRINT_DEBUG #define PRINT_PROFILE_TIME @@ -225,7 +220,6 @@ uint64_t x, y, range_item, w = WATERFALL_START, i = 0; int osd_x = 1, osd_y = 2, col = 0, max_bin = 32; uint64_t ranges_count = 0; -float freq = 0; int rssi = 0; int state = 0; @@ -238,7 +232,6 @@ constexpr int samples = SAMPLES_RSSI; uint8_t result_index = 0; uint8_t button_pressed_counter = 0; -uint64_t loop_cnt = 0; #ifndef LILYGO // #define JOYSTICK_ENABLED @@ -366,6 +359,27 @@ void osdProcess() } #endif +struct RadioScan : Scan +{ + float getRSSI() override; +}; + +float RadioScan::getRSSI() +{ +#ifdef USING_SX1280PA + // radio.startReceive(); + // get instantaneous RSSI value + // When PR will be merged we can use radi.getRSSI(false); + uint8_t data[3] = {0, 0, 0}; // RssiInst, Status, RFU + radio.mod->SPIreadStream(RADIOLIB_SX128X_CMD_GET_RSSI_INST, data, 3); + return ((float)data[0] / (-2.0)); +#else + return radio.getRSSI(false); +#endif +} + +RadioScan r; + #define WATERFALL_SENSITIVITY 0.05 DecoratedBarChart *bar; WaterfallChart *waterChart; @@ -532,7 +546,6 @@ void setup(void) #endif float vbat; float resolution; - loop_cnt = 0; bt_start = millis(); wf_start = millis(); @@ -551,7 +564,7 @@ void setup(void) delay(10); if (button.pressed()) { - SOUND_ON = !SOUND_ON; + r.sound_on = !r.sound_on; tone(BUZZER_PIN, 205, 100); delay(50); tone(BUZZER_PIN, 205, 100); @@ -662,12 +675,17 @@ void setup(void) xTaskCreate(logToSerialTask, "LOG_DATA_JSON", 2048, NULL, 1, NULL); #endif + stacked.reset(0, 0, display.width(), display.height()); + bar = new DecoratedBarChart(display, 0, 0, display.width(), 0, FREQ_BEGIN, FREQ_END, LO_RSSI_THRESHOLD, HI_RSSI_THRESHOLD, -(float)show_db_after); - stacked.reset(0, 0, display.width(), display.height() - 6); + size_t b = stacked.addChart(bar); + Chart *statusBar = new StatusBar(display, 0, 0, display.width(), r); + +#if (WATERFALL_ENABLED == true) size_t *multiples = new size_t[6]{5, 3, 4, 15, 4, 3}; WaterfallModel *model = new WaterfallModel((size_t)display.width(), 1000, 6, multiples); @@ -679,12 +697,11 @@ void setup(void) new WaterfallChart(display, 0, WATERFALL_START, display.width(), 0, FREQ_BEGIN, FREQ_END, -(float)show_db_after, WATERFALL_SENSITIVITY, model); - stacked.reset(0, 0, display.width(), display.height() - 6); - - size_t b = stacked.addChart(bar); size_t c = stacked.addChart(waterChart); + stacked.setHeight(c, stacked.height - WATERFALL_START - statusBar->height); +#endif - stacked.setHeight(c, stacked.height - WATERFALL_START); + size_t d = stacked.addChart(statusBar); stacked.setHeight(b, stacked.height); #ifdef UPTIME_CLOCK @@ -788,12 +805,12 @@ void drone_sound_alarm(int drone_detection_level, int detection_count, tone_freq_db = 285 - tone_freq_db; } - if (detection_count == 1 && SOUND_ON) + if (r.detection_count == 1 && r.sound_on) { tone(BUZZER_PIN, tone_freq_db, 10); // same action ??? but first time } - if (detection_count % 5 == 0 && SOUND_ON) + if (r.detection_count % 5 == 0 && r.sound_on) { tone(BUZZER_PIN, tone_freq_db, 10); // same action ??? but every 5th time @@ -801,7 +818,7 @@ void drone_sound_alarm(int drone_detection_level, int detection_count, } else { - if (detection_count % 20 == 0 && SOUND_ON) + if (r.detection_count % 20 == 0 && r.sound_on) { tone(BUZZER_PIN, 205, 10); // same action ??? but every 20th detection @@ -815,7 +832,7 @@ void joystickMoveCursor(int joy_x_pressed) if (joy_x_pressed > 0) { cursor_x_position--; - display.drawString(cursor_x_position, 0, String((int)freq)); + display.drawString(cursor_x_position, 0, String((int)r.current_frequency)); display.drawLine(cursor_x_position, 1, cursor_x_position, 10); display.display(); delay(10); @@ -823,7 +840,7 @@ void joystickMoveCursor(int joy_x_pressed) else if (joy_x_pressed < 0) { cursor_x_position++; - display.drawString(cursor_x_position, 0, String((int)freq)); + display.drawString(cursor_x_position, 0, String((int)r.current_frequency)); display.drawLine(cursor_x_position, 1, cursor_x_position, 10); display.display(); delay(10); @@ -831,7 +848,7 @@ void joystickMoveCursor(int joy_x_pressed) if (cursor_x_position > DISPLAY_WIDTH || cursor_x_position < 0) { cursor_x_position = 0; - display.drawString(cursor_x_position, 0, String((int)freq)); + display.drawString(cursor_x_position, 0, String((int)r.current_frequency)); display.drawLine(cursor_x_position, 1, cursor_x_position, 10); display.display(); delay(10); @@ -869,45 +886,23 @@ void check_ranges() } } -struct RadioScan : Scan -{ - float getRSSI() override; -}; - -float RadioScan::getRSSI() -{ -#ifdef USING_SX1280PA - // radio.startReceive(); - // get instantaneous RSSI value - // When PR will be merged we can use radi.getRSSI(false); - uint8_t data[3] = {0, 0, 0}; // RssiInst, Status, RFU - radio.mod->SPIreadStream(RADIOLIB_SX128X_CMD_GET_RSSI_INST, data, 3); - return ((float)data[0] / (-2.0)); -#else - return radio.getRSSI(false); -#endif -} - // MAX Frequency RSSI BIN value of the samples int max_rssi_x = 999; -RadioScan r; - void loop(void) { - UI_displayDecorate(0, 0, false); // some default values + r.led_flag = false; - detection_count = 0; + r.detection_count = 0; drone_detected_frequency_start = 0; ranges_count = 0; // reset scan time #ifdef PRINT_PROFILE_TIME scan_time = 0; - // general purpose loop counter - loop_cnt++; loop_start = millis(); #endif + r.epoch++; if (!ANIMATED_RELOAD || !single_page_scan) { @@ -923,8 +918,8 @@ void loop(void) RANGE_PER_PAGE = range; } - fr_begin = FREQ_BEGIN; - fr_end = fr_begin; + r.fr_begin = FREQ_BEGIN; + r.fr_end = r.fr_begin; // 50 is a single-screen range // TODO: Make 50 a variable with the option to show the full range @@ -946,14 +941,14 @@ void loop(void) range = RANGE_PER_PAGE; if (ranges_count == 0) { - fr_begin = (range_item == 0) ? fr_begin : fr_begin += range; - fr_end = fr_begin + RANGE_PER_PAGE; + r.fr_begin = (range_item == 0) ? r.fr_begin : r.fr_begin + range; + r.fr_end = r.fr_begin + RANGE_PER_PAGE; } else { - fr_begin = SCAN_RANGES[range_item] / 1000; - fr_end = SCAN_RANGES[range_item] % 1000; - range = fr_end - fr_begin; + r.fr_begin = SCAN_RANGES[range_item] / 1000; + r.fr_end = SCAN_RANGES[range_item] % 1000; + range = r.fr_end - r.fr_begin; } #ifdef DISABLED_CODE @@ -964,11 +959,6 @@ void loop(void) } #endif - if (single_page_scan == false) - { - UI_displayDecorate(fr_begin, fr_end, true); - } - drone_detected_frequency_start = 0; display.setTextAlignment(TEXT_ALIGN_RIGHT); @@ -999,26 +989,29 @@ void loop(void) // Because of the SCAN_RBW_FACTOR x is not a display coordinate anymore // x > STEPS on SCAN_RBW_FACTOR int display_x = x / SCAN_RBW_FACTOR; - waterfall[display_x] = false; float step = (range * ((float)x / (STEPS * SCAN_RBW_FACTOR))); - freq = fr_begin + step; - LOG("setFrequency:%f\n", freq); + r.current_frequency = r.fr_begin + step; + LOG("setFrequency:%f\n", r.current_frequency); #ifdef USING_SX1280PA - state = radio.setFrequency(freq); // 1280 doesn't have calibration + state = + radio.setFrequency(r.current_frequency); // 1280 doesn't have calibration radio.startReceive(RADIOLIB_SX128X_RX_TIMEOUT_INF); #elif USING_SX1276 state = radio.setFrequency(freq); #else - state = radio.setFrequency(freq, false); // false = no calibration need here + state = radio.setFrequency(r.current_frequency, + false); // false = no calibration need here #endif int radio_error_count = 0; if (state != RADIOLIB_ERR_NONE) { - display.drawString( - 0, 64 - 10, "E(" + String(state) + "):setFrequency:" + String(freq)); - Serial.println("E(" + String(state) + "):setFrequency:" + String(freq)); + display.drawString(0, 64 - 10, + "E(" + String(state) + + "):setFrequency:" + String(r.current_frequency)); + Serial.println("E(" + String(state) + + "):setFrequency:" + String(r.current_frequency)); display.display(); delay(2); radio_error_count++; @@ -1026,7 +1019,7 @@ void loop(void) continue; } - LOG("Step:%d Freq: %f\n", x, freq); + LOG("Step:%d Freq: %f\n", x, r.current_frequency); // SpectralScan Method #ifdef METHOD_SPECTRAL { @@ -1106,9 +1099,13 @@ void loop(void) rr = LO_RSSI_THRESHOLD; } - waterChart->updatePoint(millis(), freq, rr); + r.drone_detection_level = drone_detection_level; - int updated = bar->bar.updatePoint(freq, rr); +#if (WATERFALL_ENABLED == true) + waterChart->updatePoint(millis(), r.current_frequency, rr); +#endif + + int updated = bar->bar.updatePoint(r.current_frequency, rr); if (first_run || ANIMATED_RELOAD) { @@ -1121,30 +1118,18 @@ void loop(void) if (detected_y[display_x] == false) // detection threshold match { // Set LED to ON (filtered in UI component) - UI_setLedFlag(true); -#if (WATERFALL_ENABLED == true) - if (single_page_scan) - { - // Drone detection true for waterfall - if (!waterfall[display_x]) - { - waterfall[display_x] = true; - display.setColor(WHITE); - // display.setPixel(display_x, w); - } - } -#endif + r.led_flag = true; if (drone_detected_frequency_start == 0) { // mark freq start - drone_detected_frequency_start = freq; + drone_detected_frequency_start = r.current_frequency; } // mark freq end ... will shift right to last detected range - drone_detected_frequency_end = freq; - if (SOUND_ON == true) + drone_detected_frequency_end = r.current_frequency; + if (r.sound_on) { - drone_sound_alarm(drone_detection_level, detection_count, + drone_sound_alarm(r.drone_detection_level, r.detection_count, max_rssi_x * 2); } @@ -1165,34 +1150,13 @@ void loop(void) #endif } } -#if (WATERFALL_ENABLED == true) - if ((single_page_scan) && (waterfall[display_x] != true) && new_pixel) - { - // If drone not found set dark pixel on the waterfall - // TODO: make something like scrolling up if possible - waterfall[display_x] = false; - display.setColor(BLACK); - // display.setPixel(display_x, w); - display.setColor(WHITE); - } -#endif } -#ifdef PRINT_DEBUG - for (int y = 0; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) - { - if (filtered_result[y] == 1) - { - LOG("Pixel:%i(%i):%i,", display_x, x, y); - } - } -#endif - #ifdef JOYSTICK_ENABLED // Draw joystick cursor and Frequency RSSI value if (display_x == cursor_x_position) { - display.drawString(display_x - 1, 0, String((int)freq)); + display.drawString(display_x - 1, 0, String((int)r.current_frequency)); display.drawLine(display_x, 1, display_x, 12); // if method scan RSSI we can get exact RSSI value display.drawString(display_x + 17, 0, "-" + String((int)max_rssi_x * 4)); @@ -1205,7 +1169,7 @@ void loop(void) // count detected if (detected) { - detection_count++; + r.detection_count++; } #ifdef PRINT_DEBUG @@ -1216,7 +1180,7 @@ void loop(void) display.display(); } - if (buttonPressHandler(freq) == false) + if (buttonPressHandler(r.current_frequency) == false) break; // wait a little bit before the next scan, @@ -1265,15 +1229,6 @@ void loop(void) { w = WATERFALL_START; } -#if (WATERFALL_ENABLED == true) - // Draw waterfall position cursor - if (single_page_scan) - { - display.setColor(BLACK); - // display.drawHorizontalLine(0, w, STEPS); - display.setColor(WHITE); - } -#endif stacked.draw(); // Render display data here diff --git a/src/ui.cpp b/src/ui.cpp index ea376dd..e488060 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -2,6 +2,8 @@ #include "RadioLib.h" #include "global_config.h" #include "images.h" +#include +#include // ------------------------------------------------- // LOCAL DEFINES @@ -12,24 +14,9 @@ // #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; - -#ifdef Vision_Master_E290 -static DEPG0290BxS800FxX_BW *display_instance; -#else -//(0x3c, SDA_OLED, SCL_OLED, DISPLAY_GEOMETRY); -static SSD1306Wire *display_instance; -#endif // 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 unsigned int drone_detected_frequency_start; extern unsigned int drone_detected_frequency_end; extern unsigned int ranges_count; @@ -40,230 +27,138 @@ extern unsigned int range_item; extern uint64_t loop_time; -#ifndef Vision_Master_E290 -void UI_Init(SSD1306Wire *display_ptr) +void UI_Init(Display_t *display_ptr) { - // init pointer to display instance. - display_instance = display_ptr; // check for null ??? - display_instance->clear(); + display_ptr->clear(); // draw the UCOG welcome logo - display_instance->drawXbm(0, 2, 128, 64, epd_bitmap_ucog); - display_instance->display(); + display_ptr->drawXbm(0, 2, 128, 64, epd_bitmap_ucog); + display_ptr->display(); } -#endif -#ifdef Vision_Master_E290 -void UI_Init(DEPG0290BxS800FxX_BW *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(); -} -#endif - -void UI_setLedFlag(bool new_status) { led_flag = new_status; } - -void clearStatus(void) +void StatusBar::clearStatus(void) { // clear status line - display_instance->setColor(BLACK); - display_instance->fillRect(0, ROW_STATUS_TEXT + 2, 128, 13); - display_instance->setColor(WHITE); + display.setColor(BLACK); + display.fillRect(pos_x, pos_y, width, height); } void UI_clearPlotter(void) { // clear the scan plot rectangle (top part) - display_instance->setColor(BLACK); - display_instance->fillRect(0, 10, STEPS, HEIGHT - 10); - display_instance->setColor(WHITE); + // display_instance->setColor(BLACK); + // display_instance->fillRect(0, 10, STEPS, HEIGHT - 10); + // display_instance->setColor(WHITE); } void UI_clearTopStatus(void) { // clear the scan plot rectangle (top part) - display_instance->setColor(BLACK); - display_instance->fillRect(0, 0, STEPS, 10); - 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; - bool correction; - int pixels_per_step; - int correction_number; - int tick; - int tick_minor; - int median; - - first_tick = 0; - //+ (every - (fr_begin - (int)(fr_begin / every) * every)); - /*if (first_tick < fr_begin) - { - first_tick += every; - }*/ - correction = false; - pixels_per_step = STEPS / (RANGE_PER_PAGE / every); - if (STEPS / RANGE_PER_PAGE != 0) - { - 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); - } -#endif - } + // display_instance->setColor(BLACK); + // display_instance->fillRect(0, 0, STEPS, 10); + // display_instance->setColor(WHITE); } void UI_drawCursor(int16_t possition) { // Draw animated vertical cursor on reload process - display_instance->setColor(BLACK); - display_instance->drawVerticalLine(possition, 0, HEIGHT); - display_instance->drawVerticalLine(possition + 1, 0, HEIGHT); - display_instance->drawVerticalLine(possition + 2, 0, HEIGHT); - display_instance->setColor(WHITE); + // display_instance->setColor(BLACK); + // display_instance->drawVerticalLine(possition, 0, HEIGHT); + // display_instance->drawVerticalLine(possition + 1, 0, HEIGHT); + // display_instance->drawVerticalLine(possition + 2, 0, HEIGHT); + // display_instance->setColor(WHITE); } /** * @brief Decorates the display: everything but the plot itself. */ -void UI_displayDecorate(int begin = 0, int end = 0, bool redraw = false) +void StatusBar::draw() { + uint16_t text_y = pos_y + height - 10; + if (!ui_initialized) { // Drone detection level - display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); - display_instance->drawString(128, 0, String(drone_detection_level)); + display.setTextAlignment(TEXT_ALIGN_RIGHT); + display.drawString(width, 0, String(r.drone_detection_level)); } - if (!ui_initialized || redraw) + if (!ui_initialized) { // Clear something - display_instance->setColor(BLACK); - display_instance->fillRect(0, SCALE_TEXT_TOP + 1, 128, 12); - display_instance->setColor(WHITE); - + /* 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)); + display.setTextAlignment(TEXT_ALIGN_RIGHT); + display.drawString(pos_x + width, 0, String(r.drone_detection_level)); // Frequency start - display_instance->setTextAlignment(TEXT_ALIGN_LEFT); - display_instance->drawString(0, ROW_STATUS_TEXT, - (begin == 0) ? String(FREQ_BEGIN) : String(begin)); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawString(pos_x, text_y, + (r.fr_begin == 0) ? String(FREQ_BEGIN) : String(r.fr_begin)); // Frequency detected - display_instance->setTextAlignment(TEXT_ALIGN_CENTER); - display_instance->drawString(128 / 2, ROW_STATUS_TEXT, - (begin == 0) ? String(median_frequency) - : String(begin + ((end - begin) / 2))); + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(pos_x + width / 2, text_y, + (r.fr_begin == 0) + ? String(median_frequency) + : String(r.fr_begin + ((r.fr_end - r.fr_begin) / 2))); // Frequency end - display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); - display_instance->drawString(128, ROW_STATUS_TEXT, - (end == 0) ? String(FREQ_END) : String(end)); + display.setTextAlignment(TEXT_ALIGN_RIGHT); + display.drawString(pos_x + width, text_y, + (r.fr_end == 0) ? String(FREQ_END) : String(r.fr_end)); } // Status text block - if (led_flag) // 'drone' detected + if (r.led_flag) // 'drone' detected { - display_instance->setTextAlignment(TEXT_ALIGN_CENTER); + display.setTextAlignment(TEXT_ALIGN_CENTER); // clear status line clearStatus(); - display_instance->drawString(start_scan_text + 2, ROW_STATUS_TEXT, - String(drone_detected_frequency_start) + ">RF<" + - String(drone_detected_frequency_end)); + display.setColor(WHITE); + display.drawString(pos_x + width / 2, text_y, + String(drone_detected_frequency_start) + ">RF<" + + String(drone_detected_frequency_end)); } else { // "Scanning" - display_instance->setTextAlignment(TEXT_ALIGN_CENTER); + display.setTextAlignment(TEXT_ALIGN_CENTER); // clear status line clearStatus(); - if (scan_progress_count == 0) + String s = "Scan \\"; + if (scan_progress_count == 1) { - display_instance->drawString(start_scan_text, ROW_STATUS_TEXT, "Scan \\"); - } - else if (scan_progress_count == 1) - { - display_instance->drawString(start_scan_text, ROW_STATUS_TEXT, "Scan |"); + s = "Scan |"; } else if (scan_progress_count == 2) { - display_instance->drawString(start_scan_text, ROW_STATUS_TEXT, "Scan /"); + s = "Scan /"; } else if (scan_progress_count == 3) { - display_instance->drawString(start_scan_text, ROW_STATUS_TEXT, "Scan -"); + s = "Scan -"; } scan_progress_count++; if (scan_progress_count >= 4) { scan_progress_count = 0; } + display.setColor(WHITE); + display.drawString(pos_x + width / 2 - 3, text_y, s); } - if (led_flag == true && detection_count >= 5) + if (r.led_flag && r.detection_count >= 5) { digitalWrite(LED, HIGH); - if (SOUND_ON) + if (r.sound_on) { tone(BUZZER_PIN, 104, 100); } digitalWrite(REB_PIN, HIGH); - led_flag = false; + r.led_flag = false; } - else if (!redraw) + else if (!r.led_flag) { digitalWrite(LED, LOW); } @@ -271,29 +166,28 @@ void UI_displayDecorate(int begin = 0, int end = 0, bool redraw = false) if (ranges_count == 0) { #ifdef DEBUG - display_instance->setTextAlignment(TEXT_ALIGN_LEFT); - display_instance->drawString(0, ROW_STATUS_TEXT, String(loop_time)); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawString(pos_x, text_y, String(loop_time)); #else - display_instance->setTextAlignment(TEXT_ALIGN_LEFT); - display_instance->drawString(0, ROW_STATUS_TEXT, String(FREQ_BEGIN)); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawString(pos_x, text_y, String(FREQ_BEGIN)); #endif - display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); - display_instance->drawString(128, ROW_STATUS_TEXT, String(FREQ_END)); + display.setTextAlignment(TEXT_ALIGN_RIGHT); + display.drawString(pos_x + width, text_y, String(FREQ_END)); } else if (ranges_count > 0) { - display_instance->setTextAlignment(TEXT_ALIGN_LEFT); - display_instance->drawString(0, ROW_STATUS_TEXT, - String(SCAN_RANGES[range_item] / 1000) + "-" + - String(SCAN_RANGES[range_item] % 1000)); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawString(pos_x, text_y, + 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, ROW_STATUS_TEXT, - String(SCAN_RANGES[range_item + 1] / 1000) + - "-" + - String(SCAN_RANGES[range_item + 1] % 1000)); + display.setTextAlignment(TEXT_ALIGN_RIGHT); + display.drawString(pos_x + width, text_y, + String(SCAN_RANGES[range_item + 1] / 1000) + "-" + + String(SCAN_RANGES[range_item + 1] % 1000)); } } ui_initialized = true; From bdd00039b26b3e9cd9c1fbfd0369a0496bbff4d6 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Sat, 5 Oct 2024 16:35:01 +0100 Subject: [PATCH 52/80] Events for modular UI and other listeners --- lib/charts/BarChart.cpp | 14 ++++++++ lib/charts/StackedChart.cpp | 13 +++++++- lib/charts/WaterfallChart.cpp | 10 ++++++ lib/charts/charts.h | 11 +++++-- lib/events/events.h | 45 ++++++++++++++++++++++++++ lib/scan/scan.cpp | 45 ++++++++++++++++++++++++-- lib/scan/scan.h | 19 ++++++++--- src/main.cpp | 61 ++++++++++++++--------------------- test/test_rssi.cpp | 7 ++-- 9 files changed, 175 insertions(+), 50 deletions(-) create mode 100644 lib/events/events.h diff --git a/lib/charts/BarChart.cpp b/lib/charts/BarChart.cpp index 36c17d7..f92c63a 100644 --- a/lib/charts/BarChart.cpp +++ b/lib/charts/BarChart.cpp @@ -102,6 +102,20 @@ int BarChart::y2pos(float y) return height - height * (y - min_y) / (max_y - min_y); } +void BarChart::onEvent(Event &e) +{ + if (e.type != DETECTED) + { + return; + } + + int u = updatePoint(e.emitter.current_frequency, e.detected.rssi); + if (e.emitter.animated) + { + drawOne(u); + } +} + void DecoratedBarChart::reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { Chart::reset(x, y, w, h); diff --git a/lib/charts/StackedChart.cpp b/lib/charts/StackedChart.cpp index 1c879dc..2c6ca73 100644 --- a/lib/charts/StackedChart.cpp +++ b/lib/charts/StackedChart.cpp @@ -12,7 +12,8 @@ size_t StackedChart::addChart(Chart *c) cc[charts_sz] = c; free(charts); - c->reset(pos_x + c->pos_x, pos_y + c->pos_y, trim_w(c->pos_x, c->width, width), c->height); + c->reset(pos_x + c->pos_x, pos_y + c->pos_y, trim_w(c->pos_x, c->width, width), + c->height); charts = cc; return charts_sz++; } @@ -82,3 +83,13 @@ void StackedChart::draw() for (int i = 0; i < charts_sz; i++) charts[i]->draw(); } + +void StackedChart::onEvent(Event &e) +{ + if (e.type != SCAN_TASK_COMPLETE) + { + return; + } + + draw(); +} diff --git a/lib/charts/WaterfallChart.cpp b/lib/charts/WaterfallChart.cpp index af83090..9828794 100644 --- a/lib/charts/WaterfallChart.cpp +++ b/lib/charts/WaterfallChart.cpp @@ -55,3 +55,13 @@ int WaterfallChart::x2pos(float x) return width * (x - min_x) / (max_x - min_x); } + +void WaterfallChart::onEvent(Event &e) +{ + if (e.type != DETECTED) + { + return; + } + + updatePoint(e.time_ms, e.detected.freq, e.detected.rssi); +} diff --git a/lib/charts/charts.h b/lib/charts/charts.h index d6d11b4..d159cc0 100644 --- a/lib/charts/charts.h +++ b/lib/charts/charts.h @@ -10,6 +10,7 @@ typedef OLEDDisplay Display_t; #endif #include +#include #include #include @@ -58,7 +59,7 @@ struct ProgressChart : Chart virtual void drawOne(int x) = 0; }; -struct BarChart : ProgressChart +struct BarChart : ProgressChart, Listener { float min_x, max_x, min_y, max_y; float level_y; @@ -83,6 +84,7 @@ struct BarChart : ProgressChart int updatePoint(float x, float y) override; void drawOne(int x) override; void draw() override; + void onEvent(Event &) override; int x2pos(float x); int y2pos(float y); @@ -109,7 +111,7 @@ struct DecoratedBarChart : Chart void draw() override; }; -struct StackedChart : Chart +struct StackedChart : Chart, Listener { Chart **charts; size_t charts_sz; @@ -134,9 +136,11 @@ struct StackedChart : Chart void reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) override; void draw() override; + + void onEvent(Event &e) override; }; -struct WaterfallChart : Chart +struct WaterfallChart : Chart, Listener { float min_x, max_x; float level_y, threshold; @@ -156,6 +160,7 @@ struct WaterfallChart : Chart void reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) override; void draw() override; + void onEvent(Event &e) override; int x2pos(float x); }; diff --git a/lib/events/events.h b/lib/events/events.h new file mode 100644 index 0000000..539277c --- /dev/null +++ b/lib/events/events.h @@ -0,0 +1,45 @@ +#ifndef LORASA_EVENTS_H +#define LORASA_EVENTS_H + +struct Event; +enum EventType +{ + DETECTED = 0, + SCAN_TASK_COMPLETE, + _MAX_EVENT_TYPE // unused as event type +}; +struct Listener; + +#include +#include + +struct Event +{ + EventType type; + uint64_t epoch; + uint64_t time_ms; + + Scan &emitter; + + union + { + struct + { + float rssi; + float freq; + bool trigger; + bool detected; + size_t detected_at; + } detected; + }; + + Event(Scan &emitter, EventType type, uint64_t time_ms) + : emitter(emitter), type(type), epoch(emitter.epoch), time_ms(time_ms) {}; +}; + +struct Listener +{ + virtual void onEvent(Event &event) = 0; +}; + +#endif diff --git a/lib/scan/scan.cpp b/lib/scan/scan.cpp index 5f584b4..028cf42 100644 --- a/lib/scan/scan.cpp +++ b/lib/scan/scan.cpp @@ -66,8 +66,8 @@ uint16_t Scan::rssiMethod(size_t samples, uint16_t *result, size_t res_size) return max_signal; } -size_t Scan::detect(uint16_t *result, bool *filtered_result, size_t result_size, - int samples) +Event Scan::detect(uint16_t *result, bool *filtered_result, size_t result_size, + int samples) { size_t max_rssi_x = result_size; @@ -122,7 +122,46 @@ size_t Scan::detect(uint16_t *result, bool *filtered_result, size_t result_size, } } - return max_rssi_x; + Event event(*this, EventType::DETECTED, 0); + event.epoch = epoch; + event.detected.detected = max_rssi_x < result_size; + event.detected.freq = current_frequency; + event.detected.rssi = + event.detected.detected ? -(float)result[max_rssi_x] : LO_RSSI_THRESHOLD; + event.detected.detected_at = max_rssi_x; + event.detected.trigger = + event.detected.detected && event.detected.rssi >= trigger_level; + detection_count++; + + return event; +} + +size_t Scan::addEventListener(EventType t, Listener &l) +{ + size_t c = listener_count[(size_t)t]; + Listener **new_list = new Listener *[c + 1]; + new_list[c] = &l; + listener_count[(size_t)t] = c + 1; + + if (c > 0) + { + Listener **old_list = eventListeners[(size_t)t]; + memcpy(new_list, old_list, c * sizeof(Listener *)); + delete[] old_list; + } + + eventListeners[(size_t)t] = new_list; + return c; +} + +void Scan::fireEvent(Event &event) +{ + Listener **list = eventListeners[(size_t)event.type]; + size_t c = listener_count[(size_t)event.type]; + for (int i = 0; i < c; i++) + { + list[i]->onEvent(event); + } } #endif diff --git a/lib/scan/scan.h b/lib/scan/scan.h index b471e7e..68b8cb5 100644 --- a/lib/scan/scan.h +++ b/lib/scan/scan.h @@ -1,4 +1,5 @@ #include +#include #include #ifndef LORASA_CORE_H @@ -41,11 +42,18 @@ struct Scan bool sound_on; bool led_flag; uint64_t detection_count; + bool animated; + float trigger_level; + + Listener **eventListeners[(size_t)EventType::_MAX_EVENT_TYPE]; + size_t listener_count[(size_t)EventType::_MAX_EVENT_TYPE]; Scan() : epoch(0), current_frequency(0), fr_begin(0), fr_end(0), - drone_detection_level(0), sound_on(false), led_flag(false), - detection_count(0) {}; + drone_detection_level(0), sound_on(false), led_flag(false), detection_count(0), + animated(false), trigger_level(0), listener_count{ + 0, + } {}; virtual float getRSSI() = 0; @@ -57,8 +65,11 @@ struct Scan // those values that represent a detection event. // It returns index that represents strongest signal at which a detection event // occurred. - static size_t detect(uint16_t *result, bool *filtered_result, size_t result_size, - int samples); + Event detect(uint16_t *result, bool *filtered_result, size_t result_size, + int samples); + + size_t addEventListener(EventType t, Listener &l); + void fireEvent(Event &e); }; // Remove reading without neighbors diff --git a/src/main.cpp b/src/main.cpp index 1a6590a..c6e6514 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -42,6 +42,7 @@ #define RADIOLIB_GODMODE (1) #include +#include #include #ifndef LILYGO @@ -197,7 +198,7 @@ bool first_run, new_pixel, detected_x = false; // drone detection flag bool detected = false; uint64_t drone_detection_level = DEFAULT_DRONE_DETECTION_LEVEL; -uint64_t show_db_after = 80; +#define TRIGGER_LEVEL -80.0 uint64_t drone_detected_frequency_start = 0; uint64_t drone_detected_frequency_end = 0; bool single_page_scan = false; @@ -675,12 +676,14 @@ void setup(void) xTaskCreate(logToSerialTask, "LOG_DATA_JSON", 2048, NULL, 1, NULL); #endif + r.trigger_level = TRIGGER_LEVEL; stacked.reset(0, 0, display.width(), display.height()); + r.addEventListener(SCAN_TASK_COMPLETE, stacked); bar = new DecoratedBarChart(display, 0, 0, display.width(), 0, FREQ_BEGIN, FREQ_END, - LO_RSSI_THRESHOLD, HI_RSSI_THRESHOLD, - -(float)show_db_after); + LO_RSSI_THRESHOLD, HI_RSSI_THRESHOLD, r.trigger_level); + r.addEventListener(DETECTED, bar->bar); size_t b = stacked.addChart(bar); Chart *statusBar = new StatusBar(display, 0, 0, display.width(), r); @@ -695,10 +698,12 @@ void setup(void) waterChart = new WaterfallChart(display, 0, WATERFALL_START, display.width(), 0, FREQ_BEGIN, - FREQ_END, -(float)show_db_after, WATERFALL_SENSITIVITY, model); + FREQ_END, r.trigger_level, WATERFALL_SENSITIVITY, model); size_t c = stacked.addChart(waterChart); stacked.setHeight(c, stacked.height - WATERFALL_START - statusBar->height); + + r.addEventListener(DETECTED, *waterChart); #endif size_t d = stacked.addChart(statusBar); @@ -1053,6 +1058,7 @@ void loop(void) LOG("METHOD RSSI"); uint16_t max_rssi = r.rssiMethod(SAMPLES_RSSI, result, RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE); + if (max_x_rssi[display_x] > max_rssi) { max_x_rssi[display_x] = max_rssi; @@ -1077,42 +1083,24 @@ void loop(void) display.setColor(WHITE); } #endif - size_t detected_at = r.detect( - result, filtered_result, RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE, samples); + Event event = r.detect(result, filtered_result, + RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE, samples); + event.time_ms = millis(); + size_t detected_at = event.detected.detected_at; if (max_rssi_x > detected_at) { // MAx bin Value not RSSI max_rssi_x = detected_at; } - detected = detected_at < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; + detected = event.detected.detected; detected_y[display_x] = false; - float rr; - if (detected) - { - rr = -(float)result[detected_at]; - } - else - { - rr = LO_RSSI_THRESHOLD; - } - + float rr = event.detected.rssi; r.drone_detection_level = drone_detection_level; -#if (WATERFALL_ENABLED == true) - waterChart->updatePoint(millis(), r.current_frequency, rr); -#endif - - int updated = bar->bar.updatePoint(r.current_frequency, rr); - - if (first_run || ANIMATED_RELOAD) - { - bar->bar.drawOne(updated); - } - - if (detected_at <= drone_detection_level) + if (event.detected.trigger) { // check if we should alarm about a drone presence if (detected_y[display_x] == false) // detection threshold match @@ -1152,6 +1140,8 @@ void loop(void) } } + r.fireEvent(event); + #ifdef JOYSTICK_ENABLED // Draw joystick cursor and Frequency RSSI value if (display_x == cursor_x_position) @@ -1166,16 +1156,10 @@ void loop(void) #ifdef PRINT_PROFILE_TIME scan_time += (millis() - scan_start_time); #endif - // count detected - if (detected) - { - r.detection_count++; - } - #ifdef PRINT_DEBUG Serial.println("....\n"); #endif - if (first_run || ANIMATED_RELOAD) + if (r.animated) { display.display(); } @@ -1230,7 +1214,10 @@ void loop(void) w = WATERFALL_START; } - stacked.draw(); + { + Event event(r, SCAN_TASK_COMPLETE, millis()); + r.fireEvent(event); + } // Render display data here #ifdef UPTIME_CLOCK diff --git a/test/test_rssi.cpp b/test/test_rssi.cpp index 3d82f29..a38d166 100644 --- a/test/test_rssi.cpp +++ b/test/test_rssi.cpp @@ -48,14 +48,17 @@ void test_detect() uint16_t samples[test_sz] = {20, 50, 55, 60, 0, 70, 75, 80, 0, 90, 0, 100, 110}; bool result[test_sz]; - size_t r = Scan::detect(samples, result, test_sz, 1); + TestScan test_scan({}, 0); + Event e = test_scan.detect(samples, result, test_sz, 1); + size_t r = e.detected.detected_at; bool expect[test_sz] = {1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1}; TEST_ASSERT_EQUAL_INT16(0, r); TEST_ASSERT_EQUAL_INT8_ARRAY(expect, result, test_sz); - r = Scan::detect(samples, result, test_sz, 2); + Event e2 = test_scan.detect(samples, result, test_sz, 2); + r = e2.detected.detected_at; bool expect2[test_sz] = {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}; From b9e2bffb93cbcae1abfbbab9dd1f73ca93a21ef5 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Sun, 6 Oct 2024 17:26:08 +0100 Subject: [PATCH 53/80] drone_sound_alarm as event listener --- lib/scan/scan.cpp | 15 +++++++++++++++ lib/scan/scan.h | 1 + src/main.cpp | 29 ++++++++++++++++++++--------- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/lib/scan/scan.cpp b/lib/scan/scan.cpp index 028cf42..8c4fbed 100644 --- a/lib/scan/scan.cpp +++ b/lib/scan/scan.cpp @@ -154,6 +154,21 @@ size_t Scan::addEventListener(EventType t, Listener &l) return c; } +struct CallbackFunction : Listener +{ + void (*cb)(void *arg, Event &e); + void *arg; + + CallbackFunction(void cb(void *arg, Event &e), void *arg) : cb(cb), arg(arg) {} + + void onEvent(Event &e) { cb(arg, e); } +}; + +size_t Scan::addEventListener(EventType t, void cb(void *arg, Event &e), void *arg) +{ + return addEventListener(t, *(new CallbackFunction(cb, arg))); +} + void Scan::fireEvent(Event &event) { Listener **list = eventListeners[(size_t)event.type]; diff --git a/lib/scan/scan.h b/lib/scan/scan.h index 68b8cb5..b46ef83 100644 --- a/lib/scan/scan.h +++ b/lib/scan/scan.h @@ -69,6 +69,7 @@ struct Scan int samples); size_t addEventListener(EventType t, Listener &l); + size_t addEventListener(EventType t, void cb(void *, Event &), void *arg); void fireEvent(Event &e); }; diff --git a/src/main.cpp b/src/main.cpp index c6e6514..9ddba7a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -520,6 +520,8 @@ void logToSerialTask(void *parameter) } } +void drone_sound_alarm(void *arg, Event &e); + void setup(void) { #ifdef LILYGO @@ -678,12 +680,10 @@ void setup(void) r.trigger_level = TRIGGER_LEVEL; stacked.reset(0, 0, display.width(), display.height()); - r.addEventListener(SCAN_TASK_COMPLETE, stacked); bar = new DecoratedBarChart(display, 0, 0, display.width(), 0, FREQ_BEGIN, FREQ_END, LO_RSSI_THRESHOLD, HI_RSSI_THRESHOLD, r.trigger_level); - r.addEventListener(DETECTED, bar->bar); size_t b = stacked.addChart(bar); Chart *statusBar = new StatusBar(display, 0, 0, display.width(), r); @@ -709,6 +709,10 @@ void setup(void) size_t d = stacked.addChart(statusBar); stacked.setHeight(b, stacked.height); + r.addEventListener(DETECTED, bar->bar); + r.addEventListener(DETECTED, drone_sound_alarm, &r); + r.addEventListener(SCAN_TASK_COMPLETE, stacked); + #ifdef UPTIME_CLOCK uptime = new UptimeClock(display, millis()); #endif @@ -796,9 +800,21 @@ bool buttonPressHandler(float freq) return true; } -void drone_sound_alarm(int drone_detection_level, int detection_count, - int tone_freq_db = 205) +void drone_sound_alarm(void *arg, Event &e) { + if (e.type != DETECTED) + { + return; + } + + Scan &r = *((Scan *)arg); + if (!r.sound_on) + return; + + int tone_freq_db = e.detected.detected_at * 2; + int drone_detection_level = r.drone_detection_level; + int detection_count = r.detection_count; + // If level is set to sensitive, // start beeping every 10th frequency and shorter // it improves performance less short beep delays... @@ -1115,11 +1131,6 @@ void loop(void) // mark freq end ... will shift right to last detected range drone_detected_frequency_end = r.current_frequency; - if (r.sound_on) - { - drone_sound_alarm(r.drone_detection_level, r.detection_count, - max_rssi_x * 2); - } #ifdef LOG_DATA_JSON frequency_scan_result.begin = drone_detected_frequency_start; From fe0bf7d658f2cdfa723b49b1dfa3c1510c39725a Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Wed, 9 Oct 2024 19:36:49 +0100 Subject: [PATCH 54/80] Fixup button interaction --- lib/charts/BarChart.cpp | 16 +++- lib/charts/WaterfallChart.cpp | 2 + src/main.cpp | 176 ++++++++++++++++++++++------------ 3 files changed, 131 insertions(+), 63 deletions(-) diff --git a/lib/charts/BarChart.cpp b/lib/charts/BarChart.cpp index f92c63a..305cdbd 100644 --- a/lib/charts/BarChart.cpp +++ b/lib/charts/BarChart.cpp @@ -109,6 +109,8 @@ void BarChart::onEvent(Event &e) return; } + level_y = e.emitter.trigger_level; + int u = updatePoint(e.emitter.current_frequency, e.detected.rssi); if (e.emitter.animated) { @@ -134,6 +136,8 @@ void DecoratedBarChart::draw() display.setColor(WHITE); display.setTextAlignment(TEXT_ALIGN_LEFT); + uint16_t first_untouched = 0; + for (uint16_t x = 0; x < width; x++) { float y = bar.ys[x]; @@ -142,7 +146,7 @@ void DecoratedBarChart::draw() String s = String(bar.ys[x], 0); uint16_t w = display.getStringWidth(s); uint16_t x1 = x; - for (; x < x1 + w; x++) + for (; x < x1 + w && x < width; x++) { if (bar.ys[x] > y) { @@ -152,7 +156,15 @@ void DecoratedBarChart::draw() } } - display.drawString(x1, pos_y, s); + if (x > width && first_untouched <= width - w) + { + x1 = width - w; + } + + first_untouched = x; + + if (x1 + w <= width) + display.drawString(pos_x + x1, pos_y, s); } } diff --git a/lib/charts/WaterfallChart.cpp b/lib/charts/WaterfallChart.cpp index 9828794..f3b6553 100644 --- a/lib/charts/WaterfallChart.cpp +++ b/lib/charts/WaterfallChart.cpp @@ -63,5 +63,7 @@ void WaterfallChart::onEvent(Event &e) return; } + level_y = e.emitter.trigger_level; + updatePoint(e.time_ms, e.detected.freq, e.detected.rssi); } diff --git a/src/main.cpp b/src/main.cpp index 9ddba7a..bda4cde 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -725,10 +725,9 @@ int binToRSSI(int bin) return 11 + (bin * 4); } -// return true if continue the code is false break the loop -bool buttonPressHandler(float freq) +// is there an input using Hot Button or joystick +bool buttonInputRequested() { - // Detection level button short press if (button.pressedFor(100) #ifdef JOYSTICK_ENABLED || joy_btn_click() @@ -736,68 +735,63 @@ bool buttonPressHandler(float freq) ) { button.update(); - button_pressed_counter = 0; - // if long press stop - while (button.pressedNow() + if (button.pressedNow() #ifdef JOYSTICK_ENABLED - || joy_btn_click() + || joy_btn_click() #endif ) { - // Print Curent frequency once - if (button_pressed_counter == 0) - { - display.setTextAlignment(TEXT_ALIGN_CENTER); - display.drawString(128 / 2, 0, String(freq)); - display.display(); - } - delay(10); - button_pressed_counter++; - if (button_pressed_counter > 150) - { - digitalWrite(LED, HIGH); - delay(150); - digitalWrite(LED, LOW); - } - } - if (button_pressed_counter > 150) - { - // Remove Curent Frequency Text - display.setTextAlignment(TEXT_ALIGN_CENTER); - display.setColor(BLACK); - display.drawString(128 / 2, 0, String(freq)); - display.setColor(WHITE); - display.display(); - return false; - } - if (button_pressed_counter > 50 && button_pressed_counter < 150) - { - if (!joy_btn_clicked) - { - // 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(); - } - return false; - } - button.update(); - display.setTextAlignment(TEXT_ALIGN_RIGHT); - // erase old drone detection level 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; + return true; } } - return true; + + return false; +} + +enum ButtonEvent +{ + NONE = 0, + LONG_PRESS, + SHORT_PRESS, + TOO_SHORT, + SUSPEND +}; + +ButtonEvent buttonPressEvent() +{ + button_pressed_counter = 0; + // if long press stop + while (button.pressedNow() +#ifdef JOYSTICK_ENABLED + || joy_btn_click() +#endif + ) + { + delay(10); + button_pressed_counter++; + if (button_pressed_counter > 150) + { + digitalWrite(LED, HIGH); + delay(150); + digitalWrite(LED, LOW); + } + } + if (button_pressed_counter > 150) + { + return LONG_PRESS; + } + + if (button_pressed_counter > 50) + { + if (!joy_btn_clicked) + { + return SUSPEND; + } + return SHORT_PRESS; + } + button.update(); + + return TOO_SHORT; } void drone_sound_alarm(void *arg, Event &e) @@ -1175,8 +1169,68 @@ void loop(void) display.display(); } - if (buttonPressHandler(r.current_frequency) == false) - break; + if (buttonInputRequested()) + { + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(display.width() / 2, 0, String(r.current_frequency)); + display.display(); + + ButtonEvent e = buttonPressEvent(); + + if (e == LONG_PRESS) + { + // Remove Curent Frequency Text + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.setColor(BLACK); + display.drawString(display.width() / 2, 0, + String(r.current_frequency)); + display.setColor(WHITE); + display.display(); + + break; + } + + if (e == SUSPEND) + { + // 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; + } + + if (e == SHORT_PRESS) + break; + + if (e == TOO_SHORT) + { + String v = String(r.trigger_level) + " dB"; + uint16_t w = display.getStringWidth(v); + display.setTextAlignment(TEXT_ALIGN_RIGHT); + // erase old drone detection level value + display.setColor(BLACK); + display.fillRect(display.width() - w, 0, 13, w); + display.setColor(WHITE); + + // dt is roughly single-pixel increment + float dt = + bar->bar.height == 0 + ? 0.0 + : (LO_RSSI_THRESHOLD - HI_RSSI_THRESHOLD) / bar->bar.height; + r.trigger_level += dt; + if (r.trigger_level <= LO_RSSI_THRESHOLD) + { + r.trigger_level = HI_RSSI_THRESHOLD; + } + + // print new value + display.drawString(display.width(), 0, v); + tone(BUZZER_PIN, 104, 150); + + bar->bar.redraw_all = true; + } + } // wait a little bit before the next scan, // otherwise the SX1262 hangs From 7f8c08a777a5be1e9d3ffd5e7ad8037eb12da782 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Sat, 12 Oct 2024 13:02:01 +0100 Subject: [PATCH 55/80] Add ALL_EVENTS listeners --- lib/events/events.h | 5 +++-- lib/scan/scan.cpp | 7 +++++++ lib/scan/scan.h | 4 ++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/events/events.h b/lib/events/events.h index 539277c..329dea5 100644 --- a/lib/events/events.h +++ b/lib/events/events.h @@ -4,9 +4,10 @@ struct Event; enum EventType { - DETECTED = 0, + ALL_EVENTS = 0, // used only at registration time + DETECTED, SCAN_TASK_COMPLETE, - _MAX_EVENT_TYPE // unused as event type + _MAX_EVENT_TYPE = SCAN_TASK_COMPLETE // unused as event type }; struct Listener; diff --git a/lib/scan/scan.cpp b/lib/scan/scan.cpp index 8c4fbed..89ff530 100644 --- a/lib/scan/scan.cpp +++ b/lib/scan/scan.cpp @@ -177,6 +177,13 @@ void Scan::fireEvent(Event &event) { list[i]->onEvent(event); } + + list = eventListeners[(size_t)EventType::ALL_EVENTS]; + c = listener_count[(size_t)EventType::ALL_EVENTS]; + for (int i = 0; i < c; i++) + { + list[i]->onEvent(event); + } } #endif diff --git a/lib/scan/scan.h b/lib/scan/scan.h index b46ef83..c6b113c 100644 --- a/lib/scan/scan.h +++ b/lib/scan/scan.h @@ -45,8 +45,8 @@ struct Scan bool animated; float trigger_level; - Listener **eventListeners[(size_t)EventType::_MAX_EVENT_TYPE]; - size_t listener_count[(size_t)EventType::_MAX_EVENT_TYPE]; + Listener **eventListeners[(size_t)EventType::_MAX_EVENT_TYPE + 1]; + size_t listener_count[(size_t)EventType::_MAX_EVENT_TYPE + 1]; Scan() : epoch(0), current_frequency(0), fr_begin(0), fr_end(0), From bf663d43236ee195e4715a6245338adaac56dff0 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Sat, 12 Oct 2024 13:04:14 +0100 Subject: [PATCH 56/80] Capture data for JSON on events --- src/main.cpp | 61 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index bda4cde..f192580 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -471,34 +471,53 @@ struct frequency_scan_result { uint64_t begin; uint64_t end; + uint64_t last_epoch; + int16_t rssi; // deliberately not a float; floats can pin task to wrong core forever } frequency_scan_result; +void eventListenerForMSP(void *arg, Event &e) +{ + if (e.type == EventType::DETECTED) + { + if (e.epoch != frequency_scan_result.last_epoch || + e.detected.rssi > frequency_scan_result.rssi) + { + frequency_scan_result.last_epoch = e.epoch; + frequency_scan_result.rssi = e.detected.rssi; + } + + return; + } + + if (e.type == EventType::SCAN_TASK_COMPLETE) + { + // notify async communication that the data is ready + return; + } +} + void logToSerialTask(void *parameter) { #ifdef HELTEC JsonDocument doc; -#endif char jsonOutput[200]; +#endif + + + uint64_t last_epoch = frequency_scan_result.last_epoch; + frequency_scan_result.rssi = -999; for (;;) { - if (frequency_scan_result.begin != frequency_scan_result.end) + vTaskDelay(LOG_DATA_JSON_INTERVAL / portTICK_PERIOD_MS); + if (frequency_scan_result.begin != frequency_scan_result.end || + frequency_scan_result.last_epoch != last_epoch) { - String max_result = "-999"; - int highest_value_scanned = 999; - - for (int rssi_item = 0; rssi_item < STEPS / WINDOW_SIZE; rssi_item++) + int16_t highest_value_scanned = frequency_scan_result.rssi; + frequency_scan_result.rssi = -999; + last_epoch = frequency_scan_result.last_epoch; + if (highest_value_scanned == -999) { - if (max_x_window[rssi_item] != 0 && - max_x_window[rssi_item] < highest_value_scanned) - { - max_result = "-" + String(max_x_window[rssi_item]); - highest_value_scanned = max_x_window[rssi_item]; - } - } - if (highest_value_scanned == 999) - { - vTaskDelay(LOG_DATA_JSON_INTERVAL / portTICK_PERIOD_MS); continue; } @@ -510,13 +529,13 @@ void logToSerialTask(void *parameter) serializeJson(doc, jsonOutput); Serial.println(jsonOutput); #else - Serial.printf("{\"low_range_freq\": %ull, \"high_range_freq\": %ull, " - "\"value\": \"%s\"}\n", + Serial.printf("{\"low_range_freq\": %" PRIu64 + ", \"high_range_freq\": %" PRIu64 ", " + "\"value\": \"%" PRIi16 "\"}\n", frequency_scan_result.begin, frequency_scan_result.end, - max_result); + highest_value_scanned); #endif } - vTaskDelay(LOG_DATA_JSON_INTERVAL / portTICK_PERIOD_MS); } } @@ -713,6 +732,8 @@ void setup(void) r.addEventListener(DETECTED, drone_sound_alarm, &r); r.addEventListener(SCAN_TASK_COMPLETE, stacked); + r.addEventListener(DETECTED, eventListenerForMSP, NULL); + #ifdef UPTIME_CLOCK uptime = new UptimeClock(display, millis()); #endif From daf2c19685507c8486a854935f51fc9cd5ec916b Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Sat, 12 Oct 2024 14:36:03 +0100 Subject: [PATCH 57/80] JSON output via notification --- src/main.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index f192580..a985301 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -215,7 +215,7 @@ uint64_t scan_start_time = 0; // log data via serial console, JSON format: // #define LOG_DATA_JSON true -int LOG_DATA_JSON_INTERVAL = 100; +int LOG_DATA_JSON_INTERVAL = 1000; // Log at least every second uint64_t x, y, range_item, w = WATERFALL_START, i = 0; int osd_x = 1, osd_y = 2, col = 0, max_bin = 32; @@ -475,6 +475,8 @@ struct frequency_scan_result int16_t rssi; // deliberately not a float; floats can pin task to wrong core forever } frequency_scan_result; +TaskHandle_t logToSerial = NULL; + void eventListenerForMSP(void *arg, Event &e) { if (e.type == EventType::DETECTED) @@ -492,6 +494,10 @@ void eventListenerForMSP(void *arg, Event &e) if (e.type == EventType::SCAN_TASK_COMPLETE) { // notify async communication that the data is ready + if (logToSerial != NULL) + { + xTaskNotifyGive(logToSerial); + } return; } } @@ -509,7 +515,7 @@ void logToSerialTask(void *parameter) for (;;) { - vTaskDelay(LOG_DATA_JSON_INTERVAL / portTICK_PERIOD_MS); + ulTaskNotifyTake(true, pdMS_TO_TICKS(LOG_DATA_JSON_INTERVAL)); if (frequency_scan_result.begin != frequency_scan_result.end || frequency_scan_result.last_epoch != last_epoch) { @@ -694,7 +700,7 @@ void setup(void) #endif #ifdef LOG_DATA_JSON - xTaskCreate(logToSerialTask, "LOG_DATA_JSON", 2048, NULL, 1, NULL); + xTaskCreate(logToSerialTask, "LOG_DATA_JSON", 2048, NULL, 1, &logToSerial); #endif r.trigger_level = TRIGGER_LEVEL; @@ -732,7 +738,7 @@ void setup(void) r.addEventListener(DETECTED, drone_sound_alarm, &r); r.addEventListener(SCAN_TASK_COMPLETE, stacked); - r.addEventListener(DETECTED, eventListenerForMSP, NULL); + r.addEventListener(ALL_EVENTS, eventListenerForMSP, NULL); #ifdef UPTIME_CLOCK uptime = new UptimeClock(display, millis()); From c1c02b5cfd698abf2e505d0c0f970da72487a946 Mon Sep 17 00:00:00 2001 From: Egor Date: Sat, 12 Oct 2024 22:58:11 -0700 Subject: [PATCH 58/80] error: 'max_result' was not declared in this scope --- src/main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index a985301..a3d9b44 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -508,7 +508,6 @@ void logToSerialTask(void *parameter) JsonDocument doc; char jsonOutput[200]; #endif - uint64_t last_epoch = frequency_scan_result.last_epoch; frequency_scan_result.rssi = -999; @@ -530,7 +529,7 @@ void logToSerialTask(void *parameter) #ifdef HELTEC doc["low_range_freq"] = frequency_scan_result.begin; doc["high_range_freq"] = frequency_scan_result.end; - doc["value"] = max_result; + // doc["value"] = max_result; /ToDO: Fix serializeJson(doc, jsonOutput); Serial.println(jsonOutput); From 672ab951b26b6f99d5f4ace230a17267ce5e8d42 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Sun, 13 Oct 2024 07:32:20 +0100 Subject: [PATCH 59/80] Fixup JSON version of output --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index a3d9b44..1d96db0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -529,7 +529,7 @@ void logToSerialTask(void *parameter) #ifdef HELTEC doc["low_range_freq"] = frequency_scan_result.begin; doc["high_range_freq"] = frequency_scan_result.end; - // doc["value"] = max_result; /ToDO: Fix + doc["value"] = String(highest_value_scanned); serializeJson(doc, jsonOutput); Serial.println(jsonOutput); From 783e695771fde102d8c1876256a5c902a9173e82 Mon Sep 17 00:00:00 2001 From: Egor Date: Sun, 13 Oct 2024 01:14:04 -0700 Subject: [PATCH 60/80] Add HTML server --- data/index.html | 40 ++++++++++++++++ data/style.css | 118 ++++++++++++++++++++++++++++++++++++++++++++++++ data/text.txt | 0 include/File.h | 43 ++++++++++++++++++ platformio.ini | 3 ++ src/main.cpp | 19 ++++++++ 6 files changed, 223 insertions(+) create mode 100644 data/index.html create mode 100644 data/style.css create mode 100644 data/text.txt create mode 100644 include/File.h diff --git a/data/index.html b/data/index.html new file mode 100644 index 0000000..92cddf1 --- /dev/null +++ b/data/index.html @@ -0,0 +1,40 @@ + + + + + ESP Wi-Fi Manager + + + + + + +
+

LORA SA ESP32 CONFIG

+
+
+
+
+
+

+ +
+ +
+ +
+ +
+ +
+ +
+ +

+
+
+
+
+ + + diff --git a/data/style.css b/data/style.css new file mode 100644 index 0000000..491d8c4 --- /dev/null +++ b/data/style.css @@ -0,0 +1,118 @@ +html { + font-family: Arial, Helvetica, sans-serif; + display: inline-block; + text-align: center; +} + +h1 { + font-size: 1.8rem; + color: white; +} + +p { + font-size: 1.4rem; +} + +.topnav { + overflow: hidden; + background-color: #0A1128; +} + +body { + margin: 0; +} + +.content { + padding: 5%; +} + +.card-grid { + max-width: 800px; + margin: 0 auto; + display: grid; + grid-gap: 2rem; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); +} + +.card { + background-color: white; + box-shadow: 2px 2px 12px 1px rgba(140, 140, 140, .5); +} + +.card-title { + font-size: 1.2rem; + font-weight: bold; + color: #034078 +} + +input[type=submit] { + border: none; + color: #FEFCFB; + background-color: #034078; + padding: 15px 15px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + width: 100px; + margin-right: 10px; + border-radius: 4px; + transition-duration: 0.4s; +} + +input[type=submit]:hover { + background-color: #1282A2; +} + +input[type=text], +input[type=number], +select { + width: 50%; + padding: 12px 20px; + margin: 18px; + display: inline-block; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; +} + +label { + font-size: 1.2rem; +} + +.value { + font-size: 1.2rem; + color: #1282A2; +} + +.state { + font-size: 1.2rem; + color: #1282A2; +} + +button { + border: none; + color: #FEFCFB; + padding: 15px 32px; + text-align: center; + font-size: 16px; + width: 100px; + border-radius: 4px; + transition-duration: 0.4s; +} + +.button-on { + background-color: #034078; +} + +.button-on:hover { + background-color: #1282A2; +} + +.button-off { + background-color: #858585; +} + +.button-off:hover { + background-color: #252524; +} diff --git a/data/text.txt b/data/text.txt new file mode 100644 index 0000000..e69de29 diff --git a/include/File.h b/include/File.h new file mode 100644 index 0000000..d726c40 --- /dev/null +++ b/include/File.h @@ -0,0 +1,43 @@ +#include "FS.h" +#include + +String readFile(fs::FS &fs, const char *path) +{ + Serial.printf("Reading file: %s\r\n", path); + + File file = fs.open(path); + if (!file || file.isDirectory()) + { + Serial.println("- failed to open file for reading"); + return String(""); + } + String content; + Serial.println("- read from file:"); + while (file.available()) + { + content = file.readStringUntil('\n'); + } + file.close(); + return content; +} + +void writeFile(fs::FS &fs, const char *path, const char *message) +{ + Serial.printf("Writing file: %s\r\n", path); + + File file = fs.open(path, FILE_WRITE); + if (!file) + { + Serial.println("- failed to open file for writing"); + return; + } + if (file.print(message)) + { + Serial.println("- file written"); + } + else + { + Serial.println("- write failed"); + } + file.close(); +} diff --git a/platformio.ini b/platformio.ini index 32ddad0..2868df9 100644 --- a/platformio.ini +++ b/platformio.ini @@ -26,12 +26,15 @@ framework = arduino upload_speed = 921600 monitor_speed = 115200 board_build.f_cpu = 240000000 +board_build.filesystem = littlefs lib_deps = ropg/Heltec_ESP32_LoRa_v3@^0.9.1 bblanchon/ArduinoJson@^7.2.0 + ESP Async WebServer build_flags = -DHELTEC_POWER_BUTTON -DHELTEC + [env:heltec_wifi_lora_32_V3_433] platform = espressif32 diff --git a/src/main.cpp b/src/main.cpp index a3d9b44..20fa879 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,9 +27,18 @@ #ifdef HELTEC #include #endif +#include "FS.h" +#include +#include +#include +#include #include #include +#include "WIFI_SERVER.h" + +#define FORMAT_LITTLEFS_IF_FAILED true + // #define OSD_ENABLED true // #define WIFI_SCANNING_ENABLED true // #define BT_SCANNING_ENABLED true @@ -548,6 +557,7 @@ void drone_sound_alarm(void *arg, Event &e); void setup(void) { + #ifdef LILYGO setupBoards(); // true for disable U8g2 display library delay(500); @@ -580,6 +590,7 @@ void setup(void) pinMode(BUZZER_PIN, OUTPUT); pinMode(REB_PIN, OUTPUT); heltec_setup(); + serverStart(); #ifdef JOYSTICK_ENABLED calibrate_joy(); pinMode(JOY_BTN_PIN, INPUT_PULLUP); @@ -600,6 +611,14 @@ void setup(void) } init_radio(); + + if (!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)) + { + Serial.println("LittleFS Mount Failed"); + } + + // writeFile(LittleFS, "/text.txt", "{WIFI:{name:\"sdfsdf\", Password:\"sdfsdf\"}"); + Serial.println(readFile(LittleFS, "/text.txt")); #ifndef LILYGO vbat = heltec_vbat(); both.printf("V battery: %.2fV (%d%%)\n", vbat, heltec_battery_percent(vbat)); From 6ce1058c6658d51f8e4bf5cb70fcb2bd86dd2df6 Mon Sep 17 00:00:00 2001 From: Egor Date: Sun, 13 Oct 2024 01:14:14 -0700 Subject: [PATCH 61/80] add server --- include/WIFI_SERVER.h | 176 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 include/WIFI_SERVER.h diff --git a/include/WIFI_SERVER.h b/include/WIFI_SERVER.h new file mode 100644 index 0000000..0460306 --- /dev/null +++ b/include/WIFI_SERVER.h @@ -0,0 +1,176 @@ +#include +#include +#include +#include + +// Create AsyncWebServer object on port 80 +AsyncWebServer server(80); + +// Search for parameter in HTTP POST request +const char *PARAM_INPUT_1 = "ssid"; +const char *PARAM_INPUT_2 = "pass"; +const char *PARAM_INPUT_3 = "ip"; +const char *PARAM_INPUT_4 = "gateway"; +const char *PARAM_INPUT_5 = "fstart"; +const char *PARAM_INPUT_6 = "fend"; + +// File paths to save input values permanently +const char *ssidPath = "/ssid.txt"; +const char *passPath = "/pass.txt"; +const char *ipPath = "/ip.txt"; +const char *gatewayPath = "/gateway.txt"; + +// Variables to save values from HTML form +String ssid = "LoraSA", pass = "1234567890", ip, gateway, fstart, fend; + +IPAddress localIP(192, 168, 1, 200); +// Set your Gateway IP address +IPAddress localGateway(192, 168, 1, 1); +IPAddress subnet(255, 255, 0, 0); + +// Timer variables +unsigned long previousMillis = 0; +const long interval = 10000; // interval to wait for Wi-Fi connection (milliseconds) + +// Initialize WiFi +bool initWiFi() +{ + if (ssid == "" || ip == "") + { + Serial.println("Undefined SSID or IP address."); + return false; + } + + WiFi.mode(WIFI_STA); + // localIP.fromString(ip.c_str()); + // localGateway.fromString(gateway.c_str()); + + if (!WiFi.config(localIP, localGateway, subnet)) + { + Serial.println("STA Failed to configure"); + return false; + } + WiFi.begin(ssid.c_str(), pass.c_str()); + Serial.println("Connecting to WiFi..."); + + unsigned long currentMillis = millis(); + previousMillis = currentMillis; + + while (WiFi.status() != WL_CONNECTED) + { + currentMillis = millis(); + if (currentMillis - previousMillis >= interval) + { + Serial.println("Failed to connect."); + return false; + } + } + + Serial.println(WiFi.localIP()); + return true; +} + +void serverStart() +{ + if (initWiFi()) + { + // Route for root / web page + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) + { request->send(LittleFS, "/index.html", "text/html"); }); + server.serveStatic("/", LittleFS, "/"); + + /* // Route to set GPIO state to HIGH + server.on("/on", HTTP_GET, + [](AsyncWebServerRequest *request) + { + digitalWrite(ledPin, HIGH); + request->send(LittleFS, "/index.html", "text/html", false, + processor); + }); + + // Route to set GPIO state to LOW + server.on("/off", HTTP_GET, + [](AsyncWebServerRequest *request) + { + digitalWrite(ledPin, LOW); + request->send(LittleFS, "/index.html", "text/html", false, + processor); + });*/ + server.begin(); + } + else + { + // Connect to Wi-Fi network with SSID and password + Serial.println("Setting AP (Access Point)"); + // NULL sets an open Access Point + WiFi.softAP("LoraSA", NULL); + + IPAddress IP = WiFi.softAPIP(); + Serial.print("AP IP address: "); + Serial.println(IP); + + // Web Server Root URL + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) + { request->send(LittleFS, "/index.html", "text/html"); }); + + server.serveStatic("/", LittleFS, "/"); + + server.on("/", HTTP_POST, + [](AsyncWebServerRequest *request) + { + int params = request->params(); + for (int i = 0; i < params; i++) + { + const AsyncWebParameter *p = request->getParam(i); + if (p->isPost()) + { + // HTTP POST ssid value + if (p->name() == PARAM_INPUT_1) + { + ssid = p->value().c_str(); + Serial.print("SSID set to: "); + Serial.println(ssid); + // Write file to save value + writeFile(LittleFS, ssidPath, ssid.c_str()); + } + // HTTP POST pass value + if (p->name() == PARAM_INPUT_2) + { + pass = p->value().c_str(); + Serial.print("Password set to: "); + Serial.println(pass); + // Write file to save value + writeFile(LittleFS, passPath, pass.c_str()); + } + // HTTP POST ip value + if (p->name() == PARAM_INPUT_3) + { + ip = p->value().c_str(); + Serial.print("IP Address set to: "); + Serial.println(ip); + // Write file to save value + writeFile(LittleFS, ipPath, ip.c_str()); + } + // HTTP POST gateway value + if (p->name() == PARAM_INPUT_4) + { + gateway = p->value().c_str(); + Serial.print("Gateway set to: "); + Serial.println(gateway); + // Write file to save value + writeFile(LittleFS, gatewayPath, gateway.c_str()); + } + // Serial.printf("POST[%s]: %s\n", p->name().c_str(), + // p->value().c_str()); + } + } + request->send(200, "text/plain", + "Done. ESP will restart, connect to your router and " + "go to IP address: " + + ip); + delay(3000); + ESP.restart(); + }); + server.begin(); + } +} From b002afb789cb522430a2fffbd9f05749ba545238 Mon Sep 17 00:00:00 2001 From: Egor Date: Sun, 13 Oct 2024 18:31:26 -0700 Subject: [PATCH 62/80] Add LR1121 and File config WIFI --- include/File.h | 12 +++ include/LiLyGo.h | 4 + include/WIFI_SERVER.h | 208 +++++++++++++++++++++--------------------- lib/scan/scan.h | 4 +- platformio.ini | 30 ++++++ src/main.cpp | 77 ++++++++++++---- 6 files changed, 213 insertions(+), 122 deletions(-) diff --git a/include/File.h b/include/File.h index d726c40..6f502ec 100644 --- a/include/File.h +++ b/include/File.h @@ -1,6 +1,16 @@ #include "FS.h" #include +// Initialize LittleFS +void initLittleFS() +{ + if (!LittleFS.begin(true)) + { + Serial.println("An error has occurred while mounting LittleFS"); + } + Serial.println("LittleFS mounted successfully"); +} + String readFile(fs::FS &fs, const char *path) { Serial.printf("Reading file: %s\r\n", path); @@ -24,6 +34,7 @@ String readFile(fs::FS &fs, const char *path) void writeFile(fs::FS &fs, const char *path, const char *message) { Serial.printf("Writing file: %s\r\n", path); + Serial.printf("Content: %s\r\n", message); File file = fs.open(path, FILE_WRITE); if (!file) @@ -34,6 +45,7 @@ void writeFile(fs::FS &fs, const char *path, const char *message) if (file.print(message)) { Serial.println("- file written"); + delay(500); } else { diff --git a/include/LiLyGo.h b/include/LiLyGo.h index c9c88ee..1d44603 100644 --- a/include/LiLyGo.h +++ b/include/LiLyGo.h @@ -35,6 +35,10 @@ SX1280 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUS // Default SPI on pins from pins_arduino.h SX1262 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN); #endif // end USING_SX1262 +#ifdef USING_LR1121 +// Default SPI on pins from pins_arduino.h +LR1121 radio = new Module(RADIO_CS_PIN, RADIO_DIO9_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN); +#endif // end USING_LR1121 #ifdef USING_SX1276 // Default SPI on pins from pins_arduino.h SX1276 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN); diff --git a/include/WIFI_SERVER.h b/include/WIFI_SERVER.h index 0460306..89d95de 100644 --- a/include/WIFI_SERVER.h +++ b/include/WIFI_SERVER.h @@ -7,25 +7,23 @@ AsyncWebServer server(80); // Search for parameter in HTTP POST request -const char *PARAM_INPUT_1 = "ssid"; -const char *PARAM_INPUT_2 = "pass"; -const char *PARAM_INPUT_3 = "ip"; -const char *PARAM_INPUT_4 = "gateway"; -const char *PARAM_INPUT_5 = "fstart"; -const char *PARAM_INPUT_6 = "fend"; +const String SSID = "ssid"; +const String PASS = "pass"; +const String IP = "ip"; +const String GATEWAY = "gateway"; +const String FSTART = "fstart"; +const String FEND = "fend"; // File paths to save input values permanently -const char *ssidPath = "/ssid.txt"; -const char *passPath = "/pass.txt"; -const char *ipPath = "/ip.txt"; -const char *gatewayPath = "/gateway.txt"; +// const char *ssidPath = "/ssid.txt"; // Variables to save values from HTML form -String ssid = "LoraSA", pass = "1234567890", ip, gateway, fstart, fend; +String ssid = "LoraSA", pass = "1234567890", ip = "192.168.1.100", + gateway = "192.168.1.1", fstart = "", fend = ""; -IPAddress localIP(192, 168, 1, 200); +IPAddress localIP; // Set your Gateway IP address -IPAddress localGateway(192, 168, 1, 1); +IPAddress localGateway; IPAddress subnet(255, 255, 0, 0); // Timer variables @@ -35,6 +33,11 @@ const long interval = 10000; // interval to wait for Wi-Fi connection (milliseco // Initialize WiFi bool initWiFi() { + Serial.println("SSID:" + ssid); + Serial.println("PSWD:" + pass); + Serial.println("IP:" + ip); + Serial.println("SUB:" + subnet); + Serial.println("GATAWAY:" + gateway); if (ssid == "" || ip == "") { Serial.println("Undefined SSID or IP address."); @@ -42,8 +45,8 @@ bool initWiFi() } WiFi.mode(WIFI_STA); - // localIP.fromString(ip.c_str()); - // localGateway.fromString(gateway.c_str()); + localIP.fromString(ip.c_str()); + localGateway.fromString(gateway.c_str()); if (!WiFi.config(localIP, localGateway, subnet)) { @@ -70,37 +73,100 @@ bool initWiFi() return true; } +void writeParameterToFile(String value, String file) +{ + // Write file to save value + writeFile(LittleFS, file.c_str(), value.c_str()); +} + +void writeParameterToParameterFile(String param, String value) +{ + String file = String("/" + param + ".txt"); + // Write file to save value + writeParameterToFile(value, file.c_str()); +} + +String readParameterFromParameterFile(String param) +{ + String file = String("/" + param + ".txt"); + return readFile(LittleFS, file.c_str()); +} + +void serverServer() +{ + // Route for root / web page + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) + { request->send(LittleFS, "/index.html", "text/html"); }); + + server.serveStatic("/", LittleFS, "/"); + + server.on("/", HTTP_POST, + [](AsyncWebServerRequest *request) + { + int params = request->params(); + for (int i = 0; i < params; i++) + { + Serial.println("Parameter " + String(i) + ": " + + request->getParam(i)->value()); + } + Serial.println(request->params()); + + String p = request->getParam(SSID, true)->value(); + writeParameterToParameterFile(SSID, p); + + p = request->getParam(PASS, true)->value(); + writeParameterToParameterFile(PASS, p); + + p = request->getParam(IP, true)->value(); + writeParameterToParameterFile(IP, p); + + p = request->getParam(GATEWAY, true)->value(); + writeParameterToParameterFile(GATEWAY, p); + + p = request->getParam(FSTART, true)->value(); + writeParameterToParameterFile(FSTART, p); + + p = request->getParam(FEND, true)->value(); + writeParameterToParameterFile(FEND, p); + + request->send(200, "text/plain", + "Done. ESP will restart, connect to your router and " + "go to IP address: " + + ip); + delay(3000); + ESP.restart(); + }); + + /* // Route to set GPIO state to HIGH + server.on("/on", HTTP_GET, + [](AsyncWebServerRequest *request) + { + digitalWrite(ledPin, HIGH); + request->send(LittleFS, "/index.html", "text/html", false, + processor); + }); + + // Route to set GPIO state to LOW + server.on("/off", HTTP_GET, + [](AsyncWebServerRequest *request) + { + digitalWrite(ledPin, LOW); + request->send(LittleFS, "/index.html", "text/html", false, + processor); + });*/ + server.begin(); +} + void serverStart() { if (initWiFi()) { - // Route for root / web page - server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) - { request->send(LittleFS, "/index.html", "text/html"); }); - server.serveStatic("/", LittleFS, "/"); - - /* // Route to set GPIO state to HIGH - server.on("/on", HTTP_GET, - [](AsyncWebServerRequest *request) - { - digitalWrite(ledPin, HIGH); - request->send(LittleFS, "/index.html", "text/html", false, - processor); - }); - - // Route to set GPIO state to LOW - server.on("/off", HTTP_GET, - [](AsyncWebServerRequest *request) - { - digitalWrite(ledPin, LOW); - request->send(LittleFS, "/index.html", "text/html", false, - processor); - });*/ - server.begin(); + Serial.println("Setting Secure WIFI (Access Point)"); + serverServer(); } else { - // Connect to Wi-Fi network with SSID and password + // Connect to Wi-Fi network with default SSID and password Serial.println("Setting AP (Access Point)"); // NULL sets an open Access Point WiFi.softAP("LoraSA", NULL); @@ -109,68 +175,6 @@ void serverStart() Serial.print("AP IP address: "); Serial.println(IP); - // Web Server Root URL - server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) - { request->send(LittleFS, "/index.html", "text/html"); }); - - server.serveStatic("/", LittleFS, "/"); - - server.on("/", HTTP_POST, - [](AsyncWebServerRequest *request) - { - int params = request->params(); - for (int i = 0; i < params; i++) - { - const AsyncWebParameter *p = request->getParam(i); - if (p->isPost()) - { - // HTTP POST ssid value - if (p->name() == PARAM_INPUT_1) - { - ssid = p->value().c_str(); - Serial.print("SSID set to: "); - Serial.println(ssid); - // Write file to save value - writeFile(LittleFS, ssidPath, ssid.c_str()); - } - // HTTP POST pass value - if (p->name() == PARAM_INPUT_2) - { - pass = p->value().c_str(); - Serial.print("Password set to: "); - Serial.println(pass); - // Write file to save value - writeFile(LittleFS, passPath, pass.c_str()); - } - // HTTP POST ip value - if (p->name() == PARAM_INPUT_3) - { - ip = p->value().c_str(); - Serial.print("IP Address set to: "); - Serial.println(ip); - // Write file to save value - writeFile(LittleFS, ipPath, ip.c_str()); - } - // HTTP POST gateway value - if (p->name() == PARAM_INPUT_4) - { - gateway = p->value().c_str(); - Serial.print("Gateway set to: "); - Serial.println(gateway); - // Write file to save value - writeFile(LittleFS, gatewayPath, gateway.c_str()); - } - // Serial.printf("POST[%s]: %s\n", p->name().c_str(), - // p->value().c_str()); - } - } - request->send(200, "text/plain", - "Done. ESP will restart, connect to your router and " - "go to IP address: " + - ip); - delay(3000); - ESP.restart(); - }); - server.begin(); + serverServer(); } } diff --git a/lib/scan/scan.h b/lib/scan/scan.h index c6b113c..38b610e 100644 --- a/lib/scan/scan.h +++ b/lib/scan/scan.h @@ -27,7 +27,9 @@ constexpr float HI_RSSI_THRESHOLD = -44.0; constexpr float LO_RSSI_THRESHOLD = HI_RSSI_THRESHOLD - 66; // number of samples for RSSI method -#define SAMPLES_RSSI 12 // 21 // +#ifndef SAMPLES_RSSI +#define SAMPLES_RSSI 13 // 21 // +#endif #ifdef USING_SX1280PA #define SAMPLES_RSSI 20 #endif diff --git a/platformio.ini b/platformio.ini index 2868df9..a1d92eb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -76,6 +76,36 @@ build_flags = -DARDUINO_LILYGO_T3_S3_V1_X -DARDUINO_USB_MODE=1 +[env:lilygo-T3S3-v1-2-lr1121] +platform = espressif32 +board = t3_s3_v1_x +framework = arduino +upload_speed = 921600 +monitor_speed = 115200 +board_build.f_cpu = 240000000 +board_build.filesystem = littlefs +lib_deps = + ropg/Heltec_ESP32_LoRa_v3@^0.9.1 + RadioLib + U8g2 + XPowersLib + bblanchon/ArduinoJson@^7.2.0 + ESP Async WebServer +build_flags = + -DLILYGO + -DT3_S3_V1_2_LR1121 + -DT3_V1_3_SX1262 + -DARDUINO_LILYGO_T3S3_LR1121 + -DESP32 + -DSAMPLES_RSSI=5 + -DUSING_LR1121 + -DFREQ_BEGIN=2400 + -DFREQ_END=2500 + -DARDUINO_ARCH_ESP32 + -DARDUINO_USB_CDC_ON_BOOT=1 + -DARDUINO_LILYGO_T3_S3_V1_X + -DARDUINO_USB_MODE=1 + [env:lilygo-T3S3-v1-2-sx1280] platform = espressif32 diff --git a/src/main.cpp b/src/main.cpp index 20fa879..1d60514 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -54,6 +54,8 @@ #include #include +#include + #ifndef LILYGO #include // This file contains a binary patch for the SX1262 @@ -143,8 +145,9 @@ int SCAN_RANGES[] = {}; // MHZ per page // to put everything into one page set RANGE_PER_PAGE = FREQ_END - 800 -uint64_t RANGE_PER_PAGE = FREQ_END - FREQ_BEGIN; // FREQ_END - FREQ_BEGIN +uint64_t RANGE_PER_PAGE; // FREQ_END - FREQ_BEGIN +uint64_t CONF_FREQ_END, CONF_FREQ_BEGIN; // To Enable Multi Screen scan // uint64_t RANGE_PER_PAGE = 50; // Default Range on Menu Button Switch @@ -177,16 +180,8 @@ constexpr int WINDOW_SIZE = 15; // if more than 100 it can freeze #define SAMPLES 35 //(scan time = 1294) -#define RANGE (int)(FREQ_END - FREQ_BEGIN) - -#define SINGLE_STEP (float)(RANGE / (STEPS * SCAN_RBW_FACTOR)) - -uint64_t range = (int)(FREQ_END - FREQ_BEGIN); - -uint64_t iterations = RANGE / RANGE_PER_PAGE; - -// uint64_t range_frequency = FREQ_END - FREQ_BEGIN; -uint64_t median_frequency = (FREQ_BEGIN + FREQ_END) / 2; +uint64_t RANGE, range, iterations, median_frequency; +float SINGLE_STEP; // #define DISABLE_PLOT_CHART false // unused @@ -376,13 +371,20 @@ struct RadioScan : Scan float RadioScan::getRSSI() { -#ifdef USING_SX1280PA +#if defined(USING_SX1280PA) // radio.startReceive(); // get instantaneous RSSI value // When PR will be merged we can use radi.getRSSI(false); uint8_t data[3] = {0, 0, 0}; // RssiInst, Status, RFU radio.mod->SPIreadStream(RADIOLIB_SX128X_CMD_GET_RSSI_INST, data, 3); return ((float)data[0] / (-2.0)); + +#elif defined(USING_LR1121) + // Try getRssiInst + float rssi; + radio.getRssiInst(&rssi); + // pass the replies + return rssi; #else return radio.getRSSI(false); #endif @@ -401,7 +403,7 @@ void init_radio() { // initialize SX1262 FSK modem at the initial frequency both.println("Init radio"); -#ifdef USING_SX1280PA +#if defined(USING_SX1280PA) || defined(USING_LR1121) state = radio.beginGFSK(FREQ_BEGIN); #else state = radio.beginFSK(FREQ_BEGIN); @@ -590,7 +592,7 @@ void setup(void) pinMode(BUZZER_PIN, OUTPUT); pinMode(REB_PIN, OUTPUT); heltec_setup(); - serverStart(); + #ifdef JOYSTICK_ENABLED calibrate_joy(); pinMode(JOY_BTN_PIN, INPUT_PULLUP); @@ -612,16 +614,53 @@ void setup(void) init_radio(); - if (!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)) - { - Serial.println("LittleFS Mount Failed"); - } + initLittleFS(); // writeFile(LittleFS, "/text.txt", "{WIFI:{name:\"sdfsdf\", Password:\"sdfsdf\"}"); - Serial.println(readFile(LittleFS, "/text.txt")); + ssid = readParameterFromParameterFile(SSID); + Serial.println("SSID: " + ssid); + + pass = readParameterFromParameterFile(PASS); + Serial.println("PASS: " + pass); + + ip = readParameterFromParameterFile(IP); + Serial.println("PASS: " + ip); + + gateway = readParameterFromParameterFile(GATEWAY); + Serial.println("GATEWAY: " + gateway); + + fstart = readParameterFromParameterFile(FSTART); + Serial.println("FSTART: " + fstart); + + fend = readParameterFromParameterFile(FEND); + Serial.println("FEND: " + fend); + + both.println("Starting WIFI-SERVER"); + serverStart(); + + CONF_FREQ_BEGIN = (fstart == "") ? FREQ_BEGIN : atoi(fstart.c_str()); + CONF_FREQ_END = (fend == "") ? FREQ_END : atoi(fend.c_str()); + + both.println("C FREQ BEGIN:" + String(CONF_FREQ_BEGIN)); + both.println("C FREQ END:" + String(CONF_FREQ_END)); + + RANGE_PER_PAGE = CONF_FREQ_END - CONF_FREQ_BEGIN; // FREQ_END - FREQ_BEGIN + + RANGE = (int)(CONF_FREQ_END - CONF_FREQ_BEGIN); + + SINGLE_STEP = (float)(RANGE / (STEPS * SCAN_RBW_FACTOR)); + + range = (int)(CONF_FREQ_END - CONF_FREQ_BEGIN); + + iterations = RANGE / RANGE_PER_PAGE; + + // uint64_t range_frequency = FREQ_END - FREQ_BEGIN; + median_frequency = (CONF_FREQ_BEGIN + CONF_FREQ_END) / 2; + #ifndef LILYGO vbat = heltec_vbat(); both.printf("V battery: %.2fV (%d%%)\n", vbat, heltec_battery_percent(vbat)); + delay(1000); #endif // end not LILYGO #ifdef WIFI_SCANNING_ENABLED WiFi.mode(WIFI_STA); From 7c3d48a44dc136d6b4a695af5bea5ad7c2b0241f Mon Sep 17 00:00:00 2001 From: Egor Date: Sun, 13 Oct 2024 20:00:42 -0700 Subject: [PATCH 63/80] samples settings --- data/index.html | 6 +++-- include/WIFI_SERVER.h | 51 +++++++++++++++++++++++++++---------- src/main.cpp | 59 ++++++++++++++++++++++++------------------- src/ui.cpp | 11 +++++--- 4 files changed, 82 insertions(+), 45 deletions(-) diff --git a/data/index.html b/data/index.html index 92cddf1..82bfe6a 100644 --- a/data/index.html +++ b/data/index.html @@ -26,9 +26,11 @@
-
+
-
+
+ +

diff --git a/include/WIFI_SERVER.h b/include/WIFI_SERVER.h index 89d95de..93a715d 100644 --- a/include/WIFI_SERVER.h +++ b/include/WIFI_SERVER.h @@ -19,7 +19,7 @@ const String FEND = "fend"; // Variables to save values from HTML form String ssid = "LoraSA", pass = "1234567890", ip = "192.168.1.100", - gateway = "192.168.1.1", fstart = "", fend = ""; + gateway = "192.168.1.1", fstart = "", fend = "", smpls = ""; IPAddress localIP; // Set your Gateway IP address @@ -111,23 +111,48 @@ void serverServer() } Serial.println(request->params()); - String p = request->getParam(SSID, true)->value(); - writeParameterToParameterFile(SSID, p); + String p; + if (request->hasParam(IP, true)) + { + p = request->getParam(IP, true)->value(); + writeParameterToParameterFile(IP, p); + } - p = request->getParam(PASS, true)->value(); - writeParameterToParameterFile(PASS, p); + if (request->hasParam(IP, true)) + { + p = request->getParam(IP, true)->value(); + writeParameterToParameterFile(IP, p); + } - p = request->getParam(IP, true)->value(); - writeParameterToParameterFile(IP, p); + if (request->hasParam(IP, true)) + { + p = request->getParam(IP, true)->value(); + writeParameterToParameterFile(IP, p); + } - p = request->getParam(GATEWAY, true)->value(); - writeParameterToParameterFile(GATEWAY, p); + if (request->hasParam(GATEWAY, true)) + { + p = request->getParam(GATEWAY, true)->value(); + writeParameterToParameterFile(GATEWAY, p); + } - p = request->getParam(FSTART, true)->value(); - writeParameterToParameterFile(FSTART, p); + if (request->hasParam(FSTART, true)) + { + p = request->getParam(FSTART, true)->value(); + writeParameterToParameterFile(FSTART, p); + } - p = request->getParam(FEND, true)->value(); - writeParameterToParameterFile(FEND, p); + if (request->hasParam(FEND, true)) + { + p = request->getParam(FEND, true)->value(); + writeParameterToParameterFile(FEND, p); + } + + if (request->hasParam("samples", true)) + { + p = request->getParam("samples", true)->value(); + writeParameterToParameterFile("samples", p); + } request->send(200, "text/plain", "Done. ESP will restart, connect to your router and " diff --git a/src/main.cpp b/src/main.cpp index 9f3dbec..883889e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -145,10 +145,9 @@ int SCAN_RANGES[] = {}; // MHZ per page // to put everything into one page set RANGE_PER_PAGE = FREQ_END - 800 -uint64_t RANGE_PER_PAGE; // FREQ_END - FREQ_BEGIN +uint64_t RANGE_PER_PAGE; // FREQ_END - CONF_FREQ_BEGIN -uint64_t CONF_FREQ_END, CONF_FREQ_BEGIN; -// To Enable Multi Screen scan +uint64_t CONF_FREQ_END, CONF_FREQ_BEGIN; // To Enable Multi Screen scan // uint64_t RANGE_PER_PAGE = 50; // Default Range on Menu Button Switch @@ -228,11 +227,12 @@ uint64_t ranges_count = 0; int rssi = 0; int state = 0; +int CONF_SAMPLES; #ifdef METHOD_SPECTRAL -constexpr int samples = SAMPLES; +int samples = SAMPLES; #endif #ifdef METHOD_RSSI -constexpr int samples = SAMPLES_RSSI; +int samples = SAMPLES_RSSI; #endif uint8_t result_index = 0; @@ -305,8 +305,8 @@ void osdProcess() // memset(max_step_range, 33, 30); max_bin = 32; - osd.displayString(12, 1, String(FREQ_BEGIN)); - osd.displayString(12, OSD_WIDTH - 8, String(FREQ_END)); + osd.displayString(12, 1, String(CONF_FREQ_BEGIN)); + osd.displayString(12, OSD_WIDTH - 8, String(CONF_FREQ_END)); // Finding biggest in result // Skiping 0 and 32 31 to avoid overflow for (int i = 1; i < MAX_POWER_LEVELS - 3; i++) @@ -343,7 +343,7 @@ void osdProcess() #ifdef OSD_SIDE_BAR { osd.displayString(col, OSD_WIDTH - 7, - String(FREQ_BEGIN + (col * osd_mhz_in_bin)) + "-" + + String(CONF_FREQ_BEGIN + (col * osd_mhz_in_bin)) + "-" + String(max_step_range) + " "); } #endif @@ -404,9 +404,9 @@ void init_radio() // initialize SX1262 FSK modem at the initial frequency both.println("Init radio"); #if defined(USING_SX1280PA) || defined(USING_LR1121) - state = radio.beginGFSK(FREQ_BEGIN); + state = radio.beginGFSK(CONF_FREQ_BEGIN); #else - state = radio.beginFSK(FREQ_BEGIN); + state = radio.beginFSK(CONF_FREQ_BEGIN); #endif if (state == RADIOLIB_ERR_NONE) { @@ -458,7 +458,7 @@ void init_radio() // calibrate only once ,,, at startup // TODO: check documentation (9.2.1) if we must calibrate in certain ranges #ifdef USING_SX1280PA - state = radio.setFrequency(FREQ_BEGIN); + state = radio.setFrequency(CONF_FREQ_BEGIN); if (state != RADIOLIB_ERR_NONE) { Serial.println("Error:setFrequency:" + String(state)); @@ -470,9 +470,9 @@ void init_radio() } #elif USING_SX1276 // Sets carrier frequency. Allowed values range from 137.0 MHz to 1020.0 MHz. - radio.setFrequency(FREQ_BEGIN); + radio.setFrequency(CONF_FREQ_BEGIN); #else - radio.setFrequency(FREQ_BEGIN, true); + radio.setFrequency(CONF_FREQ_BEGIN, true); #endif delay(50); @@ -612,8 +612,6 @@ void setup(void) } } - init_radio(); - initLittleFS(); // writeFile(LittleFS, "/text.txt", "{WIFI:{name:\"sdfsdf\", Password:\"sdfsdf\"}"); @@ -635,16 +633,22 @@ void setup(void) fend = readParameterFromParameterFile(FEND); Serial.println("FEND: " + fend); + smpls = readParameterFromParameterFile("samples"); + Serial.println("SAMPLES: " + smpls); + both.println("Starting WIFI-SERVER"); serverStart(); + CONF_SAMPLES = (smpls == "") ? samples : atoi(smpls.c_str()); + samples = CONF_SAMPLES; CONF_FREQ_BEGIN = (fstart == "") ? FREQ_BEGIN : atoi(fstart.c_str()); CONF_FREQ_END = (fend == "") ? FREQ_END : atoi(fend.c_str()); both.println("C FREQ BEGIN:" + String(CONF_FREQ_BEGIN)); both.println("C FREQ END:" + String(CONF_FREQ_END)); + both.println("C SAMPLES:" + String(CONF_SAMPLES)); - RANGE_PER_PAGE = CONF_FREQ_END - CONF_FREQ_BEGIN; // FREQ_END - FREQ_BEGIN + RANGE_PER_PAGE = CONF_FREQ_END - CONF_FREQ_BEGIN; // FREQ_END - CONF_FREQ_BEGIN RANGE = (int)(CONF_FREQ_END - CONF_FREQ_BEGIN); @@ -654,9 +658,11 @@ void setup(void) iterations = RANGE / RANGE_PER_PAGE; - // uint64_t range_frequency = FREQ_END - FREQ_BEGIN; + // uint64_t range_frequency = FREQ_END - CONF_FREQ_BEGIN; median_frequency = (CONF_FREQ_BEGIN + CONF_FREQ_END) / 2; + init_radio(); + #ifndef LILYGO vbat = heltec_vbat(); both.printf("V battery: %.2fV (%d%%)\n", vbat, heltec_battery_percent(vbat)); @@ -763,8 +769,9 @@ void setup(void) r.trigger_level = TRIGGER_LEVEL; stacked.reset(0, 0, display.width(), display.height()); - bar = new DecoratedBarChart(display, 0, 0, display.width(), 0, FREQ_BEGIN, FREQ_END, - LO_RSSI_THRESHOLD, HI_RSSI_THRESHOLD, r.trigger_level); + bar = new DecoratedBarChart(display, 0, 0, display.width(), 0, CONF_FREQ_BEGIN, + CONF_FREQ_END, LO_RSSI_THRESHOLD, HI_RSSI_THRESHOLD, + r.trigger_level); size_t b = stacked.addChart(bar); @@ -778,9 +785,9 @@ void setup(void) delete[] multiples; - waterChart = - new WaterfallChart(display, 0, WATERFALL_START, display.width(), 0, FREQ_BEGIN, - FREQ_END, r.trigger_level, WATERFALL_SENSITIVITY, model); + waterChart = new WaterfallChart(display, 0, WATERFALL_START, display.width(), 0, + CONF_FREQ_BEGIN, CONF_FREQ_END, r.trigger_level, + WATERFALL_SENSITIVITY, model); size_t c = stacked.addChart(waterChart); stacked.setHeight(c, stacked.height - WATERFALL_START - statusBar->height); @@ -1011,13 +1018,13 @@ void loop(void) } // do the scan - range = FREQ_END - FREQ_BEGIN; + range = CONF_FREQ_END - CONF_FREQ_BEGIN; if (RANGE_PER_PAGE > range) { RANGE_PER_PAGE = range; } - r.fr_begin = FREQ_BEGIN; + r.fr_begin = CONF_FREQ_BEGIN; r.fr_end = r.fr_begin; // 50 is a single-screen range @@ -1150,7 +1157,7 @@ void loop(void) // Spectrum analyzer using getRSSI { LOG("METHOD RSSI"); - uint16_t max_rssi = r.rssiMethod(SAMPLES_RSSI, result, + uint16_t max_rssi = r.rssiMethod(CONF_SAMPLES, result, RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE); if (max_x_rssi[display_x] > max_rssi) @@ -1162,7 +1169,7 @@ void loop(void) // if this code is not executed LORA radio doesn't work // basically SX1262 requires delay - // osd.displayString(12, 1, String(FREQ_BEGIN)); + // osd.displayString(12, 1, String(CONF_FREQ_BEGIN)); // osd.displayString(12, 30 - 8, String(FREQ_END)); // delay(2); diff --git a/src/ui.cpp b/src/ui.cpp index e488060..77bdc58 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -16,6 +16,8 @@ // temporary dirty import ... to be solved durring upcoming refactoring extern unsigned int RANGE_PER_PAGE; +extern uint64_t CONF_FREQ_BEGIN; +extern uint64_t CONF_FREQ_END; extern unsigned int median_frequency; extern unsigned int drone_detected_frequency_start; extern unsigned int drone_detected_frequency_end; @@ -95,7 +97,8 @@ void StatusBar::draw() // Frequency start display.setTextAlignment(TEXT_ALIGN_LEFT); display.drawString(pos_x, text_y, - (r.fr_begin == 0) ? String(FREQ_BEGIN) : String(r.fr_begin)); + (r.fr_begin == 0) ? String(CONF_FREQ_BEGIN) + : String(r.fr_begin)); // Frequency detected display.setTextAlignment(TEXT_ALIGN_CENTER); @@ -106,7 +109,7 @@ void StatusBar::draw() // Frequency end display.setTextAlignment(TEXT_ALIGN_RIGHT); display.drawString(pos_x + width, text_y, - (r.fr_end == 0) ? String(FREQ_END) : String(r.fr_end)); + (r.fr_end == 0) ? String(CONF_FREQ_END) : String(r.fr_end)); } // Status text block @@ -170,11 +173,11 @@ void StatusBar::draw() display.drawString(pos_x, text_y, String(loop_time)); #else display.setTextAlignment(TEXT_ALIGN_LEFT); - display.drawString(pos_x, text_y, String(FREQ_BEGIN)); + display.drawString(pos_x, text_y, String(CONF_FREQ_BEGIN)); #endif display.setTextAlignment(TEXT_ALIGN_RIGHT); - display.drawString(pos_x + width, text_y, String(FREQ_END)); + display.drawString(pos_x + width, text_y, String(CONF_FREQ_END)); } else if (ranges_count > 0) { From a62c91d938c75042a0ea35b9166e4cab9cf27eb1 Mon Sep 17 00:00:00 2001 From: Egor Date: Sun, 13 Oct 2024 21:30:03 -0700 Subject: [PATCH 64/80] WiFi load --- src/main.cpp | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 883889e..3815cfc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -49,11 +49,11 @@ // public and so will be exposed to the user. This allows direct manipulation of the // library internals. #define RADIOLIB_GODMODE (1) +#define RADIOLIB_CHECK_PARAMS (0) #include #include #include - #include #ifndef LILYGO @@ -612,6 +612,34 @@ void setup(void) } } + display.clear(); + + both.println("CLICK for WIFI settings."); + + for (int i = 0; i < 200; i++) + { + + both.print("."); + + button.update(); + delay(10); + if (button.pressedNow()) + { + both.println("-----------"); + both.println("Starting WIFI-SERVER..."); + tone(BUZZER_PIN, 205, 100); + delay(50); + tone(BUZZER_PIN, 205, 500); + tone(BUZZER_PIN, 205, 100); + delay(50); + + serverStart(); + break; + } + } + both.print("\n"); + + both.println("Init File System"); initLittleFS(); // writeFile(LittleFS, "/text.txt", "{WIFI:{name:\"sdfsdf\", Password:\"sdfsdf\"}"); @@ -636,9 +664,6 @@ void setup(void) smpls = readParameterFromParameterFile("samples"); Serial.println("SAMPLES: " + smpls); - both.println("Starting WIFI-SERVER"); - serverStart(); - CONF_SAMPLES = (smpls == "") ? samples : atoi(smpls.c_str()); samples = CONF_SAMPLES; CONF_FREQ_BEGIN = (fstart == "") ? FREQ_BEGIN : atoi(fstart.c_str()); From 527ce829394b4ee46d9958d212ed0386ee47d6a9 Mon Sep 17 00:00:00 2001 From: Egor Date: Sun, 13 Oct 2024 21:53:18 -0700 Subject: [PATCH 65/80] Fix WiFi settings --- src/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 3815cfc..2d0d074 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -618,7 +618,6 @@ void setup(void) for (int i = 0; i < 200; i++) { - both.print("."); button.update(); @@ -627,6 +626,7 @@ void setup(void) { both.println("-----------"); both.println("Starting WIFI-SERVER..."); + // Error here: E (15752) ledc: ledc_get_duty(745): LEDC is not initialized tone(BUZZER_PIN, 205, 100); delay(50); tone(BUZZER_PIN, 205, 500); @@ -634,6 +634,8 @@ void setup(void) delay(50); serverStart(); + both.println("Ready to Connect: 192.168.4.1"); + delay(600); break; } } From 41bd4c3b96850ba05c2fef83f0de2cb1b94e969b Mon Sep 17 00:00:00 2001 From: Egor Date: Sun, 13 Oct 2024 22:03:46 -0700 Subject: [PATCH 66/80] move to function readConfigFile --- src/main.cpp | 91 +++++++++++++++++++++++++++------------------------- 1 file changed, 48 insertions(+), 43 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 2d0d074..88721c5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -557,6 +557,53 @@ void logToSerialTask(void *parameter) void drone_sound_alarm(void *arg, Event &e); +void readConfigFile() +{ + // writeFile(LittleFS, "/text.txt", "{WIFI:{name:\"sdfsdf\", Password:\"sdfsdf\"}"); + ssid = readParameterFromParameterFile(SSID); + Serial.println("SSID: " + ssid); + + pass = readParameterFromParameterFile(PASS); + Serial.println("PASS: " + pass); + + ip = readParameterFromParameterFile(IP); + Serial.println("PASS: " + ip); + + gateway = readParameterFromParameterFile(GATEWAY); + Serial.println("GATEWAY: " + gateway); + + fstart = readParameterFromParameterFile(FSTART); + Serial.println("FSTART: " + fstart); + + fend = readParameterFromParameterFile(FEND); + Serial.println("FEND: " + fend); + + smpls = readParameterFromParameterFile("samples"); + Serial.println("SAMPLES: " + smpls); + + CONF_SAMPLES = (smpls == "") ? samples : atoi(smpls.c_str()); + samples = CONF_SAMPLES; + CONF_FREQ_BEGIN = (fstart == "") ? FREQ_BEGIN : atoi(fstart.c_str()); + CONF_FREQ_END = (fend == "") ? FREQ_END : atoi(fend.c_str()); + + both.println("C FREQ BEGIN:" + String(CONF_FREQ_BEGIN)); + both.println("C FREQ END:" + String(CONF_FREQ_END)); + both.println("C SAMPLES:" + String(CONF_SAMPLES)); + + RANGE_PER_PAGE = CONF_FREQ_END - CONF_FREQ_BEGIN; // FREQ_END - CONF_FREQ_BEGIN + + RANGE = (int)(CONF_FREQ_END - CONF_FREQ_BEGIN); + + SINGLE_STEP = (float)(RANGE / (STEPS * SCAN_RBW_FACTOR)); + + range = (int)(CONF_FREQ_END - CONF_FREQ_BEGIN); + + iterations = RANGE / RANGE_PER_PAGE; + + // uint64_t range_frequency = FREQ_END - CONF_FREQ_BEGIN; + median_frequency = (CONF_FREQ_BEGIN + CONF_FREQ_END) / 2; +} + void setup(void) { @@ -644,49 +691,7 @@ void setup(void) both.println("Init File System"); initLittleFS(); - // writeFile(LittleFS, "/text.txt", "{WIFI:{name:\"sdfsdf\", Password:\"sdfsdf\"}"); - ssid = readParameterFromParameterFile(SSID); - Serial.println("SSID: " + ssid); - - pass = readParameterFromParameterFile(PASS); - Serial.println("PASS: " + pass); - - ip = readParameterFromParameterFile(IP); - Serial.println("PASS: " + ip); - - gateway = readParameterFromParameterFile(GATEWAY); - Serial.println("GATEWAY: " + gateway); - - fstart = readParameterFromParameterFile(FSTART); - Serial.println("FSTART: " + fstart); - - fend = readParameterFromParameterFile(FEND); - Serial.println("FEND: " + fend); - - smpls = readParameterFromParameterFile("samples"); - Serial.println("SAMPLES: " + smpls); - - CONF_SAMPLES = (smpls == "") ? samples : atoi(smpls.c_str()); - samples = CONF_SAMPLES; - CONF_FREQ_BEGIN = (fstart == "") ? FREQ_BEGIN : atoi(fstart.c_str()); - CONF_FREQ_END = (fend == "") ? FREQ_END : atoi(fend.c_str()); - - both.println("C FREQ BEGIN:" + String(CONF_FREQ_BEGIN)); - both.println("C FREQ END:" + String(CONF_FREQ_END)); - both.println("C SAMPLES:" + String(CONF_SAMPLES)); - - RANGE_PER_PAGE = CONF_FREQ_END - CONF_FREQ_BEGIN; // FREQ_END - CONF_FREQ_BEGIN - - RANGE = (int)(CONF_FREQ_END - CONF_FREQ_BEGIN); - - SINGLE_STEP = (float)(RANGE / (STEPS * SCAN_RBW_FACTOR)); - - range = (int)(CONF_FREQ_END - CONF_FREQ_BEGIN); - - iterations = RANGE / RANGE_PER_PAGE; - - // uint64_t range_frequency = FREQ_END - CONF_FREQ_BEGIN; - median_frequency = (CONF_FREQ_BEGIN + CONF_FREQ_END) / 2; + readConfigFile(); init_radio(); From 1c65cd745a0137dbff7458a78aa0447ce63a787b Mon Sep 17 00:00:00 2001 From: KonradIT Date: Mon, 14 Oct 2024 23:38:43 +0200 Subject: [PATCH 67/80] Modify SpectrumScan.py to use json data in serial (LOG_DATA_JSON must be set to true) --- .gitignore | 2 ++ SpectrumScan.py | 88 +++++++++++++++++++------------------------------ 2 files changed, 35 insertions(+), 55 deletions(-) diff --git a/.gitignore b/.gitignore index 89cc49c..3711360 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ .vscode/c_cpp_properties.json .vscode/launch.json .vscode/ipch + +out/ diff --git a/SpectrumScan.py b/SpectrumScan.py index acc37a0..ee74707 100644 --- a/SpectrumScan.py +++ b/SpectrumScan.py @@ -11,6 +11,8 @@ import matplotlib.pyplot as plt from datetime import datetime from argparse import RawTextHelpFormatter +import json + # number of samples in each scanline SCAN_WIDTH = 33 @@ -50,17 +52,15 @@ def printProgressBar (iteration, total, prefix = '', suffix = '', decimals = 1, if iteration == total: print() +def parse_line(line): + json_line = json.loads(line) + return json_line def main(): parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter, description=''' - RadioLib SX126x_Spectrum_Scan plotter script. Displays output from SX126x_Spectrum_Scan example - as grayscale and + Parse serial data from LOG_DATA_JSON functionality. - Depends on pyserial and matplotlib, install by: - 'python3 -m pip install pyserial matplotlib' - - Step-by-step guide on how to use the script: - 1. Upload the SX126x_Spectrum_Scan example to your Arduino board with SX1262 connected. + 1. #define LOG_DATA_JSON true - add this line in main.cpp, upload to device 2. Run the script with appropriate arguments. 3. Once the scan is complete, output files will be saved to out/ ''') @@ -89,88 +89,66 @@ def main(): help=f'Default starting frequency in MHz') args = parser.parse_args() - freq_mode = False - scan_len = args.len - if (args.freq != -1): - freq_mode = True - scan_len = 1000 - - # create the color map and the result array - arr = np.zeros((SCAN_WIDTH, scan_len)) + # create the result array + arr = np.zeros((scan_len, SCAN_WIDTH)) # scanline counter row = 0 - # list of frequencies in frequency mode + # list of frequencies freq_list = [] # open the COM port with serial.Serial(args.port, args.speed, timeout=None) as com: while(True): # update the progress bar - if not freq_mode: - printProgressBar(row, scan_len) + printProgressBar(row, scan_len) # read a single line try: line = com.readline().decode('utf-8') except: continue + print(line) + if "{" in line: + try: + data = parse_line(line) + except: + continue - if SCAN_MARK_FREQ in line: - new_freq = float(line.split(' ')[1]) - if (len(freq_list) > 1) and (new_freq < freq_list[-1]): - break + freq = data["low_range_freq"] + rssi = int(data["value"]) - freq_list.append(new_freq) - print('{:.3f}'.format(new_freq), end = '\r') - continue - - # check the markers - if (SCAN_MARK_START in line) and (SCAN_MARK_END in line): - # get the values - scanline = line[len(SCAN_MARK_START):-len(SCAN_MARK_END)].split(',') - for col in range(SCAN_WIDTH): - arr[col][row] = int(scanline[col]) + if freq not in freq_list: + freq_list.append(freq) + + col = freq_list.index(freq) + arr[row][col] = rssi # increment the row counter row = row + 1 # check if we're done - if (not freq_mode) and (row >= scan_len): + if (row >= scan_len): break - - # scale to the number of scans (sum of any given scanline) - num_samples = arr.sum(axis=0)[0] - arr *= (num_samples/arr.max()) - - if freq_mode: - scan_len = len(freq_list) # create the figure - fig, ax = plt.subplots() + fig, ax = plt.subplots(figsize=(12, 8)) # display the result as heatmap - extent = [0, scan_len, -4*(SCAN_WIDTH + 1), args.offset] - if freq_mode: - extent[0] = freq_list[0] - extent[1] = freq_list[-1] - im = ax.imshow(arr[:,:scan_len], cmap=args.map, extent=extent) - fig.colorbar(im) + extent = [0, scan_len, freq_list[0], freq_list[-1]] + im = ax.imshow(arr.T, cmap=args.map, extent=extent, aspect='auto', origin='lower') + fig.colorbar(im, label='RSSI (dBm)') - # set some properites and show + # set some properties and show timestamp = datetime.now().strftime('%y-%m-%d %H-%M-%S') title = f'RadioLib SX126x Spectral Scan {timestamp}' - if freq_mode: - plt.xlabel("Frequency [Hz]") - else: - plt.xlabel("Time [sample]") - plt.ylabel("RSSI [dBm]") - ax.set_aspect('auto') + plt.xlabel("Time (sample)") + plt.ylabel("Frequency (MHz)") fig.suptitle(title) fig.canvas.manager.set_window_title(title) plt.savefig(f'{OUT_PATH}/{title.replace(" ", "_")}.png', dpi=300) plt.show() if __name__ == "__main__": - main() \ No newline at end of file + main() From e6c92e4eb09936aeec8fcdbdfcecd3fec0c7bd35 Mon Sep 17 00:00:00 2001 From: KonradIT Date: Mon, 14 Oct 2024 23:45:35 +0200 Subject: [PATCH 68/80] Cleaned up, linted, etc... --- SpectrumScan.py | 117 ++++++++++++++++++++---------------------------- 1 file changed, 49 insertions(+), 68 deletions(-) diff --git a/SpectrumScan.py b/SpectrumScan.py index ee74707..ee713a3 100644 --- a/SpectrumScan.py +++ b/SpectrumScan.py @@ -7,32 +7,21 @@ import sys import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt - +import json from datetime import datetime from argparse import RawTextHelpFormatter -import json +# Constants +SCAN_WIDTH = 33 # number of samples in each scanline +OUT_PATH = "out" # output path for saved files -# number of samples in each scanline -SCAN_WIDTH = 33 - -# scanline Serial start/end markers -SCAN_MARK_START = 'SCAN ' -SCAN_MARK_FREQ = 'FREQ ' -SCAN_MARK_END = ' END' - -# output path -OUT_PATH = 'out' - -# default settings +# Default settings DEFAULT_BAUDRATE = 115200 DEFAULT_COLOR_MAP = 'viridis' DEFAULT_SCAN_LEN = 200 DEFAULT_RSSI_OFFSET = -11 -# Print iterations progress -# from https://stackoverflow.com/questions/3173320/text-progress-bar-in-terminal-with-block-characters -def printProgressBar (iteration, total, prefix = '', suffix = '', decimals = 1, length = 50, fill = '█', printEnd = "\r"): +def print_progress_bar(iteration, total, prefix='', suffix='', decimals=1, length=50, fill='█', print_end="\r"): """ Call in a loop to create terminal progress bar @params: @@ -43,80 +32,72 @@ def printProgressBar (iteration, total, prefix = '', suffix = '', decimals = 1, decimals - Optional : positive number of decimals in percent complete (Int) length - Optional : character length of bar (Int) fill - Optional : bar fill character (Str) - printEnd - Optional : end character (e.g. "\r", "\r\n") (Str) + print_end - Optional : end character (e.g. "\r", "\r\n") (Str) """ percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total))) - filledLength = int(length * iteration // total) - bar = fill * filledLength + '-' * (length - filledLength) - print(f'\r{prefix} |{bar}| {percent}% {suffix}', end = printEnd) + filled_length = int(length * iteration // total) + bar = fill * filled_length + '-' * (length - filled_length) + print(f'\r{prefix} |{bar}| {percent}% {suffix}', end=print_end) if iteration == total: print() def parse_line(line): - json_line = json.loads(line) - return json_line + """Parse a JSON line from the serial input.""" + return json.loads(line) def main(): - parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter, description=''' + parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter, description='''\ Parse serial data from LOG_DATA_JSON functionality. 1. #define LOG_DATA_JSON true - add this line in main.cpp, upload to device 2. Run the script with appropriate arguments. 3. Once the scan is complete, output files will be saved to out/ ''') - parser.add_argument('port', - type=str, - help='COM port to connect to the device') - parser.add_argument('--speed', - default=DEFAULT_BAUDRATE, - type=int, - help=f'COM port baudrate (defaults to {DEFAULT_BAUDRATE})') - parser.add_argument('--map', - default=DEFAULT_COLOR_MAP, - type=str, - help=f'Matplotlib color map to use for the output (defaults to "{DEFAULT_COLOR_MAP}")') - parser.add_argument('--len', - default=DEFAULT_SCAN_LEN, - type=int, - help=f'Number of scanlines to record (defaults to {DEFAULT_SCAN_LEN})') - parser.add_argument('--offset', - default=DEFAULT_RSSI_OFFSET, - type=int, - help=f'Default RSSI offset in dBm (defaults to {DEFAULT_RSSI_OFFSET})') - parser.add_argument('--freq', - default=-1, - type=float, - help=f'Default starting frequency in MHz') + parser.add_argument('port', type=str, help='COM port to connect to the device') + parser.add_argument('--speed', default=DEFAULT_BAUDRATE, type=int, + help=f'COM port baudrate (defaults to {DEFAULT_BAUDRATE})') + parser.add_argument('--map', default=DEFAULT_COLOR_MAP, type=str, + help=f'Matplotlib color map to use for the output (defaults to "{DEFAULT_COLOR_MAP}")') + parser.add_argument('--len', default=DEFAULT_SCAN_LEN, type=int, + help=f'Number of scanlines to record (defaults to {DEFAULT_SCAN_LEN})') + parser.add_argument('--offset', default=DEFAULT_RSSI_OFFSET, type=int, + help=f'Default RSSI offset in dBm (defaults to {DEFAULT_RSSI_OFFSET})') + parser.add_argument('--freq', default=-1, type=float, + help='Default starting frequency in MHz') args = parser.parse_args() - # create the result array + # Create the result array + scan_len = args.len arr = np.zeros((scan_len, SCAN_WIDTH)) - # scanline counter + # Scanline counter row = 0 - # list of frequencies + # List of frequencies freq_list = [] - # open the COM port + # Open the COM port with serial.Serial(args.port, args.speed, timeout=None) as com: - while(True): - # update the progress bar - printProgressBar(row, scan_len) + while True: + # Update the progress bar + print_progress_bar(row, scan_len) - # read a single line + # Read a single line try: - line = com.readline().decode('utf-8') - except: + line = com.readline().decode('utf-8').strip() + except UnicodeDecodeError: continue - print(line) - if "{" in line: + + if line.startswith("{"): try: data = parse_line(line) - except: + except json.JSONDecodeError: continue + # get the lowest frequency for now, could be averaged too. freq = data["low_range_freq"] + + # value in negative, eg: -70 rssi = int(data["value"]) if freq not in freq_list: @@ -125,24 +106,24 @@ def main(): col = freq_list.index(freq) arr[row][col] = rssi - # increment the row counter - row = row + 1 + # Increment the row counter + row += 1 - # check if we're done - if (row >= scan_len): + # Check if we're done + if row >= scan_len: break - # create the figure + # Create the figure fig, ax = plt.subplots(figsize=(12, 8)) - # display the result as heatmap + # Display the result as heatmap extent = [0, scan_len, freq_list[0], freq_list[-1]] im = ax.imshow(arr.T, cmap=args.map, extent=extent, aspect='auto', origin='lower') fig.colorbar(im, label='RSSI (dBm)') - # set some properties and show + # Set plot properties and show timestamp = datetime.now().strftime('%y-%m-%d %H-%M-%S') - title = f'RadioLib SX126x Spectral Scan {timestamp}' + title = f'LoraSA Spectral Scan {timestamp}' plt.xlabel("Time (sample)") plt.ylabel("Frequency (MHz)") fig.suptitle(title) From 1ace56054d74f125b965dae591b28e357a4fb4d4 Mon Sep 17 00:00:00 2001 From: Egor Date: Fri, 18 Oct 2024 01:08:08 -0700 Subject: [PATCH 69/80] add webserver lib --- platformio.ini | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/platformio.ini b/platformio.ini index a1d92eb..08f0ebe 100644 --- a/platformio.ini +++ b/platformio.ini @@ -45,6 +45,8 @@ monitor_speed = 115200 board_build.f_cpu = 240000000 lib_deps = ropg/Heltec_ESP32_LoRa_v3@^0.9.1 + bblanchon/ArduinoJson@^7.2.0 + ESP Async WebServer build_flags = -DHELTEC_POWER_BUTTON -DHELTEC @@ -64,6 +66,7 @@ lib_deps = RadioLib U8g2 XPowersLib + ESP Async WebServer build_flags = -DLILYGO -DT3_S3_V1_2_SX1262 @@ -119,6 +122,7 @@ lib_deps = RadioLib U8g2 XPowersLib + ESP Async WebServer build_flags = -DLILYGO -DT3_S3_V1_2_SX1280_PA @@ -143,6 +147,7 @@ lib_deps = RadioLib U8g2 XPowersLib + ESP Async WebServer build_flags = -DLILYGO -DT3_V1_6_SX1276 From 55d26f0aff66c120a025a1cf9dcd065b344b22e2 Mon Sep 17 00:00:00 2001 From: Egor Date: Fri, 18 Oct 2024 01:15:20 -0700 Subject: [PATCH 70/80] forgot to add little file system --- platformio.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platformio.ini b/platformio.ini index 08f0ebe..e85aa6e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -43,6 +43,7 @@ framework = arduino upload_speed = 921600 monitor_speed = 115200 board_build.f_cpu = 240000000 +board_build.filesystem = littlefs lib_deps = ropg/Heltec_ESP32_LoRa_v3@^0.9.1 bblanchon/ArduinoJson@^7.2.0 @@ -61,6 +62,7 @@ framework = arduino upload_speed = 921600 monitor_speed = 115200 board_build.f_cpu = 240000000 +board_build.filesystem = littlefs lib_deps = ropg/Heltec_ESP32_LoRa_v3@^0.9.1 RadioLib @@ -117,6 +119,7 @@ framework = arduino upload_speed = 921600 monitor_speed = 115200 board_build.f_cpu = 240000000 +board_build.filesystem = littlefs lib_deps = ropg/Heltec_ESP32_LoRa_v3@^0.9.1 RadioLib @@ -142,6 +145,7 @@ board = esp32dev framework = arduino upload_speed = 115200 monitor_speed = 115200 +board_build.filesystem = littlefs lib_deps = ropg/Heltec_ESP32_LoRa_v3@^0.9.1 RadioLib From 62ac49e84b91b18383a3dc20d6b2dc266aaf30ba Mon Sep 17 00:00:00 2001 From: Egor Date: Wed, 13 Nov 2024 21:34:45 -0800 Subject: [PATCH 71/80] t-190 fix --- tft_src/main.cpp | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/tft_src/main.cpp b/tft_src/main.cpp index 9eae332..610861e 100644 --- a/tft_src/main.cpp +++ b/tft_src/main.cpp @@ -13,9 +13,9 @@ // #define ARDUINO_USB_CDC_ON_BOOT 1 // #define LoRaWAN_DEBUG_LEVEL 0 #include "HT_ST7789spi.h" -#include "global_config.h" +// #include "global_config.h" #include "images.h" -#include "ui.h" +// #include "ui.h" #include #include @@ -71,6 +71,8 @@ constexpr bool DRAW_DETECTION_TICKS = true; #define SAMPLES_RSSI 5 // 21 // #define FREQ_BEGIN 650 +#define FREQ_END 960 +#define BANDWIDTH 467.0 #define DEFAULT_DRONE_DETECTION_LEVEL 90 #define RANGE (int)(FREQ_END - FREQ_BEGIN) @@ -315,6 +317,7 @@ constexpr unsigned int STATUS_BAR_HEIGHT = 5; void loop() { + Serial.println("Loop"); if (screen_update_loop_counter == 0) { fr_x[x1] = 0; @@ -331,7 +334,7 @@ void loop() fr_x[x1] = fr; int u = 0; - int additional_samples = 10; + int additional_samples = 0; // Clear old data with the cursor ... st7789->drawFastVLine(x1, lower_level, -lower_level + 11, ST7789_BLACK); @@ -351,8 +354,15 @@ void loop() additional_samples--; } - radio.setFrequency((float)fr + (float)(rssi_mhz_step * u), - false); // false = no calibration need here + bool calibrate = true; + float freq = (float)fr + (float)(rssi_mhz_step * u); + if ((int)freq % 10 == 0) + { + calibrate = true; + } + radio.setFrequency(freq, + /*false*/ calibrate); // false = no calibration need here + // Serial.println((float)fr + (float)(rssi_mhz_step * u)); u++; if (rssi_mhz_step * u >= mhz_step) { @@ -363,6 +373,7 @@ void loop() rssi_single_start = millis(); } rssi2 = radio.getRSSI(false); + // Serial.print(" RSSI : " + String(rssi2)); scan_iterations++; if (rssi_single_end == 0) { @@ -383,13 +394,17 @@ void loop() #ifdef PRINT_DEBUG Serial.println(String(fr) + ":" + String(rssi2)); #endif + int lineHeight = 0; + st7789->drawPixel(x1, rssiToPix(rssi2), rssiToColor(abs(rssi2))); st7789->drawPixel(x1, rssiToPix(rssi2) - 1, rssiToColor(abs(rssi2))); st7789->drawPixel(x1, rssiToPix(rssi2) - 2, rssiToColor(abs(rssi2))); + st7789->drawFastVLine(x1, rssiToPix(rssi2), lower_level - rssiToPix(rssi2), + rssiToColor(abs(rssi2))); // Draw Update Cursor st7789->drawFastVLine(x1 + 1, lower_level, -lower_level + 11, ST7789_BLACK); st7789->drawFastVLine(x1 + 2, lower_level, -lower_level + 11, ST7789_BLACK); - st7789->drawFastVLine(x1 + 3, lower_level, -lower_level + 11, ST7789_BLACK); + // st7789->drawFastVLine(x1 + 3, lower_level, -lower_level + 11, ST7789_BLACK); if (max_scan_rssi[x1] == -999) { @@ -452,7 +467,10 @@ void loop() // Waterfall cursor st7789->drawFastHLine(0, w + 1, DISPLAY_WIDTH, ST7789_BLACK); - st7789->drawFastHLine(0, w + 2, DISPLAY_WIDTH, ST7789_BLACK); + if (w < WATERFALL_END) + { + st7789->drawFastHLine(0, w + 2, DISPLAY_WIDTH, ST7789_ORANGE); + } // drone detection level line if (x1 % 2 == 0) From 2914f249cdbd459c4f79fb20d18381a2a5ec08c9 Mon Sep 17 00:00:00 2001 From: Egor Date: Wed, 13 Nov 2024 21:37:02 -0800 Subject: [PATCH 72/80] add comment --- tft_src/main.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tft_src/main.cpp b/tft_src/main.cpp index 610861e..e094d85 100644 --- a/tft_src/main.cpp +++ b/tft_src/main.cpp @@ -399,8 +399,12 @@ void loop() st7789->drawPixel(x1, rssiToPix(rssi2), rssiToColor(abs(rssi2))); st7789->drawPixel(x1, rssiToPix(rssi2) - 1, rssiToColor(abs(rssi2))); st7789->drawPixel(x1, rssiToPix(rssi2) - 2, rssiToColor(abs(rssi2))); - st7789->drawFastVLine(x1, rssiToPix(rssi2), lower_level - rssiToPix(rssi2), - rssiToColor(abs(rssi2))); + + if (true /*draw full line*/) + { + st7789->drawFastVLine(x1, rssiToPix(rssi2), lower_level - rssiToPix(rssi2), + rssiToColor(abs(rssi2))); + } // Draw Update Cursor st7789->drawFastVLine(x1 + 1, lower_level, -lower_level + 11, ST7789_BLACK); st7789->drawFastVLine(x1 + 2, lower_level, -lower_level + 11, ST7789_BLACK); From cbd4175e28ca1bb9fb7bc56d4cfbf92faedc5c9c Mon Sep 17 00:00:00 2001 From: Egor Date: Wed, 13 Nov 2024 21:44:58 -0800 Subject: [PATCH 73/80] add low level --- tft_src/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tft_src/main.cpp b/tft_src/main.cpp index e094d85..f3583f2 100644 --- a/tft_src/main.cpp +++ b/tft_src/main.cpp @@ -133,6 +133,8 @@ uint64_t scan_time = 0; uint64_t scan_start_time = 0; #endif +// To remove waterfall adjust this and this +#define LOWER_LEVEL 108 #define WATERFALL_START 115 #define WATERFALL_END DISPLAY_HEIGHT - 10 - 2 @@ -249,7 +251,7 @@ void battery() } } -constexpr int lower_level = 108; +constexpr int lower_level = LOWER_LEVEL; constexpr int up_level = 40; int rssiToPix(int rssi) { From 33042ae71e9bd06a40c42db34db485585dd0b4d8 Mon Sep 17 00:00:00 2001 From: Egor Date: Wed, 13 Nov 2024 21:54:14 -0800 Subject: [PATCH 74/80] add waterfall disable --- tft_src/main.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tft_src/main.cpp b/tft_src/main.cpp index f3583f2..3a92526 100644 --- a/tft_src/main.cpp +++ b/tft_src/main.cpp @@ -137,6 +137,7 @@ uint64_t scan_start_time = 0; #define LOWER_LEVEL 108 #define WATERFALL_START 115 #define WATERFALL_END DISPLAY_HEIGHT - 10 - 2 +#define DISABLE_WATERFALL 0 // to disable set to 1 uint64_t x, y, range_item, w = WATERFALL_START, i = 0; int osd_x = 1, osd_y = 2, col = 0, max_bin = 32; @@ -440,8 +441,11 @@ void loop() // Writing pixel only if it is bigger than drone detection level if (abs(max_scan_rssi[x1]) < drone_detection_level) { - // Waterfall Pixel - st7789->drawPixel(x1, w, rssiToColor(abs(max_scan_rssi[x1]), true)); + if (DISABLE_WATERFALL == 0) + { + // Waterfall Pixel + st7789->drawPixel(x1, w, rssiToColor(abs(max_scan_rssi[x1]), true)); + } detailed_scan_candidate[(int)fr] = (int)fr; } @@ -471,11 +475,14 @@ void loop() window_max_rssi = -999; } - // Waterfall cursor - st7789->drawFastHLine(0, w + 1, DISPLAY_WIDTH, ST7789_BLACK); - if (w < WATERFALL_END) + if (DISABLE_WATERFALL == 0) { - st7789->drawFastHLine(0, w + 2, DISPLAY_WIDTH, ST7789_ORANGE); + // Waterfall cursor + st7789->drawFastHLine(0, w + 1, DISPLAY_WIDTH, ST7789_BLACK); + if (w < WATERFALL_END) + { + st7789->drawFastHLine(0, w + 2, DISPLAY_WIDTH, ST7789_ORANGE); + } } // drone detection level line From 5f273c2f73ef6318d8176d2b97c2f9c435560d5a Mon Sep 17 00:00:00 2001 From: Egor Date: Thu, 14 Nov 2024 12:52:14 -0800 Subject: [PATCH 75/80] scaling display --- tft_src/main.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tft_src/main.cpp b/tft_src/main.cpp index 3a92526..4010648 100644 --- a/tft_src/main.cpp +++ b/tft_src/main.cpp @@ -70,18 +70,19 @@ constexpr bool DRAW_DETECTION_TICKS = true; // number of samples for RSSI method #define SAMPLES_RSSI 5 // 21 // -#define FREQ_BEGIN 650 -#define FREQ_END 960 +#define FREQ_BEGIN 150 +#define FREQ_END 950 #define BANDWIDTH 467.0 +#define MHZ_PX (float)((float)(FREQ_END - FREQ_BEGIN) / DISPLAY_WIDTH) #define DEFAULT_DRONE_DETECTION_LEVEL 90 #define RANGE (int)(FREQ_END - FREQ_BEGIN) -#define SINGLE_STEP (float)(RANGE / (STEPS * SCAN_RBW_FACTOR)) +// #define SINGLE_STEP (float)(RANGE / (STEPS * SCAN_RBW_FACTOR)) uint64_t range = (int)(FREQ_END - FREQ_BEGIN); uint64_t fr_begin = FREQ_BEGIN; -uint64_t fr_end = FREQ_BEGIN; +uint64_t fr_end = FREQ_END; // Feature to scan diapasones. Other frequency settings will be ignored. // int SCAN_RANGES[] = {850890, 920950}; @@ -92,7 +93,7 @@ int SCAN_RANGES[] = {}; // uint64_t RANGE_PER_PAGE = FREQ_END - FREQ_BEGIN; // FREQ_END - FREQ_BEGIN // Override or e-ink -uint64_t RANGE_PER_PAGE = FREQ_BEGIN + DISPLAY_WIDTH; +uint64_t RANGE_PER_PAGE = FREQ_END - FREQ_BEGIN; // FREQ_BEGIN + DISPLAY_WIDTH; uint64_t iterations = RANGE / RANGE_PER_PAGE; @@ -291,7 +292,7 @@ long timeSinceLastModeSwitch = 0; float fr = FREQ_BEGIN, fr_x[STEPS + 5], vbat = 0; // MHz in one screen pix step // END will be Begin + 289 * mhz_step -constexpr int mhz_step = 1; +float mhz_step = MHZ_PX; // TODO: make end_freq // Measure RSS every step constexpr float rssi_mhz_step = 0.33; From 38c0a8540d83d130ff8bb48b32a5bc72c708484a Mon Sep 17 00:00:00 2001 From: Egor Date: Fri, 15 Nov 2024 14:05:13 -0800 Subject: [PATCH 76/80] remove waterfall move chart down --- tft_src/main.cpp | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/tft_src/main.cpp b/tft_src/main.cpp index 4010648..ce0ebdc 100644 --- a/tft_src/main.cpp +++ b/tft_src/main.cpp @@ -135,10 +135,11 @@ uint64_t scan_start_time = 0; #endif // To remove waterfall adjust this and this -#define LOWER_LEVEL 108 +#define LOWER_LEVEL DISPLAY_HEIGHT - 22 // 108 +#define SPECTR_CHART_STAR_TOP 40 + 30; #define WATERFALL_START 115 #define WATERFALL_END DISPLAY_HEIGHT - 10 - 2 -#define DISABLE_WATERFALL 0 // to disable set to 1 +#define DISABLE_WATERFALL 1 // to disable set to 1 uint64_t x, y, range_item, w = WATERFALL_START, i = 0; int osd_x = 1, osd_y = 2, col = 0, max_bin = 32; @@ -254,7 +255,7 @@ void battery() } constexpr int lower_level = LOWER_LEVEL; -constexpr int up_level = 40; +constexpr int up_level = SPECTR_CHART_STAR_TOP; int rssiToPix(int rssi) { // Bigger is lower signal @@ -262,11 +263,24 @@ int rssiToPix(int rssi) { return lower_level - 1; } - if (abs(rssi) <= up_level) + if (abs(rssi) <= up_level && lower_level < 130) { return up_level; } - return abs(rssi); + // if chart moved to the bottom + if (lower_level > 130) + { + int returnRssi = rssi - up_level / 3; + if (returnRssi >= lower_level) + { + return lower_level - 1; + } + return abs(returnRssi); + } + else + { + return abs(rssi); + } } // @@ -403,11 +417,22 @@ void loop() st7789->drawPixel(x1, rssiToPix(rssi2), rssiToColor(abs(rssi2))); st7789->drawPixel(x1, rssiToPix(rssi2) - 1, rssiToColor(abs(rssi2))); st7789->drawPixel(x1, rssiToPix(rssi2) - 2, rssiToColor(abs(rssi2))); + if (LOWER_LEVEL > 140) + { + st7789->drawPixel(x1, rssiToPix(rssi2) - 3, rssiToColor(abs(rssi2))); + st7789->drawPixel(x1, rssiToPix(rssi2) - 5, rssiToColor(abs(rssi2))); + st7789->drawPixel(x1, rssiToPix(rssi2) - 6, rssiToColor(abs(rssi2))); + st7789->drawPixel(x1, rssiToPix(rssi2) - 7, rssiToColor(abs(rssi2))); + st7789->drawPixel(x1, rssiToPix(rssi2) - 8, rssiToColor(abs(rssi2))); + st7789->drawPixel(x1, rssiToPix(rssi2) - 9, rssiToColor(abs(rssi2))); + st7789->drawPixel(x1, rssiToPix(rssi2) - 10, rssiToColor(abs(rssi2))); + } if (true /*draw full line*/) { - st7789->drawFastVLine(x1, rssiToPix(rssi2), lower_level - rssiToPix(rssi2), - rssiToColor(abs(rssi2))); + // st7789->drawFastVLine(x1, rssiToPix(rssi2), lower_level - + // rssiToPix(rssi2), + // rssiToColor(abs(rssi2))); } // Draw Update Cursor st7789->drawFastVLine(x1 + 1, lower_level, -lower_level + 11, ST7789_BLACK); From 0d9164db75a15db1461bd1942ef385fa84af04a2 Mon Sep 17 00:00:00 2001 From: Egor Date: Fri, 15 Nov 2024 14:09:27 -0800 Subject: [PATCH 77/80] add line --- tft_src/main.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tft_src/main.cpp b/tft_src/main.cpp index ce0ebdc..62251a5 100644 --- a/tft_src/main.cpp +++ b/tft_src/main.cpp @@ -430,9 +430,8 @@ void loop() if (true /*draw full line*/) { - // st7789->drawFastVLine(x1, rssiToPix(rssi2), lower_level - - // rssiToPix(rssi2), - // rssiToColor(abs(rssi2))); + st7789->drawFastVLine(x1, rssiToPix(rssi2), lower_level - rssiToPix(rssi2), + rssiToColor(abs(rssi2))); } // Draw Update Cursor st7789->drawFastVLine(x1 + 1, lower_level, -lower_level + 11, ST7789_BLACK); From 85349f2a8c8e417069cebd64a0105a4444f5bb21 Mon Sep 17 00:00:00 2001 From: Egor Date: Fri, 15 Nov 2024 16:38:21 -0800 Subject: [PATCH 78/80] some fixes --- tft_src/main.cpp | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/tft_src/main.cpp b/tft_src/main.cpp index 62251a5..4d48dbb 100644 --- a/tft_src/main.cpp +++ b/tft_src/main.cpp @@ -136,7 +136,7 @@ uint64_t scan_start_time = 0; // To remove waterfall adjust this and this #define LOWER_LEVEL DISPLAY_HEIGHT - 22 // 108 -#define SPECTR_CHART_STAR_TOP 40 + 30; +#define SPECTR_CHART_STAR_TOP 40 + 50; #define WATERFALL_START 115 #define WATERFALL_END DISPLAY_HEIGHT - 10 - 2 #define DISABLE_WATERFALL 1 // to disable set to 1 @@ -270,8 +270,9 @@ int rssiToPix(int rssi) // if chart moved to the bottom if (lower_level > 130) { - int returnRssi = rssi - up_level / 3; - if (returnRssi >= lower_level) + int returnRssi = lower_level + abs(rssi) - up_level - 21; + Serial.println("RSSI: " + String(rssi)); + if (returnRssi >= lower_level - 2) { return lower_level - 1; } @@ -417,16 +418,8 @@ void loop() st7789->drawPixel(x1, rssiToPix(rssi2), rssiToColor(abs(rssi2))); st7789->drawPixel(x1, rssiToPix(rssi2) - 1, rssiToColor(abs(rssi2))); st7789->drawPixel(x1, rssiToPix(rssi2) - 2, rssiToColor(abs(rssi2))); - if (LOWER_LEVEL > 140) - { - st7789->drawPixel(x1, rssiToPix(rssi2) - 3, rssiToColor(abs(rssi2))); - st7789->drawPixel(x1, rssiToPix(rssi2) - 5, rssiToColor(abs(rssi2))); - st7789->drawPixel(x1, rssiToPix(rssi2) - 6, rssiToColor(abs(rssi2))); - st7789->drawPixel(x1, rssiToPix(rssi2) - 7, rssiToColor(abs(rssi2))); - st7789->drawPixel(x1, rssiToPix(rssi2) - 8, rssiToColor(abs(rssi2))); - st7789->drawPixel(x1, rssiToPix(rssi2) - 9, rssiToColor(abs(rssi2))); - st7789->drawPixel(x1, rssiToPix(rssi2) - 10, rssiToColor(abs(rssi2))); - } + st7789->drawPixel(x1, rssiToPix(rssi2) - 3, rssiToColor(abs(rssi2))); + st7789->drawPixel(x1, rssiToPix(rssi2) - 4, rssiToColor(abs(rssi2))); if (true /*draw full line*/) { From 6028e13ef6a6bd2a9114d0189adf400223cc1477 Mon Sep 17 00:00:00 2001 From: Egor Date: Sun, 24 Nov 2024 13:28:34 -0800 Subject: [PATCH 79/80] Ad legend and fix screen scaling --- eink_src/main.cpp | 2 +- tft_src/main.cpp | 114 ++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 97 insertions(+), 19 deletions(-) diff --git a/eink_src/main.cpp b/eink_src/main.cpp index 71a35a0..ee82ccb 100644 --- a/eink_src/main.cpp +++ b/eink_src/main.cpp @@ -364,7 +364,7 @@ void loop() for (int i = 0; i < SAMPLES_RSSI; i++) { state = radio.setFrequency((float)fr + (float)(rssi_mhz_step * u), - false); // false = no calibration need here + true); // false = no calibration need here int radio_error_count = 0; if (state != RADIOLIB_ERR_NONE) { diff --git a/tft_src/main.cpp b/tft_src/main.cpp index 4d48dbb..ac220ef 100644 --- a/tft_src/main.cpp +++ b/tft_src/main.cpp @@ -18,6 +18,23 @@ // #include "ui.h" #include #include +#include + +struct Entry +{ + String drone; // Drone name + int fstart; // Fr Start + int fend; // Fr End + int y; // y(vertical) position + uint16_t color; // color +}; + +// Define and initialize the vector +std::vector fpvArray = {{"FPV-ELRS", 160, 350, 100, ST7789_BLUE}, + {"915-ELRS", 700, 1000, 100, ST7789_ORANGE}, + {"FPV433-ELRS", 350, 530, 100, ST7789_YELLOW}, + {"Orlan", 820, 940, 98, ST7789_GREEN}, + {"Zala", 830, 950, 80, ST7789_MAGENTA}}; #define st7789_CS_Pin 39 #define st7789_REST_Pin 40 @@ -74,7 +91,8 @@ constexpr bool DRAW_DETECTION_TICKS = true; #define FREQ_END 950 #define BANDWIDTH 467.0 #define MHZ_PX (float)((float)(FREQ_END - FREQ_BEGIN) / DISPLAY_WIDTH) -#define DEFAULT_DRONE_DETECTION_LEVEL 90 +#define DEFAULT_DRONE_DETECTION_LEVEL -90 +#define DRONE_LEGEND 1; #define RANGE (int)(FREQ_END - FREQ_BEGIN) @@ -117,7 +135,7 @@ bool waterfall[STEPS], detected_y[STEPS]; // 20 - ??? steps of the waterfall bool first_run, new_pixel, detected_x = false; // drone detection flag bool detected = false; -uint64_t drone_detection_level = DEFAULT_DRONE_DETECTION_LEVEL; +int64_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; @@ -135,11 +153,22 @@ uint64_t scan_start_time = 0; #endif // To remove waterfall adjust this and this -#define LOWER_LEVEL DISPLAY_HEIGHT - 22 // 108 -#define SPECTR_CHART_STAR_TOP 40 + 50; -#define WATERFALL_START 115 +#define ZERO_LEVEL 110 // Equal to minimal RSSI +#define ZERO_SHIFT 42 +#define LOWER_LEVEL ZERO_LEVEL + ZERO_SHIFT // 108(zero) - (40 moving down) +#define SPECTR_CHART_STAR_TOP 42; +#define WATERFALL_START 119 #define WATERFALL_END DISPLAY_HEIGHT - 10 - 2 + +#ifndef DISABLE_WATERFALL #define DISABLE_WATERFALL 1 // to disable set to 1 +#endif + +#if DISABLE_WATERFALL == 0 +#define ZERO_LEVEL 110 +#define ZERO_SHIFT 0 +#define LOWER_LEVEL ZERO_LEVEL +#endif uint64_t x, y, range_item, w = WATERFALL_START, i = 0; int osd_x = 1, osd_y = 2, col = 0, max_bin = 32; @@ -254,6 +283,25 @@ void battery() } } +void drawDroneLegend() +{ + // Draw FPV array Names + for (const auto &entry : fpvArray) + { + int pixelStart = (entry.fstart - FREQ_BEGIN) / MHZ_PX; + int pixelEnd = (entry.fend - FREQ_BEGIN) / MHZ_PX; + int length = (pixelEnd - pixelStart); + // Serial.println("Pixel Start: " + String(pixelStart)); + // Serial.println("MHinPIX: " + String(MHZ_PX)); + int median = length / 2; + if (entry.fstart < FREQ_END) + { + st7789->drawFastHLine(pixelStart, entry.y, length, entry.color); + drawText(pixelStart, entry.y - 10, entry.drone, entry.color); + } + } +} + constexpr int lower_level = LOWER_LEVEL; constexpr int up_level = SPECTR_CHART_STAR_TOP; int rssiToPix(int rssi) @@ -270,13 +318,13 @@ int rssiToPix(int rssi) // if chart moved to the bottom if (lower_level > 130) { - int returnRssi = lower_level + abs(rssi) - up_level - 21; - Serial.println("RSSI: " + String(rssi)); - if (returnRssi >= lower_level - 2) + int returnRssi = abs(rssi - ZERO_SHIFT); + // Serial.println("RSSI: " + String(rssi)); + if (returnRssi >= lower_level) { return lower_level - 1; } - return abs(returnRssi); + return returnRssi; } else { @@ -322,6 +370,7 @@ int window_max_rssi = -999; int window_max_fr = -999; int max_scan_rssi[STEPS + 2]; int max_history_rssi[STEPS + 2]; +int historical_loops = 50, h = 0; long display_scan_start = 0; long display_scan_end = 0; long display_scan_i_end = 0; @@ -336,7 +385,7 @@ constexpr unsigned int STATUS_BAR_HEIGHT = 5; void loop() { - Serial.println("Loop"); + // Serial.println("Loop"); if (screen_update_loop_counter == 0) { fr_x[x1] = 0; @@ -357,7 +406,16 @@ void loop() // Clear old data with the cursor ... st7789->drawFastVLine(x1, lower_level, -lower_level + 11, ST7789_BLACK); + // Draw max history line + if (h == historical_loops) + { + st7789->drawLine(x1, rssiToPix(max_history_rssi[x1]), x1, lower_level, + ST7789_BLACK /*gray*/); + // clear history + max_history_rssi[x1] = -999; + } + st7789->drawLine(x1, rssiToPix(max_history_rssi[x1]), x1, lower_level, 12710 /*gray*/); // Fetch samples @@ -429,7 +487,8 @@ void loop() // Draw Update Cursor st7789->drawFastVLine(x1 + 1, lower_level, -lower_level + 11, ST7789_BLACK); st7789->drawFastVLine(x1 + 2, lower_level, -lower_level + 11, ST7789_BLACK); - // st7789->drawFastVLine(x1 + 3, lower_level, -lower_level + 11, ST7789_BLACK); + // st7789->drawFastVLine(x1 + 3, lower_level, -lower_level + 11, + // ST7789_BLACK); if (max_scan_rssi[x1] == -999) { @@ -457,7 +516,7 @@ void loop() } } // Writing pixel only if it is bigger than drone detection level - if (abs(max_scan_rssi[x1]) < drone_detection_level) + if (abs(max_scan_rssi[x1]) < abs(drone_detection_level)) { if (DISABLE_WATERFALL == 0) { @@ -474,7 +533,7 @@ void loop() // Draw legend for windows if (x1 % rssi_window_size == 0 || x1 == DISPLAY_WIDTH) { - if (abs(window_max_rssi) < drone_detection_level && window_max_rssi != 0 && + if (abs(window_max_rssi) < abs(drone_detection_level) && window_max_rssi != 0 && window_max_rssi != -999) { y2 = 15; @@ -519,9 +578,9 @@ void loop() button_pressed_counter = 0; if (button.pressed()) { - drone_detection_level++; - if (drone_detection_level > 107) - drone_detection_level = DEFAULT_DRONE_DETECTION_LEVEL - 20; + drone_detection_level--; + if (drone_detection_level < -107) + drone_detection_level = DEFAULT_DRONE_DETECTION_LEVEL + 20; while (button.pressedNow()) { delay(100); @@ -540,6 +599,14 @@ void loop() heltec_deep_sleep(); } + // Drone legend every 1/4 of the screen + if (x1 % (STEPS / 4) == 0) + { +#ifdef DRONE_LEGEND + drawDroneLegend(); +#endif + } + // Main N x-axis full loop end logic if (x1 >= STEPS) { @@ -551,17 +618,28 @@ void loop() #ifdef PRINT_DEBUG Serial.println("Screen End for Output: " + String(screen_update_loop_counter)); #endif + // Doing output only after full scan if (screen_update_loop_counter + 1 == SCANS_PER_DISPLAY) { +#ifdef DRONE_LEGEND + drawDroneLegend(); +#endif + + h++; + if (h == historical_loops - 1) + { + h = 0; + } + // Scan results to max Mhz and dB in window display_scan_end = millis(); st7789->fillRect(0, 0, DISPLAY_WIDTH, 11, ST7789_BLACK); drawText(0, 0, "T:" + String(display_scan_end - display_scan_start) + "/" + - String(rssi_single_end - rssi_single_start) + " L:-" + - String(drone_detection_level) + "dB", + String(rssi_single_end - rssi_single_start) + + " L:" + String(drone_detection_level) + "dB", ST7789_BLUE); /// battery(); From f9f423446f44ebc71011ffff85d02f6a4b98f9d3 Mon Sep 17 00:00:00 2001 From: KonradIT Date: Mon, 25 Nov 2024 22:31:52 +0100 Subject: [PATCH 80/80] Fix for lilygo T3S3 LR1121 TCXO voltage + calibration skip --- src/main.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 88721c5..76803a8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -403,8 +403,10 @@ void init_radio() { // initialize SX1262 FSK modem at the initial frequency both.println("Init radio"); -#if defined(USING_SX1280PA) || defined(USING_LR1121) +#if defined(USING_SX1280PA) state = radio.beginGFSK(CONF_FREQ_BEGIN); +#elif defined(USING_LR1121) + state = radio.beginGFSK(CONF_FREQ_BEGIN, 4.8F, 5.0F, 156.2F, 10, 16U, 1.7F); #else state = radio.beginFSK(CONF_FREQ_BEGIN); #endif @@ -1140,7 +1142,7 @@ void loop(void) state = radio.setFrequency(freq); #else state = radio.setFrequency(r.current_frequency, - false); // false = no calibration need here + true); // true = no calibration need here #endif int radio_error_count = 0; if (state != RADIOLIB_ERR_NONE)