This shows you the differences between two versions of the page.
pm:prj2025:avaduva:vlad_andrei.chira [2025/05/24 20:40] vlad_andrei.chira [Software Design] |
pm:prj2025:avaduva:vlad_andrei.chira [2025/05/24 21:37] (current) vlad_andrei.chira [Hardware Design] |
||
---|---|---|---|
Line 55: | Line 55: | ||
For the pushbutton: one side --> Gnd, other side --> Port 33 | For the pushbutton: one side --> Gnd, other side --> Port 33 | ||
- | The battery positive line goes into a switch that disable the battery when using the USB. Then it goes into the TP4056 charging module, who's output is then stepped up to 5V by the miniature boost converter. | + | The battery positive line goes into a switch that disables the battery when using the USB. Then it goes into the TP4056 charging module, who's output is then stepped up to 5V by the miniature boost converter. |
===== Software Design ===== | ===== Software Design ===== | ||
Line 76: | Line 76: | ||
I really wanted to port ImGui library to embedded, because it is an amazing library and fantastic for this UI use case. It is hardware and OS agnostic, it just requires a way to draw triangles essentially. That means a software renderer drawing into a framebuffer is absolutely possible. The framebuffer is then passed to the TFT library and blit it to the screen. Alas, this ESP32 does not have external PSRAM, so it's not possible to store the entire framebuffer into memory. You don't need to store the entire framebuffer, especially if you write a wrapper on top of the TFT library and call those functions to draw the primitives. Due to time constraints, I gave up trying to port it. But I may come back one day and finish the port! | I really wanted to port ImGui library to embedded, because it is an amazing library and fantastic for this UI use case. It is hardware and OS agnostic, it just requires a way to draw triangles essentially. That means a software renderer drawing into a framebuffer is absolutely possible. The framebuffer is then passed to the TFT library and blit it to the screen. Alas, this ESP32 does not have external PSRAM, so it's not possible to store the entire framebuffer into memory. You don't need to store the entire framebuffer, especially if you write a wrapper on top of the TFT library and call those functions to draw the primitives. Due to time constraints, I gave up trying to port it. But I may come back one day and finish the port! | ||
- | WiFi Controller | + | **WiFi Controller** |
This class is used to scan for WiFi networks. Since the ESP only spends a few milliseconds per channel to capture access points, every scan will be different: some networks will be missing. Which is why this class caches networks and evicts them if they haven't been seen in more than 30 seconds. The cache is a map with bssid as keys and a custom data type made up of <code>MyNetwork</code> and <code>lastSeen</code> as values. | This class is used to scan for WiFi networks. Since the ESP only spends a few milliseconds per channel to capture access points, every scan will be different: some networks will be missing. Which is why this class caches networks and evicts them if they haven't been seen in more than 30 seconds. The cache is a map with bssid as keys and a custom data type made up of <code>MyNetwork</code> and <code>lastSeen</code> as values. | ||
<code cpp> | <code cpp> | ||
Line 109: | Line 110: | ||
</code> | </code> | ||
- | ===== Rezultate Obţinute ===== | + | ** Sniffer Controller ** |
- | <note tip> | + | This class handles turning on Promiscuous Mode and sniffing all management frames by channel hopping around. It is responsible both for recording the MAC addresses it finds as well as monitoring for deauth frames. |
- | Care au fost rezultatele obţinute în urma realizării proiectului vostru. | + | |
- | </note> | + | The way the ESP32 firmware handles this is by registering a callback function that will be called for every captured frame. It only provides the buffer, so I must define a custom data type that defines an IEEE 802.11 header and cast to it: |
+ | <code cpp> | ||
+ | typedef struct { | ||
+ | uint16_t frame_ctrl; | ||
+ | uint16_t duration_id; | ||
+ | uint8_t addr1[6]; | ||
+ | uint8_t addr2[6]; | ||
+ | uint8_t addr3[6]; | ||
+ | uint16_t seq_ctrl; | ||
+ | } __attribute__((packed)) ieee80211_hdr_t; | ||
+ | </code> | ||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | esp_wifi_stop(); // tear down any previous mode | ||
+ | delay(100); | ||
+ | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); | ||
+ | esp_wifi_init(&cfg); | ||
+ | esp_wifi_set_storage(WIFI_STORAGE_RAM); | ||
+ | esp_wifi_set_mode(WIFI_MODE_NULL); | ||
+ | esp_wifi_start(); | ||
+ | delay(100); | ||
+ | |||
+ | esp_wifi_set_promiscuous_rx_cb(_promiscCallback); | ||
+ | wifi_promiscuous_filter_t flt = {}; | ||
+ | flt.filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT; | ||
+ | esp_wifi_set_promiscuous_filter(&flt); | ||
+ | |||
+ | // Enable promiscuous mode | ||
+ | esp_wifi_set_promiscuous(true); | ||
+ | </code> | ||
+ | |||
+ | I can then retrieve the MAC address and the management frame type like this: | ||
+ | <code cpp> | ||
+ | // In 802.11 header: skip Frame Control (2), Duration (2), Addr1 (6) = offset 10 | ||
+ | const uint8_t* srcMac = pkt->payload + 10; | ||
+ | char macStr[18]; | ||
+ | snprintf(macStr, sizeof(macStr), | ||
+ | "%02X:%02X:%02X:%02X:%02X:%02X", | ||
+ | srcMac[0], srcMac[1], srcMac[2], | ||
+ | srcMac[3], srcMac[4], srcMac[5]); | ||
+ | |||
+ | Serial.printf("MAC: %s RSSI: %d CH: %u\n", | ||
+ | macStr, | ||
+ | pkt->rx_ctrl.rssi, | ||
+ | pkt->rx_ctrl.channel); | ||
+ | </code> | ||
+ | |||
+ | To channel hop, in the loop() function I must run: | ||
+ | <code cpp> | ||
+ | // Channel hop 1–13 every 200 ms to catch all APs | ||
+ | static uint8_t channel = 1; | ||
+ | esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); | ||
+ | channel = (channel % 13) + 1; | ||
+ | delay(200); | ||
+ | </code> | ||
+ | |||
+ | ** UI code ** | ||
+ | |||
+ | The TFT library only provides primitives and text, so drawing graphs means drawing them from scratch. The DisplayManager class handles the UI based on the state of the application (see Finite State Machine approach above). | ||
+ | |||
+ | <code cpp> | ||
+ | void DisplayManager::update(bool dirty) { | ||
+ | if (currentState_ == lastState_ && !dirty) return; | ||
+ | |||
+ | switch (currentState_) { | ||
+ | case State::SplashScreen: drawSplashScreen(); break; | ||
+ | case State::MainMenu: drawMainMenu(); break; | ||
+ | case State::NetworkScan: drawNetworkScan(); break; | ||
+ | case State::ChannelOccupancy: drawChannelOccupancy(); break; | ||
+ | case State::SignalStrength: drawSignalStrength(); break; | ||
+ | case State::MacSniffing: drawMacSniffing(); break; | ||
+ | case State::DeauthMonitoring: drawDeauthMonitoring(); break; | ||
+ | } | ||
+ | lastState_ = currentState_; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | SPI is not fast enough to draw the entire screen continuously without flicker, so only if the state changed or the dirty flag is set manually will (re)rendering occur. Alternatively I can only draw things that will change, such as a graph curve, but keep all labels and axes untouched. | ||
+ | ===== Results ===== | ||
+ | |||
+ | Here are some photos of the UI: | ||
+ | |||
+ | {{:pm:prj2025:avaduva:splash.jpeg?700|}} | ||
+ | |||
+ | {{:pm:prj2025:avaduva:scan_list.jpeg?700|}} | ||
+ | |||
+ | {{:pm:prj2025:avaduva:main_menu.jpeg?700|}} | ||
+ | {{:pm:prj2025:avaduva:channel_occ.jpeg?700|}} | ||
===== Concluzii ===== | ===== Concluzii ===== | ||
Line 125: | Line 214: | ||
</note> | </note> | ||
- | ===== Jurnal ===== | ||
- | |||
- | <note tip> | ||
- | Puteți avea și o secțiune de jurnal în care să poată urmări asistentul de proiect progresul proiectului. | ||
- | </note> | ||
===== Bibliografie/Resurse ===== | ===== Bibliografie/Resurse ===== |