JAndrassy / WiFiEspAT

Arduino networking library. Standard Arduino WiFi networking API over ESP8266 or ESP32 AT commands.
GNU Lesser General Public License v2.1
288 stars 44 forks source link

[question] [enhancement] "dynamic" print #3

Closed atesin closed 4 years ago

atesin commented 4 years ago

hi... thanks for your library, seems very cool .. i am trying to learn how to use, is a little hard to me since i am not a programmer but a sysadmin

i have an iot project (a cheap automated irrigation system for poor farmers) with arduino, this library, esp8266 (esp-01s) and the LoBo firmware (because the official firmware didn't let me save configs), and my nginx/php/mariadb webserver

i am developing a small embedded http server, mostly for inputting wifi credentials through a local webpage to connect the module and the server through internet... for example i know with this library i can do something like this...

// after receive +IPD and process http request url
void sendHtmlDashboard(WiFiClient client)
{
  client.flush(); // pseudocode to catch the idea
  char userName[20] = getUsernameSomeWay();
  char newMessages[10] = getNumMessagesSomeWay();

  client.print( F(
    "HTTP 200 OK\r\n"
    "Server: arduino/esp\r\n"
    // ... 2KB of additional http headers...
  );

  client.print( F(
    "<html>..."
    "<head, title, meta, style, script, etc... />"
    // ... 2KB of additional html headers...
  );

  client.print( F("welcome ") );
  client.print( userName );
  client.print( F(", you have ") );
  client.print( newMessages );
  client.print( F(" unread messages...") );
  // ... 2kb of additional dynamic webpage responses...

  client.stop();
}

... but i know with this will have several AT+CIPSEND commands, slowing down the overall responsiveness and resources.... so instead we can do it with 1 single cipsend command like this...

// after receive +IPD and process http request url
void sendHtmlDashboard(WiFiClient client)
{
  client.flush(); // pseudocode to catch the idea
  char userName[20] = getUsernameSomeWay();
  char newMessages[10] = getNumMessagesSomeWay();

  char response[4096] = '\0'; // note currently this can't be done with just 2kb ram

  strcpy_P( response, (PGM_P) F(
    "HTTP 200 OK\r\n"
    "Server: arduino/esp\r\n"
    // ... 2KB of additional http headers...
  );

  strcpy_P( response, (PGM_P) F(
    "<html>..."
    "<head, title, meta, style, script, etc... />"
    // ... 2KB of additional html headers...
  );

  strcpy_P( response, (PGM_P) F("welcome ") );
  strcpy( response, username );
  strcpy_P( response, (PGM_P) F(", you have ") );
  strcpy( response, messages );
  strcpy_P( response, (PGM_P) F(" new messages...") );
  // ... 2kb of additional webpage content in response...

  client.print( response ); // the single response
  client.stop();
}

... but with this way you must build the char string response before send it, which could consume a lot of ram.... i thought an alternative aproach that i don't know how to implement with this library...

// after receive +IPD and process http request url
void sendHtmlDashboard(WiFiClient client)
{
  clientl.flush(); // pseudocode to catch the idea
  char userName[20] = getUsernameSomeWay();
  char newMessages[10] = getNumMessagesSomeWay();

  // no more need to declare a char string with this techique, continue reading for details

  const __FlashStringHelper * httpHeaders = F(
    "HTTP 200 OK\r\n"
    "Server: arduino/esp\r\n"
    // ... 2KB of additional http headers...
  );

  const __FlashStringHelper * htmlHeaders = F(
    "<html>..."
    "<head, title, meta, style, script, etc... />"
    // ... 2KB of additional html headers...
  );

  const __FlashStringHelper * welcome = F("welcome ");
  const __FlashStringHelper * youHave = F(", you have ");
  const __FlashStringHelper * unreadMessages = F(" unread messages...") );
  // ... 2kb of additional webpage content in response...

  // send total data length in first place
  client.sendDataLength(
      strlen_P( (PGM_P) httpHeaders )
    + strlen_P( (PGM_P) htmlHeaders )
    + strlen_P( (PGM_P) welcome )
    + strlen( username )
    + strlen_P( (PGM_P) youHave )
    + strlen( newMessages )
    + strlen( (PGM_P) unreadMessages )
    // add additional content length if any
  );

  // ... then send the raw data itself, all at once
  // the esp8266 is already waiting "length" data because of the previous command
  client.sendData( httpHeaders );
  client.sendData( htmlHeaders );
  client.sendData( welcome );
  client.sendData( username );
  client.sendData( youHave )
  client.sendData( newmessages );
  client.sendData( unreadMessages );
  // send additional webpage content if any

  // done, now just close the link
  client.stop();
}

... or maybe a multi parameter function/macro like "client.write( str1, str2, str3 ...) (with vararg, variadic, sentinel, array of pointers, idk)

.... now the questions...

thanks for your fantastic library, for your patience reading, and in advance for your possible response... i am hungry waiting your response :D

JAndrassy commented 4 years ago

The WiFiEspAT library uses a buffer so the prints are collected and sent as 32 or 64 bytes pieces. You can configure the buffer size.

I have a separate StreamLib library as solution for things you mention. My main Arduino project Regulator is the largest example of StreamLib with web server and client.

atesin commented 4 years ago

hi... thanks for your kindly response ... but how about the 3 questions?

i am not so skilled... i am cracking my head trying to find some custom "AT+CIPSEND:, ; esp.find('>') ; esp.print(...) ; esp.print(...) ; esp.print(...) ..." with this library :) ....

another additional question... is there some WiFiClient.printRaw() or WiFiClient.find() function? .. i think with this you can send data directly from progmem to uart no needing an intermediate buffer

JAndrassy commented 4 years ago

I answered the questions with links, but OK, I go a little more in detail, but you must read the docs and study the code

WFiClient is derived from Arduino common Stream class so you can use all functions you know from Serial, including printing a F() macro string. ('printRaw' is write()) With StreamLib classes you have additionally printf, which can use format string from PROGMEM too

the HTTP chunked encoding with ChunkedPrint class solves the unknown-http-body-length problem as you can read in StreamLib's README.md

my code as examples:

the linked codes work with all Arduino networking libraries because the Clients are the same, so this is not about WiFiEspAT