diff --git a/include/BT_WIFI_scan.h b/include/BT_WIFI_scan.h index 8313111..d0b2be5 100644 --- a/include/BT_WIFI_scan.h +++ b/include/BT_WIFI_scan.h @@ -14,7 +14,7 @@ void setOSD() {} // TODO: Make Async Scan // https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/scan-examples.html#async-scan -void scanWiFi(void) +void scanWiFi(DFRobot_OSD osd) { osd.clear(); osd.displayString(14, 2, "Scanning WiFi.."); @@ -50,3 +50,61 @@ void scanWiFi(void) } osd.displayChar(14, 1, 0x10f); } + +//********************** +// BLE devices scan. +//*********************** +// TODO: Make Async Scan +// https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLETests/SampleAsyncScan.cpp +void scanBT(DFRobot_OSD osd) +{ + osd.clear(); + osd.displayString(14, 2, "Scanning BT..."); + cycleCnt++; + + BLEDevice::init(""); + BLEScan *pBLEScan = BLEDevice::getScan(); + // active scan uses more power, but get results faster + pBLEScan->setActiveScan(true); + // TODO: adjust interval and window + pBLEScan->setInterval(0x50); + pBLEScan->setWindow(0x30); + +#ifdef SERIAL_PRINT + Serial.printf("Start BLE scan for %d seconds...\n", BT_SCAN_TIME); +#endif + + BLEScanResults foundDevices = pBLEScan->start(BT_SCAN_TIME); + int count = foundDevices.getCount(); +#ifdef PRINT_DEBUG + Serial.printf("Found devices: %d \n", count); +#endif + present = false; + for (int i = 0; i < count; i++) + { + BLEAdvertisedDevice device = foundDevices.getDevice(i); + String currDevAddr = device.getAddress().toString().c_str(); + String deviceName; + if (device.haveName()) + { + deviceName = device.getName().c_str(); + } + else + { + deviceName = currDevAddr; + } + +#ifdef PRINT_DEBUG + Serial.printf("Found device #%s/%s with RSSI: %d \n", currDevAddr, deviceName, + device.getRSSI()); +#endif + osd.displayString(i + 1, 1, + "BT:" + deviceName + ":" + String(device.getRSSI()) + " \n"); + } +#ifdef PRINT_DEBUG + Serial.println("Scan done!"); + Serial.printf("Cycle counter: %d, Free heap: %d \n", cycleCnt, ESP.getFreeHeap()); +#endif + osd.displayChar(14, 1, 0x10f); + scanFinished = true; +} diff --git a/platformio.ini b/platformio.ini index e89e8e3..51f29ca 100644 --- a/platformio.ini +++ b/platformio.ini @@ -9,10 +9,12 @@ ; https://docs.platformio.org/page/projectconf.html [platformio] -;; for env:vision-master-e290 -;;src_dir = eink_src -;; for env:heltec_wifi_lora_32_V3 -;;src_dir = src ;;Default +; for env:vision-master-e190 +src_dir = tft_src +; for env:vision-master-e290 +; src_dir = eink_src +; for env:heltec_wifi_lora_32_V3 +; src_dir = src ;;Default [env:heltec_wifi_lora_32_V3] platform = espressif32 @@ -23,11 +25,12 @@ 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] platform = espressif32 -board = t3_s3_v1_x ;platformio doesn't have right boatd check local boards bolder +board = t3_s3_v1_x framework = arduino upload_speed = 921600 monitor_speed = 115200 @@ -35,8 +38,9 @@ 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 ; flag for our code + -DLILYGO -DT3_S3_V1_2_SX1262 -DT3_V1_3_SX1262 -DARDUINO_LILYGO_T3S3_SX1262 @@ -45,8 +49,7 @@ build_flags = -DARDUINO_ARCH_ESP32 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_LILYGO_T3_S3_V1_X - -DARDUINO_USB_MODE=1 - ;-UARDUINO_USB_CDC_ON_BOOT + -DARDUINO_USB_MODE=1 [env:heltec_wifi_lora_32_V3-test-signal-generator] platform = espressif32 @@ -58,6 +61,7 @@ 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] @@ -68,10 +72,10 @@ monitor_speed = 115200 monitor_filters = esp32_exception_decoder board_upload.use_1200bps_touch = true build_flags = - -DHELTEC_BOARD=37 - -DSLOW_CLK_TPYE=1 + -DHELTEC_BOARD=37 + -DSLOW_CLK_TPYE=1 -DARDUINO_USB_CDC_ON_BOOT=1 - -DLoRaWAN_DEBUG_LEVEL=0 + -DLoRaWAN_DEBUG_LEVEL=0 -DE290 -DESP32 -DVision_Master_E290 @@ -88,6 +92,40 @@ lib_deps = SPI Wire adafruit/Adafruit BusIO @ 1.9.6 - https://github.com/HelTecAutomation/Heltec_ESP32/ + 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 +board = heltec_wifi_lora_32_V3 +framework = arduino +monitor_speed = 115200 +monitor_filters = esp32_exception_decoder +board_upload.use_1200bps_touch = true +build_flags = + -DHELTEC_BOARD=38 + -DSLOW_CLK_TPYE=1 + -DARDUINO_USB_CDC_ON_BOOT=1 + -DLoRaWAN_DEBUG_LEVEL=0 + -DT190 + -DESP32 + -DVision_Master_T190 + -DRADIOLIB_EXCLUDE_CC1101=true + -DRADIOLIB_EXCLUDE_LR11x0=true + -DRADIOLIB_EXCLUDE_NRF24=true + -DRADIOLIB_EXCLUDE_RF69=true + -DRADIOLIB_EXCLUDE_RFM2X=true + -DRADIOLIB_EXCLUDE_SI443X=true + -DRADIOLIB_EXCLUDE_SX1231=true + -DRADIOLIB_EXCLUDE_SX127X=true + -DRADIOLIB_EXCLUDE_SX128X=true +lib_deps = + SPI + Wire + adafruit/Adafruit BusIO @ 1.9.6 + 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 diff --git a/src/main.cpp b/src/main.cpp index 349d856..ccc69f1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -143,17 +143,6 @@ 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}; -// project components -#ifdef WIFI_SCANNING_ENABLED -#include "WiFi.h" -#endif -#ifdef BT_SCANNING_ENABLED -#include -#include -#include -#include -#endif - #include "global_config.h" #include "ui.h" @@ -287,110 +276,22 @@ uint64_t loop_cnt = 0; #include "joyStick.h" -#ifdef WIFI_SCANNING_ENABLED -// WiFi Scan -// TODO: Make Async Scan -// https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/scan-examples.html#async-scan -void scanWiFi() -{ - osd.clear(); - osd.displayString(14, 2, "Scanning WiFi.."); - int n = WiFi.scanNetworks(); -#ifdef PRINT_DEBUG - Serial.println("scan done"); - if (n == 0) - { - Serial.println("no networks found"); - } -#endif - if (n > 0) - { -#ifdef PRINT_DEBUG - Serial.print(n); - Serial.println(" networks found"); -#endif - for (int i = 0; i < n; ++i) - { -// Print SSID and RSSI for each network found -#ifdef PRINT_DEBUG - Serial.print(i + 1); - Serial.print(": "); - Serial.print(WiFi.SSID(i)); - Serial.print(" ("); - Serial.print(WiFi.RSSI(i)); - Serial.print(")"); - Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " " : "*"); -#endif - osd.displayString(i + 1, 1, - "WF:" + String((WiFi.SSID(i) + ":" + WiFi.RSSI(i)))); - } - } - osd.displayChar(14, 1, 0x10f); -} +// project components +#if (defined(WIFI_SCANNING_ENABLED) || defined(BT_SCANNING_ENABLED)) && \ + defined(OSD_ENABLED) +#include "BT_WIFI_scan.h" #endif -#ifdef BT_SCANNING_ENABLED -//********************** -// BLE devices scan. -//*********************** -// TODO: Make Async Scan -// https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLETests/SampleAsyncScan.cpp -void scanBT() -{ - osd.clear(); - osd.displayString(14, 2, "Scanning BT..."); - cycleCnt++; - - BLEDevice::init(""); - BLEScan *pBLEScan = BLEDevice::getScan(); - // active scan uses more power, but get results faster - pBLEScan->setActiveScan(true); - // TODO: adjust interval and window - pBLEScan->setInterval(0x50); - pBLEScan->setWindow(0x30); - -#ifdef SERIAL_PRINT - Serial.printf("Start BLE scan for %d seconds...\n", BT_SCAN_TIME); +#if defined(WIFI_SCANNING_ENABLED) && defined(OSD_ENABLED) +scanWiFi(osd) #endif - BLEScanResults foundDevices = pBLEScan->start(BT_SCAN_TIME); - int count = foundDevices.getCount(); -#ifdef PRINT_DEBUG - Serial.printf("Found devices: %d \n", count); -#endif - present = false; - for (int i = 0; i < count; i++) - { - BLEAdvertisedDevice device = foundDevices.getDevice(i); - String currDevAddr = device.getAddress().toString().c_str(); - String deviceName; - if (device.haveName()) - { - deviceName = device.getName().c_str(); - } - else - { - deviceName = currDevAddr; - } - -#ifdef PRINT_DEBUG - Serial.printf("Found device #%s/%s with RSSI: %d \n", currDevAddr, deviceName, - device.getRSSI()); -#endif - osd.displayString(i + 1, 1, - "BT:" + deviceName + ":" + String(device.getRSSI()) + " \n"); - } -#ifdef PRINT_DEBUG - Serial.println("Scan done!"); - Serial.printf("Cycle counter: %d, Free heap: %d \n", cycleCnt, ESP.getFreeHeap()); -#endif - osd.displayChar(14, 1, 0x10f); - scanFinished = true; -} +#if defined(BT_SCANNING_ENABLED) && defined(OSD_ENABLED) + scanBT(osd) #endif #ifdef OSD_ENABLED -unsigned short selectFreqChar(int bin, int start_level = 0) + unsigned short selectFreqChar(int bin, int start_level = 0) { if (bin >= start_level) { @@ -515,6 +416,7 @@ void init_radio() } } +#ifdef METHOD_SPECTRAL // upload a patch to the SX1262 to enable spectral scan // NOTE: this patch is uploaded into volatile memory, // and must be re-uploaded on every power up @@ -524,6 +426,7 @@ void init_radio() // enable spectral scan and must be uploaded again on every power cycle. RADIOLIB_OR_HALT(radio.uploadPatch(sx126x_patch_scan, sizeof(sx126x_patch_scan))); // configure scan bandwidth and disable the data shaping +#endif both.println("Setting up radio"); RADIOLIB_OR_HALT(radio.setRxBandwidth(BANDWIDTH)); @@ -688,6 +591,78 @@ int binToRSSI(int bin) return 11 + (bin * 4); } +// returns tru if continue the code is false breake the loop +bool buttonPressHandler(float freq) +{ + // Detection level button short press + if (button.pressedFor(100) +#ifdef JOYSTICK_ENABLED + || joy_btn_click() +#endif + ) + { + button.update(); + button_pressed_counter = 0; + // if long press stop + while (button.pressedNow() +#ifdef JOYSTICK_ENABLED + || joy_btn_click() +#endif + ) + { + delay(10); + // Print Curent frequency + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(128 / 2, 0, String(freq)); + display.display(); + button_pressed_counter++; + if (button_pressed_counter > 150) + { + digitalWrite(LED, HIGH); + delay(150); + digitalWrite(LED, LOW); + } + } + if (button_pressed_counter > 150) + { + // Remove Curent 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; +} + void drone_sound_alarm(int drone_detection_level, int detection_count) { // If level is set to sensitive, @@ -716,48 +691,105 @@ void drone_sound_alarm(int drone_detection_level, int detection_count) } } +void joystickMoveCursor(int joy_x_pressed) +{ + + if (joy_x_pressed > 0) + { + cursor_x_position--; + display.drawString(cursor_x_position, 0, String((int)freq)); + display.drawLine(cursor_x_position, 1, cursor_x_position, 10); + display.display(); + delay(10); + } + else if (joy_x_pressed < 0) + { + cursor_x_position++; + display.drawString(cursor_x_position, 0, String((int)freq)); + display.drawLine(cursor_x_position, 1, cursor_x_position, 10); + display.display(); + delay(10); + } + if (cursor_x_position > DISPLAY_WIDTH || cursor_x_position < 0) + { + cursor_x_position = 0; + display.drawString(cursor_x_position, 0, String((int)freq)); + display.drawLine(cursor_x_position, 1, cursor_x_position, 10); + display.display(); + delay(10); + } +} + +bool is_new_x_pixel(int x) +{ + if (x % SCAN_RBW_FACTOR == 0) + return true; + else + return false; +} + +void check_ranges() +{ + if (RANGE_PER_PAGE == range) + { + single_page_scan = true; + } + else + { + single_page_scan = false; + } + + for (int range : SCAN_RANGES) + { + ranges_count++; + } + + if (ranges_count > 0) + { + iterations = ranges_count; + single_page_scan = false; + } +} // MAX Frequency RSSI value of the samples int max_rssi_x = 999; void loop(void) { + UI_displayDecorate(0, 0, false); // some default values - { - UI_displayDecorate(0, 0, false); // some default values + detection_count = 0; + drone_detected_frequency_start = 0; + ranges_count = 0; - detection_count = 0; - drone_detected_frequency_start = 0; - ranges_count = 0; + // reset scan time + scan_time = 0; - // reset scan time - scan_time = 0; - - // general purpose loop counter - loop_cnt++; + // general purpose loop counter + loop_cnt++; #ifdef PRINT_PROFILE_TIME - loop_start = millis(); + loop_start = millis(); #endif - if (!ANIMATED_RELOAD || !single_page_scan) - { - // clear the scan plot rectangle - UI_clearPlotter(); - } + if (!ANIMATED_RELOAD || !single_page_scan) + { + // clear the scan plot rectangle + UI_clearPlotter(); + } - // do the scan - range = FREQ_END - FREQ_BEGIN; - if (RANGE_PER_PAGE > range) - { - RANGE_PER_PAGE = range; - } + // do the scan + range = FREQ_END - FREQ_BEGIN; + if (RANGE_PER_PAGE > range) + { + RANGE_PER_PAGE = range; + } - fr_begin = FREQ_BEGIN; - fr_end = fr_begin; + fr_begin = FREQ_BEGIN; + fr_end = fr_begin; - // 50 is a single-screen range - // TODO: Make 50 a variable with the option to show the full range - iterations = range / RANGE_PER_PAGE; + // 50 is a single-screen range + // TODO: Make 50 a variable with the option to show the full range + iterations = range / RANGE_PER_PAGE; #if 0 // disabled code if (range % RANGE_PER_PAGE != 0) @@ -767,562 +799,447 @@ void loop(void) } #endif - if (RANGE_PER_PAGE == range) + check_ranges(); + + // Iterating by small ranges by 50 Mhz each pixel is 0.4 Mhz + for (range_item = 0; range_item < iterations; range_item++) + { + range = RANGE_PER_PAGE; + if (ranges_count == 0) { - single_page_scan = true; + fr_begin = (range_item == 0) ? fr_begin : fr_begin += range; + fr_end = fr_begin + RANGE_PER_PAGE; } else { - single_page_scan = false; + fr_begin = SCAN_RANGES[range_item] / 1000; + fr_end = SCAN_RANGES[range_item] % 1000; + range = fr_end - fr_begin; } - for (int range : SCAN_RANGES) - { - ranges_count++; - } - - if (ranges_count > 0) - { - iterations = ranges_count; - single_page_scan = false; - } - - // Iterating by small ranges by 50 Mhz each pixel is 0.4 Mhz - for (range_item = 0; range_item < iterations; range_item++) - { - 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; - } - else - { - fr_begin = SCAN_RANGES[range_item] / 1000; - fr_end = SCAN_RANGES[range_item] % 1000; - range = fr_end - fr_begin; - } - #ifdef DISABLED_CODE - if (!ANIMATED_RELOAD || !single_page_scan) - { - // clear the scan plot rectangle - UI_clearPlotter(); - } + if (!ANIMATED_RELOAD || !single_page_scan) + { + // clear the scan plot rectangle + UI_clearPlotter(); + } #endif - if (single_page_scan == false) + if (single_page_scan == false) + { + UI_displayDecorate(fr_begin, fr_end, true); + } + + drone_detected_frequency_start = 0; + display.setTextAlignment(TEXT_ALIGN_RIGHT); + + for (int i = 0; i < MAX_POWER_LEVELS; i++) + { + max_bins_array_value[i] = 0; + } + + // horizontal (x axis) Frequency loop + osd_x = 1, osd_y = 2, col = 0, max_bin = 0; + // x loop + for (x = 0; x < STEPS * SCAN_RBW_FACTOR; x++) + { + new_pixel = is_new_x_pixel(x); + if (ANIMATED_RELOAD && SCAN_RBW_FACTOR == 1) { - UI_displayDecorate(fr_begin, fr_end, true); + UI_drawCursor(x); } - - drone_detected_frequency_start = 0; - display.setTextAlignment(TEXT_ALIGN_RIGHT); - - for (int i = 0; i < MAX_POWER_LEVELS; i++) + if (new_pixel && ANIMATED_RELOAD && SCAN_RBW_FACTOR > 1) { - max_bins_array_value[i] = 0; + UI_drawCursor((int)(x / SCAN_RBW_FACTOR)); } - // horizontal (x axis) Frequency loop - osd_x = 1, osd_y = 2, col = 0, max_bin = 0; - // x loop - for (x = 0; x < STEPS * SCAN_RBW_FACTOR; x++) - { - start: - - if (x % SCAN_RBW_FACTOR == 0) - new_pixel = true; - else - new_pixel = false; - if (ANIMATED_RELOAD && SCAN_RBW_FACTOR == 1) - { - UI_drawCursor(x); - } - if (new_pixel && ANIMATED_RELOAD && SCAN_RBW_FACTOR > 1) - { - UI_drawCursor((int)(x / SCAN_RBW_FACTOR)); - } - #ifdef PRINT_PROFILE_TIME - scan_start_time = millis(); + scan_start_time = millis(); #endif - // Real display pixel x - axis. - // 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))); + // Real display pixel x - axis. + // 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; + freq = fr_begin + step; #ifdef PRINT_DEBUG - Serial.println("setFrequency:" + String(freq)); + Serial.println("setFrequency:" + String(freq)); #endif #ifdef LILYGO - state = - radio.setFrequency(freq, false); // false = no calibration need here + state = radio.setFrequency(freq, false); // false = no calibration need here #else - state = - radio.setFrequency(freq, false); // false = no calibration need here + state = radio.setFrequency(freq, false); // false = no calibration need here #endif - int radio_error_count = 0; - if (state != RADIOLIB_ERR_NONE) + 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.display(); + delay(2); + radio_error_count++; + if (radio_error_count > 10) + continue; + } + +#ifdef PRINT_DEBUG + Serial.printf("Step:%d Freq: %f\n", x, freq); +#endif + // SpectralScan Method +#ifdef METHOD_SPECTRAL + { + // start spectral scan third parameter is a sleep interval + radio.spectralScanStart(SAMPLES, 1); + // wait for spectral scan to finish + radio_error_count = 0; + while (radio.spectralScanGetStatus() != 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)); + Serial.println("radio.spectralScanGetStatus ERROR: "); + Serial.println(radio.spectralScanGetStatus()); + display.drawString(0, 64 - 20, + "E:specScSta:" + + String(radio.spectralScanGetStatus())); display.display(); - delay(2); + heltec_delay(ONE_MILLISEC * 2); radio_error_count++; if (radio_error_count > 10) continue; } -#ifdef PRINT_DEBUG - Serial.printf("Step:%d Freq: %f\n", x, freq); -#endif - // SpectralScan Method -#ifdef METHOD_SPECTRAL - { - // start spectral scan third parameter is a sleep interval - radio.spectralScanStart(SAMPLES, 1); - // wait for spectral scan to finish - radio_error_count = 0; - while (radio.spectralScanGetStatus() != RADIOLIB_ERR_NONE) - { - Serial.println("radio.spectralScanGetStatus ERROR: "); - Serial.println(radio.spectralScanGetStatus()); - display.drawString(0, 64 - 20, - "E:specScSta:" + - String(radio.spectralScanGetStatus())); - display.display(); - heltec_delay(ONE_MILLISEC * 2); - radio_error_count++; - if (radio_error_count > 10) - continue; - } - - // read the results Array to which the results will be saved - state = radio.spectralScanGetResult(result); - display.drawString(0, 64 - 10, "scanGetResult:" + String(state)); - } + // read the results Array to which the results will be saved + state = radio.spectralScanGetResult(result); + display.drawString(0, 64 - 10, "scanGetResult:" + String(state)); + } #endif #ifdef METHOD_RSSI - // Spectrum analyzer using getRSSI + // 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++) { -#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++) - { - result[i] = 0; - } - result_index = 0; - // N of samples - for (int r = 0; r < SAMPLES_RSSI; r++) - { - rssi = radio.getRSSI(false); - // 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; - } - -#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 value only rss is negative so smaller is bigger - if (result[result_index] > rssi) - { - result[result_index] = rssi; - } - } - else - { - Serial.print("Out-of-Range: result_index %d\n"); - } - } + result[i] = 0; } -#endif // SCAN_METHOD == METHOD_RSSI - - // 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, 30 - 8, String(FREQ_END)); - // delay(2); - -#ifdef OSD_ENABLED - osdProcess(); -#endif -#ifdef JOYSTICK_ENABLED - if (display_x == cursor_x_position) - { - display.setColor(BLACK); - display.fillRect(display_x - 20, 3, 36, 11); - display.setColor(WHITE); - } -#endif - detected = false; - detected_y[display_x] = false; - max_rssi_x = 999; - - for (y = 0; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) + result_index = 0; + // N of samples + for (int r = 0; r < SAMPLES_RSSI; r++) { + rssi = radio.getRSSI(false); + // 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; + } #ifdef PRINT_DEBUG - Serial.print(String(y) + ":"); - Serial.print(String(result[y]) + ","); + Serial.printf("RSSI: %d IDX: %d\n", rssi, result_index); #endif -#if !defined(FILTER_SPECTRUM_RESULTS) || FILTER_SPECTRUM_RESULTS == false - if (result[y] && result[y] != 0) + // avoid buffer overflow + if (result_index < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE) { - filtered_result[y] = 1; + // Saving max value only rss is negative so smaller is bigger + if (result[result_index] > rssi) + { + result[result_index] = rssi; + } } else { - filtered_result[y] = 0; + Serial.print("Out-of-Range: result_index %d\n"); } + } + } +#endif // SCAN_METHOD == METHOD_RSSI + + // 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, 30 - 8, String(FREQ_END)); + // delay(2); + +#ifdef OSD_ENABLED + osdProcess(); +#endif +#ifdef JOYSTICK_ENABLED + if (display_x == cursor_x_position) + { + display.setColor(BLACK); + display.fillRect(display_x - 20, 3, 36, 11); + display.setColor(WHITE); + } +#endif + detected = false; + detected_y[display_x] = false; + max_rssi_x = 999; + + for (y = 0; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) + { + +#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 // 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) + 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 - 3))) { - // 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 (((result[y + 1] != 0) && (result[y + 2] != 0)) || + (result[y - 1] != 0)) + { + filtered_result[y] = 1; + } + else { - if (((result[y + 1] != 0) && (result[y + 2] != 0)) || - (result[y - 1] != 0)) - { - filtered_result[y] = 1; - } - else - { #ifdef PRINT_DEBUG - Serial.print("Filtered:" + String(x) + ":" + String(y) + - ","); + Serial.print("Filtered:" + String(x) + ":" + String(y) + ","); #endif - } } - } // not filtering if samples == 1 - else if (result[y] > 0 && samples == 1) - { - filtered_result[y] = 1; } + } // not filtering if samples == 1 + else if (result[y] > 0 && samples == 1) + { + filtered_result[y] = 1; + } #endif - // check if we should alarm about a drone presence - if ((filtered_result[y] == 1) // we have some data and - && (y <= drone_detection_level) && - detected_y[display_x] == false) // detection threshold match - { - // Set LED to ON (filtered in UI component) - UI_setLedFlag(true); + // 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 + { + // 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 - if (drone_detected_frequency_start == 0) - { - // mark freq start - drone_detected_frequency_start = freq; - } - - // mark freq end ... will shift right to last detected range - drone_detected_frequency_end = freq; - if (SOUND_ON == true) - { - drone_sound_alarm(drone_detection_level, detection_count); - } - - if (DRAW_DETECTION_TICKS == true) - { - // draw vertical line on top of display for "drone detected" - // frequencies - if (!detected_y[display_x]) - { - display.drawLine(display_x, 1, display_x, 4); - detected_y[display_x] = true; - } - } - } -#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) { - // 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); + // Drone detection true for waterfall + if (!waterfall[display_x]) + { + waterfall[display_x] = true; + display.setColor(WHITE); + display.setPixel(display_x, w); + } } #endif - // next 2 If's ... adds !!!! 10ms of runtime ......tfk ??? + if (drone_detected_frequency_start == 0) + { + // mark freq start + drone_detected_frequency_start = freq; + } + + // mark freq end ... will shift right to last detected range + drone_detected_frequency_end = freq; + if (SOUND_ON == true) + { + drone_sound_alarm(drone_detection_level, detection_count); + } + + if (DRAW_DETECTION_TICKS == true) + { + // draw vertical line on top of display for "drone detected" + // frequencies + if (!detected_y[display_x]) + { + display.drawLine(display_x, 1, display_x, 4); + detected_y[display_x] = true; + } + } + } +#if (WATERFALL_ENABLED == true) + if ((filtered_result[y] == 1) && (y <= drone_detection_level) && + (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 + // next 2 If's ... adds !!!! 10ms of runtime ......tfk ??? + 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_rssi_x = y; + } + // Set signal level pixel + if (y < MAX_POWER_LEVELS - START_LOW) + { + display.setPixel(display_x, y + START_LOW); + } + if (!detected) + { + detected = true; + } + } + + // ------------------------------------------------------------- + // Draw "Detection Level line" every 2 pixel + // ------------------------------------------------------------- + if ((y == drone_detection_level) && (display_x % 2 == 0)) + { + display.setColor(WHITE); 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_rssi_x = y; - } - // Set signal level pixel - if (y < MAX_POWER_LEVELS - START_LOW) - { - display.setPixel(display_x, y + START_LOW); - } - if (!detected) - { - detected = true; - } + display.setColor(INVERSE); } + display.setPixel(display_x, y + START_LOW); + // display.setPixel(display_x, y + START_LOW - 1); // 2 px wide - // ------------------------------------------------------------- - // Draw "Detection Level line" every 2 pixel - // ------------------------------------------------------------- - if ((y == drone_detection_level) && (display_x % 2 == 0)) - { - 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.setColor(WHITE); } + } #ifdef JOYSTICK_ENABLED - if (display_x == cursor_x_position) - { - - display.drawString(display_x - 1, 0, String((int)freq)); - 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)); - } + // Draw joystick cursor and Frequency RSSI value + if (display_x == cursor_x_position) + { + display.drawString(display_x - 1, 0, String((int)freq)); + 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)); + } #endif #ifdef PRINT_PROFILE_TIME - scan_time += (millis() - scan_start_time); + scan_time += (millis() - scan_start_time); #endif - // count detected - if (detected) - { - detection_count++; - } + // count detected + if (detected) + { + detection_count++; + } #ifdef PRINT_DEBUG - Serial.println("....\n"); + Serial.println("....\n"); #endif - if (first_run || ANIMATED_RELOAD) - { - display.display(); - } + if (first_run || ANIMATED_RELOAD) + { + display.display(); + } +// LiLyGo doesn't have button ;( +// ToDO: Check if we use BOOT button #ifndef LILYGO - // Detection level button short press - if (button.pressedFor(100) -#ifdef JOYSTICK_ENABLED - || joy_btn_click() -#endif - ) - { - button.update(); - button_pressed_counter = 0; - // if long press stop - while (button.pressedNow() -#ifdef JOYSTICK_ENABLED - || joy_btn_click() -#endif - ) - { - delay(10); - // Print Curent frequency - display.setTextAlignment(TEXT_ALIGN_CENTER); - display.drawString(128 / 2, 0, String(freq)); - display.display(); - button_pressed_counter++; - if (button_pressed_counter > 150) - { - digitalWrite(LED, HIGH); - delay(150); - digitalWrite(LED, LOW); - } - } - if (button_pressed_counter > 150) - { - // Remove Curent Frequency Text - display.setTextAlignment(TEXT_ALIGN_CENTER); - display.setColor(BLACK); - display.drawString(128 / 2, 0, String(freq)); - display.setColor(WHITE); - display.display(); - break; - } - if (button_pressed_counter > 50 && button_pressed_counter < 150) - { - 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(); - } - break; - } - 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; - } - } - + if (buttonPressHandler() == false) + break; #endif // END LILYGO - // wait a little bit before the next scan, - // otherwise the SX1262 hangs - // Add more logic before instead of long delay... - int delay_cnt = 1; + // wait a little bit before the next scan, + // otherwise the SX1262 hangs + // Add more logic before instead of long delay... + int delay_cnt = 1; #ifdef METHOD_SPECTRAL - if (false && state != RADIOLIB_ERR_NONE) + if (false && state != RADIOLIB_ERR_NONE) + { + if (delay_cnt == 1) { - if (delay_cnt == 1) - { - Serial.println("E:getResult"); - display.drawString(0, 64 - 10, "E:getResult"); - // trying to use display as delay.. - display.display(); - } - else - { - heltec_delay(ONE_MILLISEC * 2); - Serial.println("E:getStatus"); - display.drawString(0, 64 - 10, "E:getResult"); - // trying to use display as delay.. - display.display(); - } - - Serial.println("spectralScanGetStatus ERROR(" + - String(radio.spectralScanGetStatus()) + - ") hard delay(2) - " + String(delay_cnt)); - // if error than speed is slow animating chart - ANIMATED_RELOAD = true; - delay(50); - delay_cnt++; + Serial.println("E:getResult"); + display.drawString(0, 64 - 10, "E:getResult"); + // trying to use display as delay.. + display.display(); } + else + { + heltec_delay(ONE_MILLISEC * 2); + Serial.println("E:getStatus"); + display.drawString(0, 64 - 10, "E:getResult"); + // trying to use display as delay.. + display.display(); + } + + Serial.println("spectralScanGetStatus ERROR(" + + String(radio.spectralScanGetStatus()) + + ") hard delay(2) - " + String(delay_cnt)); + // if error than speed is slow animating chart + ANIMATED_RELOAD = true; + delay(50); + delay_cnt++; + } #endif - // TODO: move osd logic here as a daley ;) - // Loop is needed if heltec_delay(1) not used - heltec_loop(); + // TODO: move osd logic here as a daley ;) + // Loop is needed if heltec_delay(1) not used + heltec_loop(); // Move joystick #ifdef JOYSTICK_ENABLED - int joy_x_pressed = get_joy_x(true); - if (joy_x_pressed > 0) - { - cursor_x_position--; - display.drawString(cursor_x_position, 0, String((int)freq)); - display.drawLine(cursor_x_position, 1, cursor_x_position, 10); - display.display(); - delay(10); - } - else if (joy_x_pressed < 0) - { - cursor_x_position++; - display.drawString(cursor_x_position, 0, String((int)freq)); - display.drawLine(cursor_x_position, 1, cursor_x_position, 10); - display.display(); - delay(10); - } - if (cursor_x_position > DISPLAY_WIDTH || cursor_x_position < 0) - { - cursor_x_position = 0; - display.drawString(cursor_x_position, 0, String((int)freq)); - display.drawLine(cursor_x_position, 1, cursor_x_position, 10); - display.display(); - delay(10); - } -#endif - } - w++; - if (w > ROW_STATUS_TEXT + 1) - { - 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 - // Render display data here - display.display(); -#ifdef OSD_ENABLED - // Sometimes OSD prints entire screen with the digits. - // We need clean the screen to fix it. - // We can do it every time but to optimise doing every N times - if (global_counter != 0 && global_counter % 10 == 0) - { -#if !defined(BT_SCANNING_ENABLED) && !defined(WIFI_SCANNING_ENABLED) - osd.clear(); - osd.displayChar(14, 1, 0x10f); - global_counter = 0; -#endif - } - ANIMATED_RELOAD = false; - global_counter++; + int joy_x_pressed = get_joy_x(true); + joystickMoveCursor(joy_x_pressed); #endif } + w++; + if (w > ROW_STATUS_TEXT + 1) + { + 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 + // Render display data here + display.display(); +#ifdef OSD_ENABLED + // Sometimes OSD prints entire screen with the digits. + // We need clean the screen to fix it. + // We can do it every time but to optimise doing every N times + if (global_counter != 0 && global_counter % 10 == 0) + { +#if !defined(BT_SCANNING_ENABLED) && !defined(WIFI_SCANNING_ENABLED) + osd.clear(); + osd.displayChar(14, 1, 0x10f); + global_counter = 0; +#endif + } + ANIMATED_RELOAD = false; + global_counter++; +#endif } #ifdef PRINT_DEBUG // Serial.println("----"); diff --git a/tft_src/images.h b/tft_src/images.h new file mode 100644 index 0000000..5021a8f --- /dev/null +++ b/tft_src/images.h @@ -0,0 +1,109 @@ +constexpr unsigned char epd_bitmap_ucog[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x10, 0x84, 0xC3, 0x81, 0x03, 0x00, 0x60, 0x00, 0xCC, 0x07, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x30, 0xE6, 0xF3, 0xE3, 0x07, 0x00, 0x60, 0x00, 0x0C, 0x3E, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x30, 0x66, 0x10, 0x36, 0x00, 0x00, 0x60, 0x00, 0x06, 0xF8, + 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x30, 0x26, 0x18, 0x36, 0x00, 0x00, 0x60, 0x00, + 0x07, 0xE0, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x30, 0x26, 0x18, 0x36, 0x07, 0x00, + 0x60, 0x00, 0x03, 0x00, 0x1F, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x30, 0x26, 0x18, 0x36, + 0x06, 0x00, 0x60, 0x80, 0x01, 0x00, 0x7C, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x30, 0x66, + 0x38, 0x76, 0x06, 0x0C, 0x60, 0xC0, 0x01, 0x00, 0xF0, 0x01, 0x78, 0x00, 0x00, 0x00, + 0xE0, 0xE3, 0xF3, 0xE3, 0x07, 0x0E, 0x60, 0xC0, 0x03, 0x00, 0x80, 0x0F, 0x1C, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x60, 0xE0, 0x07, 0x00, 0x00, 0x3E, + 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x60, 0x60, 0x0E, 0x00, + 0x00, 0xF8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x60, 0x70, + 0x1C, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, + 0x60, 0x30, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x60, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x00, 0xF0, 0x18, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xFC, 0x0D, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xFE, 0x0F, 0xC0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x67, 0x0F, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x61, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, + 0x60, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x60, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7E, 0xE0, 0xE0, 0x01, 0x00, 0x00, 0x00, 0xF0, 0x7F, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x67, 0xE0, 0x83, 0x03, 0x00, 0x00, 0xE0, 0xFF, 0xE1, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x63, 0x60, 0x07, 0x07, 0x00, 0xC0, 0xFF, + 0x03, 0x80, 0x03, 0x00, 0x00, 0x08, 0x00, 0x02, 0xF0, 0x61, 0x60, 0x1C, 0x1F, 0x80, + 0xFF, 0x07, 0x00, 0x00, 0x0E, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xF8, + 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x18, 0x00, 0x02, 0xF0, 0x7F, + 0x60, 0xDC, 0x1F, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x02, + 0xD0, 0x61, 0x60, 0x07, 0x0F, 0x00, 0xC0, 0xFF, 0x03, 0x80, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x90, 0x67, 0xE0, 0x83, 0x03, 0x00, 0x00, 0xF0, 0xFF, 0xC1, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xE0, 0xC1, 0x01, 0x00, 0x00, 0x00, 0xF8, 0x7F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x60, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x61, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x67, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xEE, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0xFC, 0x0D, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0xF0, 0x18, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x60, 0x18, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x60, 0x30, 0x38, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x60, 0x70, + 0x3C, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, + 0x60, 0x60, 0x1E, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x07, 0x60, 0xE0, 0x0F, 0x00, 0x00, 0x3E, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x03, 0x00, 0x0F, 0x60, 0xC0, 0x03, 0x00, 0x80, 0x0F, 0x1C, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x01, 0x07, 0x00, 0x0C, 0x60, 0xC0, 0x01, 0x00, 0xF0, 0x01, 0x38, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x81, 0x05, 0x00, 0x08, 0x60, 0xC0, 0x01, 0x00, 0x7C, 0x00, + 0xE0, 0x00, 0x00, 0x00, 0x00, 0x80, 0x83, 0x0C, 0x00, 0x00, 0x60, 0x00, 0x03, 0x00, + 0x1F, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x0C, 0x00, 0x00, 0x60, 0x00, + 0x07, 0xE0, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x0F, 0x00, 0x00, + 0x60, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x1F, + 0x00, 0x00, 0x60, 0x00, 0x0E, 0x3E, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x80, + 0x67, 0x18, 0x00, 0x00, 0x60, 0x00, 0xCC, 0x07, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x00, 0xE0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0xFC, 0x01, 0x00, 0x00, + 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, +}; + +const uint8_t batteryfull[] = { + 0x00, 0x00, 0xE0, 0x00, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, + 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0x00, 0x00, +}; +const uint8_t battery6[] = { + 0x00, 0x00, 0xE0, 0x00, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, + 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0x00, 0x00, +}; +const uint8_t battery5[] = { + 0x00, 0x00, 0xE0, 0x00, 0xF8, 0x03, 0xF8, 0x03, 0x18, 0x03, 0xF8, 0x03, 0xF8, + 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0x00, 0x00, +}; +const uint8_t battery4[] = { + 0x00, 0x00, 0xE0, 0x00, 0xF8, 0x03, 0xF8, 0x03, 0x18, 0x03, 0x18, 0x03, 0xF8, + 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0x00, 0x00, +}; +const uint8_t battery3[] = { + 0x00, 0x00, 0xE0, 0x00, 0xF8, 0x03, 0xF8, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, + 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0x00, 0x00, +}; +const uint8_t battery2[] = { + 0x00, 0x00, 0xE0, 0x00, 0xF8, 0x03, 0xF8, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, + 0x03, 0x18, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0x00, 0x00, +}; +const uint8_t battery1[] = { + 0x00, 0x00, 0xE0, 0x00, 0xF8, 0x03, 0xF8, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, + 0x03, 0x18, 0x03, 0x18, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0x00, 0x00, +}; +const uint8_t battery0[] = { + 0x00, 0x00, 0xE0, 0x00, 0xF8, 0x03, 0xF8, 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, + 0x03, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0xF8, 0x03, 0xF8, 0x03, 0x00, 0x00, +}; diff --git a/tft_src/main.cpp b/tft_src/main.cpp new file mode 100644 index 0000000..4411a0e --- /dev/null +++ b/tft_src/main.cpp @@ -0,0 +1,572 @@ +/* Heltec Automation Ink screen example + * NOTE!!!: to upload we new code you need to press button BOOT and RESET or you will + * have serial error. After upload you need reset device... + * + * Description: + * 1.Inherited from ssd1306 for drawing points, lines, and functions + * + * All code e link examples you cand find here: + * */ +// Variables required to boot Heltec E290 defined at platformio.ini +// #define HELTEC_BOARD 37 +// #define SLOW_CLK_TPYE 1 +// #define ARDUINO_USB_CDC_ON_BOOT 1 +// #define LoRaWAN_DEBUG_LEVEL 0 +#include "HT_ST7789spi.h" +#include "global_config.h" +#include "images.h" +#include "ui.h" +#include +#include + +#define st7789_CS_Pin 39 +#define st7789_REST_Pin 40 +#define st7789_DC_Pin 47 +#define st7789_SCLK_Pin 38 +#define st7789_MOSI_Pin 48 +#define st7789_LED_K_Pin 17 +#define st7789_VTFT_CTRL_Pin 7 + +// lcd object pointer, it's a 240x135 lcd display, Adafruit dependcy +static HT_ST7789 *st7789 = NULL; +static SPIClass *gspi_lcd = NULL; + +char buffer[256]; + +// Disabling default Heltec lib OLED display +#define HELTEC_NO_DISPLAY +#define DISPLAY_WIDTH 320 +#define DISPLAY_HEIGHT 170 +// Without this line Lora Radio doesn't work with heltec lib +#define ARDUINO_heltec_wifi_32_lora_V3 +#include "heltec_unofficial.h" + +// We are not using spectral scan here only RSSI method +// #include "modules/SX126x/patches/SX126x_patch_scan.h" +// #define PRINT_DEBUG + +// TODO: move variables to common file +// <--- Spectrum display Variables START +#define SCAN_METHOD +#define METHOD_SPECTRAL +// numbers of the spectrum screen lines = width of screen +#define STEPS DISPLAY_WIDTH // 128 +// Number of samples for each scan. Fewer samples = better temporal resolution. +#define MAX_POWER_LEVELS 33 +// multiplies STEPS * N to increase scan resolution. +#define SCAN_RBW_FACTOR 1 // 2 +// Print spectrum values pixels at once or by line +bool ANIMATED_RELOAD = false; +// Remove reading without neighbors +#define FILTER_SPECTRUM_RESULTS true +#define FILTER_SAMPLES_MIN +constexpr bool DRAW_DETECTION_TICKS = true; +// Number of samples for each frequency scan. Fewer samples = better temporal resolution. +// if more than 100 it can freez +#define SAMPLES 35 //(scan time = 1294) +// number of samples for RSSI method +#define SAMPLES_RSSI 5 // 21 // + +#define FREQ_BEGIN 650 + +#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 fr_begin = FREQ_BEGIN; +uint64_t fr_end = FREQ_BEGIN; + +// Feature to scan diapasones. Other frequency settings will be ignored. +// int SCAN_RANGES[] = {850890, 920950}; +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 + +// Override or e-ink +uint64_t RANGE_PER_PAGE = FREQ_BEGIN + DISPLAY_WIDTH; + +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; + +// #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]; + +// Waterfall array +bool waterfall[STEPS], detected_y[STEPS]; // 20 - ??? steps of the waterfall + +// global variable + +// Used as a Led Light and Buzzer/count trigger +bool first_run, new_pixel, detected_x = false; +// drone detection flag +bool detected = false; +uint64_t drone_detection_level = 90; +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 + +#ifdef PRINT_PROFILE_TIME +uint64_t loop_start = 0; +uint64_t loop_time = 0; +uint64_t scan_time = 0; +uint64_t scan_start_time = 0; +#endif + +#define WATERFALL_START 115 +#define WATERFALL_END DISPLAY_HEIGHT - 10 - 2 + +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; + +#ifdef METHOD_SPECTRAL +constexpr int samples = SAMPLES; +#endif +#ifdef METHOD_RSSI +constexpr int samples = SAMPLES_RSSI; +#endif + +uint8_t result_index = 0; +uint8_t button_pressed_counter = 0; +uint64_t loop_cnt = 0; +// <--- Spectrum display Variables END + +#define DIRECTION ANGLE_0_DEGREE + +// TODO: move to common file +void init_radio() +{ + // initialize SX1262 FSK modem at the initial frequency + Serial.println("Init radio"); + RADIOLIB_OR_HALT(radio.beginFSK(FREQ_BEGIN)); + + // upload a patch to the SX1262 to enable spectral scan + // NOTE: this patch is uploaded into volatile memory, + // and must be re-uploaded on every power up + Serial.println("Upload SX1262 patch"); + + // Upload binary patch into the SX126x device RAM. Patch is needed to e.g., + // enable spectral scan and must be uploaded again on every power cycle. + // RADIOLIB_OR_HALT(radio.uploadPatch(sx126x_patch_scan, sizeof(sx126x_patch_scan))); + // configure scan bandwidth and disable the data shaping + + Serial.println("Setting up radio"); + RADIOLIB_OR_HALT(radio.setRxBandwidth(BANDWIDTH)); + + // and disable the data shaping + RADIOLIB_OR_HALT(radio.setDataShaping(RADIOLIB_SHAPING_NONE)); + Serial.println("Starting scanning..."); + + // calibrate only once ,,, at startup + // TODO: check documentation (9.2.1) if we must calibrate in certain ranges + radio.setFrequency(FREQ_BEGIN, true); + delay(50); +} + +#define HEIGHT 4 + +void drawText(uint16_t x, uint16_t y, String text, uint16_t color = ST7789_WHITE) +{ + st7789->setCursor(x, y); + st7789->setTextColor(color); + st7789->setTextWrap(true); + st7789->print(text.c_str()); +} + +void drawSetupText() +{ + // create more fonts at http://oleddisplay.squix.ch/ + /** *display.setTextAlignment(TEXT_ALIGN_LEFT); + display.setFont(ArialMT_Plain_10); + display.drawString(0, 0, "Spectrum Analyzer Lora SA"); + display.setFont(ArialMT_Plain_16); + display.drawString(0, 10, "SX 1262"); + display.setFont(ArialMT_Plain_24); + display.drawString(0, 26, "TFT display"); + display.drawString(0, 56, "RF Spectrum TFT-ray"); + display.setFont(ArialMT_Plain_24); + **/ +} + +#define battery_w 13 +#define battery_h 13 +#define BATTERY_PIN 7 + +void battery() +{ + analogReadResolution(12); + int battery_levl = analogRead(BATTERY_PIN) / 238.7; // battary/4096*3.3* coefficient + float battery_one = 0.4125; +#ifdef PRINT_DEBUG + Serial.printf("ADC analog value = %.2f\n", battery_levl); +#endif + // display.drawString(257, 0, String(heltec_battery_percent(battery_levl)) + "%"); + // TODO: battery voltage doesn't work + if (battery_levl < battery_one) + { + // display.drawXbm(275, 0, battery_w, battery_h, battery0); + } + else if (battery_levl < 2 * battery_one && battery_levl > battery_one) + { + // display.drawXbm(285, 0, battery_w, battery_h, battery1); + } + else if (battery_levl < 3 * battery_one && battery_levl > 2 * battery_one) + { + // display.drawXbm(285, 0, battery_w, battery_h, battery2); + } + else if (battery_levl < 4 * battery_one && battery_levl > 3 * battery_one) + { + // display.drawXbm(285, 0, battery_w, battery_h, battery3); + } + else if (battery_levl < 5 * battery_one && battery_levl > 4 * battery_one) + { + // display.drawXbm(285, 0, battery_w, battery_h, battery4); + } + else if (battery_levl < 6 * battery_one && battery_levl > 5 * battery_one) + { + // display.drawXbm(285, 0, battery_w, battery_h, battery5); + } + else if (battery_levl < 7 * battery_one && battery_levl > 6 * battery_one) + { + // display.drawXbm(285, 0, battery_w, battery_h, battery6); + } + else if (battery_levl < 7 * battery_one && battery_levl > 6 * battery_one) + { + // display.drawXbm(285, 0, battery_w, battery_h, batteryfull); + } +} + +void VextON(void) +{ + pinMode(18, OUTPUT); + digitalWrite(18, HIGH); +} +void VextOFF(void) // Vext default OFF +{ + pinMode(18, OUTPUT); + digitalWrite(18, LOW); +} + +constexpr int lower_level = 108; +constexpr int up_level = 40; +int rssiToPix(int rssi) +{ + // Bigger is lower signal + if (abs(rssi) >= lower_level) + { + return lower_level - 1; + } + if (abs(rssi) <= up_level) + { + return up_level; + } + return abs(rssi); +} + +// +int rssiToColor(int rssi, bool waterfall = false) +{ + if (rssi < 80) + return 63488; + if (rssi < 85) + return 0xfa08; + if (rssi < 90) + return 0xcfe0; + if (rssi < 95) + return 0x01ff; + if (rssi < 100) + return 0x8d5f; + if (waterfall) + return ST7789_BLACK; + return ST7789_WHITE; +} + +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; +// TODO: make end_freq +// Measure RSS every step +constexpr float rssi_mhz_step = 0.33; +int rssi2 = 0; +int x1 = 0, y2 = 0; +unsigned int screen_update_loop_counter = 0; +unsigned int x_screen_update = 0; +int rssi_printed = 0; +constexpr int rssi_window_size = 30; +int max_i_rssi = -999; +int window_max_rssi = -999; +int window_max_fr = -999; +int max_scan_rssi[STEPS + 2]; +int max_history_rssi[STEPS + 2]; +long display_scan_start = 0; +long display_scan_end = 0; +long display_scan_i_end = 0; +int scan_iterations = 0; +// will be changed to false after first run +bool clear_rssi_history = true; + +constexpr unsigned int SCANS_PER_DISPLAY = 1; +constexpr unsigned int STATUS_BAR_HEIGHT = 5; + +void loop() +{ + if (screen_update_loop_counter == 0) + { + // Zero arrays + for (int i = 0; i < STEPS; i++) + { + fr_x[x1] = 0; + max_scan_rssi[i] = -999; + if (clear_rssi_history == true) + max_history_rssi[i] = -999; + } + clear_rssi_history = false; + display_scan_start = millis(); + } + fr_x[x1] = fr; + // Draw max history line + st7789->drawLine(x1, rssiToPix(max_history_rssi[x1]), x1, lower_level, + 12710 /*gray*/); + int u = 0; + // Fetch samples + for (int i = 0; i < SAMPLES_RSSI; i++) + { + + radio.setFrequency((float)fr + (float)(rssi_mhz_step * u), + false); // false = no calibration need here + u++; + if (rssi_mhz_step * u >= mhz_step) + { + u = 0; + } + + rssi2 = radio.getRSSI(false); + scan_iterations++; + if (rssi2 > lower_level) + continue; +#ifdef PRINT_DEBUG + Serial.println(String(fr) + ":" + String(rssi2)); +#endif + // display.drawString(x1, (int)y2, String(fr) + ":" + String(rssi2)); + // display.setPixel(x1, rssiToPix(rssi2)); + 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))); + // Draw Update Cursor + st7789->drawFastVLine(x1 + 1, lower_level, -lower_level + 25, ST7789_BLACK); + st7789->drawFastVLine(x1 + 2, lower_level, -lower_level + 25, ST7789_BLACK); + st7789->drawFastVLine(x1 + 3, lower_level, -lower_level + 25, ST7789_BLACK); + + if (max_scan_rssi[x1] < rssi2) + { + max_scan_rssi[x1] = rssi2; + if (max_history_rssi[x1] < max_scan_rssi[x1]) + { + max_history_rssi[x1] = rssi2; + } + } + } + // Waterfall Pixel + st7789->drawPixel(x1, w, rssiToColor(abs(max_scan_rssi[x1]), true)); + + // Waterfall cursor + st7789->drawFastHLine(0, w + 1, DISPLAY_WIDTH, ST7789_BLACK); + st7789->drawFastHLine(0, w + 2, DISPLAY_WIDTH, ST7789_BLACK); + + // drone detection level line + if (x1 % 2 == 0) + { + // display.setPixel(x1, rssiToPix(drone_detection_level)); + st7789->drawPixel(x1, rssiToPix(drone_detection_level) + 3, ST7789_GREEN); + } + fr += mhz_step; + x1++; + if (display_scan_i_end == 0) + { + display_scan_i_end = millis(); + } + // Main N x-axis full loop end logic + if (x1 >= STEPS) + { + w++; + if (w > WATERFALL_END) + w = WATERFALL_START; + if (screen_update_loop_counter + 1 == SCANS_PER_DISPLAY) + { + // max Mhz and dB in window + for (int i = 0; i < STEPS; i++) + { + // Max dB in window + if (window_max_rssi < max_scan_rssi[i]) + { + // Max Mhz in window + window_max_fr = fr_x[i]; + window_max_rssi = max_scan_rssi[i]; + } + if (i % rssi_window_size == 0 || (i % (DISPLAY_WIDTH - 1)) == 0) + { + + if (abs(window_max_rssi) < drone_detection_level) + { + y2 = 10; + + /** display.setFont(ArialMT_Plain_10); + display.drawStringMaxWidth(i - rssi_window_size, y2, + rssi_window_size, + String(window_max_rssi) + "dB"); + display.drawString(i - rssi_window_size + 5, y2 + 10, + String(window_max_fr)); + */ + drawText(i - rssi_window_size, y2, String(window_max_rssi) + "dB", + rssiToColor(window_max_rssi)); + drawText(i - rssi_window_size + 5, y2, + String(String(window_max_fr)) + "dB", + rssiToColor(window_max_rssi)); + // Vertical lines between windows + for (int l = y2; l < 100; l += 4) + { + st7789->drawPixel(i, l, ST7789_YELLOW); + // display.setPixel(i, l); + } + } + window_max_rssi = -999; + } + } + + display_scan_end = millis(); + + // display.setFont(ArialMT_Plain_10); + drawText(0, 0, + "T:" + String(display_scan_end - display_scan_start) + "/" + + String(display_scan_i_end - display_scan_start) + " L:-" + + String(drone_detection_level) + "dB", + ST7789_BLUE); + + /// battery(); + // iteration full scan / samples pixel step / numbers of scan per display + drawText(DISPLAY_WIDTH - ((DISPLAY_WIDTH / 6) * 2) + 20, 0, + "i:" + String(scan_iterations) + "/" + String(SAMPLES_RSSI) + "/" + + String(SCANS_PER_DISPLAY), + ST7789_GREEN); + // Scan resolution - r + // Mhz in pixel - s + drawText(DISPLAY_WIDTH - ((DISPLAY_WIDTH / 6) * 2) - 55, 0, + "r:" + String(rssi_mhz_step) + " s:" + String(mhz_step), ST7789_RED); + + // Draw a line horizontally + st7789->drawFastHLine(0, lower_level + 1, DISPLAY_WIDTH, ST7789_WHITE); + // Generate Ticks + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + if (x % (DISPLAY_WIDTH / 2) == 0 && x > 5) + { + st7789->drawFastVLine(x, lower_level + 1, 11, ST7789_WHITE); + // central tick width + st7789->drawFastVLine(x - 1, lower_level + 1, 8, ST7789_WHITE); + st7789->drawFastVLine(x + 1, lower_level + 1, 8, ST7789_WHITE); + } + if (x % 10 == 0 || x == 0) + st7789->drawFastVLine(x, lower_level + 1, 6, ST7789_WHITE); + if (x % 5 == 0) + st7789->drawFastVLine(x, lower_level + 1, 3, ST7789_WHITE); + } + // st7789.setFont(ArialMT_Plain_10); + + // Begin Mhz + drawText(1, DISPLAY_HEIGHT - 10, String(FREQ_BEGIN)); + // Median -1/2 Mhz + drawText((DISPLAY_WIDTH / 4) - 10, DISPLAY_HEIGHT - 10, + String(FREQ_BEGIN + (((int)fr - FREQ_BEGIN) / 4))); + // Median Mhz + drawText((DISPLAY_WIDTH / 2) - 10, DISPLAY_HEIGHT - 10, + String(FREQ_BEGIN + (((int)fr - FREQ_BEGIN) / 2))); + // Median + 1/2 Mhz + drawText((DISPLAY_WIDTH - (DISPLAY_WIDTH / 4)) - 10, DISPLAY_HEIGHT - 10, + String(FREQ_BEGIN + + (((int)fr - FREQ_BEGIN) - ((int)fr - FREQ_BEGIN) / 4))); + // End Mhz + drawText(DISPLAY_WIDTH - 24, 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(); + // st7789->fillRect(0, 0, DISPLAY_WIDTH, lower_level, ST7789_BLACK); + screen_update_loop_counter = 0; + scan_iterations = 0; + display_scan_i_end = 0; + } + fr = FREQ_BEGIN; + x1 = 0; + rssi_printed = 0; + // Prevent screen_update_loop_counter++ when it is just nulled + if (scan_iterations > 0) + { + screen_update_loop_counter++; + } + } +#ifdef PRINT_DEBUG + Serial.println("Full Scan:" + String(screen_update_loop_counter)); +#endif +} + +void setup() +{ + Serial.begin(115200); + pinMode(7, OUTPUT); + digitalWrite(7, LOW); + delay(20); + gspi_lcd = new SPIClass(HSPI); + st7789 = + new HT_ST7789(240, 320, gspi_lcd, st7789_CS_Pin, st7789_DC_Pin, st7789_REST_Pin); + gspi_lcd->begin(st7789_SCLK_Pin, -1, st7789_MOSI_Pin, st7789_CS_Pin); + // set up slave select pins as outputs as the Arduino API + pinMode(gspi_lcd->pinSS(), OUTPUT); + st7789->init(170, 320); + + Serial.printf("Ready!\r\n"); + st7789->setRotation(1); + st7789->fillScreen(ST7789_BLACK); + drawText(0, 0, "init >>> ", ST7789_WHITE); + + pinMode(st7789_LED_K_Pin, OUTPUT); + digitalWrite(st7789_LED_K_Pin, HIGH); + // pinMode(5, OUTPUT); + // digitalWrite(5, HIGH); + + st7789->fillScreen(ST7789_BLACK); + // st7789->drawFastHLine(0, 15, 320, ST7789_WHITE); + st7789->drawXBitmap(100, 50, epd_bitmap_ucog, 128, 64, ST7789_WHITE); + init_radio(); + state = radio.startReceive(RADIOLIB_SX126X_RX_TIMEOUT_NONE); + if (state != RADIOLIB_ERR_NONE) + { + Serial.print(F("Failed to start receive mode, error code: ")); + Serial.println(state); + } + heltec_setup(); + delay(2500); + st7789->fillScreen(ST7789_BLACK); +}