Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use the async client? #18

Closed
x-labz opened this issue Aug 18, 2016 · 27 comments
Closed

How to use the async client? #18

x-labz opened this issue Aug 18, 2016 · 27 comments

Comments

@x-labz
Copy link

x-labz commented Aug 18, 2016

My current code, which actually not works....

AsyncClient async_client;

.
.
.

String req ;
int8_t connected = async_client.connected();
Serial.print("connected: ");
Serial.println(connected) ;

  if (!connected  ) {
    Serial.println("connecting..") ;
    async_client.connect("192.168.0.17", 80);
  }
  else {
    String req = "POST /data HTTP/1.1 \nHost: " ;
    req += config_data.host_ip ;
    req += "/data" ;
    req += "\nCache-Control: no-cache \nContent-Type: application/json \nContent-Length: ";
    req += payload.length() ;
    req += "\nAccept: */*\n";
    req += payload.c_str() ;

    Serial.println(req);

    if ( async_client.canSend()) {
      async_client.add(req.c_str(), req.length() );
      bool sent = async_client.send();
      async_client.close();
      Serial.print("sent...") ;
      Serial.println(sent) ;
    }
  }

What is missing? How to make it work? HEEEELP PLEEASE!!

@me-no-dev
Copy link
Owner

me-no-dev commented Aug 18, 2016

here is a little example:

static AsyncClient * aClient = NULL;

void runAsyncClient(){
  if(aClient)//client already exists
    return;

  aClient = new AsyncClient();
  if(!aClient)//could not allocate client
    return;

  aClient->onError([](void * arg, AsyncClient * client, int error){
    Serial.println("Connect Error");
    aClient = NULL;
    delete client;
  }, NULL);

  aClient->onConnect([](void * arg, AsyncClient * client){
    Serial.println("Connected");
    aClient->onError(NULL, NULL);

    client->onDisconnect([](void * arg, AsyncClient * c){
      Serial.println("Disconnected");
      aClient = NULL;
      delete c;
    }, NULL);

    client->onData([](void * arg, AsyncClient * c, void * data, size_t len){
      Serial.print("\r\nData: ");
      Serial.println(len);
      uint8_t * d = (uint8_t*)data;
      for(size_t i=0; i<len;i++)
        Serial.write(d[i]);
    }, NULL);

    //send the request
    client->write("GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n");
  }, NULL);

  if(!aClient->connect("www.google.com", 80)){
    Serial.println("Connect Fail");
    AsyncClient * client = aClient;
    aClient = NULL;
    delete client;
  }
}

Serial:

Connected

Data: 471
HTTP/1.0 302 Found 
Cache-Control: private
Content-Type: text/html; charset=UTF-8 
Location: http://www.google.bg/?gfe_rd=cr&ei=WuC1V8u2CtKz8wffnI2QCA
Content-Length: 258
Date: Thu, 18 Aug 2016 16:20:42 GMT

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="http://www.google.bg/?gfe_rd=cr&amp;ei=WuC1V8u2CtKz8wffnI2QCA">here</A>.
</BODY></HTML>
Disconnected

@x-labz
Copy link
Author

x-labz commented Aug 18, 2016

Cool! Thank You! :-)

@judge2005
Copy link

I don't suppose you are working on an async HTTP client are you? I wish I had the time to do that!

@me-no-dev
Copy link
Owner

I wish someone had it too :) I do not right now :) once ESP32 is more stable-ish I can have time to play with Async and add more :)

@judge2005
Copy link

I added some code to my repo. Hopefully it can serve as a starting point, or if anyone needs something similar...

@VicSpectator
Copy link

VicSpectator commented May 12, 2017

Thank you very much for the example! It looks like easy enough :) Could anybody answer my question? Where should I execute "runAsyncClient()"-function? I execute it after connecting to my AP. However it tries to connect to my TCP-server before connecting to AP. It goes out from my Serial output:

scandone
f 0, Connect Faillllll
scandone
state: 0 -> 2 (b0)
state: 2 -> 3 (0)
state: 3 -> 5 (10)
add 0
aid 1
cnt 

connected with testtest, channel 11
ip:192.168.1.1,mask:192.168.1.0,gw:192.168.1.1
pm open,type:2 0

Connect Faillllll - is a text message like your "Serial.println("Connect Fail");"
My code:

void setup()
{
	Serial.begin(115200);
	Serial.println();
	Serial.setDebugOutput(true);
	WiFi.mode(WIFI_STA);
	WiFi.config(local_ip, gateway_ip, subnet_ip);
	WiFi.begin(ssid, password);
        runAsyncClient();
}

What's wrong with my code? :)
UPD:
I don't really understand what is going on but my fix helped me:

WiFi.begin(ssid, password);
while ( WiFi.status() != WL_CONNECTED )
{
   delay ( 500 );
   Serial.print ( "." );
}
runAsyncClient();

@me-no-dev
Copy link
Owner

Its simple. When you call wifi.begin it starts to connect but the function returns immediately, so you need to wait for it to connect or use events to start async on connect

@mkeyno
Copy link

mkeyno commented May 13, 2017

@judge2005 I suggest to make" _parseLine(); " virtual in your local lib and make override function similar to _parseLine(); so easily could get benefit of that instead of making your own request parsing function

@VicSpectator
Copy link

I have one more small question about AsyncClient. Is it possible to check client connection status synchronously? For example in loop() cycle when I want to send anything to TCP-server.
Of course I can create global variables and update then in callback function onDisconnect and after successfull result of aClient->connect. But I'm sure that there is more efficient way to check connections of all clients. Is it possible to do by aClient->state()? I tried this but I don't understand all the states. Also I tried connected() method.

@me-no-dev
Copy link
Owner

you need to have a reference to the client

static AsyncClient * client = NULL;

void someMethodThatCreatesClient(){
  //.....
  client = createdAsyncClient;
  //......
}

void onThatClientDisconnect(){
  client = NULL;
}

void loop(){
  if(client){
    Serial.printf("ClientState: %u", client->state());
  }
}

@VicSpectator
Copy link

Sounds like "If you haven't defined client you can't check it's state" :) Thank you. It's clear.

@gunhansancar
Copy link

Hey @me-no-dev,

Thank you very much for the great library.

I have one question to you about the example that you gave earlier.
Just to give you simple context:
I make request every seconds using asyncclient by measuring the elapsed time time etc.

The problem that I have is after client onData is called, it waits almost 10 seconds to disconnect so that I can make another request eventually.

client->onData([](void * arg, AsyncClient * c, void * data, size_t len){
      Serial.print("\r\nData: ");
      Serial.println(len);
      uint8_t * d = (uint8_t*)data;
      for(size_t i=0; i<len;i++)
        Serial.write(d[i]);
    }, NULL);

So to prevent that I added stop inside the initial conditions like below:

if(aClient){
    Serial.println("Wifi - client already exists");
    aClient->stop();
    return;
  }

But this caused unexpected crashes sometimes:

Exception 28: LoadProhibited: A load referenced a page mapped with an attribute that does not permit loads
Decoding 11 results
0x402230eb: tcp_recved at /Users/igrokhotkov/espressif/arduino/tools/sdk/lwip/src/core/tcp.c line 603
0x40203bcd: Print::write(char const*) at /Users/gunhansancar/Library/Arduino15/packages/esp8266/hardware/esp8266/2.3.0/cores/esp8266/Print.cpp line 100
0x40203bf8: Print::println() at /Users/gunhansancar/Library/Arduino15/packages/esp8266/hardware/esp8266/2.3.0/cores/esp8266/Print.cpp line 100
0x402035cc: AsyncClient::stop() at /Users/gunhansancar/Documents/Arduino/libraries/ESPAsyncTCP/src/ESPAsyncTCP.cpp line 650
0x40202075: runAsyncClient() at /Users/gunhansancar/Documents/Arduino/dinedash_full/dinedash_full.ino line 64

I also tried to null and delete the client onData but it caused constant exceptions.

So I altered to code from stop to close:

 if(aClient){
    Serial.println("Wifi - client already exists");
    aClient->close(true);
    return;
  }

Now it seems the previous exceptions are gone. But I am not sure if this is the correct and safe approach to solve this problem. I am hoping you would direct me to the correct path.

@me-no-dev
Copy link
Owner

it's up to you to close the connection when needed :) if the other end does not close it, then it's your responsibility to do so :)

@gunhansancar
Copy link

@me-no-dev Thanks for quick reply awesome 👍
But my concern is about crashes that I received when I used aClient->stop();
Since I changed it to aClient->close(true); and now it's fine. So I just want to make sure that It won't crash again like when I used stop() right? That's what I am asking. Is it crash free to use close() function? Or is there any other method that can help to stop the connection immediately without causing any crashes?

@diraniyoussef
Copy link

@me-no-dev in this comment you used aClient inside "onError" event, and it doesn't work in Arduino IDE. Maybe it's a bug. It probably says something like 'this' not being captured. Even a local variable inside the method runAsyncClient() (in the same comment) is not captured and so can't be used inside the function. I found a way out and it's by making the variables I wish to use outside the class! And it worked.
I have a request please, show us how we can use the callback function (the second argument) in the event methods onError, onConnect, ... In your comment it was NULL.
Example please for non-experts.

@diraniyoussef
Copy link

diraniyoussef commented Aug 11, 2018

@me-no-dev Another question:
You mentioned in your example
//send the request
client->write("GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n");
so the argument is a string
while here
"write" is defined such as
size_t AsyncClient::write(const char* data) {
so the argument is a char pointer

@Pablo2048
Copy link

??? how do you know that "GET..." is a String class? It's ordinary character array and not an object so it is given as const char * when passing to function as a parameter...

@diraniyoussef
Copy link

Regarding the example used by me_no_dev

client->onData([](void * arg, AsyncClient * c, void * data, size_t len){
      Serial.print("\r\nData: ");
      Serial.println(len);
      uint8_t * d = (uint8_t*)data;
      for(size_t i=0; i<len;i++)
        Serial.write(d[i]);
    }, NULL);

Do we have to manually free memory occupied by arg, c, or data pointer arguments? Or is it made automatically?

@PurpleAir
Copy link

Today, after a lot of time trying to find the reason for a crash when no WiFi connection was present, I found the following:

The sample by me-no-dev has the following line:
aClient->onError([](void * arg, AsyncClient * client, int error)...
The error event callback never gets called and the ESP crashes.

it seems the error type is wrong. The following fixes it:
aClient->onError([](void * arg, AsyncClient * client, err_t error)...

(Note the "err_t" instead of int) now the callback is executed and the crash goes away.

I hope this helps someone!

@PurpleAir
Copy link

PurpleAir commented Oct 1, 2018

Another complication:
With no WiFi connection, the following code seems not to call the "Connect fail" part (connect does not return false):

if(!aClient->connect("192.168.1.100", 80)){
    Serial.println("Connect Fail");
    AsyncClient * client = aClient;
    aClient = NULL;
    delete client;
  }

Is this because a DNS lookup is not performed?
*** Update, it also happens on a local lookup, ie: "somecomputer.local" host.**

@diraniyoussef
Copy link

diraniyoussef commented Oct 2, 2018

@adrionics

Another complication:
With no WiFi connection, the following code seems not to call the "Connect fail" part (connect does not return false):

Shouldn't onError be triggered as well in this case?
When the server was shut down, I think it was triggered with error number 13 if I recall it right.

@PurpleAir
Copy link

Yes, after 18 seconds an error Connection aborted (-13).

@masoudjamali137
Copy link

hi

what is the size of data received?
how can I increase data buffer size or receive time?

I get my received data in 2 packets

Data: 536
HTTP/1.1 200 OK
Cache-Control: no-cache, private
Content-Type: application/json
Server: Microsoft-IIS/7.5
X-Powered-By: PHP/7.2.2
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
X-Powered-By: ASP.NET
Date: Wed, 17 Jul 2019 05:23:07 GMT
Content-Length: 339

{"data":{"schedule":{"fri":{"start":"07:30","finish":"17:30"},"mon":{"start":"07:30","finish":"19:30"},"sat":{"start":"07:30","finish":"17:30"},"sun":{"start":"07:30","finish":"17:30"},"tur":{"start":"07:30","finish":"17:30"},"tus":{"start":"07:30","finish":"18:30"},"

Data: 71
wed":{"start":"07:30","finish":"20:30"}},"epoch":1559050621},"crc":147}

@srxdevnet
Copy link

Hello there! Greetings from Argentina!
I have some issues trying to send a POST request with application/x-www-form-urlencoded content type. On the server side i´m getting "malformed request" or no data is sent (only the request line and headers).

Thank you very much in advance!

@srxdevnet
Copy link

srxdevnet commented Jun 25, 2020

... i´ll forgot, i´m using client->write() to send the http request string:

POST /hello HTTP/1.1
Host: 192.168.3.91
User-Agent: sTap_2517826
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 49

serial=CL-TAP-P1-000002517826&firmware=v1.2.0.3

@PurpleAir
Copy link

Not really the place for the question, but I will try to help you in that your problem is likely the content length is wrong. It should be 47 (do not include the line feeds after the headers).

@srxdevnet
Copy link

Excelent! That Works!
Thanks a lot!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests