Search code examples
arduinowifiesp32multitasking

How to pass the this pointer to a task inside a class?


I am creating a library to manage our wifi network connection on many ESP32 devices.

Inside my library, i have a task that is looping with a 1 second pause started with an xTaskCreate inside my .begin() function. The problem is that i can't start the task if i haven't make it static in my .h file. So once the task is declared as static, i don't have access to the this-> pointer inside this task. Is there anyway i can send the this-> pointer inside my task like in parameters or something like that ?

Here is some code:

class EraWiFi{
    public:
        EraWiFi();
        /*!
        * @brief Initialize WiFi
        * @return Returns true if successful
        */

        bool begin(fs::FS &fs);
        /*!
        * @brief Start EraWiFi service
        * @return Returns true if successful
        */

        void downloadWiFi();

    private:
        static void wifiTask(void * parameter);
};


bool EraWiFi::begin(fs::FS &fs){
    File file = fs.open("/WiFi.json", FILE_READ);
    if(file){
        DynamicJsonDocument wifi(2048);
        DeserializationError error = deserializeJson(wifi, file);
        file.close();
        if (!error){
            Serial.println("We have a VALID WiFi.json configurations file!");
            Serial.println("Adding wifi networks:");
            for (JsonArray arr : wifi.as<JsonArray>()){
                Serial.print("SSID: ");
                Serial.print(arr[0].as<char*>());
                Serial.print(", KEY: ");
                Serial.println(arr[1].as<char*>());
                wifiMulti.addAP(arr[0].as<char*>(), arr[1].as<char*>());
            }
        }else{
            Serial.println("We don't have a VALID WiFi.json configurations file!");
        }
    }else{
        Serial.println("There is no WiFi.json configurations file!");
    }
    wifiMulti.addAP("Testing", "1234");
    xTaskCreate(
        this->wifiTask,    // Function that should be called
        "WiFiTask",   // Name of the task (for debugging)
        10000,            // Stack size (bytes)
        (void*)&this,            // Parameter to pass
        1,               // Task priority
        NULL   // Task handle
    );
    return true;
}



void EraWiFi::downloadWiFi(){
    Serial.println("Downloading WiFi.json from ErabliTEK Server.");
    HTTPClient http;
    // Send request
    http.useHTTP10(true);
    String url = "https://testing.testing.com/wifis/getWiFi/";
    http.begin(url);
    int httpResponseCode =  http.GET();
    if(httpResponseCode == HTTP_CODE_OK){
        // Parse response
        DynamicJsonDocument doc(2048);
        DeserializationError error = deserializeJson(doc, http.getStream());
        // Disconnect
        http.end();
        if (!error){
        File file = fs.open("/WiFi.json", FILE_WRITE);
        if(file){
            // Serialize JSON to file
            if (serializeJson(doc, file) == 0) {
            Serial.println("Error saving WiFi.json!");
            Serial.println(http.getString());
            }else{
            Serial.println("Succesfully saved WiFi.json!");
            }
        }
        // Close the file
        file.close();
        }else{
        Serial.println("Error downloading WiFi.json");
        }
    }else{
        Serial.println("Problem connecting to " + url + " with http code: " + String(httpResponseCode));
    }
}



void EraWiFi::wifiTask(void * parameters){
    bool wifiConnected = false;
    for(;;){
        uint8_t wifiStatus = wifiMulti.run();
        if((wifiStatus != WL_CONNECTED) and wifiConnected) {
            wifiConnected = false;
            Serial.println("WiFi not connected!");
        }else if((wifiStatus == WL_CONNECTED) and !wifiConnected){
            wifiConnected = true;
            Serial.println("WiFi Connected.");
            Serial.print("SSID: ");
            Serial.println(WiFi.SSID());
            Serial.print("KEY: ");
            Serial.println(WiFi.psk());
            Serial.print("IP Address: ");
            Serial.println(WiFi.localIP());
            EraWiFi::downloadWiFi();
        }
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

Solution

  • You're already doing that, you just don't realize it. Have a look at the FreeRTOS documentation on xTaskCreate() which explains how the fourth parameter pvParameters is a void pointer that gets passed on to the new task as its (one and only) input parameter.

    The commented line here means you're taking the address of your EraWiFi object and passing it to the task:

        xTaskCreate(
            this->wifiTask,
            "WiFiTask",
            10000,
            (void*)&this,  //< Pointer gets forwarded to the task
            1,
            NULL
        );
    

    To use the pointer inside your task, you only need to cast it back from void*:

    void EraWiFi::wifiTask(void * parameters){
        EraWifi* p = static_cast<EraWifi*>(parameters); //< That's the same pointer
        p->someMethod();
    ...
    

    PS. Also note (before you poke at the same object from different threads, possibly causing hard-to-find bugs) that FreeRTOS provides excellent inter-thread communication facilities - the queues and task notifications often being the most useful ones. Check out their tutorial/book for an explanation.