Просмотр исходного кода

Add DELETE requests and Arduino source code

jblanked 1 год назад
Родитель
Сommit
68d4faa863

+ 1 - 1
CHANGELOG.md

@@ -1,6 +1,6 @@
 ## 0.4 (New Keyboard and HTTP Methods)
 - Updated the text input to match the text input from the UART Terminal app (big thanks to xMasterx)
-- Added POST and PUT requests (with payloads)
+- Added POST, DELETE, and PUT requests (with payloads)
 - Added headers
 - Added more error handling
 

+ 2 - 1
README.md

@@ -30,6 +30,7 @@
    - https://httpbin.org/get Returns GET data.
    - https://httpbin.org/post Returns POST data.
    - https://httpbin.org/put Returns PUT data.
+   - https://httpbin.org/delete Returns DELETE data.
 
 5. **Running the Request**: Select **Run** from the main submenu to start the HTTP request process. The app will:
    - **Send Request**: Transmit the HTTP request via serial to the WiFi Dev Board.
@@ -46,7 +47,7 @@
    - Enter the complete URL of the website you intend to crawl (e.g., https://www.example.com/).
 
 2. **HTTP Method**
-   - Choose between GET, POST, and PUT.
+   - Choose between GET, POST, DELETE, and PUT.
 
 3. **Headers**
    - Add your required headers to be used in your HTTP requests

BIN
assets/.DS_Store


BIN
assets/FlipperHTTP/.DS_Store


+ 937 - 0
assets/FlipperHTTP/Arduino/FlipperHTTP.h

@@ -0,0 +1,937 @@
+// FlipperHTTP.h for flipper-http.ino
+
+#include <WiFi.h>
+#include <HTTPClient.h>
+#include <WiFiClientSecure.h>
+#include "SPIFFS.h"
+#include <ArduinoJson.h>
+#include <Arduino.h>
+#include <ArduinoHttpClient.h>
+
+#define B_PIN 4 // Blue
+#define G_PIN 5 // Green
+#define R_PIN 6 // Red
+
+#define ON LOW
+#define OFF HIGH
+
+class FlipperHTTP
+{
+public:
+    // Constructor
+    FlipperHTTP()
+    {
+    }
+
+    // Main methods for flipper-http.ino
+    void setup();
+    void loop();
+
+    // HTTP Methods
+    String get(String url);
+    String get(String url, const char *headerKeys[], const char *headerValues[], int headerSize);
+    String post(String url, String payload);
+    String post(String url, String payload, const char *headerKeys[], const char *headerValues[], int headerSize);
+    String put(String url, String payload);
+    String put(String url, String payload, const char *headerKeys[], const char *headerValues[], int headerSize);
+    String delete_request(String url, String payload);
+    String delete_request(String url, String payload, const char *headerKeys[], const char *headerValues[], int headerSize);
+
+    // Save and Load settings to and from SPIFFS
+    bool saveWifiSettings(String data);
+    bool loadWifiSettings();
+
+    // Connect to Wifi using the loaded SSID and Password
+    bool connectToWifi();
+
+    // Check if the Dev Board is connected to Wifi
+    bool isConnectedToWifi() { return WiFi.status() == WL_CONNECTED; }
+
+    // Read serial data until newline character
+    String readSerialLine();
+
+    // Clear serial buffer to avoid any residual data
+    void clearSerialBuffer()
+    {
+        while (Serial.available() > 0)
+        {
+            Serial.read();
+        }
+    }
+
+    // Turn on and off the LED
+    void ledAction(int pin = G_PIN, int timeout = 250)
+    {
+        digitalWrite(pin, ON);
+        delay(timeout);
+        digitalWrite(pin, OFF);
+        delay(timeout);
+    }
+
+    // Display LED sequence when Wifi Board is first connected to the Flipper
+    void ledStart()
+    {
+        pinMode(B_PIN, OUTPUT); // Set Blue Pin mode as output
+        pinMode(G_PIN, OUTPUT); // Set Green Pin mode as output
+        pinMode(R_PIN, OUTPUT); // Set Red Pin mode as output
+
+        digitalWrite(B_PIN, OFF);
+        digitalWrite(R_PIN, OFF);
+
+        ledAction();
+        ledAction();
+        ledAction();
+    }
+
+    // Starting LED (Green only)
+    void ledStatus()
+    {
+        digitalWrite(B_PIN, OFF);
+        digitalWrite(R_PIN, OFF);
+        digitalWrite(G_PIN, ON);
+    }
+
+    // Turn off all LEDs
+    void ledOff()
+    {
+        digitalWrite(B_PIN, OFF);
+        digitalWrite(G_PIN, OFF);
+        digitalWrite(R_PIN, OFF);
+    }
+
+private:
+    const char *settingsFilePath = "/flipper-http.json"; // Path to the settings file in the SPIFFS file system
+    char loadedSSID[64] = {0};                           // Variable to store SSID
+    char loadedPassword[64] = {0};                       // Variable to store password
+
+    bool readSerialSettings(String receivedData, bool connectAfterSave);
+};
+
+//  Connect to Wifi using the loaded SSID and Password
+bool FlipperHTTP::connectToWifi()
+{
+    if (String(loadedSSID) == "" || String(loadedPassword) == "")
+    {
+        Serial.println("[ERROR] WiFi SSID or Password is empty.");
+        return false;
+    }
+
+    WiFi.disconnect(true); // Ensure WiFi is disconnected before reconnecting
+    WiFi.begin(loadedSSID, loadedPassword);
+
+    int i = 0;
+    while (!this->isConnectedToWifi() && i < 20)
+    {
+        delay(500);
+        i++;
+        Serial.print(".");
+    }
+    Serial.println(); // Move to next line after dots
+
+    if (this->isConnectedToWifi())
+    {
+        Serial.println("[SUCCESS] Successfully connected to Wifi.");
+        return true;
+    }
+    else
+    {
+        Serial.println("[ERROR] Failed to connect to Wifi.");
+        return false;
+    }
+}
+
+// Save Wifi settings to SPIFFS
+bool FlipperHTTP::saveWifiSettings(String jsonData)
+{
+    File file = SPIFFS.open(settingsFilePath, FILE_WRITE);
+    if (!file)
+    {
+        Serial.println("[ERROR] Failed to open file for writing.");
+        return false;
+    }
+
+    file.print(jsonData);
+    file.close();
+    Serial.println("[SUCCESS] Settings saved to SPIFFS.");
+    return true;
+}
+
+// Load Wifi settings from SPIFFS
+bool FlipperHTTP::loadWifiSettings()
+{
+    File file = SPIFFS.open(settingsFilePath, FILE_READ);
+    if (!file)
+    {
+        Serial.println("[ERROR] Failed to open file for reading.");
+        return "";
+    }
+
+    // Read the entire file content
+    String fileContent = file.readString();
+    file.close();
+
+    return fileContent;
+}
+
+String FlipperHTTP::readSerialLine()
+{
+    String receivedData = "";
+
+    while (Serial.available() > 0)
+    {
+        char incomingChar = Serial.read();
+        if (incomingChar == '\n')
+        {
+            break;
+        }
+        receivedData += incomingChar;
+        delay(1); // Minimal delay to allow buffer to fill
+    }
+
+    receivedData.trim(); // Remove any leading/trailing whitespace
+
+    return receivedData;
+}
+
+bool FlipperHTTP::readSerialSettings(String receivedData, bool connectAfterSave)
+{
+    DynamicJsonDocument doc(1024);
+    DeserializationError error = deserializeJson(doc, receivedData);
+
+    if (error)
+    {
+        Serial.print("[ERROR] Failed to parse JSON: ");
+        Serial.println(error.c_str());
+        return false;
+    }
+
+    // Extract values from JSON
+    if (doc.containsKey("ssid") && doc.containsKey("password"))
+    {
+        strlcpy(loadedSSID, doc["ssid"], sizeof(loadedSSID));
+        strlcpy(loadedPassword, doc["password"], sizeof(loadedPassword));
+    }
+    else
+    {
+        Serial.println("[ERROR] JSON does not contain ssid and password.");
+        return false;
+    }
+
+    // Save to SPIFFS
+    if (!this->saveWifiSettings(receivedData))
+    {
+        Serial.println("[ERROR] Failed to save settings to file.");
+        return false;
+    }
+
+    // Attempt to reconnect with new settings
+    if (connectAfterSave && this->connectToWifi())
+    {
+        Serial.println("[SUCCESS] Connected to the new Wifi network.");
+    }
+    else
+    {
+        Serial.println("[WARNING] Saved settings but failed to connect.");
+    }
+
+    return true;
+}
+
+String FlipperHTTP::get(String url)
+{
+    WiFiClientSecure client;
+    client.setInsecure(); // Bypass certificate validation
+
+    HTTPClient http;
+    String payload = "";
+
+    if (http.begin(client, url))
+    {
+        int httpCode = http.GET();
+
+        if (httpCode > 0)
+        {
+            payload = http.getString();
+            http.end();
+            return payload;
+        }
+        else
+        {
+            Serial.print("[ERROR] GET Request Failed, error: ");
+            Serial.println(http.errorToString(httpCode).c_str());
+        }
+        http.end();
+    }
+    else
+    {
+        Serial.println("[ERROR] Unable to connect to the server.");
+    }
+
+    // Clear serial buffer to avoid any residual data
+    this->clearSerialBuffer();
+
+    return payload;
+}
+
+String FlipperHTTP::get(String url, const char *headerKeys[], const char *headerValues[], int headerSize)
+{
+    WiFiClientSecure client;
+    client.setInsecure(); // Bypass certificate
+
+    HTTPClient http;
+    String payload = "";
+
+    http.collectHeaders(headerKeys, headerSize);
+
+    if (http.begin(client, url))
+    {
+
+        for (int i = 0; i < headerSize; i++)
+        {
+            http.addHeader(headerKeys[i], headerValues[i]);
+        }
+
+        int httpCode = http.GET();
+
+        if (httpCode > 0)
+        {
+            payload = http.getString();
+            http.end();
+            return payload;
+        }
+        else
+        {
+            Serial.print("[ERROR] GET Request Failed, error: ");
+            Serial.println(http.errorToString(httpCode).c_str());
+        }
+        http.end();
+    }
+    else
+    {
+        Serial.println("[ERROR] Unable to connect to the server.");
+    }
+
+    // Clear serial buffer to avoid any residual data
+    this->clearSerialBuffer();
+
+    return payload;
+}
+
+String FlipperHTTP::delete_request(String url, String payload)
+{
+    WiFiClientSecure client;
+    client.setInsecure(); // Bypass certificate
+
+    HTTPClient http;
+    String response = "";
+
+    if (http.begin(client, url))
+    {
+        int httpCode = http.sendRequest("DELETE", payload);
+
+        if (httpCode > 0)
+        {
+            response = http.getString();
+            http.end();
+            return response;
+        }
+        else
+        {
+            Serial.print("[ERROR] DELETE Request Failed, error: ");
+            Serial.println(http.errorToString(httpCode).c_str());
+        }
+        http.end();
+    }
+
+    else
+    {
+        Serial.println("[ERROR] Unable to connect to the server.");
+    }
+
+    // Clear serial buffer to avoid any residual data
+    this->clearSerialBuffer();
+
+    return response;
+}
+
+String FlipperHTTP::delete_request(String url, String payload, const char *headerKeys[], const char *headerValues[], int headerSize)
+{
+    WiFiClientSecure client;
+    client.setInsecure(); // Bypass certificate
+
+    HTTPClient http;
+    String response = "";
+
+    http.collectHeaders(headerKeys, headerSize);
+
+    if (http.begin(client, url))
+    {
+
+        for (int i = 0; i < headerSize; i++)
+        {
+            http.addHeader(headerKeys[i], headerValues[i]);
+        }
+
+        int httpCode = http.sendRequest("DELETE", payload);
+
+        if (httpCode > 0)
+        {
+            response = http.getString();
+            http.end();
+            return response;
+        }
+        else
+        {
+            Serial.print("[ERROR] DELETE Request Failed, error: ");
+            Serial.println(http.errorToString(httpCode).c_str());
+        }
+        http.end();
+    }
+
+    else
+    {
+        Serial.println("[ERROR] Unable to connect to the server.");
+    }
+
+    // Clear serial buffer to avoid any residual data
+    this->clearSerialBuffer();
+
+    return response;
+}
+
+String FlipperHTTP::post(String url, String payload, const char *headerKeys[], const char *headerValues[], int headerSize)
+{
+    WiFiClientSecure client;
+    client.setInsecure(); // Bypass certificate
+
+    HTTPClient http;
+    String response = "";
+
+    http.collectHeaders(headerKeys, headerSize);
+
+    if (http.begin(client, url))
+    {
+
+        for (int i = 0; i < headerSize; i++)
+        {
+            http.addHeader(headerKeys[i], headerValues[i]);
+        }
+
+        int httpCode = http.POST(payload);
+
+        if (httpCode > 0)
+        {
+            response = http.getString();
+            http.end();
+            return response;
+        }
+        else
+        {
+            Serial.print("[ERROR] POST Request Failed, error: ");
+            Serial.println(http.errorToString(httpCode).c_str());
+        }
+        http.end();
+    }
+
+    else
+    {
+        Serial.println("[ERROR] Unable to connect to the server.");
+    }
+
+    // Clear serial buffer to avoid any residual data
+    this->clearSerialBuffer();
+
+    return response;
+}
+
+String FlipperHTTP::post(String url, String payload)
+{
+    WiFiClientSecure client;
+    client.setInsecure(); // Bypass certificate
+
+    HTTPClient http;
+    String response = "";
+
+    if (http.begin(client, url))
+    {
+
+        int httpCode = http.POST(payload);
+
+        if (httpCode > 0)
+        {
+            response = http.getString();
+            http.end();
+            return response;
+        }
+        else
+        {
+            Serial.print("[ERROR] POST Request Failed, error: ");
+            Serial.println(http.errorToString(httpCode).c_str());
+        }
+        http.end();
+    }
+
+    else
+    {
+        Serial.println("[ERROR] Unable to connect to the server.");
+    }
+
+    // Clear serial buffer to avoid any residual data
+    this->clearSerialBuffer();
+
+    return response;
+}
+
+String FlipperHTTP::put(String url, String payload, const char *headerKeys[], const char *headerValues[], int headerSize)
+{
+    WiFiClientSecure client;
+    client.setInsecure(); // Bypass certificate
+
+    HTTPClient http;
+    String response = "";
+
+    http.collectHeaders(headerKeys, headerSize);
+
+    if (http.begin(client, url))
+    {
+
+        for (int i = 0; i < headerSize; i++)
+        {
+            http.addHeader(headerKeys[i], headerValues[i]);
+        }
+
+        int httpCode = http.PUT(payload);
+
+        if (httpCode > 0)
+        {
+            response = http.getString();
+            http.end();
+            return response;
+        }
+        else
+        {
+            Serial.print("[ERROR] PUT Request Failed, error: ");
+            Serial.println(http.errorToString(httpCode).c_str());
+        }
+        http.end();
+    }
+
+    else
+    {
+        Serial.println("[ERROR] Unable to connect to the server.");
+    }
+
+    // Clear serial buffer to avoid any residual data
+    this->clearSerialBuffer();
+
+    return response;
+}
+
+String FlipperHTTP::put(String url, String payload)
+{
+    WiFiClientSecure client;
+    client.setInsecure(); // Bypass certificate
+
+    HTTPClient http;
+    String response = "";
+
+    if (http.begin(client, url))
+    {
+        int httpCode = http.PUT(payload);
+
+        if (httpCode > 0)
+        {
+            response = http.getString();
+            http.end();
+            return response;
+        }
+        else
+        {
+            Serial.print("[ERROR] PUT Request Failed, error: ");
+            Serial.println(http.errorToString(httpCode).c_str());
+        }
+        http.end();
+    }
+
+    else
+    {
+        Serial.println("[ERROR] Unable to connect to the server.");
+    }
+
+    // Clear serial buffer to avoid any residual data
+    this->clearSerialBuffer();
+
+    return response;
+}
+
+void FlipperHTTP::setup()
+{
+    Serial.begin(115200);
+    // Initialize SPIFFS
+    if (!SPIFFS.begin(true))
+    {
+        Serial.println("[ERROR] SPIFFS initialization failed.");
+        ESP.restart();
+    }
+
+    this->ledStart();
+    Serial.flush();
+}
+
+void FlipperHTTP::loop()
+{
+    // Check if there's incoming serial data
+    if (Serial.available() > 0)
+    {
+        // Read the incoming serial data until newline
+        String _data = this->readSerialLine();
+
+        if (_data.length() == 0)
+        {
+            // No complete command received
+            return;
+        }
+
+        this->ledStatus();
+
+        // Ping/Pong to see if board/flipper is connected
+        if (_data.startsWith("[PING]"))
+        {
+            Serial.println("[PONG]");
+        }
+        // Handle [WIFI/SAVE] command
+        else if (_data.startsWith("[WIFI/SAVE]"))
+        {
+            // Extract JSON data by removing the command part
+            String jsonData = _data.substring(strlen("[WIFI/SAVE]"));
+            jsonData.trim(); // Remove any leading/trailing whitespace
+
+            // Parse and save the settings
+            if (this->readSerialSettings(jsonData, true))
+            {
+                Serial.println("[SUCCESS] Wifi settings saved.");
+            }
+            else
+            {
+                Serial.println("[ERROR] Failed to save Wifi settings.");
+            }
+        }
+        // Handle [WIFI/CONNECT] command
+        else if (_data == "[WIFI/CONNECT]")
+        {
+            // Check if WiFi is already connected
+            if (!this->isConnectedToWifi())
+            {
+                // Attempt to connect to Wifi
+                if (this->connectToWifi())
+                {
+                    Serial.println("[SUCCESS] Connected to Wifi.");
+                }
+                else
+                {
+                    Serial.println("[ERROR] Failed to connect to Wifi.");
+                }
+            }
+            else
+            {
+                Serial.println("[INFO] Already connected to Wifi.");
+            }
+        }
+        // Handle [WIFI/DISCONNECT] command
+        else if (_data == "[WIFI/DISCONNECT]")
+        {
+            WiFi.disconnect(true);
+            Serial.println("[DISCONNECTED] Wifi has been disconnected.");
+        }
+        // Handle [GET] command
+        else if (_data.startsWith("[GET]"))
+        {
+
+            if (!this->isConnectedToWifi() && !this->connectToWifi())
+            {
+                Serial.println("[ERROR] Not connected to Wifi. Failed to reconnect.");
+                this->ledOff();
+                return;
+            }
+            // Extract URL by removing the command part
+            String url = _data.substring(strlen("[GET]"));
+            url.trim();
+
+            // GET request
+            String getData = this->get(url);
+            if (getData != "")
+            {
+                Serial.println("[GET/SUCCESS] GET request successful.");
+                Serial.println(getData);
+                Serial.flush();
+                Serial.println();
+                Serial.println("[GET/END]");
+            }
+            else
+            {
+                Serial.println("[ERROR] GET request failed or returned empty data.");
+            }
+        }
+        // Handle [GET/HTTP] command
+        else if (_data.startsWith("[GET/HTTP]"))
+        {
+            if (!this->isConnectedToWifi() && !this->connectToWifi())
+            {
+                Serial.println("[ERROR] Not connected to Wifi. Failed to reconnect.");
+                this->ledOff();
+                return;
+            }
+
+            // Extract the JSON by removing the command part
+            String jsonData = _data.substring(strlen("[GET/HTTP]"));
+            jsonData.trim();
+
+            DynamicJsonDocument doc(1024);
+            DeserializationError error = deserializeJson(doc, jsonData);
+
+            if (error)
+            {
+                Serial.print("[ERROR] Failed to parse JSON.");
+                this->ledOff();
+                return;
+            }
+
+            // Extract values from JSON
+            if (!doc.containsKey("url"))
+            {
+                Serial.println("[ERROR] JSON does not contain url.");
+                this->ledOff();
+                return;
+            }
+            String url = doc["url"];
+
+            // Extract headers if available
+            const char *headerKeys[10];
+            const char *headerValues[10];
+            int headerSize = 0;
+
+            if (doc.containsKey("headers"))
+            {
+                JsonObject headers = doc["headers"];
+                for (JsonPair header : headers)
+                {
+                    headerKeys[headerSize] = header.key().c_str();
+                    headerValues[headerSize] = header.value();
+                    headerSize++;
+                }
+            }
+
+            // GET request
+            String getData = this->get(url, headerKeys, headerValues, headerSize);
+            if (getData != "")
+            {
+                Serial.println("[GET/SUCCESS] GET request successful.");
+                Serial.println(getData);
+                Serial.flush();
+                Serial.println();
+                Serial.println("[GET/END]");
+            }
+            else
+            {
+                Serial.println("[ERROR] GET request failed or returned empty data.");
+            }
+        }
+        // Handle [POST/HTTP] command
+        else if (_data.startsWith("[POST/HTTP]"))
+        {
+            if (!this->isConnectedToWifi() && !this->connectToWifi())
+            {
+                Serial.println("[ERROR] Not connected to Wifi. Failed to reconnect.");
+                this->ledOff();
+                return;
+            }
+
+            // Extract the JSON by removing the command part
+            String jsonData = _data.substring(strlen("[POST/HTTP]"));
+            jsonData.trim();
+
+            DynamicJsonDocument doc(1024);
+            DeserializationError error = deserializeJson(doc, jsonData);
+
+            if (error)
+            {
+                Serial.print("[ERROR] Failed to parse JSON.");
+                this->ledOff();
+                return;
+            }
+
+            // Extract values from JSON
+            if (!doc.containsKey("url") || !doc.containsKey("payload"))
+            {
+                Serial.println("[ERROR] JSON does not contain url or payload.");
+                this->ledOff();
+                return;
+            }
+            String url = doc["url"];
+            String payload = doc["payload"];
+
+            // Extract headers if available
+            const char *headerKeys[10];
+            const char *headerValues[10];
+            int headerSize = 0;
+
+            if (doc.containsKey("headers"))
+            {
+                JsonObject headers = doc["headers"];
+                for (JsonPair header : headers)
+                {
+                    headerKeys[headerSize] = header.key().c_str();
+                    headerValues[headerSize] = header.value();
+                    headerSize++;
+                }
+            }
+
+            // POST request
+            String postData = this->post(url, payload, headerKeys, headerValues, headerSize);
+            if (postData != "")
+            {
+                Serial.println("[POST/SUCCESS] POST request successful.");
+                Serial.println(postData);
+                Serial.flush();
+                Serial.println();
+                Serial.println("[POST/END]");
+            }
+            else
+            {
+                Serial.println("[ERROR] POST request failed or returned empty data.");
+            }
+        }
+        // Handle [PUT/HTTP] command
+        else if (_data.startsWith("[PUT/HTTP]"))
+        {
+            if (!this->isConnectedToWifi() && !this->connectToWifi())
+            {
+                Serial.println("[ERROR] Not connected to Wifi. Failed to reconnect.");
+                this->ledOff();
+                return;
+            }
+
+            // Extract the JSON by removing the command part
+            String jsonData = _data.substring(strlen("[PUT/HTTP]"));
+            jsonData.trim();
+
+            DynamicJsonDocument doc(1024);
+            DeserializationError error = deserializeJson(doc, jsonData);
+
+            if (error)
+            {
+                Serial.print("[ERROR] Failed to parse JSON.");
+                this->ledOff();
+                return;
+            }
+
+            // Extract values from JSON
+            if (!doc.containsKey("url") || !doc.containsKey("payload"))
+            {
+                Serial.println("[ERROR] JSON does not contain url or payload.");
+                this->ledOff();
+                return;
+            }
+            String url = doc["url"];
+            String payload = doc["payload"];
+
+            // Extract headers if available
+            const char *headerKeys[10];
+            const char *headerValues[10];
+            int headerSize = 0;
+
+            if (doc.containsKey("headers"))
+            {
+                JsonObject headers = doc["headers"];
+                for (JsonPair header : headers)
+                {
+                    headerKeys[headerSize] = header.key().c_str();
+                    headerValues[headerSize] = header.value();
+                    headerSize++;
+                }
+            }
+
+            // PUT request
+            String putData = this->put(url, payload, headerKeys, headerValues, headerSize);
+            if (putData != "")
+            {
+                Serial.println("[PUT/SUCCESS] PUT request successful.");
+                Serial.println(putData);
+                Serial.flush();
+                Serial.println();
+                Serial.println("[PUT/END]");
+            }
+            else
+            {
+                Serial.println("[ERROR] PUT request failed or returned empty data.");
+            }
+        }
+        // Handle [DELETE/HTTP] command
+        else if (_data.startsWith("[DELETE/HTTP]"))
+        {
+            if (!this->isConnectedToWifi() && !this->connectToWifi())
+            {
+                Serial.println("[ERROR] Not connected to Wifi. Failed to reconnect.");
+                this->ledOff();
+                return;
+            }
+
+            // Extract the JSON by removing the command part
+            String jsonData = _data.substring(strlen("[DELETE/HTTP]"));
+            jsonData.trim();
+
+            DynamicJsonDocument doc(1024);
+            DeserializationError error = deserializeJson(doc, jsonData);
+
+            if (error)
+            {
+                Serial.print("[ERROR] Failed to parse JSON.");
+                this->ledOff();
+                return;
+            }
+
+            // Extract values from JSON
+            if (!doc.containsKey("url") || !doc.containsKey("payload"))
+            {
+                Serial.println("[ERROR] JSON does not contain url or payload.");
+                this->ledOff();
+                return;
+            }
+            String url = doc["url"];
+            String payload = doc["payload"];
+
+            // Extract headers if available
+            const char *headerKeys[10];
+            const char *headerValues[10];
+            int headerSize = 0;
+
+            if (doc.containsKey("headers"))
+            {
+                JsonObject headers = doc["headers"];
+                for (JsonPair header : headers)
+                {
+                    headerKeys[headerSize] = header.key().c_str();
+                    headerValues[headerSize] = header.value();
+                    headerSize++;
+                }
+            }
+
+            // DELETE request
+            String deleteData = this->delete_request(url, payload, headerKeys, headerValues, headerSize);
+            if (deleteData != "")
+            {
+                Serial.println("[DELETE/SUCCESS] DELETE request successful.");
+                Serial.println(deleteData);
+                Serial.flush();
+                Serial.println();
+                Serial.println("[DELETE/END]");
+            }
+            else
+            {
+                Serial.println("[ERROR] DELETE request failed or returned empty data.");
+            }
+        }
+
+        this->ledOff();
+    }
+}

+ 15 - 0
assets/FlipperHTTP/Arduino/flipper-http.ino

@@ -0,0 +1,15 @@
+// FlipperHTTP Library
+
+#include <FlipperHTTP.h>
+
+FlipperHTTP fhttp;
+
+void setup()
+{
+  fhttp.setup();
+}
+
+void loop()
+{
+  fhttp.loop();
+}

+ 3 - 1
assets/FlipperHTTP/README.md

@@ -42,6 +42,7 @@ Star the repository (https://github.com/jblanked/WebCrawler-FlipperZero) and fol
 | `flipper_http_get_request_with_headers`          | `bool`           | `const char *url`, `const char *headers`                                                                       | Sends a GET request with custom headers to the specified URL.                                      |
 | `flipper_http_post_request_with_headers`         | `bool`           | `const char *url`, `const char *headers`, `const char *payload`                                                 | Sends a POST request with custom headers and a payload to the specified URL.                       |
 | `flipper_http_put_request_with_headers`          | `bool`           | `const char *url`, `const char *headers`, `const char *payload`                                                 | Sends a PUT request with custom headers and a payload to the specified URL.                        |
+| `flipper_http_delete_request_with_headers`          | `bool`           | `const char *url`, `const char *headers`, `const char *payload`                                                 | Sends a DELETE request with custom headers and a payload to the specified URL.                        |
 | `flipper_http_save_received_data`                | `bool`           | `size_t bytes_received`, `const char line_buffer[]`                                                            | Saves the received data to the SD card, with the specified size and buffer.                        |
 
 
@@ -59,4 +60,5 @@ Star the repository (https://github.com/jblanked/WebCrawler-FlipperZero) and fol
 | `fhttp.get_request`                    | `string`         | `url: string`                                       | Sends a GET request to the specified URL and returns the response of the response.             |
 | `fhttp.get_request_with_headers`       | `string`         | `url: string`, `headers: string`                    | Sends a GET request with headers and returns the response of the response.                    |
 | `fhttp.post_request_with_headers`      | `string`         | `url: string`, `headers: string`, `payload: string` | Sends a POST request with headers and payload, and returns the response of the response.       |
-| `fhttp.put_request_with_headers`       | `string`         | `url: string`, `headers: string`, `payload: string` | Sends a PUT request with headers and payload, and returns the response of the response.        |
+| `fhttp.put_request_with_headers`       | `string`         | `url: string`, `headers: string`, `payload: string` | Sends a PUT request with headers and payload, and returns the response of the response.        |
+| `fhttp.delete_request_with_headers`       | `string`         | `url: string`, `headers: string`, `payload: string` | Sends a PUT request with headers and payload, and returns the response of the response.        |

+ 115 - 2
assets/FlipperHTTP/flipper_http.h

@@ -36,6 +36,7 @@ bool flipper_http_get_request(const char *url);
 bool flipper_http_get_request_with_headers(const char *url, const char *headers);
 bool flipper_http_post_request_with_headers(const char *url, const char *headers, const char *payload);
 bool flipper_http_put_request_with_headers(const char *url, const char *headers, const char *payload);
+bool flipper_http_delete_request_with_headers(const char *url, const char *headers, const char *payload);
 //---
 bool flipper_http_save_received_data(size_t bytes_received, const char line_buffer[]);
 
@@ -89,6 +90,9 @@ typedef struct
 
     bool started_receiving_put; // Indicates if a PUT request has started
     bool just_started_put;      // Indicates if PUT data reception has just started
+
+    bool started_receiving_delete; // Indicates if a DELETE request has started
+    bool just_started_delete;      // Indicates if DELETE data reception has just started
 } FlipperHTTP;
 
 // Declare uart as extern to prevent multiple definitions
@@ -104,10 +108,13 @@ static FlipperHTTP fhttp;
 void get_timeout_timer_callback(void *context)
 {
     UNUSED(context);
-    FURI_LOG_E(HTTP_TAG, "Timeout reached: 2 seconds without receiving [GET/END]...");
+    FURI_LOG_E(HTTP_TAG, "Timeout reached: 2 seconds without receiving the end.");
 
     // Reset the state
     fhttp.started_receiving_get = false;
+    fhttp.started_receiving_post = false;
+    fhttp.started_receiving_put = false;
+    fhttp.started_receiving_delete = false;
 
     // Free received data if any
     if (fhttp.received_data)
@@ -274,7 +281,7 @@ bool flipper_http_init(FlipperHTTP_Callback callback, void *context)
 
     if (!fhttp.get_timeout_timer)
     {
-        FURI_LOG_E(HTTP_TAG, "Failed to allocate GET timeout timer.");
+        FURI_LOG_E(HTTP_TAG, "Failed to allocate HTTP request timeout timer.");
         // Cleanup resources
         furi_hal_serial_async_rx_stop(fhttp.serial_handle);
         furi_hal_serial_disable_direction(fhttp.serial_handle, FuriHalSerialDirectionRx);
@@ -620,6 +627,42 @@ bool flipper_http_put_request_with_headers(const char *url, const char *headers,
     // The response will be handled asynchronously via the callback
     return true;
 }
+// Function to send a DELETE request with headers
+/**
+ * @brief      Send a DELETE request to the specified URL.
+ * @return     true if the request was successful, false otherwise.
+ * @param      url  The URL to send the DELETE request to.
+ * @param      headers  The headers to send with the DELETE request.
+ * @param      data  The data to send with the DELETE request.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_delete_request_with_headers(const char *url, const char *headers, const char *payload)
+{
+    if (!url || !headers || !payload)
+    {
+        FURI_LOG_E("FlipperHTTP", "Invalid arguments provided to flipper_http_delete_request_with_headers.");
+        return false;
+    }
+
+    // Prepare DELETE request command with headers and data
+    char command[256];
+    int ret = snprintf(command, sizeof(command), "[DELETE/HTTP]{\"url\":\"%s\",\"headers\":%s,\"payload\":%s}", url, headers, payload);
+    if (ret < 0 || ret >= (int)sizeof(command))
+    {
+        FURI_LOG_E("FlipperHTTP", "Failed to format DELETE request command with headers and data.");
+        return false;
+    }
+
+    // Send DELETE request via UART
+    if (!flipper_http_send_data(command))
+    {
+        FURI_LOG_E("FlipperHTTP", "Failed to send DELETE request command with headers and data.");
+        return false;
+    }
+
+    // The response will be handled asynchronously via the callback
+    return true;
+}
 // Function to handle received data asynchronously
 /**
  * @brief      Callback function to handle received data asynchronously.
@@ -837,6 +880,69 @@ void flipper_http_rx_callback(const char *line, void *context)
         return;
     }
 
+    // Check if we've started receiving data from a DELETE request
+    else if (fhttp.started_receiving_delete)
+    {
+        // Restart the timeout timer each time new data is received
+        furi_timer_restart(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
+
+        if (strstr(line, "[DELETE/END]") != NULL)
+        {
+            FURI_LOG_I(HTTP_TAG, "DELETE request completed.");
+            // Stop the timer since we've completed the DELETE request
+            furi_timer_stop(fhttp.get_timeout_timer);
+
+            if (fhttp.received_data)
+            {
+                flipper_http_save_received_data(strlen(fhttp.received_data), fhttp.received_data);
+                free(fhttp.received_data);
+                fhttp.received_data = NULL;
+                fhttp.started_receiving_delete = false;
+                fhttp.just_started_delete = false;
+                fhttp.state = IDLE;
+                return;
+            }
+            else
+            {
+                FURI_LOG_E(HTTP_TAG, "No data received.");
+                fhttp.started_receiving_delete = false;
+                fhttp.just_started_delete = false;
+                fhttp.state = IDLE;
+                return;
+            }
+        }
+
+        // Append the new line to the existing data
+        if (fhttp.received_data == NULL)
+        {
+            fhttp.received_data = (char *)malloc(strlen(line) + 2); // +2 for newline and null terminator
+            if (fhttp.received_data)
+            {
+                strcpy(fhttp.received_data, line);
+                fhttp.received_data[strlen(line)] = '\n';     // Add newline
+                fhttp.received_data[strlen(line) + 1] = '\0'; // Null terminator
+            }
+        }
+        else
+        {
+            size_t current_len = strlen(fhttp.received_data);
+            size_t new_size = current_len + strlen(line) + 2; // +2 for newline and null terminator
+            fhttp.received_data = (char *)realloc(fhttp.received_data, new_size);
+            if (fhttp.received_data)
+            {
+                memcpy(fhttp.received_data + current_len, line, strlen(line)); // Copy line at the end of the current data
+                fhttp.received_data[current_len + strlen(line)] = '\n';        // Add newline
+                fhttp.received_data[current_len + strlen(line) + 1] = '\0';    // Null terminator
+            }
+        }
+
+        if (!fhttp.just_started_delete)
+        {
+            fhttp.just_started_delete = true;
+        }
+        return;
+    }
+
     // Handle different types of responses
     if (strstr(line, "[SUCCESS]") != NULL || strstr(line, "[CONNECTED]") != NULL)
     {
@@ -873,6 +979,13 @@ void flipper_http_rx_callback(const char *line, void *context)
         fhttp.state = RECEIVING;
         return;
     }
+    else if (strstr(line, "[DELETE/SUCCESS]") != NULL)
+    {
+        FURI_LOG_I(HTTP_TAG, "DELETE request succeeded.");
+        fhttp.started_receiving_delete = true;
+        fhttp.state = RECEIVING;
+        return;
+    }
     else if (strstr(line, "[DISCONNECTED]") != NULL)
     {
         FURI_LOG_I(HTTP_TAG, "WiFi disconnected successfully.");

+ 21 - 4
assets/FlipperHTTP/flipper_http.js

@@ -115,7 +115,6 @@ let fhttp = {
     get_request: function (url) {
         serial.write('[GET]' + url);
         if (this.read_data(500) === "[GET/SUCCESS] GET request successful.") {
-            print("GET request successful");
             while (true) {
                 let line = this.read_data(500);
                 if (line === "[GET/END]") {
@@ -137,7 +136,6 @@ let fhttp = {
     get_request_with_headers: function (url, headers) {
         serial.write('[GET/HTTP]{url:"' + url + '",headers:' + headers + '}');
         if (this.read_data(500) === "[GET/SUCCESS] GET request successful.") {
-            print("GET request successful");
             while (true) {
                 let line = this.read_data(500);
                 if (line === "[GET/END]") {
@@ -159,7 +157,6 @@ let fhttp = {
     post_request_with_headers: function (url, headers, data) {
         serial.write('[POST/HTTP]{"url":"' + url + '","headers":' + headers + ',"payload":' + data + '}');
         if (this.read_data(500) === "[POST/SUCCESS] POST request successful.") {
-            print("POST request successful");
             while (true) {
                 let line = this.read_data(500);
                 if (line === "[POST/END]") {
@@ -181,7 +178,6 @@ let fhttp = {
     put_request_with_headers: function (url, headers, data) {
         serial.write('[PUT/HTTP]{"url":"' + url + '","headers":' + headers + ',"payload":' + data + '}');
         if (this.read_data(500) === "[PUT/SUCCESS] PUT request successful.") {
-            print("PUT request successful");
             while (true) {
                 let line = this.read_data(500);
                 if (line === "[PUT/END]") {
@@ -199,6 +195,27 @@ let fhttp = {
         this.clear_buffer(); // Clear the buffer
         return "";
     },
+    // send DELETE request with headers
+    delete_request_with_headers: function (url, headers, data) {
+        serial.write('[DELETE/HTTP]{"url":"' + url + '","headers":' + headers + ',"payload":' + data + '}');
+        if (this.read_data(500) === "[DELETE/SUCCESS] DELETE request successful.") {
+            while (true) {
+                let line = this.read_data(500);
+                if (line === "[DELETE/END]") {
+                    break;
+                }
+                if (line !== undefined) {
+                    this.clear_buffer(false); // Clear the buffer
+                    return line;
+                }
+            }
+        }
+        else {
+            print("DELETE request failed");
+        }
+        this.clear_buffer(); // Clear the buffer
+        return "";
+    },
     // Helper function to check if a string contains another string
     includes: function (text, search) {
         let stringLength = text.length;

BIN
assets/FlipperHTTP/flipper_http_bootloader.bin


BIN
assets/FlipperHTTP/flipper_http_firmware_a.bin


BIN
assets/FlipperHTTP/flipper_http_partitions.bin


+ 115 - 2
flipper_http.h

@@ -36,6 +36,7 @@ bool flipper_http_get_request(const char *url);
 bool flipper_http_get_request_with_headers(const char *url, const char *headers);
 bool flipper_http_post_request_with_headers(const char *url, const char *headers, const char *payload);
 bool flipper_http_put_request_with_headers(const char *url, const char *headers, const char *payload);
+bool flipper_http_delete_request_with_headers(const char *url, const char *headers, const char *payload);
 //---
 bool flipper_http_save_received_data(size_t bytes_received, const char line_buffer[]);
 
@@ -89,6 +90,9 @@ typedef struct
 
     bool started_receiving_put; // Indicates if a PUT request has started
     bool just_started_put;      // Indicates if PUT data reception has just started
+
+    bool started_receiving_delete; // Indicates if a DELETE request has started
+    bool just_started_delete;      // Indicates if DELETE data reception has just started
 } FlipperHTTP;
 
 // Declare uart as extern to prevent multiple definitions
@@ -104,10 +108,13 @@ static FlipperHTTP fhttp;
 void get_timeout_timer_callback(void *context)
 {
     UNUSED(context);
-    FURI_LOG_E(HTTP_TAG, "Timeout reached: 2 seconds without receiving [GET/END]...");
+    FURI_LOG_E(HTTP_TAG, "Timeout reached: 2 seconds without receiving the end.");
 
     // Reset the state
     fhttp.started_receiving_get = false;
+    fhttp.started_receiving_post = false;
+    fhttp.started_receiving_put = false;
+    fhttp.started_receiving_delete = false;
 
     // Free received data if any
     if (fhttp.received_data)
@@ -274,7 +281,7 @@ bool flipper_http_init(FlipperHTTP_Callback callback, void *context)
 
     if (!fhttp.get_timeout_timer)
     {
-        FURI_LOG_E(HTTP_TAG, "Failed to allocate GET timeout timer.");
+        FURI_LOG_E(HTTP_TAG, "Failed to allocate HTTP request timeout timer.");
         // Cleanup resources
         furi_hal_serial_async_rx_stop(fhttp.serial_handle);
         furi_hal_serial_disable_direction(fhttp.serial_handle, FuriHalSerialDirectionRx);
@@ -620,6 +627,42 @@ bool flipper_http_put_request_with_headers(const char *url, const char *headers,
     // The response will be handled asynchronously via the callback
     return true;
 }
+// Function to send a DELETE request with headers
+/**
+ * @brief      Send a DELETE request to the specified URL.
+ * @return     true if the request was successful, false otherwise.
+ * @param      url  The URL to send the DELETE request to.
+ * @param      headers  The headers to send with the DELETE request.
+ * @param      data  The data to send with the DELETE request.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_delete_request_with_headers(const char *url, const char *headers, const char *payload)
+{
+    if (!url || !headers || !payload)
+    {
+        FURI_LOG_E("FlipperHTTP", "Invalid arguments provided to flipper_http_delete_request_with_headers.");
+        return false;
+    }
+
+    // Prepare DELETE request command with headers and data
+    char command[256];
+    int ret = snprintf(command, sizeof(command), "[DELETE/HTTP]{\"url\":\"%s\",\"headers\":%s,\"payload\":%s}", url, headers, payload);
+    if (ret < 0 || ret >= (int)sizeof(command))
+    {
+        FURI_LOG_E("FlipperHTTP", "Failed to format DELETE request command with headers and data.");
+        return false;
+    }
+
+    // Send DELETE request via UART
+    if (!flipper_http_send_data(command))
+    {
+        FURI_LOG_E("FlipperHTTP", "Failed to send DELETE request command with headers and data.");
+        return false;
+    }
+
+    // The response will be handled asynchronously via the callback
+    return true;
+}
 // Function to handle received data asynchronously
 /**
  * @brief      Callback function to handle received data asynchronously.
@@ -837,6 +880,69 @@ void flipper_http_rx_callback(const char *line, void *context)
         return;
     }
 
+    // Check if we've started receiving data from a DELETE request
+    else if (fhttp.started_receiving_delete)
+    {
+        // Restart the timeout timer each time new data is received
+        furi_timer_restart(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
+
+        if (strstr(line, "[DELETE/END]") != NULL)
+        {
+            FURI_LOG_I(HTTP_TAG, "DELETE request completed.");
+            // Stop the timer since we've completed the DELETE request
+            furi_timer_stop(fhttp.get_timeout_timer);
+
+            if (fhttp.received_data)
+            {
+                flipper_http_save_received_data(strlen(fhttp.received_data), fhttp.received_data);
+                free(fhttp.received_data);
+                fhttp.received_data = NULL;
+                fhttp.started_receiving_delete = false;
+                fhttp.just_started_delete = false;
+                fhttp.state = IDLE;
+                return;
+            }
+            else
+            {
+                FURI_LOG_E(HTTP_TAG, "No data received.");
+                fhttp.started_receiving_delete = false;
+                fhttp.just_started_delete = false;
+                fhttp.state = IDLE;
+                return;
+            }
+        }
+
+        // Append the new line to the existing data
+        if (fhttp.received_data == NULL)
+        {
+            fhttp.received_data = (char *)malloc(strlen(line) + 2); // +2 for newline and null terminator
+            if (fhttp.received_data)
+            {
+                strcpy(fhttp.received_data, line);
+                fhttp.received_data[strlen(line)] = '\n';     // Add newline
+                fhttp.received_data[strlen(line) + 1] = '\0'; // Null terminator
+            }
+        }
+        else
+        {
+            size_t current_len = strlen(fhttp.received_data);
+            size_t new_size = current_len + strlen(line) + 2; // +2 for newline and null terminator
+            fhttp.received_data = (char *)realloc(fhttp.received_data, new_size);
+            if (fhttp.received_data)
+            {
+                memcpy(fhttp.received_data + current_len, line, strlen(line)); // Copy line at the end of the current data
+                fhttp.received_data[current_len + strlen(line)] = '\n';        // Add newline
+                fhttp.received_data[current_len + strlen(line) + 1] = '\0';    // Null terminator
+            }
+        }
+
+        if (!fhttp.just_started_delete)
+        {
+            fhttp.just_started_delete = true;
+        }
+        return;
+    }
+
     // Handle different types of responses
     if (strstr(line, "[SUCCESS]") != NULL || strstr(line, "[CONNECTED]") != NULL)
     {
@@ -873,6 +979,13 @@ void flipper_http_rx_callback(const char *line, void *context)
         fhttp.state = RECEIVING;
         return;
     }
+    else if (strstr(line, "[DELETE/SUCCESS]") != NULL)
+    {
+        FURI_LOG_I(HTTP_TAG, "DELETE request succeeded.");
+        fhttp.started_receiving_delete = true;
+        fhttp.state = RECEIVING;
+        return;
+    }
     else if (strstr(line, "[DISCONNECTED]") != NULL)
     {
         FURI_LOG_I(HTTP_TAG, "WiFi disconnected successfully.");

+ 3 - 10
web_crawler_callback.h

@@ -100,17 +100,10 @@ static void web_crawler_view_draw_callback(Canvas *canvas, void *context)
             }
             else
             {
-                canvas_draw_str(canvas, 0, 10, "Sending GET request...");
+                canvas_draw_str(canvas, 0, 10, "Sending DELETE request...");
 
-                // Perform GET request and handle the response
-                if (app_instance->headers == NULL || app_instance->headers[0] == '\0' || strstr(app_instance->headers, " ") == NULL)
-                {
-                    get_success = flipper_http_get_request(app_instance->path);
-                }
-                else
-                {
-                    get_success = flipper_http_get_request_with_headers(app_instance->path, app_instance->headers);
-                }
+                // Perform DELETE request and handle the response
+                get_success = flipper_http_delete_request_with_headers(app_instance->path, app_instance->headers, app_instance->payload);
             }
 
             canvas_draw_str(canvas, 0, 20, "Sent!");

+ 1 - 1
web_crawler_e.h

@@ -16,7 +16,7 @@
 #include <storage/storage.h>
 
 #define TAG "WebCrawler"
-static char *http_method_names[] = {"GET", "POST", "PUT"};
+static char *http_method_names[] = {"GET", "POST", "PUT", "DELETE"};
 
 // Define the submenu items for our WebCrawler application
 typedef enum

+ 1 - 1
web_crawler_i.h

@@ -255,7 +255,7 @@ WebCrawlerApp *web_crawler_app_alloc()
 
     // Add item to the configuration screen
     app->path_item = variable_item_list_add(app->variable_item_list_request, "Path", 0, NULL, NULL);
-    app->http_method_item = variable_item_list_add(app->variable_item_list_request, "HTTP Method", 3, web_crawler_http_method_change, app);
+    app->http_method_item = variable_item_list_add(app->variable_item_list_request, "HTTP Method", 4, web_crawler_http_method_change, app);
     app->headers_item = variable_item_list_add(app->variable_item_list_request, "Headers", 0, NULL, NULL);
     app->payload_item = variable_item_list_add(app->variable_item_list_request, "Payload", 0, NULL, NULL);
     //