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 · 22 comments

Comments

Projects
None yet
9 participants
@x-labz

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

This comment has been minimized.

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

This comment has been minimized.

x-labz commented Aug 18, 2016

Cool! Thank You! :-)

@me-no-dev me-no-dev closed this Sep 23, 2016

@judge2005

This comment has been minimized.

judge2005 commented Jan 13, 2017

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

This comment has been minimized.

Owner

me-no-dev commented Jan 16, 2017

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

This comment has been minimized.

judge2005 commented Jan 23, 2017

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

@VicSpectator

This comment has been minimized.

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

This comment has been minimized.

Owner

me-no-dev commented May 12, 2017

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

This comment has been minimized.

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

This comment has been minimized.

VicSpectator commented May 16, 2017

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

This comment has been minimized.

Owner

me-no-dev commented May 16, 2017

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

This comment has been minimized.

VicSpectator commented May 16, 2017

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

@gunhansancar

This comment has been minimized.

gunhansancar commented Oct 12, 2017

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

This comment has been minimized.

Owner

me-no-dev commented Oct 12, 2017

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

This comment has been minimized.

gunhansancar commented Oct 12, 2017

@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

This comment has been minimized.

diraniyoussef commented Aug 10, 2018

@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

This comment has been minimized.

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

This comment has been minimized.

Pablo2048 commented Aug 11, 2018

??? 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

This comment has been minimized.

diraniyoussef commented Aug 16, 2018

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?

@adrionics

This comment has been minimized.

adrionics commented Sep 30, 2018

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!

@adrionics

This comment has been minimized.

adrionics 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

This comment has been minimized.

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.

@adrionics

This comment has been minimized.

adrionics commented Oct 2, 2018

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment