Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ESP8266WebServer setting a value inside a class

Tags:

c++

c

esp8266

I'm having a bit of trouble with the ESP8266WebServer. My WebServer{} class is wrapped around the ESP8266WebServer object and looks like this:

Header file:

#include <WiFiClient.h>

#ifndef WebServer_h
#define WebServer_h

#include "Arduino.h"

class WebServer {
    public:
        WebServer();
        void begin();
        void handleClient();
        void finishedProcessingData(String clientReply);
        String queryString;
    private:
        // page/url handlers
        friend void handleSomeData();
};

#endif

Cpp file:

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>

#include "Arduino.h"
#include "WebServer.h"

ESP8266WebServer server(80);

int aNumberHere = 0;
String queryString = "";

WebServer::WebServer(){
}

void handleSomeData(){
    aNumberHere++;
    queryString = "";

    // this loop appends all the queries fro the query string back into a query string
    // (not sure if there is an easier way to grab this directly from the server api)
    int totalArgs = server.args();
    for (int counter = 0; counter < totalArgs; counter++){
        queryString += server.argName(counter) +"="+ server.arg(counter);
        if(counter < (totalArgs - 1)){
            queryString += "&";
        }
    }
    Serial.println(queryString);
    Serial.println(aNumberHere);
}

void WebServer::handleClient(){
    server.handleClient();
}

void WebServer::begin(){
    server.on("/data.html", handleSomeData);
    server.begin();
}

void WebServer::finishedProcessingData(String clientReply){
    // empty the string so it isn't cached (just in case)
    Serial.print("Sending reply back to client: ");
    Serial.println(clientReply);
    queryString = "";
    server.send(200, "text/plain", clientReply);
}

The idea is to grab a query string from an http request, do some processing, then return the response.

How it is called from outside is:

WebServer webServer;
String processingResult;

void setup(){
    webServer.begin();
}

void loop(){
    delay(10);
    webServer.handleClient();

    // check if the query string has stuff in it, if it doesn't then WebServer.handleSomeData() never fired, thus no request yet
    if(webServer.queryString != ""){
        // do stuff that results in a string being returned
       processingResult = handWavyMagic();
        // then respond back to client
        webServer.finishedProcessingData(processingResult);
    }
}

My issue is that from my outside loop (which is in my main sketch), "webServer.queryString" is always equal to an empty string. If I print out "queryString" from within handleSomeData(), I can see the printed result from within "handleSomeData()" (which is correct), but it somehow can't seem to set it 'outside' that method. I did check to see that it is able to access and modify a variable (which is why "aNumberHere" is printed) and there seem to be no issues; "aNumberHere" gets incremented as expected every time I go to the corresponding url.

My only suspect is that "queryString" is a String object which for some reason can't be set, while "aNumberHere" is a primitive int which is fine. Is this correct or is something else going on here?

I have also tried making "queryString" a static variable, but with no luck -either I did it wrong or it just doesn't work that way either.

I am new to friend functions (well, new to c/c++ in general actually -any advice is welcome), though I read that it should be able to access the classes private properties just fine, so I'm not sure how to fix this.

Any ideas?

like image 865
mitim Avatar asked Oct 02 '15 03:10

mitim


2 Answers

I know, this is a bit late for the OP to help with his problem, but maybe other readers will find this useful.

To avoid splitting parts of the logic/data inside and outside the class it would be more elegant to have everything inside. Using callbacks to non-static methods of a class instance is a bit tricky (I learned it the hard way for my current project), but here is the alternative:

void WebServer::begin()
{
    // instead of server.on("/data.html", handleSomeData);
    server.on("/data.html", std::bind(&WebServer::handleSomeData, this));
    server.begin();
}

void WebServer::handleSomeData()
{
    // do whatever you need
}

This uses std:bind() to bind the instance method and its this pointer to the callback. As a result, everything is contained inside the web server instance, which is a cleaner approach.

like image 167
nullp01nter Avatar answered Nov 12 '22 03:11

nullp01nter


There is 2 different variables called queryString :

  • the global one declared in WebServer.cpp
  • the member of WebServer declared in WebServer.h

In the callback handleSomeData you set the global one, but in the loop and finishedProcessingData you access to the member of WebServer.

To make the code works, you could remove the member of WebServer and use the global one (as you did for aNumberHere) like this :

extern String queryString;

void loop(){
    delay(10);
    webServer.handleClient();

    // check if the query string has stuff in it, if it doesn't then WebServer.handleSomeData() never fired, thus no request yet
    if(queryString != ""){
        // do stuff that results in a string being returned
        processingResult = handWavyMagic();
        // then respond back to client
        webServer.finishedProcessingData(processingResult);
        queryString = "";
    }
}
like image 40
mpromonet Avatar answered Nov 12 '22 04:11

mpromonet