/* * ESP WiFi Analyzer * Require ESP8266/ESP32 board support. */ // POWER SAVING SETTING #define SCAN_INTERVAL 3000 // #define SCAN_COUNT_SLEEP 3 // #define LCD_PWR_PIN 14 /******************************************************************************* * Start of Arduino_GFX setting * * Arduino_GFX try to find the settings depends on selected board in Arduino IDE * Or you can define the display dev kit not in the board list * Defalult pin list for non display dev kit: * Arduino Nano, Micro and more: CS: 9, DC: 8, RST: 7, BL: 6, SCK: 13, MOSI: 11, MISO: 12 * ESP32 various dev board : CS: 5, DC: 27, RST: 33, BL: 22, SCK: 18, MOSI: 23, MISO: nil * ESP32-C3 various dev board : CS: 7, DC: 2, RST: 1, BL: 3, SCK: 4, MOSI: 6, MISO: nil * ESP32-S2 various dev board : CS: 34, DC: 38, RST: 33, BL: 21, SCK: 36, MOSI: 35, MISO: nil * ESP32-S3 various dev board : CS: 40, DC: 41, RST: 42, BL: 48, SCK: 36, MOSI: 35, MISO: nil * ESP8266 various dev board : CS: 15, DC: 4, RST: 2, BL: 5, SCK: 14, MOSI: 13, MISO: 12 * Raspberry Pi Pico dev board : CS: 17, DC: 27, RST: 26, BL: 28, SCK: 18, MOSI: 19, MISO: 16 * RTL8720 BW16 old patch core : CS: 18, DC: 17, RST: 2, BL: 23, SCK: 19, MOSI: 21, MISO: 20 * RTL8720_BW16 Official core : CS: 9, DC: 8, RST: 6, BL: 3, SCK: 10, MOSI: 12, MISO: 11 * RTL8722 dev board : CS: 18, DC: 17, RST: 22, BL: 23, SCK: 13, MOSI: 11, MISO: 12 * RTL8722_mini dev board : CS: 12, DC: 14, RST: 15, BL: 13, SCK: 11, MOSI: 9, MISO: 10 * Seeeduino XIAO dev board : CS: 3, DC: 2, RST: 1, BL: 0, SCK: 8, MOSI: 10, MISO: 9 * Teensy 4.1 dev board : CS: 39, DC: 41, RST: 40, BL: 22, SCK: 13, MOSI: 11, MISO: 12 ******************************************************************************/ #include #define GFX_BL DF_GFX_BL // default backlight pin, you may replace DF_GFX_BL to actual backlight pin /* More dev device declaration: https://github.com/moononournation/Arduino_GFX/wiki/Dev-Device-Declaration */ #if defined(DISPLAY_DEV_KIT) Arduino_GFX *gfx = create_default_Arduino_GFX(); #else /* !defined(DISPLAY_DEV_KIT) */ /* More data bus class: https://github.com/moononournation/Arduino_GFX/wiki/Data-Bus-Class */ Arduino_DataBus *bus = create_default_Arduino_DataBus(); /* More display class: https://github.com/moononournation/Arduino_GFX/wiki/Display-Class */ Arduino_GFX *gfx = new Arduino_ILI9341(bus, DF_GFX_RST, 3 /* rotation */, false /* IPS */); #endif /* !defined(DISPLAY_DEV_KIT) */ /******************************************************************************* * End of Arduino_GFX setting ******************************************************************************/ #if defined(ESP32) #include "WiFi.h" #else #include "ESP8266WiFi.h" #define log_i(format, ...) Serial.printf(format, ##__VA_ARGS__) #endif int16_t w, h, text_size, banner_height, graph_baseline, graph_height, channel_width, signal_width; // RSSI RANGE #define RSSI_CEILING -40 #define RSSI_FLOOR -100 // Channel color mapping from channel 1 to 14 uint16_t channel_color[] = { RED, ORANGE, YELLOW, GREEN, CYAN, BLUE, MAGENTA, RED, ORANGE, YELLOW, GREEN, CYAN, BLUE, MAGENTA}; uint8_t scan_count = 0; void setup() { Serial.begin(115200); // Serial.setDebugOutput(true); // while(!Serial); Serial.println("Arduino_GFX ESP WiFi Analyzer example"); // Set WiFi to station mode and disconnect from an AP if it was previously connected WiFi.mode(WIFI_STA); WiFi.disconnect(); delay(100); #ifdef GFX_EXTRA_PRE_INIT GFX_EXTRA_PRE_INIT(); #endif #if defined(LCD_PWR_PIN) pinMode(LCD_PWR_PIN, OUTPUT); // sets the pin as output digitalWrite(LCD_PWR_PIN, HIGH); // power on #endif #ifdef GFX_BL pinMode(GFX_BL, OUTPUT); digitalWrite(GFX_BL, HIGH); #endif // Init Display if (!gfx->begin()) { Serial.println("gfx->begin() failed!"); } w = gfx->width(); h = gfx->height(); text_size = (h < 200) ? 1 : 2; banner_height = text_size * 3 * 4; graph_baseline = h - 20; // minus 2 text lines graph_height = graph_baseline - banner_height - 30; // minus 3 text lines channel_width = w / 17; signal_width = channel_width * 2; // init banner gfx->setTextSize(text_size); gfx->fillScreen(BLACK); gfx->setTextColor(RED); gfx->setCursor(0, 0); gfx->print("ESP"); gfx->setTextColor(WHITE); gfx->print(" WiFi Analyzer"); } bool matchBssidPrefix(uint8_t *a, uint8_t *b) { for (uint8_t i = 0; i < 5; i++) { // only compare first 5 bytes if (a[i] != b[i]) { return false; } } return true; } void loop() { uint8_t ap_count_list[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; int32_t noise_list[] = {RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR}; int32_t peak_list[] = {RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR}; int16_t peak_id_list[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; int32_t channel; int16_t idx; int32_t rssi; uint8_t *bssid; String ssid; uint16_t color; int16_t height, offset, text_width; // WiFi.scanNetworks will return the number of networks found #if defined(ESP32) int n = WiFi.scanNetworks(false /* async */, true /* show_hidden */, true /* passive */, 500 /* max_ms_per_chan */); #else int n = WiFi.scanNetworks(false /* async */, true /* show_hidden */); #endif // clear old graph gfx->fillRect(0, banner_height, w, h - banner_height, BLACK); gfx->setTextSize(1); if (n == 0) { gfx->setTextColor(WHITE); gfx->setCursor(0, banner_height); gfx->println("no networks found"); } else { for (int i = 0; i < n; i++) { channel = WiFi.channel(i); idx = channel - 1; rssi = WiFi.RSSI(i); bssid = WiFi.BSSID(i); // channel peak stat if (peak_list[idx] < rssi) { peak_list[idx] = rssi; peak_id_list[idx] = i; } // check signal come from same AP bool duplicate_SSID = false; for (int j = 0; j < i; j++) { if ((WiFi.channel(j) == channel) && matchBssidPrefix(WiFi.BSSID(j), bssid)) { duplicate_SSID = true; break; } } if (!duplicate_SSID) { ap_count_list[idx]++; // noise stat int32_t noise = rssi - RSSI_FLOOR; noise *= noise; if (channel > 4) { noise_list[idx - 4] += noise; } if (channel > 3) { noise_list[idx - 3] += noise; } if (channel > 2) { noise_list[idx - 2] += noise; } if (channel > 1) { noise_list[idx - 1] += noise; } noise_list[idx] += noise; if (channel < 14) { noise_list[idx + 1] += noise; } if (channel < 13) { noise_list[idx + 2] += noise; } if (channel < 12) { noise_list[idx + 3] += noise; } if (channel < 11) { noise_list[idx + 4] += noise; } } } // plot found WiFi info for (int i = 0; i < n; i++) { channel = WiFi.channel(i); idx = channel - 1; rssi = WiFi.RSSI(i); color = channel_color[idx]; height = constrain(map(rssi, RSSI_FLOOR, RSSI_CEILING, 1, graph_height), 1, graph_height); offset = (channel + 1) * channel_width; // trim rssi with RSSI_FLOOR if (rssi < RSSI_FLOOR) { rssi = RSSI_FLOOR; } // plot chart // gfx->drawLine(offset, graph_baseline - height, offset - signal_width, graph_baseline + 1, color); // gfx->drawLine(offset, graph_baseline - height, offset + signal_width, graph_baseline + 1, color); gfx->startWrite(); gfx->writeEllipseHelper(offset, graph_baseline + 1, signal_width, height, 0b0011, color); gfx->endWrite(); if (i == peak_id_list[idx]) { // Print SSID, signal strengh and if not encrypted String ssid = WiFi.SSID(i); if (ssid.length() == 0) { ssid = WiFi.BSSIDstr(i); } text_width = (ssid.length() + 6) * 6; if (text_width > w) { offset = 0; } else { offset -= signal_width; if ((offset + text_width) > w) { offset = w - text_width; } } gfx->setTextColor(color); gfx->setCursor(offset, graph_baseline - 10 - height); gfx->print(ssid); gfx->print('('); gfx->print(rssi); gfx->print(')'); #if defined(ESP32) if (WiFi.encryptionType(i) == WIFI_AUTH_OPEN) #else if (WiFi.encryptionType(i) == ENC_TYPE_NONE) #endif { gfx->print('*'); } } } } // print WiFi stat gfx->setTextColor(WHITE); gfx->setCursor(0, banner_height); gfx->print(n); gfx->print(" networks found, lesser noise channels: "); bool listed_first_channel = false; int32_t min_noise = noise_list[0]; // init with channel 1 value for (channel = 2; channel <= 11; channel++) // channels 12-14 may not available { idx = channel - 1; log_i("min_noise: %d, noise_list[%d]: %d", min_noise, idx, noise_list[idx]); if (noise_list[idx] < min_noise) { min_noise = noise_list[idx]; } } for (channel = 1; channel <= 11; channel++) // channels 12-14 may not available { idx = channel - 1; // check channel with min noise if (noise_list[idx] == min_noise) { if (!listed_first_channel) { listed_first_channel = true; } else { gfx->print(", "); } gfx->print(channel); } } // draw graph base axle gfx->drawFastHLine(0, graph_baseline, 320, WHITE); for (channel = 1; channel <= 14; channel++) { idx = channel - 1; offset = (channel + 1) * channel_width; gfx->setTextColor(channel_color[idx]); gfx->setCursor(offset - ((channel < 10) ? 3 : 6), graph_baseline + 2); gfx->print(channel); if (ap_count_list[idx] > 0) { gfx->setCursor(offset - ((ap_count_list[idx] < 10) ? 9 : 12), graph_baseline + 8 + 2); gfx->print('{'); gfx->print(ap_count_list[idx]); gfx->print('}'); } } // Wait a bit before scanning again delay(SCAN_INTERVAL); #if defined(SCAN_COUNT_SLEEP) // POWER SAVING if (++scan_count >= SCAN_COUNT_SLEEP) { #if defined(LCD_PWR_PIN) pinMode(LCD_PWR_PIN, INPUT); // disable pin #endif #if defined(GFX_BL) pinMode(GFX_BL, INPUT); // disable pin #endif #if defined(ESP32) esp_sleep_enable_ext0_wakeup(GPIO_NUM_36, LOW); esp_deep_sleep_start(); #else ESP.deepSleep(0); #endif } #endif // defined(SCAN_COUNT_SLEEP) }