diff --git a/README.md b/README.md index f04980e..6885ced 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,10 @@ 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 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) diff --git a/include/LoRaBoards.cpp b/lib/loraboards/LoRaBoards.cpp similarity index 99% rename from include/LoRaBoards.cpp rename to lib/loraboards/LoRaBoards.cpp index 04c93a6..0331489 100644 --- a/include/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/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 99% rename from include/utilities.h rename to lib/loraboards/utilities.h index 1d1b3ef..32a804b 100644 --- a/include/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.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..f5af2c6 100644 --- a/lib/scan/scan.h +++ b/lib/scan/scan.h @@ -27,20 +27,27 @@ 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 { - 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/platformio.ini b/platformio.ini index 5ff09ab..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 @@ -63,6 +66,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 @@ -86,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 @@ -95,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 @@ -128,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 a7704e9..dd891aa 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,21 +36,24 @@ // library internals. #define RADIOLIB_GODMODE (1) -#include "radioScan/radioScan.h" +#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 @@ -133,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 @@ -144,8 +151,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}; @@ -173,7 +178,6 @@ uint64_t median_frequency = FREQ_BEGIN + FREQ_END - FREQ_BEGIN / 2; // Array to store the scan results uint16_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]; @@ -357,7 +361,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); @@ -426,6 +429,12 @@ void init_radio() void setup(void) { +#ifdef LILYGO + setupBoards(); + delay(3000); + Serial.println("setup LiLyGO board is done"); +#endif + // LED brightness heltec_led(25); #ifdef OSD_ENABLED @@ -773,8 +782,6 @@ int max_rssi_x = 999; RadioScan r; -RadioScan r; - void loop(void) { UI_displayDecorate(0, 0, false); // some default values @@ -863,7 +870,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++) { @@ -888,9 +894,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 @@ -901,9 +905,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++; @@ -911,9 +915,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 +973,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 +995,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 +1047,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 +1057,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 deleted file mode 100644 index 73150fc..0000000 --- a/src/radioScan/radioScan.cpp +++ /dev/null @@ -1,69 +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; -} - -#endif diff --git a/src/radioScan/radioScan.h b/src/radioScan/radioScan.h deleted file mode 100644 index 1c8ca8e..0000000 --- a/src/radioScan/radioScan.h +++ /dev/null @@ -1,40 +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); -}; - -#endif 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)); } diff --git a/test/test_rssi.cpp b/test/test_rssi.cpp index 0215179..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) {} @@ -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(); }