diff --git a/README.md b/README.md
index 5cc1732..7ac2249 100644
--- a/README.md
+++ b/README.md
@@ -147,8 +147,11 @@ If less, ESP32 will turn off. Fast pressing(less than 0.5 second) P button chang
1. Install VSCode
2. install Platform.IO extension

-3. Connect ESP32 to USB. Install USB drivers for Windows
-4. Clone this Git Repo or download zip of the sources
+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
+
+5. Clone this Git Repo or download zip of the sources

```bash
@@ -157,19 +160,19 @@ If less, ESP32 will turn off. Fast pressing(less than 0.5 second) P button chang
NOTE: in you case name will be Just LoraSA. I have LoraSA2 because I already have LoraSA folder
-5. Open the Project with the VS code Platform.IO
+6. Open the Project with the VS code Platform.IO


-6. Select Proper Environment
+7. Select Proper Environment

-7. Select ESP32 USB Device to program
+8. Select ESP32 USB Device to program

Note: It is theoretically possible to program via WiFi and BTH.
-8. Program your ESP32
+9. Program your ESP32

-9. 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.

You will have the UCOG SA logo and spectrum analyzing scanning screen when done.
@@ -182,6 +185,16 @@ Heltec ESP32 Lora V3:
+Or Heltec Wireless Stick. The same hardware but without or with a smaller display
+https://heltec.org/project/wireless-stick-v3/
+https://heltec.org/project/wireless-stick-lite-v2/
+
+Heltec Vision Master E290 - With large e-ink display 293x128:
+
+https://heltec.org/project/vision-master-e290/
+https://www.aliexpress.us/item/3256807048047234.html
+**NOTE: to upload a new code, you need to press BOOT + RESET button**
+
Battery with Wire JT connector :
diff --git a/boards/t3_s3_v1_x.json b/boards/t3_s3_v1_x.json
new file mode 100644
index 0000000..2ecfff8
--- /dev/null
+++ b/boards/t3_s3_v1_x.json
@@ -0,0 +1,45 @@
+{
+ "build": {
+ "arduino": {
+ "ldscript": "esp32s3_out.ld",
+ "partitions": "default.csv",
+ "memory_type": "qio_qspi"
+ },
+ "core": "esp32",
+ "extra_flags": [
+ "-DARDUINO_LILYGO_T3_S3_V1_X",
+ "-DBOARD_HAS_PSRAM",
+ "-DARDUINO_USB_CDC_ON_BOOT=1",
+ "-DARDUINO_RUNNING_CORE=1",
+ "-DARDUINO_EVENT_RUNNING_CORE=1",
+ "-DARDUINO_USB_MODE=1"
+ ],
+ "f_cpu": "240000000L",
+ "f_flash": "80000000L",
+ "flash_mode": "qio",
+ "mcu": "esp32s3",
+ "variant": "esp32s3"
+ },
+ "connectivity": [
+ "wifi"
+ ],
+ "debug": {
+ "openocd_target": "esp32s3.cfg"
+ },
+ "frameworks": [
+ "arduino",
+ "espidf"
+ ],
+ "name": "LilyGo T3-S3 Radio",
+ "upload": {
+ "flash_size": "4MB",
+ "maximum_ram_size": 327680,
+ "maximum_size": 4194304,
+ "use_1200bps_touch": true,
+ "wait_for_upload_port": true,
+ "require_upload_port": true,
+ "speed": 460800
+ },
+ "url": "https://www.lilygo.cc",
+ "vendor": "LilyGo"
+}
diff --git a/eink_src/images.h b/eink_src/images.h
new file mode 100644
index 0000000..5021a8f
--- /dev/null
+++ b/eink_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/eink_src/main.cpp b/eink_src/main.cpp
new file mode 100644
index 0000000..a87ece8
--- /dev/null
+++ b/eink_src/main.cpp
@@ -0,0 +1,497 @@
+/* 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_DEPG0290BxS800FxX_BW.h"
+#include "global_config.h"
+#include "images.h"
+#include "ui.h"
+#include
+
+// Disabling default Heltec lib OLED display
+#define HELTEC_NO_DISPLAY
+#define DISPLAY_WIDTH 296
+#define DISPLAY_HEIGHT 128
+// 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 296 // 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 30 // 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
+
+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
+
+// Initialize the display
+DEPG0290BxS800FxX_BW display(5, 4, 3, 6, 2, 1, -1,
+ 6000000); // rst,dc,cs,busy,sck,mosi,miso,frequency
+/* screen rotation
+ * ANGLE_0_DEGREE
+ * ANGLE_90_DEGREE
+ * ANGLE_180_DEGREE
+ * ANGLE_270_DEGREE
+ */
+#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 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, "e-ink display");
+ display.drawString(0, 56, "RF Spectrum X-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);
+}
+
+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];
+long display_scan_start = 0;
+long display_scan_end = 0;
+long display_scan_i_end = 0;
+int scan_iterations = 0;
+
+constexpr unsigned int SCANS_PER_DISPLAY = 5;
+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;
+ }
+ display_scan_start = millis();
+ }
+ fr_x[x1] = fr;
+ 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++;
+ 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));
+
+ if (max_scan_rssi[x1] < rssi2)
+ {
+ max_scan_rssi[x1] = rssi2;
+ }
+ }
+
+ // drone detection level line
+ if (x1 % 2 == 0)
+ {
+ display.setPixel(x1, rssiToPix(drone_detection_level));
+ }
+ 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)
+ {
+ if (screen_update_loop_counter == 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));
+ // Vertical lines between windows
+ for (int l = y2; l < 100; l += 4)
+ {
+ display.setPixel(i, l);
+ }
+ }
+ window_max_rssi = -999;
+ }
+ }
+
+ display_scan_end = millis();
+
+ 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");
+
+ // some issues with the performance.
+ // TODO: fix this issue
+ if (display_scan_end - display_scan_start > 20000)
+ {
+ esp_restart();
+ }
+
+ battery();
+ // iteration full scan / samples pixel step / numbers of scan per display
+ display.drawString(DISPLAY_WIDTH - ((DISPLAY_WIDTH / 6) * 2) - 5, 0,
+ "i:" + String(scan_iterations) + "/" + String(SAMPLES) +
+ "/" + String(SCANS_PER_DISPLAY));
+ // Scan resolution
+ display.drawString(DISPLAY_WIDTH - ((DISPLAY_WIDTH / 6) * 2) - 35, 0,
+ "r:" + String(rssi_mhz_step));
+ // Mhz in pixel
+ display.drawString(DISPLAY_WIDTH - ((DISPLAY_WIDTH / 6) * 2) - 55, 0,
+ "s:" + String(mhz_step));
+
+ // Draw a line horizontally
+ display.drawHorizontalLine(0, lower_level + 1, 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);
+ // 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);
+ if (x % 5 == 0)
+ display.drawVerticalLine(x, lower_level + 1, 3);
+ }
+ display.setFont(ArialMT_Plain_10);
+
+ // Begin Mhz
+ display.drawString(1, DISPLAY_HEIGHT - 10, String(FREQ_BEGIN));
+ // Median -1/2 Mhz
+ display.drawString((DISPLAY_WIDTH / 4) - 10, DISPLAY_HEIGHT - 10,
+ String(FREQ_BEGIN + (((int)fr - FREQ_BEGIN) / 4)));
+ // Median Mhz
+ display.drawString((DISPLAY_WIDTH / 2) - 10, DISPLAY_HEIGHT - 10,
+ String(FREQ_BEGIN + (((int)fr - FREQ_BEGIN) / 2)));
+ // Median + 1/2 Mhz
+ display.drawString((DISPLAY_WIDTH - (DISPLAY_WIDTH / 4)) - 10,
+ DISPLAY_HEIGHT - 10,
+ 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.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;
+ }
+ 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()
+{
+ // Initialising the UI will init the display too.
+ display.init();
+ // Of not this screen doesn't work
+ VextON();
+ display.screenRotate(DIRECTION);
+ display.setFont(ArialMT_Plain_10);
+ display.clear();
+ display.drawXbm((DISPLAY_WIDTH / 3) - 10, DISPLAY_HEIGHT / 4, 128, 60,
+ epd_bitmap_ucog);
+ display.display();
+ delay(1000);
+ display.clear();
+ Serial.begin(115200);
+ w = WATERFALL_START;
+ 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();
+
+ drawSetupText();
+ display.display();
+ display.clear();
+ delay(100);
+}
diff --git a/include/BT_WIFI_scan.h b/include/BT_WIFI_scan.h
index 5a4722b..d0b2be5 100644
--- a/include/BT_WIFI_scan.h
+++ b/include/BT_WIFI_scan.h
@@ -1,4 +1,110 @@
#pragma once
-extern void scanWiFiWithOSDOut();
-extern void scanBTWithOSDOut();
+#ifdef WIFI_SCANNING_ENABLED
+#include "WiFi.h"
+#endif
+#ifdef BT_SCANNING_ENABLED
+#include
+#include
+#include
+#include
+#endif
+#include "DFRobot_OSD.h"
+
+void setOSD() {}
+// TODO: Make Async Scan
+// https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/scan-examples.html#async-scan
+void scanWiFi(DFRobot_OSD osd)
+{
+ 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);
+}
+
+//**********************
+// 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/include/LiLyGo.h b/include/LiLyGo.h
new file mode 100644
index 0000000..ec8754c
--- /dev/null
+++ b/include/LiLyGo.h
@@ -0,0 +1,167 @@
+
+#define UNUSED_PIN (0)
+// LilyGo defined
+
+#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
+
+// Define for our code
+#define RST_OLED UNUSED_PIN
+#define LED BOARD_LED
+
+#include "RadioLib.h"
+// make sure the power off button works when using RADIOLIB_OR_HALT
+// (See RadioLib_convenience.h)
+#define RADIOLIB_DO_DURING_HALT heltec_delay(10)
+#include "RadioLib_convenience.h"
+#ifdef HELTEC_NO_DISPLAY
+#define HELTEC_NO_DISPLAY_INSTANCE
+#else
+#define DISPLAY_WIDTH 128
+#define DISPLAY_HEIGHT 64
+#include "OLEDDisplayUi.h"
+#include "SSD1306Wire.h"
+#endif
+#define ARDUINO_heltec_wifi_32_lora_V3
+#ifndef HELTEC_NO_RADIO_INSTANCE
+#ifndef ARDUINO_heltec_wifi_32_lora_V3
+// Assume MISO and MOSI being wrong when not using Heltec's board definition
+// and use hspi to make it work anyway. See heltec_setup() for the actual SPI setup.
+#include
+SPIClass *hspi = new SPIClass(2);
+SX1262 radio = new Module(SS, DIO1, RST_LoRa, BUSY_LoRa, *hspi);
+#else
+// 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
+
+void heltec_loop() {}
+
+void heltec_led(int led) {}
+
+void heltec_deep_sleep() {}
+
+void heltec_delay(int millisec) { delay(millisec); }
+
+#ifndef HELTEC_NO_DISPLAY_INSTANCE
+/**
+ * @class PrintSplitter
+ * @brief A class that splits the output of the Print class to two different
+ * Print objects.
+ *
+ * The PrintSplitter class is used to split the output of the Print class to two
+ * different Print objects. It overrides the write() function to write the data
+ * to both Print objects.
+ */
+class PrintSplitter : public Print
+{
+ public:
+ PrintSplitter(Print &_a, Print &_b) : a(_a), b(_b) {}
+ size_t write(uint8_t c)
+ {
+ a.write(c);
+ return b.write(c);
+ }
+ size_t write(const char *str)
+ {
+ a.write(str);
+ return b.write(str);
+ }
+
+ private:
+ Print &a;
+ Print &b;
+};
+
+#ifdef HELTEC_WIRELESS_STICK
+#define DISPLAY_GEOMETRY GEOMETRY_64_32
+#else
+#define DISPLAY_GEOMETRY GEOMETRY_128_64
+#endif
+SSD1306Wire display(0x3c, 18, 17, DISPLAY_GEOMETRY);
+PrintSplitter both(Serial, display);
+#else
+Print &both = Serial;
+#endif
+// some fake pin
+#define BUTTON 38
+#include "HotButton.h"
+HotButton button(BUTTON);
+
+// This file contains a binary patch for the SX1262
+#include "modules/SX126x/patches/SX126x_patch_scan.h"
+
+void heltec_display_power(bool on)
+{
+#ifndef HELTEC_NO_DISPLAY_INSTANCE
+ if (on)
+ {
+#ifdef HELTEC_WIRELESS_STICK
+ // They hooked the display to "external" power, and didn't tell anyone
+ heltec_ve(true);
+ delay(5);
+#endif
+ pinMode(RST_OLED, OUTPUT);
+ digitalWrite(RST_OLED, HIGH);
+ delay(1);
+ digitalWrite(RST_OLED, LOW);
+ delay(20);
+ digitalWrite(RST_OLED, HIGH);
+ }
+ else
+ {
+#ifdef HELTEC_WIRELESS_STICK
+ heltec_ve(false);
+#else
+ display.displayOff();
+#endif
+ }
+#endif
+}
+void heltec_setup()
+{
+ Serial.begin(115200);
+ Serial.println("LILYGO BOARD");
+
+#if defined(ARDUINO_ARCH_ESP32)
+ SPI.begin(RADIO_SCLK_PIN, RADIO_MISO_PIN, RADIO_MOSI_PIN);
+#elif defined(ARDUINO_ARCH_STM32)
+ SPI.setMISO(RADIO_MISO_PIN);
+ SPI.setMOSI(RADIO_MOSI_PIN);
+ SPI.setSCLK(RADIO_SCLK_PIN);
+ SPI.begin();
+#endif
+
+#ifndef ARDUINO_heltec_wifi_32_lora_V3
+ hspi->begin(SCK, MISO, MOSI, SS);
+#endif
+#ifndef HELTEC_NO_DISPLAY_INSTANCE
+ heltec_display_power(true);
+ display.init();
+ display.setContrast(200);
+ display.flipScreenVertically();
+#endif
+}
diff --git a/include/LoRaBoards.cpp b/include/LoRaBoards.cpp
new file mode 100644
index 0000000..04c93a6
--- /dev/null
+++ b/include/LoRaBoards.cpp
@@ -0,0 +1,929 @@
+/**
+ * @file boards.cpp
+ * @author Lewis He (lewishe@outlook.com)
+ * @license MIT
+ * @copyright Copyright (c) 2024 ShenZhen XinYuan Electronic Technology Co., Ltd
+ * @date 2024-04-24
+ * @last-update 2024-08-07
+ *
+ */
+
+#include "LoRaBoards.h"
+
+#if defined(HAS_SDCARD)
+SPIClass SDCardSPI(HSPI);
+#endif
+
+#if defined(ARDUINO_ARCH_STM32)
+HardwareSerial SerialGPS(GPS_RX_PIN, GPS_TX_PIN);
+#endif
+
+#if defined(ARDUINO_ARCH_ESP32)
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
+#include "hal/gpio_hal.h"
+#endif
+#include "driver/gpio.h"
+#endif // ARDUINO_ARCH_ESP32
+
+DISPLAY_MODEL *u8g2 = NULL;
+static DevInfo_t devInfo;
+
+#ifdef HAS_GPS
+static bool find_gps = false;
+#endif
+
+#ifdef HAS_PMU
+XPowersLibInterface *PMU = NULL;
+bool pmuInterrupt;
+
+static void setPmuFlag() { pmuInterrupt = true; }
+#endif
+
+bool beginPower()
+{
+#ifdef HAS_PMU
+ if (!PMU)
+ {
+ PMU = new XPowersAXP2101(PMU_WIRE_PORT);
+ if (!PMU->init())
+ {
+ Serial.println("Warning: Failed to find AXP2101 power management");
+ delete PMU;
+ PMU = NULL;
+ }
+ else
+ {
+ Serial.println("AXP2101 PMU init succeeded, using AXP2101 PMU");
+ }
+ }
+
+ if (!PMU)
+ {
+ PMU = new XPowersAXP192(PMU_WIRE_PORT);
+ if (!PMU->init())
+ {
+ Serial.println("Warning: Failed to find AXP192 power management");
+ delete PMU;
+ PMU = NULL;
+ }
+ else
+ {
+ Serial.println("AXP192 PMU init succeeded, using AXP192 PMU");
+ }
+ }
+
+ if (!PMU)
+ {
+ return false;
+ }
+
+ PMU->setChargingLedMode(XPOWERS_CHG_LED_CTRL_CHG);
+
+ pinMode(PMU_IRQ, INPUT_PULLUP);
+ attachInterrupt(PMU_IRQ, setPmuFlag, FALLING);
+
+ if (PMU->getChipModel() == XPOWERS_AXP192)
+ {
+
+ PMU->setProtectedChannel(XPOWERS_DCDC3);
+
+ // lora
+ PMU->setPowerChannelVoltage(XPOWERS_LDO2, 3300);
+ // gps
+ PMU->setPowerChannelVoltage(XPOWERS_LDO3, 3300);
+ // oled
+ PMU->setPowerChannelVoltage(XPOWERS_DCDC1, 3300);
+
+ PMU->enablePowerOutput(XPOWERS_LDO2);
+ PMU->enablePowerOutput(XPOWERS_LDO3);
+
+ // protected oled power source
+ PMU->setProtectedChannel(XPOWERS_DCDC1);
+ // protected esp32 power source
+ PMU->setProtectedChannel(XPOWERS_DCDC3);
+ // enable oled power
+ PMU->enablePowerOutput(XPOWERS_DCDC1);
+
+ // disable not use channel
+ PMU->disablePowerOutput(XPOWERS_DCDC2);
+
+ PMU->disableIRQ(XPOWERS_AXP192_ALL_IRQ);
+
+ PMU->enableIRQ(XPOWERS_AXP192_VBUS_REMOVE_IRQ | XPOWERS_AXP192_VBUS_INSERT_IRQ |
+ XPOWERS_AXP192_BAT_CHG_DONE_IRQ |
+ XPOWERS_AXP192_BAT_CHG_START_IRQ | XPOWERS_AXP192_BAT_REMOVE_IRQ |
+ XPOWERS_AXP192_BAT_INSERT_IRQ | XPOWERS_AXP192_PKEY_SHORT_IRQ);
+ }
+ else if (PMU->getChipModel() == XPOWERS_AXP2101)
+ {
+
+#if defined(CONFIG_IDF_TARGET_ESP32)
+ // Unuse power channel
+ PMU->disablePowerOutput(XPOWERS_DCDC2);
+ PMU->disablePowerOutput(XPOWERS_DCDC3);
+ PMU->disablePowerOutput(XPOWERS_DCDC4);
+ PMU->disablePowerOutput(XPOWERS_DCDC5);
+ PMU->disablePowerOutput(XPOWERS_ALDO1);
+ PMU->disablePowerOutput(XPOWERS_ALDO4);
+ PMU->disablePowerOutput(XPOWERS_BLDO1);
+ PMU->disablePowerOutput(XPOWERS_BLDO2);
+ PMU->disablePowerOutput(XPOWERS_DLDO1);
+ PMU->disablePowerOutput(XPOWERS_DLDO2);
+
+ // GNSS RTC PowerVDD 3300mV
+ PMU->setPowerChannelVoltage(XPOWERS_VBACKUP, 3300);
+ PMU->enablePowerOutput(XPOWERS_VBACKUP);
+
+ // ESP32 VDD 3300mV
+ // ! No need to set, automatically open , Don't close it
+ // PMU->setPowerChannelVoltage(XPOWERS_DCDC1, 3300);
+ // PMU->setProtectedChannel(XPOWERS_DCDC1);
+ PMU->setProtectedChannel(XPOWERS_DCDC1);
+
+ // LoRa VDD 3300mV
+ PMU->setPowerChannelVoltage(XPOWERS_ALDO2, 3300);
+ PMU->enablePowerOutput(XPOWERS_ALDO2);
+
+ // GNSS VDD 3300mV
+ PMU->setPowerChannelVoltage(XPOWERS_ALDO3, 3300);
+ PMU->enablePowerOutput(XPOWERS_ALDO3);
+
+#endif /*CONFIG_IDF_TARGET_ESP32*/
+
+#if defined(T_BEAM_S3_SUPREME)
+
+ // t-beam m.2 inface
+ // gps
+ PMU->setPowerChannelVoltage(XPOWERS_ALDO4, 3300);
+ PMU->enablePowerOutput(XPOWERS_ALDO4);
+
+ // lora
+ PMU->setPowerChannelVoltage(XPOWERS_ALDO3, 3300);
+ PMU->enablePowerOutput(XPOWERS_ALDO3);
+
+ // In order to avoid bus occupation, during initialization, the SD card and QMC
+ // sensor are powered off and restarted
+ if (ESP_SLEEP_WAKEUP_UNDEFINED == esp_sleep_get_wakeup_cause())
+ {
+ Serial.println("Power off and restart ALDO BLDO..");
+ PMU->disablePowerOutput(XPOWERS_ALDO1);
+ PMU->disablePowerOutput(XPOWERS_ALDO2);
+ PMU->disablePowerOutput(XPOWERS_BLDO1);
+ delay(250);
+ }
+
+ // Sensor
+ PMU->setPowerChannelVoltage(XPOWERS_ALDO1, 3300);
+ PMU->enablePowerOutput(XPOWERS_ALDO1);
+
+ PMU->setPowerChannelVoltage(XPOWERS_ALDO2, 3300);
+ PMU->enablePowerOutput(XPOWERS_ALDO2);
+
+ // Sdcard
+
+ PMU->setPowerChannelVoltage(XPOWERS_BLDO1, 3300);
+ PMU->enablePowerOutput(XPOWERS_BLDO1);
+
+ PMU->setPowerChannelVoltage(XPOWERS_BLDO2, 3300);
+ PMU->enablePowerOutput(XPOWERS_BLDO2);
+
+ // face m.2
+ PMU->setPowerChannelVoltage(XPOWERS_DCDC3, 3300);
+ PMU->enablePowerOutput(XPOWERS_DCDC3);
+
+ PMU->setPowerChannelVoltage(XPOWERS_DCDC4, XPOWERS_AXP2101_DCDC4_VOL2_MAX);
+ PMU->enablePowerOutput(XPOWERS_DCDC4);
+
+ PMU->setPowerChannelVoltage(XPOWERS_DCDC5, 3300);
+ PMU->enablePowerOutput(XPOWERS_DCDC5);
+
+ // not use channel
+ PMU->disablePowerOutput(XPOWERS_DCDC2);
+ // PMU->disablePowerOutput(XPOWERS_DCDC4);
+ // PMU->disablePowerOutput(XPOWERS_DCDC5);
+ PMU->disablePowerOutput(XPOWERS_DLDO1);
+ PMU->disablePowerOutput(XPOWERS_DLDO2);
+ PMU->disablePowerOutput(XPOWERS_VBACKUP);
+
+#elif defined(T_BEAM_S3_BPF)
+
+ // gps
+ PMU->setPowerChannelVoltage(XPOWERS_ALDO4, 3300);
+ PMU->enablePowerOutput(XPOWERS_ALDO4);
+
+ // Sdcard
+ PMU->setPowerChannelVoltage(XPOWERS_ALDO2, 3300);
+ PMU->enablePowerOutput(XPOWERS_ALDO2);
+
+ // Extern Power source
+ PMU->setPowerChannelVoltage(XPOWERS_DCDC3, 3300);
+ PMU->enablePowerOutput(XPOWERS_DCDC3);
+
+ PMU->setPowerChannelVoltage(XPOWERS_DCDC5, 3300);
+ PMU->enablePowerOutput(XPOWERS_DCDC5);
+
+ PMU->setPowerChannelVoltage(XPOWERS_ALDO1, 3300);
+ PMU->enablePowerOutput(XPOWERS_ALDO1);
+
+ // not use channel
+ PMU->disablePowerOutput(XPOWERS_BLDO1);
+ PMU->disablePowerOutput(XPOWERS_BLDO2);
+ PMU->disablePowerOutput(XPOWERS_DCDC4);
+ PMU->disablePowerOutput(XPOWERS_DCDC2);
+ PMU->disablePowerOutput(XPOWERS_DCDC4);
+ PMU->disablePowerOutput(XPOWERS_DCDC5);
+ PMU->disablePowerOutput(XPOWERS_DLDO1);
+ PMU->disablePowerOutput(XPOWERS_DLDO2);
+ PMU->disablePowerOutput(XPOWERS_VBACKUP);
+
+#endif
+
+ // Set constant current charge current limit
+ PMU->setChargerConstantCurr(XPOWERS_AXP2101_CHG_CUR_500MA);
+
+ // Set charge cut-off voltage
+ PMU->setChargeTargetVoltage(XPOWERS_AXP2101_CHG_VOL_4V2);
+
+ // Disable all interrupts
+ PMU->disableIRQ(XPOWERS_AXP2101_ALL_IRQ);
+ // Clear all interrupt flags
+ PMU->clearIrqStatus();
+ // Enable the required interrupt function
+ PMU->enableIRQ(
+ XPOWERS_AXP2101_BAT_INSERT_IRQ | XPOWERS_AXP2101_BAT_REMOVE_IRQ | // BATTERY
+ XPOWERS_AXP2101_VBUS_INSERT_IRQ | XPOWERS_AXP2101_VBUS_REMOVE_IRQ | // VBUS
+ XPOWERS_AXP2101_PKEY_SHORT_IRQ | XPOWERS_AXP2101_PKEY_LONG_IRQ | // POWER KEY
+ XPOWERS_AXP2101_BAT_CHG_DONE_IRQ | XPOWERS_AXP2101_BAT_CHG_START_IRQ // CHARGE
+ // XPOWERS_AXP2101_PKEY_NEGATIVE_IRQ | XPOWERS_AXP2101_PKEY_POSITIVE_IRQ |
+ // //POWER KEY
+ );
+ }
+
+ PMU->enableSystemVoltageMeasure();
+ PMU->enableVbusVoltageMeasure();
+ PMU->enableBattVoltageMeasure();
+
+ Serial.printf("=========================================\n");
+ if (PMU->isChannelAvailable(XPOWERS_DCDC1))
+ {
+ Serial.printf("DC1 : %s Voltage: %04u mV \n",
+ PMU->isPowerChannelEnable(XPOWERS_DCDC1) ? "+" : "-",
+ PMU->getPowerChannelVoltage(XPOWERS_DCDC1));
+ }
+ if (PMU->isChannelAvailable(XPOWERS_DCDC2))
+ {
+ Serial.printf("DC2 : %s Voltage: %04u mV \n",
+ PMU->isPowerChannelEnable(XPOWERS_DCDC2) ? "+" : "-",
+ PMU->getPowerChannelVoltage(XPOWERS_DCDC2));
+ }
+ if (PMU->isChannelAvailable(XPOWERS_DCDC3))
+ {
+ Serial.printf("DC3 : %s Voltage: %04u mV \n",
+ PMU->isPowerChannelEnable(XPOWERS_DCDC3) ? "+" : "-",
+ PMU->getPowerChannelVoltage(XPOWERS_DCDC3));
+ }
+ if (PMU->isChannelAvailable(XPOWERS_DCDC4))
+ {
+ Serial.printf("DC4 : %s Voltage: %04u mV \n",
+ PMU->isPowerChannelEnable(XPOWERS_DCDC4) ? "+" : "-",
+ PMU->getPowerChannelVoltage(XPOWERS_DCDC4));
+ }
+ if (PMU->isChannelAvailable(XPOWERS_DCDC5))
+ {
+ Serial.printf("DC5 : %s Voltage: %04u mV \n",
+ PMU->isPowerChannelEnable(XPOWERS_DCDC5) ? "+" : "-",
+ PMU->getPowerChannelVoltage(XPOWERS_DCDC5));
+ }
+ if (PMU->isChannelAvailable(XPOWERS_LDO2))
+ {
+ Serial.printf("LDO2 : %s Voltage: %04u mV \n",
+ PMU->isPowerChannelEnable(XPOWERS_LDO2) ? "+" : "-",
+ PMU->getPowerChannelVoltage(XPOWERS_LDO2));
+ }
+ if (PMU->isChannelAvailable(XPOWERS_LDO3))
+ {
+ Serial.printf("LDO3 : %s Voltage: %04u mV \n",
+ PMU->isPowerChannelEnable(XPOWERS_LDO3) ? "+" : "-",
+ PMU->getPowerChannelVoltage(XPOWERS_LDO3));
+ }
+ if (PMU->isChannelAvailable(XPOWERS_ALDO1))
+ {
+ Serial.printf("ALDO1: %s Voltage: %04u mV \n",
+ PMU->isPowerChannelEnable(XPOWERS_ALDO1) ? "+" : "-",
+ PMU->getPowerChannelVoltage(XPOWERS_ALDO1));
+ }
+ if (PMU->isChannelAvailable(XPOWERS_ALDO2))
+ {
+ Serial.printf("ALDO2: %s Voltage: %04u mV \n",
+ PMU->isPowerChannelEnable(XPOWERS_ALDO2) ? "+" : "-",
+ PMU->getPowerChannelVoltage(XPOWERS_ALDO2));
+ }
+ if (PMU->isChannelAvailable(XPOWERS_ALDO3))
+ {
+ Serial.printf("ALDO3: %s Voltage: %04u mV \n",
+ PMU->isPowerChannelEnable(XPOWERS_ALDO3) ? "+" : "-",
+ PMU->getPowerChannelVoltage(XPOWERS_ALDO3));
+ }
+ if (PMU->isChannelAvailable(XPOWERS_ALDO4))
+ {
+ Serial.printf("ALDO4: %s Voltage: %04u mV \n",
+ PMU->isPowerChannelEnable(XPOWERS_ALDO4) ? "+" : "-",
+ PMU->getPowerChannelVoltage(XPOWERS_ALDO4));
+ }
+ if (PMU->isChannelAvailable(XPOWERS_BLDO1))
+ {
+ Serial.printf("BLDO1: %s Voltage: %04u mV \n",
+ PMU->isPowerChannelEnable(XPOWERS_BLDO1) ? "+" : "-",
+ PMU->getPowerChannelVoltage(XPOWERS_BLDO1));
+ }
+ if (PMU->isChannelAvailable(XPOWERS_BLDO2))
+ {
+ Serial.printf("BLDO2: %s Voltage: %04u mV \n",
+ PMU->isPowerChannelEnable(XPOWERS_BLDO2) ? "+" : "-",
+ PMU->getPowerChannelVoltage(XPOWERS_BLDO2));
+ }
+ Serial.printf("=========================================\n");
+
+ // Set the time of pressing the button to turn off
+ PMU->setPowerKeyPressOffTime(XPOWERS_POWEROFF_4S);
+ uint8_t opt = PMU->getPowerKeyPressOffTime();
+ Serial.print("PowerKeyPressOffTime:");
+ switch (opt)
+ {
+ case XPOWERS_POWEROFF_4S:
+ Serial.println("4 Second");
+ break;
+ case XPOWERS_POWEROFF_6S:
+ Serial.println("6 Second");
+ break;
+ case XPOWERS_POWEROFF_8S:
+ Serial.println("8 Second");
+ break;
+ case XPOWERS_POWEROFF_10S:
+ Serial.println("10 Second");
+ break;
+ default:
+ break;
+ }
+#endif
+ return true;
+}
+
+void disablePeripherals()
+{
+
+#ifdef HAS_PMU
+ if (!PMU)
+ return;
+#if defined(T_BEAM_S3_BPF)
+ PMU->disablePowerOutput(XPOWERS_ALDO4); // gps
+ PMU->disablePowerOutput(XPOWERS_ALDO2); // Sdcard
+ PMU->disablePowerOutput(XPOWERS_DCDC3); // Extern Power source
+ PMU->disablePowerOutput(XPOWERS_DCDC5);
+ PMU->disablePowerOutput(XPOWERS_ALDO1);
+#endif
+#endif
+}
+
+void loopPMU()
+{
+#ifdef HAS_PMU
+ if (!PMU)
+ {
+ return;
+ }
+ if (!pmuInterrupt)
+ {
+ return;
+ }
+
+ pmuInterrupt = false;
+ // Get PMU Interrupt Status Register
+ uint32_t status = PMU->getIrqStatus();
+ Serial.print("STATUS => HEX:");
+ Serial.print(status, HEX);
+ Serial.print(" BIN:");
+ Serial.println(status, BIN);
+
+ if (PMU->isVbusInsertIrq())
+ {
+ Serial.println("isVbusInsert");
+ }
+ if (PMU->isVbusRemoveIrq())
+ {
+ Serial.println("isVbusRemove");
+ }
+ if (PMU->isBatInsertIrq())
+ {
+ Serial.println("isBatInsert");
+ }
+ if (PMU->isBatRemoveIrq())
+ {
+ Serial.println("isBatRemove");
+ }
+ if (PMU->isPekeyShortPressIrq())
+ {
+ Serial.println("isPekeyShortPress");
+ }
+ if (PMU->isPekeyLongPressIrq())
+ {
+ Serial.println("isPekeyLongPress");
+ }
+ if (PMU->isBatChagerDoneIrq())
+ {
+ Serial.println("isBatChagerDone");
+ }
+ if (PMU->isBatChagerStartIrq())
+ {
+ Serial.println("isBatChagerStart");
+ }
+ // Clear PMU Interrupt Status Register
+ PMU->clearIrqStatus();
+#endif
+}
+
+bool beginDisplay()
+{
+ Wire.beginTransmission(DISPLAY_ADDR);
+ if (Wire.endTransmission() == 0)
+ {
+ Serial.printf("Find Display model at 0x%X address\n", DISPLAY_ADDR);
+ u8g2 = new DISPLAY_MODEL(U8G2_R0, U8X8_PIN_NONE);
+ u8g2->begin();
+ u8g2->clearBuffer();
+ u8g2->setFont(u8g2_font_inb19_mr);
+ u8g2->drawStr(0, 30, "LilyGo");
+ u8g2->drawHLine(2, 35, 47);
+ u8g2->drawHLine(3, 36, 47);
+ u8g2->drawVLine(45, 32, 12);
+ u8g2->drawVLine(46, 33, 12);
+ u8g2->setFont(u8g2_font_inb19_mf);
+ u8g2->drawStr(58, 60, "LoRa");
+ u8g2->sendBuffer();
+ u8g2->setFont(u8g2_font_fur11_tf);
+ delay(3000);
+ return true;
+ }
+
+ Serial.printf("Warning: Failed to find Display at 0x%0X address\n", DISPLAY_ADDR);
+ return false;
+}
+
+bool beginSDCard()
+{
+#ifdef SDCARD_CS
+ if (SD.begin(SDCARD_CS, SDCardSPI))
+ {
+ uint32_t cardSize = SD.cardSize() / (1024 * 1024);
+ Serial.print("Sd Card init succeeded, The current available capacity is ");
+ Serial.print(cardSize / 1024.0);
+ Serial.println(" GB");
+ return true;
+ }
+ else
+ {
+ Serial.println("Warning: Failed to init Sd Card");
+ }
+#endif
+ return false;
+}
+
+void beginWiFi()
+{
+ if (!WiFi.softAP(BOARD_VARIANT_NAME))
+ {
+ log_e("Soft AP creation failed.");
+ }
+ IPAddress myIP = WiFi.softAPIP();
+ Serial.print("AP IP address: ");
+ Serial.println(myIP);
+}
+
+void printWakeupReason()
+{
+#ifdef ESP32
+ Serial.print("Reset reason:");
+ esp_sleep_wakeup_cause_t wakeup_reason;
+ wakeup_reason = esp_sleep_get_wakeup_cause();
+ switch (wakeup_reason)
+ {
+ case ESP_SLEEP_WAKEUP_UNDEFINED:
+ Serial.println(
+ " In case of deep sleep, reset was not caused by exit from deep sleep");
+ break;
+ case ESP_SLEEP_WAKEUP_ALL:
+ break;
+ case ESP_SLEEP_WAKEUP_EXT0:
+ Serial.println("Wakeup caused by external signal using RTC_IO");
+ break;
+ case ESP_SLEEP_WAKEUP_EXT1:
+ Serial.println("Wakeup caused by external signal using RTC_CNTL");
+ break;
+ case ESP_SLEEP_WAKEUP_TIMER:
+ Serial.println("Wakeup caused by timer");
+ break;
+ case ESP_SLEEP_WAKEUP_TOUCHPAD:
+ Serial.println("Wakeup caused by touchpad");
+ break;
+ case ESP_SLEEP_WAKEUP_ULP:
+ Serial.println("Wakeup caused by ULP program");
+ break;
+ default:
+ Serial.printf("Wakeup was not caused by deep sleep: %d\n", wakeup_reason);
+ break;
+ }
+#endif
+}
+
+void getChipInfo()
+{
+#if defined(ARDUINO_ARCH_ESP32)
+
+ Serial.println("-----------------------------------");
+
+ printWakeupReason();
+
+#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S3)
+
+ if (psramFound())
+ {
+ uint32_t psram = ESP.getPsramSize();
+ devInfo.psramSize = psram / 1024.0 / 1024.0;
+ Serial.printf("PSRAM is enable! PSRAM: %.2fMB\n", devInfo.psramSize);
+ }
+ else
+ {
+ Serial.println("PSRAM is disable!");
+ devInfo.psramSize = 0;
+ }
+
+#endif
+
+ Serial.print("Flash:");
+ devInfo.flashSize = ESP.getFlashChipSize() / 1024.0 / 1024.0;
+ devInfo.flashSpeed = ESP.getFlashChipSpeed() / 1000 / 1000;
+ devInfo.chipModel = ESP.getChipModel();
+ devInfo.chipModelRev = ESP.getChipRevision();
+ devInfo.chipFreq = ESP.getCpuFreqMHz();
+
+ Serial.print(devInfo.flashSize);
+ Serial.println(" MB");
+ Serial.print("Flash speed:");
+ Serial.print(devInfo.flashSpeed);
+ Serial.println(" M");
+ Serial.print("Model:");
+
+ Serial.println(devInfo.chipModel);
+ Serial.print("Chip Revision:");
+ Serial.println(devInfo.chipModelRev);
+ Serial.print("Freq:");
+ Serial.print(devInfo.chipFreq);
+ Serial.println(" MHZ");
+ Serial.print("SDK Ver:");
+ Serial.println(ESP.getSdkVersion());
+ Serial.print("DATE:");
+ Serial.println(__DATE__);
+ Serial.print("TIME:");
+ Serial.println(__TIME__);
+
+ Serial.print("EFUSE MAC: ");
+ Serial.print(ESP.getEfuseMac(), HEX);
+ Serial.println();
+
+ Serial.println("-----------------------------------");
+
+#elif defined(ARDUINO_ARCH_STM32)
+ uint32_t uid[3];
+
+ uid[0] = HAL_GetUIDw0();
+ uid[1] = HAL_GetUIDw1();
+ uid[2] = HAL_GetUIDw2();
+ Serial.print("STM UID: 0X");
+ Serial.print(uid[0], HEX);
+ Serial.print(uid[1], HEX);
+ Serial.print(uid[2], HEX);
+ Serial.println();
+#endif
+}
+
+void setupBoards(bool disable_u8g2)
+{
+ Serial.begin(115200);
+
+ // while (!Serial);
+
+ Serial.println("setupBoards");
+
+ getChipInfo();
+
+#if defined(ARDUINO_ARCH_ESP32)
+ SPI.begin(RADIO_SCLK_PIN, RADIO_MISO_PIN, RADIO_MOSI_PIN);
+#elif defined(ARDUINO_ARCH_STM32)
+ SPI.setMISO(RADIO_MISO_PIN);
+ SPI.setMOSI(RADIO_MOSI_PIN);
+ SPI.setSCLK(RADIO_SCLK_PIN);
+ SPI.begin();
+#endif
+
+#ifdef HAS_SDCARD
+ SDCardSPI.begin(SDCARD_SCLK, SDCARD_MISO, SDCARD_MOSI);
+#endif
+
+#ifdef I2C_SDA
+ Wire.begin(I2C_SDA, I2C_SCL);
+ scanDevices(&Wire);
+#endif
+
+#ifdef I2C1_SDA
+ Wire1.begin(I2C1_SDA, I2C1_SCL);
+#endif
+
+#ifdef HAS_GPS
+#if defined(ARDUINO_ARCH_ESP32)
+ SerialGPS.begin(GPS_BAUD_RATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
+#elif defined(ARDUINO_ARCH_STM32)
+ SerialGPS.setRx(GPS_RX_PIN);
+ SerialGPS.setTx(GPS_TX_PIN);
+ SerialGPS.begin(GPS_BAUD_RATE);
+#endif // ARDUINO_ARCH_
+#endif // HAS_GPS
+
+#if OLED_RST
+ pinMode(OLED_RST, OUTPUT);
+ digitalWrite(OLED_RST, HIGH);
+ delay(20);
+ digitalWrite(OLED_RST, LOW);
+ delay(20);
+ digitalWrite(OLED_RST, HIGH);
+ delay(20);
+#endif
+
+#ifdef BOARD_LED
+ /*
+ * T-Beam LED defaults to low level as turn on,
+ * so it needs to be forced to pull up
+ * * * * */
+#if LED_ON == LOW
+#if defined(ARDUINO_ARCH_ESP32)
+ gpio_hold_dis((gpio_num_t)BOARD_LED);
+#endif // ARDUINO_ARCH_ESP32
+#endif
+
+ pinMode(BOARD_LED, OUTPUT);
+ digitalWrite(BOARD_LED, LED_ON);
+#endif
+
+#ifdef GPS_EN_PIN
+ pinMode(GPS_EN_PIN, OUTPUT);
+ digitalWrite(GPS_EN_PIN, HIGH);
+#endif
+
+#ifdef GPS_RST_PIN
+ pinMode(GPS_RST_PIN, OUTPUT);
+ digitalWrite(GPS_RST_PIN, HIGH);
+#endif
+
+#if defined(ARDUINO_ARCH_STM32)
+ SerialGPS.println("@GSR");
+ delay(300);
+ SerialGPS.println("@GSR");
+ delay(300);
+ SerialGPS.println("@GSR");
+ delay(300);
+ SerialGPS.println("@GSR");
+ delay(300);
+ SerialGPS.println("@GSR");
+ delay(300);
+#endif
+
+#ifdef RADIO_LDO_EN
+ pinMode(RADIO_LDO_EN, OUTPUT);
+ digitalWrite(RADIO_LDO_EN, HIGH);
+#endif
+
+ beginPower();
+
+ beginSDCard();
+
+ if (!disable_u8g2)
+ {
+ beginDisplay();
+ }
+
+ beginWiFi();
+
+#ifdef HAS_GPS
+#ifdef T_BEAM_S3_BPF
+ find_gps = beginGPS();
+#endif
+#endif
+
+ Serial.println("init done . ");
+}
+
+void printResult(bool radio_online)
+{
+ Serial.print("Radio : ");
+ Serial.println((radio_online) ? "+" : "-");
+
+#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S3)
+
+ Serial.print("PSRAM : ");
+ Serial.println((psramFound()) ? "+" : "-");
+
+ Serial.print("Display : ");
+ Serial.println((u8g2) ? "+" : "-");
+
+#ifdef HAS_SDCARD
+ Serial.print("Sd Card : ");
+ Serial.println((SD.cardSize() != 0) ? "+" : "-");
+#endif
+
+#ifdef HAS_PMU
+ Serial.print("Power : ");
+ Serial.println((PMU) ? "+" : "-");
+#endif
+
+#ifdef HAS_GPS
+#ifdef T_BEAM_S3_BPF
+ Serial.print("GPS : ");
+ Serial.println((find_gps) ? "+" : "-");
+#endif
+#endif
+
+ if (u8g2)
+ {
+
+ u8g2->clearBuffer();
+ u8g2->setFont(u8g2_font_NokiaLargeBold_tf);
+ uint16_t str_w = u8g2->getStrWidth(BOARD_VARIANT_NAME);
+ u8g2->drawStr((u8g2->getWidth() - str_w) / 2, 16, BOARD_VARIANT_NAME);
+ u8g2->drawHLine(5, 21, u8g2->getWidth() - 5);
+
+ u8g2->drawStr(0, 38, "Disp:");
+ u8g2->drawStr(45, 38, (u8g2) ? "+" : "-");
+
+#ifdef HAS_SDCARD
+ u8g2->drawStr(0, 54, "SD :");
+ u8g2->drawStr(45, 54, (SD.cardSize() != 0) ? "+" : "-");
+#endif
+
+ u8g2->drawStr(62, 38, "Radio:");
+ u8g2->drawStr(120, 38, (radio_online) ? "+" : "-");
+
+#ifdef HAS_PMU
+ u8g2->drawStr(62, 54, "Power:");
+ u8g2->drawStr(120, 54, (PMU) ? "+" : "-");
+#endif
+
+ u8g2->sendBuffer();
+
+ delay(2000);
+ }
+#endif
+}
+
+static uint8_t ledState = LOW;
+static const uint32_t debounceDelay = 50;
+static uint32_t lastDebounceTime = 0;
+
+void flashLed()
+{
+#ifdef BOARD_LED
+ if ((millis() - lastDebounceTime) > debounceDelay)
+ {
+ ledState = !ledState;
+ if (ledState)
+ {
+ digitalWrite(BOARD_LED, LED_ON);
+ }
+ else
+ {
+ digitalWrite(BOARD_LED, !LED_ON);
+ }
+ lastDebounceTime = millis();
+ }
+#endif
+}
+
+void scanDevices(TwoWire *w)
+{
+ uint8_t err, addr;
+ int nDevices = 0;
+ uint32_t start = 0;
+
+ Serial.println("I2C Devices scanning");
+ for (addr = 1; addr < 127; addr++)
+ {
+ start = millis();
+ w->beginTransmission(addr);
+ delay(2);
+ err = w->endTransmission();
+ if (err == 0)
+ {
+ nDevices++;
+ switch (addr)
+ {
+ case 0x77:
+ case 0x76:
+ Serial.println("\tFind BMX280 Sensor!");
+ break;
+ case 0x34:
+ Serial.println("\tFind AXP192/AXP2101 PMU!");
+ break;
+ case 0x3C:
+ Serial.println("\tFind SSD1306/SH1106 dispaly!");
+ break;
+ case 0x51:
+ Serial.println("\tFind PCF8563 RTC!");
+ break;
+ case 0x1C:
+ Serial.println("\tFind QMC6310 MAG Sensor!");
+ break;
+ default:
+ Serial.print("\tI2C device found at address 0x");
+ if (addr < 16)
+ {
+ Serial.print("0");
+ }
+ Serial.print(addr, HEX);
+ Serial.println(" !");
+ break;
+ }
+ }
+ else if (err == 4)
+ {
+ Serial.print("Unknow error at address 0x");
+ if (addr < 16)
+ {
+ Serial.print("0");
+ }
+ Serial.println(addr, HEX);
+ }
+ }
+ if (nDevices == 0)
+ Serial.println("No I2C devices found\n");
+
+ Serial.println("Scan devices done.");
+ Serial.println("\n");
+}
+
+#ifdef HAS_GPS
+bool beginGPS()
+{
+ SerialGPS.begin(GPS_BAUD_RATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
+ bool result = false;
+ uint32_t startTimeout;
+ for (int i = 0; i < 3; ++i)
+ {
+ SerialGPS.write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
+ delay(5);
+ // Get version information
+ startTimeout = millis() + 3000;
+ Serial.print("Try to init L76K . Wait stop .");
+ while (SerialGPS.available())
+ {
+ Serial.print(".");
+ SerialGPS.readString();
+ if (millis() > startTimeout)
+ {
+ Serial.println("Wait L76K stop NMEA timeout!");
+ return false;
+ }
+ };
+ Serial.println();
+ SerialGPS.flush();
+ delay(200);
+
+ SerialGPS.write("$PCAS06,0*1B\r\n");
+ startTimeout = millis() + 500;
+ String ver = "";
+ while (!SerialGPS.available())
+ {
+ if (millis() > startTimeout)
+ {
+ Serial.println("Get L76K timeout!");
+ return false;
+ }
+ }
+ SerialGPS.setTimeout(10);
+ ver = SerialGPS.readStringUntil('\n');
+ if (ver.startsWith("$GPTXT,01,01,02"))
+ {
+ Serial.println("L76K GNSS init succeeded, using L76K GNSS Module\n");
+ result = true;
+ break;
+ }
+ delay(500);
+ }
+ // Initialize the L76K Chip, use GPS + GLONASS
+ SerialGPS.write("$PCAS04,5*1C\r\n");
+ delay(250);
+ // only ask for RMC and GGA
+ SerialGPS.write("$PCAS03,1,0,0,0,1,0,0,0,0,0,,,0,0*02\r\n");
+ delay(250);
+ // Switch to Vehicle Mode, since SoftRF enables Aviation < 2g
+ SerialGPS.write("$PCAS11,3*1E\r\n");
+ return result;
+}
+#endif
diff --git a/include/LoRaBoards.h b/include/LoRaBoards.h
new file mode 100644
index 0000000..655376e
--- /dev/null
+++ b/include/LoRaBoards.h
@@ -0,0 +1,97 @@
+/**
+ * @file boards.h
+ * @author Lewis He (lewishe@outlook.com)
+ * @license MIT
+ * @copyright Copyright (c) 2024 ShenZhen XinYuan Electronic Technology Co., Ltd
+ * @date 2024-04-25
+ * @last-update 2024-08-07
+ */
+
+#pragma once
+
+#include "utilities.h"
+
+#ifdef HAS_SDCARD
+#include
+#endif
+
+#if defined(ARDUINO_ARCH_ESP32)
+#include
+#include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+
+#ifndef DISPLAY_MODEL
+#define DISPLAY_MODEL U8G2_SSD1306_128X64_NONAME_F_HW_I2C
+#endif
+
+#ifndef OLED_WIRE_PORT
+#define OLED_WIRE_PORT Wire
+#endif
+
+#ifndef PMU_WIRE_PORT
+#define PMU_WIRE_PORT Wire
+#endif
+
+#ifndef DISPLAY_ADDR
+#define DISPLAY_ADDR 0x3C
+#endif
+
+#ifndef LORA_FREQ_CONFIG
+#define LORA_FREQ_CONFIG 915.0
+#endif
+
+typedef struct
+{
+ String chipModel;
+ float psramSize;
+ uint8_t chipModelRev;
+ uint8_t chipFreq;
+ uint8_t flashSize;
+ uint8_t flashSpeed;
+} DevInfo_t;
+
+void setupBoards(bool disable_u8g2 = false);
+
+bool beginSDCard();
+
+bool beginDisplay();
+
+void disablePeripherals();
+
+bool beginPower();
+
+void printResult(bool radio_online);
+
+void flashLed();
+
+void scanDevices(TwoWire *w);
+
+bool beginGPS();
+
+void loopPMU();
+
+#ifdef HAS_PMU
+extern XPowersLibInterface *PMU;
+extern bool pmuInterrupt;
+#endif
+extern DISPLAY_MODEL *u8g2;
+
+#define U8G2_HOR_ALIGN_CENTER(t) ((u8g2->getDisplayWidth() - (u8g2->getUTF8Width(t))) / 2)
+#define U8G2_HOR_ALIGN_RIGHT(t) (u8g2->getDisplayWidth() - u8g2->getUTF8Width(t))
+
+#if defined(ARDUINO_ARCH_ESP32)
+
+#if defined(HAS_SDCARD)
+extern SPIClass SDCardSPI;
+#endif
+
+#define SerialGPS Serial1
+#elif defined(ARDUINO_ARCH_STM32)
+extern HardwareSerial SerialGPS;
+#endif
diff --git a/include/global_config.h b/include/global_config.h
index 115bf4a..d169fff 100644
--- a/include/global_config.h
+++ b/include/global_config.h
@@ -12,7 +12,7 @@
#define BANDWIDTH 467.0
// Detection level from the 33 levels. The higher number is more sensitive
-#define DEFAULT_DRONE_DETECTION_LEVEL 20
+#define DEFAULT_DRONE_DETECTION_LEVEL 18
#define BUZZER_PIN 41
@@ -22,4 +22,7 @@
#define WATERFALL_ENABLED true
#define WATERFALL_START 37
+#ifdef LILYGO
+#define LED 46
+#endif // end not LILYGO
#endif
diff --git a/include/joyStick.h b/include/joyStick.h
new file mode 100644
index 0000000..ae10fd8
--- /dev/null
+++ b/include/joyStick.h
@@ -0,0 +1,63 @@
+// Joystick integration
+constexpr int JOY_X_PIN = 19;
+int cursor_x_position = 0;
+// Not integrated yet constexpr int JOY_Y_PIN = N/A;
+constexpr int JOY_BTN_PIN = 46;
+bool joy_btn_clicked = false;
+
+// Joystick integration
+bool joy_btn_click()
+{
+ joy_btn_clicked = true;
+ // is the output from the pushbutton inside the joystick. It’s normally open. If we
+ // use a pull-up resistor in this pin, the SW pin will be HIGH
+ // when it is not pressed, and LOW when Pressed.
+ return digitalRead(JOY_BTN_PIN) == HIGH ? false : true;
+}
+
+int joyXMid = 0;
+int cal_X = 0, cal_Y = 0;
+
+void calibrate_joy()
+{
+ for (int i = 0; i < 100; i++)
+ {
+ cal_X += analogRead(JOY_X_PIN);
+ }
+ // calibrate center
+ joyXMid = cal_X / 100;
+}
+
+int MID = 100; // 10 mid point delta arduino, use 4 for attiny
+int get_joy_x(bool logical = false)
+{
+ int joyX = analogRead(JOY_X_PIN);
+
+ /*
+ Serial.print("Calibrated_X_Voltage = ");
+ Serial.print(joyXMid);
+ Serial.print("X_Voltage = ");
+ Serial.print(joyX);
+ Serial.print("\t");
+ */
+
+ if (logical)
+ {
+ // 4095
+ if (joyX < joyXMid - MID)
+ {
+ return -1;
+ }
+ // 0-5
+ else if (joyX > joyXMid + MID)
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ return joyX;
+}
diff --git a/include/ui.h b/include/ui.h
index 2dcaa96..2ca7525 100644
--- a/include/ui.h
+++ b/include/ui.h
@@ -1,9 +1,12 @@
-
#pragma once
+#ifdef Vision_Master_E290
+#include "HT_DEPG0290BxS800FxX_BW.h"
+#else
#include "OLEDDisplayUi.h"
#include "SSD1306Wire.h"
#include
+#endif
// #include
@@ -29,7 +32,11 @@
#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_clearPlotter(void);
diff --git a/include/utilities.h b/include/utilities.h
new file mode 100644
index 0000000..1d1b3ef
--- /dev/null
+++ b/include/utilities.h
@@ -0,0 +1,551 @@
+/**
+ * @file utilities.h
+ * @author Lewis He (lewishe@outlook.com)
+ * @license MIT
+ * @copyright Copyright (c) 2024 ShenZhen XinYuan Electronic Technology Co., Ltd
+ * @date 2024-05-12
+ * @last-update 2024-08-07
+ */
+#pragma once
+
+// Support board list , Macro definition below, select the board definition to be used
+
+// #define T3_V1_3_SX1276
+// #define T3_V1_3_SX1278
+
+// #define T3_V1_6_SX1276
+// #define T3_V1_6_SX1278
+
+// #define T3_V1_6_SX1276_TCXO
+// #define T3_V3_0_SX1276_TCXO
+
+// #define T_BEAM_SX1262
+// #define T_BEAM_SX1276
+// #define T_BEAM_SX1278
+
+// #define T_BEAM_S3_SUPREME
+
+// #define T3_S3_V1_2_SX1262
+// #define T3_S3_V1_2_SX1276
+// #define T3_S3_V1_2_SX1278
+// #define T3_S3_V1_2_SX1280
+// #define T3_S3_V1_2_SX1280_PA
+// #define T3_S3_V1_2_LR1121
+
+// #define T_MOTION
+
+// #define T3_C6
+
+// #define T_BEAM_S3_BPF
+
+#define UNUSED_PIN (0)
+
+#if defined(T_BEAM_SX1262) || defined(T_BEAM_SX1276) || defined(T_BEAM_SX1278)
+
+#if defined(T_BEAM_SX1262)
+#ifndef USING_SX1262
+#define USING_SX1262
+#endif
+#elif defined(T_BEAM_SX1276)
+#ifndef USING_SX1276
+#define USING_SX1276
+#endif
+#elif defined(T_BEAM_SX1278)
+#ifndef USING_SX1278
+#define USING_SX1278
+#endif
+#endif // T_BEAM_SX1262
+
+#define GPS_RX_PIN 34
+#define GPS_TX_PIN 12
+#define BUTTON_PIN 38
+#define BUTTON_PIN_MASK GPIO_SEL_38
+#define I2C_SDA 21
+#define I2C_SCL 22
+#define PMU_IRQ 35
+
+#define RADIO_SCLK_PIN 5
+#define RADIO_MISO_PIN 19
+#define RADIO_MOSI_PIN 27
+#define RADIO_CS_PIN 18
+#define RADIO_DIO0_PIN 26
+#define RADIO_RST_PIN 23
+#define RADIO_DIO1_PIN 33
+// SX1276/78
+#define RADIO_DIO2_PIN 32
+// SX1262
+#define RADIO_BUSY_PIN 32
+
+#define BOARD_LED 4
+#define LED_ON LOW
+#define LED_OFF HIGH
+
+#define GPS_BAUD_RATE 9600
+#define HAS_GPS
+#define HAS_DISPLAY // Optional, bring your own board, no OLED !!
+#define HAS_PMU
+
+#define BOARD_VARIANT_NAME "T-Beam"
+
+#elif defined(T3_V1_3_SX1276) || defined(T3_V1_3_SX1278)
+
+#if defined(T3_V1_3_SX1276)
+
+#ifndef USING_SX1276
+#define USING_SX1276
+#endif
+
+#elif defined(T3_V1_3_SX1278)
+
+#ifndef USING_SX1278
+#define USING_SX1278
+#endif
+
+#endif // T3_V1_3_SX1276
+
+#define I2C_SDA 21
+#define I2C_SCL 22
+#define OLED_RST UNUSED_PIN
+
+#define RADIO_SCLK_PIN 5
+#define RADIO_MISO_PIN 19
+#define RADIO_MOSI_PIN 27
+#define RADIO_CS_PIN 18
+#define RADIO_DIO0_PIN 26
+#define RADIO_RST_PIN 14
+#define RADIO_DIO1_PIN 33
+
+// SX1276/78
+#define RADIO_DIO2_PIN 32
+// SX1262
+#define RADIO_BUSY_PIN 32
+
+#define ADC_PIN 35
+#define HAS_DISPLAY
+#define BOARD_VARIANT_NAME "T3 V1.3"
+
+#elif defined(T3_V1_6_SX1276) || defined(T3_V1_6_SX1278)
+
+#if defined(T3_V1_6_SX1276)
+#ifndef USING_SX1276
+#define USING_SX1276
+#endif
+#elif defined(T3_V1_6_SX1278)
+#ifndef USING_SX1278
+#define USING_SX1278
+#endif
+#endif // T3_V1_6_SX1276
+
+#define I2C_SDA 21
+#define I2C_SCL 22
+#define OLED_RST UNUSED_PIN
+
+#define RADIO_SCLK_PIN 5
+#define RADIO_MISO_PIN 19
+#define RADIO_MOSI_PIN 27
+#define RADIO_CS_PIN 18
+#define RADIO_DIO0_PIN 26
+#define RADIO_RST_PIN 23
+#define RADIO_DIO1_PIN 33
+// SX1276/78
+#define RADIO_DIO2_PIN 32
+// SX1262
+#define RADIO_BUSY_PIN 32
+
+#define SDCARD_MOSI 15
+#define SDCARD_MISO 2
+#define SDCARD_SCLK 14
+#define SDCARD_CS 13
+
+#define BOARD_LED 25
+#define LED_ON HIGH
+
+#define ADC_PIN 35
+
+#define HAS_SDCARD
+#define HAS_DISPLAY
+
+#define BOARD_VARIANT_NAME "T3 V1.6"
+
+#elif defined(T3_V1_6_SX1276_TCXO)
+
+#ifndef USING_SX1276
+#define USING_SX1276
+#endif
+
+#define I2C_SDA 21
+#define I2C_SCL 22
+#define OLED_RST UNUSED_PIN
+
+#define RADIO_SCLK_PIN 5
+#define RADIO_MISO_PIN 19
+#define RADIO_MOSI_PIN 27
+#define RADIO_CS_PIN 18
+#define RADIO_DIO0_PIN 26
+#define RADIO_RST_PIN 23
+#define RADIO_DIO1_PIN -1 // 33
+/*
+ * In the T3 V1.6.1 TCXO version, Radio DIO1 is connected to Radio’s
+ * internal temperature-compensated crystal oscillator enable
+ * */
+// TCXO pin must be set to HIGH before enabling Radio
+#define RADIO_TCXO_ENABLE 33
+#define RADIO_BUSY_PIN 32
+
+#define SDCARD_MOSI 15
+#define SDCARD_MISO 2
+#define SDCARD_SCLK 14
+#define SDCARD_CS 13
+
+#define BOARD_LED 25
+#define LED_ON HIGH
+
+#define ADC_PIN 35
+
+#define HAS_SDCARD
+#define HAS_DISPLAY
+
+#define BOARD_VARIANT_NAME "T3 V1.6 TCXO"
+
+#elif defined(T3_V3_0)
+
+#define I2C_SDA 21
+#define I2C_SCL 22
+#define OLED_RST 4
+
+#define RADIO_SCLK_PIN 5
+#define RADIO_MISO_PIN 19
+#define RADIO_MOSI_PIN 27
+#define RADIO_CS_PIN 18
+#define RADIO_RST_PIN 23
+
+// TCXO pin must be set to HIGH before enabling Radio
+#define RADIO_TCXO_ENABLE 12 // only sx1276 tcxo version
+#define RADIO_BUSY_PIN 32
+
+#if defined(USING_SX1262)
+
+#define RADIO_DIO1_PIN 26
+#define RADIO_BUSY_PIN 32
+
+#elif defined(USING_SX1276) || defined(USING_SX1278)
+//! SX1276/78 module only
+
+#define RADIO_DIO0_PIN 26
+#define RADIO_DIO1_PIN 32
+
+#elif defined(USING_LR1121)
+
+#define RADIO_DIO9_PIN 26 // LR1121 DIO9
+#define RADIO_BUSY_PIN 32 // LR1121 BUSY
+
+#endif
+
+#define SDCARD_MOSI 15
+#define SDCARD_MISO 2
+#define SDCARD_SCLK 14
+#define SDCARD_CS 13
+
+#define BOARD_LED 25
+#define LED_ON HIGH
+
+#define ADC_PIN 35
+
+#define HAS_SDCARD
+#define HAS_DISPLAY
+
+#define BOARD_VARIANT_NAME "T3 V3.0"
+
+#define BUTTON_PIN 0
+#define BAT_ADC_PIN 35
+
+#elif defined(T3_S3_V1_2_SX1262) || defined(ARDUINO_LILYGO_T3S3_SX1262) || \
+ defined(T3_S3_V1_2_SX1276) || defined(ARDUINO_LILYGO_T3S3_SX1276) || \
+ defined(T3_S3_V1_2_SX1278) || defined(ARDUINO_LILYGO_T3S3_SX1278) || \
+ defined(T3_S3_V1_2_SX1280) || defined(ARDUINO_LILYGO_T3S3_SX1280) || \
+ defined(T3_S3_V1_2_SX1280_PA) || defined(ARDUINO_LILYGO_T3S3_SX1280PA) || \
+ defined(T3_S3_V1_2_LR1121) || defined(ARDUINO_LILYGO_T3S3_LR1121)
+
+#if defined(T3_S3_V1_2_SX1262) || defined(ARDUINO_LILYGO_T3S3_SX1262)
+#ifndef USING_SX1262
+#define USING_SX1262
+#endif
+#elif defined(T3_S3_V1_2_SX1276) || defined(ARDUINO_LILYGO_T3S3_SX1276)
+#ifndef USING_SX1276
+#define USING_SX1276
+#endif
+#elif defined(T3_S3_V1_2_SX1278) || defined(ARDUINO_LILYGO_T3S3_SX1278)
+#ifndef USING_SX1278
+#define USING_SX1278
+#endif
+#elif defined(T3_S3_V1_2_SX1280) || defined(ARDUINO_LILYGO_T3S3_SX1280)
+#ifndef USING_SX1280
+#define USING_SX1280
+#endif
+#elif defined(T3_S3_V1_2_SX1280_PA) || defined(ARDUINO_LILYGO_T3S3_SX1280PA)
+#ifndef USING_SX1280PA
+#define USING_SX1280PA
+#endif
+#elif defined(T3_S3_V1_2_LR1121) || defined(ARDUINO_LILYGO_T3S3_LR1121)
+#ifndef USING_LR1121
+#define USING_LR1121
+#endif
+
+#endif // T3_S3_V1_2_SX1262
+
+#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
+
+#if defined(USING_SX1262)
+
+#define RADIO_DIO1_PIN 33
+#define RADIO_BUSY_PIN 34
+
+#elif defined(USING_SX1276) || defined(USING_SX1278)
+//! SX1276/78 module only
+#define RADIO_BUSY_PIN 33 // DIO1
+
+#define RADIO_DIO0_PIN 9
+#define RADIO_DIO1_PIN 33
+#define RADIO_DIO2_PIN 34
+#define RADIO_DIO3_PIN 21
+#define RADIO_DIO4_PIN 10
+#define RADIO_DIO5_PIN 36
+
+#elif defined(USING_SX1280)
+
+#define RADIO_DIO1_PIN 9 // SX1280 DIO1 = IO9
+#define RADIO_BUSY_PIN 36 // SX1280 BUSY = IO36
+
+#elif defined(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
+
+#elif defined(USING_LR1121)
+
+#define RADIO_DIO9_PIN 36 // LR1121 DIO9 = IO36
+#define RADIO_BUSY_PIN 34 // LR1121 BUSY = IO34
+
+#endif
+
+#define HAS_SDCARD
+#define HAS_DISPLAY
+
+#define BOARD_VARIANT_NAME "T3 S3 V1.X"
+
+#elif defined(T_BEAM_S3_SUPREME)
+
+#ifndef USING_SX1262
+#define USING_SX1262
+#endif
+
+#define I2C_SDA 17
+#define I2C_SCL 18
+
+#define I2C1_SDA 42
+#define I2C1_SCL 41
+#define PMU_IRQ 40
+
+#define GPS_RX_PIN 9
+#define GPS_TX_PIN 8
+#define GPS_WAKEUP_PIN 7
+#define GPS_PPS_PIN 6
+
+#define BUTTON_PIN 0
+#define BUTTON_PIN_MASK GPIO_SEL_0
+#define BUTTON_CONUT (1)
+#define BUTTON_ARRAY {BUTTON_PIN}
+
+#define RADIO_SCLK_PIN (12)
+#define RADIO_MISO_PIN (13)
+#define RADIO_MOSI_PIN (11)
+#define RADIO_CS_PIN (10)
+#define RADIO_DIO0_PIN (-1)
+#define RADIO_RST_PIN (5)
+#define RADIO_DIO1_PIN (1)
+#define RADIO_BUSY_PIN (4)
+
+#define SPI_MOSI (35)
+#define SPI_SCK (36)
+#define SPI_MISO (37)
+#define SPI_CS (47)
+#define IMU_CS (34)
+#define IMU_INT (33)
+
+#define SDCARD_MOSI SPI_MOSI
+#define SDCARD_MISO SPI_MISO
+#define SDCARD_SCLK SPI_SCK
+#define SDCARD_CS SPI_CS
+
+#define PIN_NONE (-1)
+#define RTC_INT (14)
+
+#define GPS_BAUD_RATE 9600
+
+#define HAS_SDCARD
+#define HAS_GPS
+#define HAS_DISPLAY
+#define HAS_PMU
+
+#define __HAS_SPI1__
+#define __HAS_SENSOR__
+
+#define PMU_WIRE_PORT Wire1
+#define DISPLAY_MODEL U8G2_SH1106_128X64_NONAME_F_HW_I2C
+#define BOARD_VARIANT_NAME "T-Beam S3"
+
+#elif defined(T_MOTION_S76G)
+
+#ifndef USING_SX1276
+#define USING_SX1276
+#endif
+
+#define RADIO_SCLK_PIN PB13
+#define RADIO_MISO_PIN PB14
+#define RADIO_MOSI_PIN PB15
+#define RADIO_CS_PIN PB12
+#define RADIO_RST_PIN PB10
+
+#define RADIO_DIO0_PIN PB11
+#define RADIO_DIO1_PIN PC13
+#define RADIO_DIO2_PIN PB9
+#define RADIO_DIO3_PIN PB4
+#define RADIO_DIO4_PIN PB3
+#define RADIO_DIO5_PIN PA15
+
+#undef RADIO_BUSY_PIN
+#undef RADIO_DIO1_PIN
+#define RADIO_BUSY_PIN PC13 // DIO1
+#define RADIO_DIO1_PIN PB11 // DIO0
+
+#define RADIO_SWITCH_PIN PA1 // 1:Rx, 0:Tx
+
+#define GPS_EN_PIN PC6
+#define GPS_RST_PIN PB2
+#define GPS_RX_PIN PC11
+#define GPS_TX_PIN PC10
+#define GPS_ENABLE_PIN PC6
+#define GPS_BAUD_RATE 115200
+#define GPS_PPS_PIN PB5
+
+#define UART_RX_PIN PA10
+#define UART_TX_PIN PA9
+
+#define I2C_SCL PB6
+#define I2C_SDA PB7
+
+#define BOARD_VARIANT_NAME "T-Motion S76G"
+
+#define HAS_GPS
+
+#elif defined(T3_C6)
+
+#ifndef USING_SX1262
+#define USING_SX1262
+#endif
+
+#define RADIO_SCLK_PIN 6
+#define RADIO_MISO_PIN 1
+#define RADIO_MOSI_PIN 0
+#define RADIO_CS_PIN 18
+#define RADIO_DIO1_PIN 23
+#define RADIO_BUSY_PIN 22
+#define RADIO_RST_PIN 21
+
+#define I2C_SDA 8
+#define I2C_SCL 9
+
+#define BOARD_LED 7
+#define LED_ON HIGH
+#define RADIO_RX_PIN 15
+#define RADIO_TX_PIN 14
+
+#define BOARD_VARIANT_NAME "T3-C6"
+
+#define USING_DIO2_AS_RF_SWITCH
+
+#elif defined(T_BEAM_S3_BPF)
+
+#ifndef USING_SX1278
+#define USING_SX1278
+#endif
+
+#define I2C_SDA 8
+#define I2C_SCL 9
+
+#define PMU_IRQ 4
+
+#define GPS_RX_PIN 5
+#define GPS_TX_PIN 6
+#define GPS_PPS_PIN 7
+
+#define BUTTON_PIN 0
+#define BUTTON_PIN_MASK GPIO_SEL_0
+#define BUTTON_CONUT (2)
+#define BUTTON_ARRAY {BUTTON_PIN, 3}
+
+#define RADIO_SCLK_PIN (41)
+#define RADIO_MISO_PIN (42)
+#define RADIO_MOSI_PIN (2)
+#define RADIO_CS_PIN (1)
+#define RADIO_RST_PIN (18)
+
+#define RADIO_DIO0_PIN (14)
+#define RADIO_DIO1_PIN (21)
+#define RADIO_DIO2_PIN (15)
+
+#define RADIO_TCXO_ENABLE (17)
+#define RADIO_LDO_EN (16)
+#define RADIO_BUSY_PIN (RADIO_DIO1_PIN)
+
+#define SPI_MOSI (11)
+#define SPI_SCK (12)
+#define SPI_MISO (13)
+#define SPI_CS (10)
+
+#define SDCARD_MOSI SPI_MOSI
+#define SDCARD_MISO SPI_MISO
+#define SDCARD_SCLK SPI_SCK
+#define SDCARD_CS SPI_CS
+
+#define PIN_NONE (-1)
+
+#define GPS_BAUD_RATE 9600
+
+#define HAS_SDCARD
+#define HAS_GPS
+#define HAS_DISPLAY
+#define HAS_PMU
+
+#define __HAS_SPI1__
+#define __HAS_SENSOR__
+
+#define PMU_WIRE_PORT Wire
+#define DISPLAY_MODEL U8G2_SH1106_128X64_NONAME_F_HW_I2C
+#define BOARD_VARIANT_NAME "T-Beam BPF"
+
+#else
+#error "When using it for the first time, please define the board model in "
+#endif
diff --git a/platformio.ini b/platformio.ini
index 5c24979..0b7648d 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -8,12 +8,124 @@
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
+[platformio]
+; 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
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
+ 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
+framework = arduino
+upload_speed = 921600
+monitor_speed = 115200
+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
+ -DT3_V1_3_SX1262
+ -DARDUINO_LILYGO_T3S3_SX1262
+ -DESP32
+ -DUSING_SX1262
+ -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
+framework = arduino
upload_speed = 115200
monitor_speed = 115200
board_build.f_cpu = 240000000
-lib_deps = ropg/Heltec_ESP32_LoRa_v3@^0.9.1
-build_flags = -DHELTEC_POWER_BUTTON
+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]
+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=37
+ -DSLOW_CLK_TPYE=1
+ -DARDUINO_USB_CDC_ON_BOOT=1
+ -DLoRaWAN_DEBUG_LEVEL=0
+ -DE290
+ -DESP32
+ -DVision_Master_E290
+ -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
+
+[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/images.cpp b/src/images.cpp
index ab61f38..d2ab5f7 100644
--- a/src/images.cpp
+++ b/src/images.cpp
@@ -3,8 +3,8 @@
//'Logo_UCOG', 128x64px
// https://www.online-utility.org/image/convert/to/XBM
// https://javl.github.io/image2cpp/
-//#define 1721604660673_width 128
-//#define 1721604660673_height 64
+// #define 1721604660673_width 128
+// #define 1721604660673_height 64
const unsigned char epd_bitmap_ucog[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
diff --git a/src/main.cpp b/src/main.cpp
index 6d3d619..acfc13b 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -21,14 +21,30 @@
https://jgromes.github.io/RadioLib/
*/
+// #define HELTEC_NO_DISPLAY
+
#include
-#include
-// This file contains a binary patch for the SX1262
-#include "modules/SX126x/patches/SX126x_patch_scan.h"
+
// #define OSD_ENABLED true
// #define WIFI_SCANNING_ENABLED true
// #define BT_SCANNING_ENABLED true
+#ifndef LILYGO
+#include
+// This file contains a binary patch for the SX1262
+#include "modules/SX126x/patches/SX126x_patch_scan.h"
+#elif 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 "LiLyGo.h"
+
+#endif // end LILYGO
+
#define BT_SCAN_DELAY 60 * 1 * 1000
#define WF_SCAN_DELAY 60 * 2 * 1000
long noDevicesMillis = 0, cycleCnt = 0;
@@ -41,9 +57,9 @@ bool scanFinished = true;
uint64_t wf_start = 0;
uint64_t bt_start = 0;
-#ifndef OSD_ENABLED
-#include "DFRobot_OSD.h"
#define MAX_POWER_LEVELS 33
+#ifdef OSD_ENABLED
+#include "DFRobot_OSD.h"
#define OSD_SIDE_BAR true
static constexpr uint16_t levels[10] = {
@@ -112,7 +128,7 @@ static constexpr uint16_t power_level[MAX_POWER_LEVELS + 1] = {
#define OSD_X_START 1
#define OSD_Y_START 16
-// TODO: Calculate dinammicaly:
+// TODO: Calculate dynamically:
// osd_steps = osd_mhz_in_bin / (FM range / LORA radio x Steps)
int osd_mhz_in_bin = 5;
int osd_steps = 12;
@@ -127,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"
@@ -152,15 +157,19 @@ typedef enum
} TSCAN_METOD_ENUM;
#define SCAN_METHOD
-#define METHOD_SPECTRAL
-// #define METHOD_RSSI // Uncomment this and comment METHOD_SPECTRAL fot RSSI
+// #define METHOD_SPECTRAL // Spectral scan method
+#define METHOD_RSSI // Uncomment this and comment METHOD_SPECTRAL fot RSSI
-// Feature to scan diapazones. Other frequency settings will be ignored.
+// 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[] = {};
// MHZ per page
-// to put everething into one page set RANGE_PER_PAGE = FREQ_END - 800
+// to put everything into one page set RANGE_PER_PAGE = FREQ_END - 800
uint64_t RANGE_PER_PAGE = FREQ_END - FREQ_BEGIN; // FREQ_END - FREQ_BEGIN
// multiplies STEPS * N to increase scan resolution.
@@ -177,16 +186,17 @@ constexpr int OSD_PIXELS_PER_CHAR = (STEPS * SCAN_RBW_FACTOR) / OSD_CHART_WIDTH;
// Print spectrum values pixels at once or by line
bool ANIMATED_RELOAD = false;
-// TODO: Ignore power lines
+// TODO: Ignore max power lines
#define UP_FILTER 5
-#define LOW_FILTER 3
+// 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;
// Number of samples for each frequency scan. Fewer samples = better temporal resolution.
-// if more than 100 it can freez
+// if more than 100 it can freeze
#define SAMPLES 35 //(scan time = 1294)
// number of samples for RSSI method
#define SAMPLES_RSSI 20 // 21 //
@@ -241,7 +251,7 @@ uint64_t scan_time = 0;
uint64_t scan_start_time = 0;
#endif
-uint64_t x, y, range_item, w, i = 0;
+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;
@@ -260,110 +270,28 @@ uint8_t result_index = 0;
uint8_t button_pressed_counter = 0;
uint64_t loop_cnt = 0;
-#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);
-}
+#ifndef LILYGO
+// #define JOYSTICK_ENABLED
#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++;
+#include "joyStick.h"
- 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);
+// project components
+#if (defined(WIFI_SCANNING_ENABLED) || defined(BT_SCANNING_ENABLED)) && \
+ defined(OSD_ENABLED)
+#include "BT_WIFI_scan.h"
#endif
- BLEScanResults foundDevices = pBLEScan->start(BT_SCAN_TIME);
- int count = foundDevices.getCount();
-#ifdef PRINT_DEBUG
- Serial.printf("Found devices: %d \n", count);
+#if defined(WIFI_SCANNING_ENABLED) && defined(OSD_ENABLED)
+scanWiFi(osd)
#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)
{
@@ -380,14 +308,14 @@ unsigned short selectFreqChar(int bin, int start_level = 0)
void osdPrintSignalLevelChart(int col, int signal_value)
{
// Third line
- if (signal_value <= 9)
+ if (signal_value <= 9 && signal_value <= drone_detection_level)
{
osd.displayChar(13, col + 2, 0x100);
osd.displayChar(14, col + 2, 0x100);
osd.displayChar(12, col + 2, selectFreqChar(signal_value, drone_detection_level));
}
// Second line
- else if (signal_value < 19)
+ else if (signal_value < 19 && signal_value <= drone_detection_level)
{
osd.displayChar(12, col + 2, 0x100);
osd.displayChar(14, col + 2, 0x100);
@@ -419,7 +347,7 @@ void osdProcess()
// filter
if (result[i] > 0
#if FILTER_SPECTRUM_RESULTS
- && ((result[i + 1] != 0 && result[i + 2] != 0) || result[i - 1] != 0)
+ && ((result[i + 1] != 0 /*&& result[i + 2] != 0*/) || result[i - 1] != 0)
#endif
)
{
@@ -443,11 +371,6 @@ void osdProcess()
// Going to the next OSD step
if (x % osd_steps == 0 && col < OSD_WIDTH)
{
- // some issue when median = 0
- if (max_step_range == 0)
- {
- max_step_range = OSD_WIDTH - 1;
- }
// OSD SIDE BAR with frequency log
#ifdef OSD_SIDE_BAR
{
@@ -473,6 +396,51 @@ void osdProcess()
}
#endif
+void init_radio()
+{
+ // initialize SX1262 FSK modem at the initial frequency
+ both.println("Init radio");
+ state == radio.beginFSK(FREQ_BEGIN);
+
+ if (state == RADIOLIB_ERR_NONE)
+ {
+ Serial.println(F("success!"));
+ }
+ else
+ {
+ Serial.print(F("failed, code "));
+ Serial.println(state);
+ while (true)
+ {
+ delay(5);
+ }
+ }
+
+#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
+ both.println("Upload SX1262 patch");
+
+ // Upload binary patch into the SX126x device RAM. Patch is needed to e.g.,
+ // enable spectral scan and must be uploaded again on every power cycle.
+ RADIOLIB_OR_HALT(radio.uploadPatch(sx126x_patch_scan, sizeof(sx126x_patch_scan)));
+ // configure scan bandwidth and disable the data shaping
+#endif
+
+ both.println("Setting up radio");
+ RADIOLIB_OR_HALT(radio.setRxBandwidth(BANDWIDTH));
+
+ // and disable the data shaping
+ RADIOLIB_OR_HALT(radio.setDataShaping(RADIOLIB_SHAPING_NONE));
+ both.println("Starting scanning...");
+
+ // 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);
+}
+
void setup(void)
{
// LED brightness
@@ -502,6 +470,10 @@ void setup(void)
pinMode(BUZZER_PIN, OUTPUT);
pinMode(REB_PIN, OUTPUT);
heltec_setup();
+#ifdef JOYSTICK_ENABLED
+ calibrate_joy();
+ pinMode(JOY_BTN_PIN, INPUT_PULLUP);
+#endif
UI_Init(&display);
for (int i = 0; i < 200; i++)
{
@@ -517,28 +489,11 @@ void setup(void)
}
}
- // initialize SX1262 FSK modem at the initial frequency
- both.println("Init radio");
- RADIOLIB_OR_HALT(radio.beginFSK(FREQ_BEGIN));
-
- // upload a patch to the SX1262 to enable spectral scan
- // NOTE: this patch is uploaded into volatile memory,
- // and must be re-uploaded on every power up
- both.println("Upload SX1262 patch");
-
- // Upload binary patch into the SX126x device RAM. Patch is needed to e.g.,
- // enable spectral scan and must be uploaded again on every power cycle.
- RADIOLIB_OR_HALT(radio.uploadPatch(sx126x_patch_scan, sizeof(sx126x_patch_scan)));
- // configure scan bandwidth and disable the data shaping
-
- both.println("Setting up radio");
- RADIOLIB_OR_HALT(radio.setRxBandwidth(BANDWIDTH));
-
- // and disable the data shaping
- RADIOLIB_OR_HALT(radio.setDataShaping(RADIOLIB_SHAPING_NONE));
- both.println("Starting scanning...");
+ init_radio();
+#ifndef LILYGO
vbat = heltec_vbat();
both.printf("V battery: %.2fV (%d%%)\n", vbat, heltec_battery_percent(vbat));
+#endif // end not LILYGO
#ifdef WIFI_SCANNING_ENABLED
WiFi.mode(WIFI_STA);
WiFi.disconnect();
@@ -565,7 +520,7 @@ void setup(void)
{
both.println("Single Page Screen MODE");
both.println("Multi Screen View Press P - button");
- both.println("Multi Screan Res: " + String(resolution) + "Mhz/tick");
+ both.println("Multi Screen Res: " + String(resolution) + "Mhz/tick");
both.println(
"Resolution: " + String((float)RANGE_PER_PAGE / (STEPS * SCAN_RBW_FACTOR)) +
"MHz/tick");
@@ -610,17 +565,15 @@ void setup(void)
display.clear();
Serial.println();
- // calibrate only once ,,, at startup
- // TODO: check documentation (9.2.1) if we must calibrate in certain ranges
- radio.setFrequency(FREQ_BEGIN, true);
- delay(50);
-
#ifdef METHOD_RSSI
// TODO: try RADIOLIB_SX126X_RX_TIMEOUT_INF
state = radio.startReceive(RADIOLIB_SX126X_RX_TIMEOUT_NONE);
if (state != RADIOLIB_ERR_NONE)
{
Serial.print(F("Failed to start receive mode, error code: "));
+ display.drawString(0, 64 - 10, "E:startReceive");
+ display.display();
+ delay(500);
Serial.println(state);
}
#endif
@@ -631,16 +584,179 @@ void setup(void)
#endif
}
-// Formula to translate 33 bin to aproximate RSSI value
+// Formula to translate 33 bin to approximate RSSI value
int binToRSSI(int bin)
{
// the first the strongest RSSI in bin value is 0
return 11 + (bin * 4);
}
+// return true if continue the code is false break 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,
+ // start beeping every 10th frequency and shorter
+ // it improves performance less short beep delays...
+ if (drone_detection_level <= 25)
+ {
+ if (detection_count == 1 && SOUND_ON)
+ {
+ tone(BUZZER_PIN, 205,
+ 10); // same action ??? but first time
+ }
+ if (detection_count % 5 == 0 && SOUND_ON)
+ {
+ tone(BUZZER_PIN, 205,
+ 10); // same action ??? but every 5th time
+ }
+ }
+ else
+ {
+ if (detection_count % 20 == 0 && SOUND_ON)
+ {
+ tone(BUZZER_PIN, 205,
+ 10); // same action ??? but every 20th detection
+ }
+ }
+}
+
+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
+
detection_count = 0;
drone_detected_frequency_start = 0;
ranges_count = 0;
@@ -648,7 +764,7 @@ void loop(void)
// reset scan time
scan_time = 0;
- // general purpose loop conter
+ // general purpose loop counter
loop_cnt++;
#ifdef PRINT_PROFILE_TIME
@@ -683,25 +799,7 @@ void loop(void)
}
#endif
- 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;
- }
+ check_ranges();
// Iterating by small ranges by 50 Mhz each pixel is 0.4 Mhz
for (range_item = 0; range_item < iterations; range_item++)
@@ -745,11 +843,7 @@ void loop(void)
// x loop
for (x = 0; x < STEPS * SCAN_RBW_FACTOR; x++)
{
-
- if (x % SCAN_RBW_FACTOR == 0)
- new_pixel = true;
- else
- new_pixel = false;
+ new_pixel = is_new_x_pixel(x);
if (ANIMATED_RELOAD && SCAN_RBW_FACTOR == 1)
{
UI_drawCursor(x);
@@ -765,13 +859,32 @@ void loop(void)
// 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 dispaly_x = x / SCAN_RBW_FACTOR;
- waterfall[dispaly_x] = false;
+ int display_x = x / SCAN_RBW_FACTOR;
+ waterfall[display_x] = false;
float step = (range * ((float)x / (STEPS * SCAN_RBW_FACTOR)));
freq = fr_begin + step;
+#ifdef PRINT_DEBUG
+ Serial.println("setFrequency:" + String(freq));
+#endif
- radio.setFrequency(freq, false); // false = no calibration need here
+#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));
+ // 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);
@@ -782,15 +895,26 @@ void loop(void)
// 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.print("radio.spectralScanGetStatus ERROR: ");
+ Serial.println("radio.spectralScanGetStatus ERROR: ");
Serial.println(radio.spectralScanGetStatus());
- heltec_delay(ONE_MILLISEC * 10);
+ 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
- radio.spectralScanGetResult(result);
+ state = radio.spectralScanGetResult(result);
+ display.drawString(0, 64 - 10, "scanGetResult:" + String(state));
}
+
#endif
#ifdef METHOD_RSSI
// Spectrum analyzer using getRSSI
@@ -810,9 +934,20 @@ void loop(void)
for (int r = 0; r < SAMPLES_RSSI; r++)
{
rssi = radio.getRSSI(false);
- // delay(ONE_MILLISEC);
// ToDO: check if 4 is correct value for 33 power bins
- result_index = uint8_t(abs(rssi) / 4); /// still not clear formula
+ // 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);
@@ -835,16 +970,25 @@ void loop(void)
#endif // SCAN_METHOD == METHOD_RSSI
// if this code is not executed LORA radio doesn't work
- // basicaly SX1262 requers delay
+ // 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[dispaly_x] = false;
+ detected_y[display_x] = false;
+ max_rssi_x = 999;
for (y = 0; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++)
{
@@ -853,7 +997,6 @@ void loop(void)
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)
{
@@ -867,6 +1010,7 @@ void loop(void)
// 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
@@ -893,24 +1037,24 @@ void loop(void)
{
filtered_result[y] = 1;
}
+
#endif
// check if we should alarm about a drone presence
if ((filtered_result[y] == 1) // we have some data and
&& (y <= drone_detection_level) &&
- detected_y[dispaly_x] == false) // detection threshold match
+ 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[dispaly_x])
+ if (!waterfall[display_x])
{
- waterfall[dispaly_x] = true;
+ waterfall[display_x] = true;
display.setColor(WHITE);
- display.setPixel(dispaly_x, w);
+ display.setPixel(display_x, w);
}
}
#endif
@@ -922,70 +1066,50 @@ void loop(void)
// mark freq end ... will shift right to last detected range
drone_detected_frequency_end = freq;
-
- // If level is set to sensitive,
- // start beeping every 10th frequency and shorter
- // it improves performance less short beep delays...
- if (drone_detection_level <= 25)
+ if (SOUND_ON == true)
{
- if (detection_count == 1 && SOUND_ON)
- {
- tone(BUZZER_PIN, 205,
- 10); // same action ??? but first time
- }
- if (detection_count % 5 == 0 && SOUND_ON)
- {
- tone(BUZZER_PIN, 205,
- 10); // same action ??? but everey 5th time
- }
- }
- else
- {
- if (detection_count % 20 == 0 && SOUND_ON)
- {
- tone(BUZZER_PIN, 205,
- 10); // same action ??? but everey 20th detection
- }
+ 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[dispaly_x])
+ if (!detected_y[display_x])
{
- display.drawLine(dispaly_x, 1, dispaly_x, 6);
- detected_y[dispaly_x] = true;
+ 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[dispaly_x] != true) && new_pixel)
+ (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[dispaly_x] = false;
+ waterfall[display_x] = false;
display.setColor(BLACK);
- display.setPixel(dispaly_x, w);
+ display.setPixel(display_x, w);
display.setColor(WHITE);
}
#endif
-
-#if 0
-
-#endif // If 0
-
// next 2 If's ... adds !!!! 10ms of runtime ......tfk ???
if (filtered_result[y] == 1)
{
#ifdef PRINT_DEBUG
- Serial.print("Pixel:" + String(dispaly_x) + "(" + String(x) + ")" +
+ Serial.print("Pixel:" + String(display_x) + "(" + String(x) + ")" +
":" + String(y) + ",");
#endif
+ if (max_rssi_x > y)
+ {
+ max_rssi_x = y;
+ }
// Set signal level pixel
- display.setPixel(dispaly_x, y);
+ if (y < MAX_POWER_LEVELS - START_LOW)
+ {
+ display.setPixel(display_x, y + START_LOW);
+ }
if (!detected)
{
detected = true;
@@ -995,20 +1119,31 @@ void loop(void)
// -------------------------------------------------------------
// Draw "Detection Level line" every 2 pixel
// -------------------------------------------------------------
- if ((y == drone_detection_level) && (dispaly_x % 2 == 0))
+ if ((y == drone_detection_level) && (display_x % 2 == 0))
{
display.setColor(WHITE);
if (filtered_result[y] == 1)
{
display.setColor(INVERSE);
}
- display.setPixel(dispaly_x, y);
- display.setPixel(dispaly_x, y - 1); // 2 px wide
+ display.setPixel(display_x, y + 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)
+ {
+ 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);
#endif
@@ -1026,75 +1161,34 @@ void loop(void)
display.display();
}
- // Detection level button short press
- if (button.pressedFor(100))
- {
- button.update();
- button_pressed_counter = 0;
- // if long press stop
- while (button.pressedNow())
- {
- delay(10);
- // Print Curent frequency
- display.setTextAlignment(TEXT_ALIGN_CENTER);
- display.drawString(128 / 2, 0, String(freq));
- display.display();
- button_pressed_counter++;
- if (button_pressed_counter > 150)
- {
- digitalWrite(LED, HIGH);
- delay(150);
- digitalWrite(LED, LOW);
- }
- }
- if (button_pressed_counter > 150)
- {
- // Remove Curent Freqancy Text
- display.setTextAlignment(TEXT_ALIGN_CENTER);
- display.setColor(BLACK);
- display.drawString(128 / 2, 0, String(freq));
- display.setColor(WHITE);
- display.display();
- break;
- }
- if (button_pressed_counter > 50 && button_pressed_counter < 150)
- {
- // Visually confirm it's off so user releases button
- display.displayOff();
- // Deep sleep (has wait for release so we don't wake up
- // immediately)
- heltec_deep_sleep();
- break;
- }
- button.update();
- display.setTextAlignment(TEXT_ALIGN_RIGHT);
- // erase old 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;
- }
- }
+// 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
- // Add more logic before insead of long delay...
+ // Add more logic before instead of long delay...
int delay_cnt = 1;
- while (radio.spectralScanGetStatus() != RADIOLIB_ERR_NONE)
+#ifdef METHOD_SPECTRAL
+ if (false && state != RADIOLIB_ERR_NONE)
{
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(" +
@@ -1102,12 +1196,18 @@ void loop(void)
") hard delay(2) - " + String(delay_cnt));
// if error than speed is slow animating chart
ANIMATED_RELOAD = true;
-
+ delay(50);
delay_cnt++;
}
- // TODO: move osd logic here as a dalay ;)
+#endif
+ // 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);
+ joystickMoveCursor(joy_x_pressed);
+#endif
}
w++;
if (w > ROW_STATUS_TEXT + 1)
@@ -1126,8 +1226,8 @@ void loop(void)
// Render display data here
display.display();
#ifdef OSD_ENABLED
- // Sometimes OSD prints entire screan with the digits.
- // We need clean the screan to fix it.
+ // 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)
{
@@ -1141,12 +1241,12 @@ void loop(void)
global_counter++;
#endif
}
-
#ifdef PRINT_DEBUG
// Serial.println("----");
#endif
loop_time = millis() - loop_start;
+ joy_btn_clicked = false;
#ifdef PRINT_PROFILE_TIME
Serial.printf("LOOP: %lld ms; SCAN: %lld ms;\n ", loop_time, scan_time);
diff --git a/src/ui.cpp b/src/ui.cpp
index 2760253..48a7bf1 100644
--- a/src/ui.cpp
+++ b/src/ui.cpp
@@ -18,9 +18,12 @@ 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;
@@ -37,6 +40,7 @@ extern unsigned int range_item;
extern uint64_t loop_time;
+#ifndef Vision_Master_E290
void UI_Init(SSD1306Wire *display_ptr)
{
// init pointer to display instance.
@@ -47,6 +51,20 @@ void UI_Init(SSD1306Wire *display_ptr)
display_instance->drawXbm(0, 2, 128, 64, epd_bitmap_ucog);
display_instance->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; }
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..4064c97
--- /dev/null
+++ b/tft_src/main.cpp
@@ -0,0 +1,634 @@
+/* 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
+// T190 button pin
+#define BUTTON GPIO_NUM_21
+#define HELTEC_POWER_BUTTON
+#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 DEFAULT_DRONE_DETECTION_LEVEL 90
+
+#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 = DEFAULT_DRONE_DETECTION_LEVEL;
+uint64_t drone_detected_frequency_start = 0;
+uint64_t drone_detected_frequency_end = 0;
+uint64_t detection_count = 0;
+bool single_page_scan = false;
+bool SOUND_ON = 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;
+
+#define MAX_MHZ_INTERVAL 2000
+// 2KB ToDo: make dynamic array or sam structure
+uint16_t detailed_scan_candidate[MAX_MHZ_INTERVAL];
+
+#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());
+}
+
+#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
+ // 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);
+ }
+}
+
+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 ST7789_RED; // Red
+ if (rssi < 85)
+ return ST7789_GREEN; // Green
+ if (rssi < 90)
+ return ST7789_YELLOW; // Yellow
+ if (rssi < 95)
+ return ST7789_BLUE; // Blue
+ if (rssi < 100)
+ return ST7789_MAGENTA; // Magenta
+ if (waterfall)
+ return ST7789_BLACK; // Black on waterfall
+ return ST7789_WHITE; // White on chart
+}
+
+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 = 45;
+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;
+long rssi_single_start = 0;
+long rssi_single_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)
+ {
+ fr_x[x1] = 0;
+ // Zero arrays
+ for (int i = 0; i < STEPS; i++)
+ {
+ 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;
+
+ int u = 0;
+ int additional_samples = 10;
+
+ // Clear old data with the cursor ...
+ st7789->drawFastVLine(x1, lower_level, -lower_level + 11, ST7789_BLACK);
+ // Draw max history line
+ st7789->drawLine(x1, rssiToPix(max_history_rssi[x1]), x1, lower_level,
+ 12710 /*gray*/);
+ // Fetch samples
+ for (int i = 0; i < SAMPLES_RSSI; i++)
+ {
+ // Checking more times curtain freq
+ if (additional_samples > 0 &&
+ (detailed_scan_candidate[(int)fr] + detailed_scan_candidate[(int)fr + 1] +
+ detailed_scan_candidate[(int)fr + 2] >
+ 0))
+ {
+ i--;
+ additional_samples--;
+ }
+
+ 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;
+ }
+ if (rssi_single_start == 0)
+ {
+ rssi_single_start = millis();
+ }
+ rssi2 = radio.getRSSI(false);
+ scan_iterations++;
+ if (rssi_single_end == 0)
+ {
+ rssi_single_end = millis();
+ }
+ if (abs(rssi2) > lower_level)
+ {
+#ifdef PRINT_DEBUG
+ Serial.print("SKIP -> " + String(fr) + ":" + String(rssi2));
+#endif
+ // if lower than detection level set any
+ if (max_scan_rssi[x1] == -999)
+ {
+ max_scan_rssi[x1] = rssi2;
+ }
+ continue;
+ }
+#ifdef PRINT_DEBUG
+ Serial.println(String(fr) + ":" + String(rssi2));
+#endif
+ 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 + 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);
+
+ if (max_scan_rssi[x1] == -999)
+ {
+ max_scan_rssi[x1] = rssi2;
+ }
+ /// -999 < -100
+ if (max_scan_rssi[x1] < rssi2)
+ {
+#ifdef PRINT_DEBUG
+ Serial.println("MAx Scan x-" + String(x1) + ": " + String(max_scan_rssi[x1]) +
+ "< " + String(rssi2));
+#endif
+ max_scan_rssi[x1] = rssi2;
+ if (max_history_rssi[x1] < max_scan_rssi[x1])
+ {
+ max_history_rssi[x1] = rssi2;
+ }
+ }
+ // Max dB in window
+ if (window_max_rssi < max_scan_rssi[x1])
+ {
+ // Max Mhz in window
+ window_max_fr = fr_x[x1];
+ window_max_rssi = max_scan_rssi[x1];
+ }
+ }
+ // 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));
+
+ detailed_scan_candidate[(int)fr] = (int)fr;
+ }
+ else
+ {
+ detailed_scan_candidate[(int)fr] = (int)0;
+ }
+ // 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 &&
+ window_max_rssi != -999)
+ {
+ y2 = 15;
+
+ drawText(x1 - rssi_window_size + 3, y2, String(window_max_rssi) + "dB",
+ rssiToColor(abs(window_max_rssi)));
+ drawText(x1 - rssi_window_size + 3, y2 + 10,
+ String((int)window_max_fr) + "MHz",
+ rssiToColor(abs(window_max_rssi)));
+ // Vertical lines between windows
+ for (int l = y2; l < 100; l += 4)
+ {
+ st7789->drawPixel(x1, l, ST7789_YELLOW);
+ }
+ }
+ window_max_rssi = -999;
+ }
+
+ // 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)
+ {
+ st7789->drawPixel(x1, rssiToPix(drone_detection_level), ST7789_GREEN);
+ }
+ fr += mhz_step;
+
+ if (display_scan_i_end == 0)
+ {
+ display_scan_i_end = millis();
+ }
+ // Button Logic
+ 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;
+ }
+ if (button.pressedFor(500))
+ {
+ while (button.pressedNow())
+ {
+ button_pressed_counter++;
+ // button.update();
+ delay(50);
+ if (button_pressed_counter > 18)
+ {
+ drawText(320 - 5, 5, "*", ST7789_WHITE);
+ }
+ }
+ drone_detection_level--;
+ }
+ if (button_pressed_counter < 18 && button_pressed_counter > 10)
+ {
+ heltec_deep_sleep();
+ }
+
+ // Main N x-axis full loop end logic
+ if (x1 >= STEPS)
+ {
+ w++;
+ if (w > WATERFALL_END)
+ {
+ w = WATERFALL_START;
+ }
+#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)
+ {
+ // 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",
+ 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));
+
+ screen_update_loop_counter = 0;
+ scan_iterations = 0;
+ display_scan_i_end = 0;
+ }
+ fr = FREQ_BEGIN;
+ rssi_single_start = 0;
+ rssi_single_end = 0;
+ x1 = 0;
+ rssi_printed = 0;
+ // Prevent screen_update_loop_counter++ when it is just nulled
+ if (scan_iterations > 0)
+ {
+ screen_update_loop_counter++;
+ }
+ }
+ // not increase at the end of scan when nulled
+ else
+ {
+ x1++;
+ }
+
+#ifdef PRINT_DEBUG
+ Serial.println("Full Scan Counter:" + String(screen_update_loop_counter));
+#endif
+}
+
+void setup()
+{
+ for (int i = 0; i < MAX_MHZ_INTERVAL; i++)
+ {
+ detailed_scan_candidate[i] = 0;
+ }
+ 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);
+ st7789->setSPISpeed(40000000);
+ /// st7789->setSPISpeed(3000000); /// default ~ 1000000
+
+ 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->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);
+}