Handle String send by client to access point on AsyncWebServer (ESP8266)

Update: My current solution can by found in my last post (from 2020-07-29).

Hi together,

I'm working on a temperature monitoring for serveral rooms: An AsyncWebServer is running on an ESP8266 and should now collect data from clients (in other rooms) to include their values in the monitoring. The clients should use "deepsleep" while they are not measuring. The deepsleep time for each client is planned to be close to 60 minutes. It would be nice, if all clients could measure at the same time and then try to send their data to the server. This leads to the situation, that the server needs to handle several data requests at the "same" time. (The server can of course handle them one by one. At the same time will be hard I guess.)

My questen: Which kind of connection should I use in this case? (For the communication between ESP8266 AsyncWebServer and other ESPs/D1 mini's as clients.) Just the ESPAsyncTCP.h library (like included in the AsyncWebServer example? A websocket connection or something different like MQTT? Please share your experiences :slight_smile:

Best Regards
Noyen

For ESP8266 and ESP32 there exists another protocol that is much faster than establishing a webserver-connection:

it is called ESP-NOW. You can send messages up to 250 bytes. This sending is done in less than half a second. With ESP-NOW up to 20 boards can join a "peergroup" and send and receive data in both directions each board from and to each other board. If you need a connection to a wireless network to display all the collected data through a website. This would require an additonal ESP8266 or ESP32 because ESP-NOW can not work in parallel with a "classical WiFi-connection" So a ESP-NOW-receiver would be connected through a serial interface with the WiFi.

If you find examples for ESP32 for ESP-NOW they use different libraries which are massive incompatible to the ESP8266-library. You have to find a ESP-NOW-code written for ESP8266. You can exchange data via ESP-NOW between ESP8266 and ESP32 you jsut have to use different libraries with different commands.

best regards Stefan

Hi Stefan,

thank you for your reply and the information. I think I will try to keep it simple (I just need to transfer a few values each hour):

Server sketch (complete example - copy, change SSID+PW, run):

#include <ESP8266WiFi.h>                //Connect to Wifi
#include <ESPAsyncWebServer.h>          //Create WebServer

AsyncWebServer server(80);

//WLAN SSID & Password
char* ssid     = "*Your-WLAN-SSID*";
char* password = "Your-WLAN-Password";
//Access Point (on ESP8266)
char* ap_ssid     = "TestDataServer";   //Define whatever you want (and copy it to the Client sketch)
char* ap_password = "ESP8266xx";        //Min. 8 Chars

//Access Point IP configuration
IPAddress ip(192,168,43,4);
IPAddress gateway(192,168,43,1);
IPAddress subnet(255,255,255,0);

const char* PARAM_MESSAGE = "message";

void setup() 
{
  Serial.begin(115200);
  //Wait for Serial monitor
  delay(200);
  Serial.println("###Setup###");

  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  //Set WiFi to access point AND station mode
  WiFi.mode(WIFI_AP_STA);  
  WiFi.begin(ssid, password);
  
  if (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("WiFi Connect Failed! Rebooting...");
    delay(1000);
    ESP.restart();
  }

  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // Initialize SPIFFS
  if(!SPIFFS.begin()){
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.html");  //the index.html file needs to be uploaded in another step (not needed for this example, just if somebody want's to copy this code)
  });

  // Not found handling
  server.onNotFound([](AsyncWebServerRequest *request){
    request->send(404);
  });

  server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String message;
    if (request->hasParam(PARAM_MESSAGE)) {
      message = request->getParam(PARAM_MESSAGE)->value();
    } else {
      message = "No message sent";
    }
    request->send(200, "text/plain", "Hello, GET: " + message);
  });

  server.begin();

  Serial.println("##Access Point (auf ESP8266) starten##");
  //Access Point (auf ESP8266) starten
  WiFi.softAPConfig(ip, gateway, subnet);
  WiFi.softAP(ap_ssid, ap_password);

  Serial.println("###Setup Ende###");
}

void loop() {
}

Client sketch (complete example - copy, run [parallel with Server on another ESP8266]):

#include <dht11.h>                  //DHT Sensor Library   
#include <WiFiClient.h>             //Create WiFi Client                
#include <ESP8266WiFiMulti.h>       //Connect to 'multi' WiFi

//DHT Sensor Setup
dht11 DHT11;
#define DHT11PIN 16

ESP8266WiFiMulti WiFiMulti;

//SSID and password of access point (running on another ESP8266)
const char* ssid = "TestDataServer";
const char* password = "ESP8266xx";

//Variables for intervall
unsigned long previousMillis = 0;
const long interval = 5000;   //60000 - will be replaced with deepsleep in a later version

void setup() {
  Serial.begin(115200);
  delay(200);
  Serial.println();

  //Connec to access point (running on another ESP8266)
  WiFi.mode(WIFI_STA);
  WiFiMulti.addAP(ssid, password);
  while((WiFiMulti.run() == WL_CONNECTED)) { 
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("Connected to WiFi");
}

void loop() {
  unsigned long currentMillis = millis();
  
  if(currentMillis - previousMillis >= interval) {
     // Check WiFi connection status
    if ((WiFiMulti.run() == WL_CONNECTED)) {
      //Read sensor values
      //int vDHT_TEST = DHT11.read(DHT11PIN);
      int vTemperature = DHT11.temperature;
      int vHumidityAir = DHT11.humidity;
      int vHumidityGround = analogRead(A0);
      Serial.println("Temperature: " + String(vTemperature) + " *C - vHumidityAir: " + String(vHumidityAir) + " % - vHumidityGround: " + String(vHumidityGround) + " %");

      //Creat WiFi client and send data to server
      WiFiClient client;
      client.println("Temperature: " + String(vTemperature) + " *C - vHumidityAir: " + String(vHumidityAir) + " % - vHumidityGround: " + String(vHumidityGround) + " %");
            
      //Save the time of the last HTTP GET request
      previousMillis = currentMillis;
    }
    else {
      Serial.println("WiFi Disconnected");
    }
  }
}

Reading data from the server is working fine (e.g. vTemperature = httpGETRequest[…]). But I would like to send data from the client to the server.

My current test:
Client sketch:

client.println("Temperature: " + vTemperature + " *C - HumidityAir: " + vHumidityAir + " % - HumidityGround: " + vHumidityGround + " %");

Server sketch:

server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String message;
    if (request->hasParam(PARAM_MESSAGE)) {
      message = request->getParam(PARAM_MESSAGE)->value();
    } else {
      message = "No message sent";
    }
    request->send(200, "text/plain", "Hello, GET: " + message);
  });

Result (on opening http://192.168.43.4/get while you are connected to the access Point and the client is also running):

"Hello, GET: No message sent"

How do I handle the String send by the client on the server correctly? I would like to write the String into a variable on the server (wich is forwarded to the AsyncWebServer an presented together with values from other clients on a website as a chart. )

Best Regards
Noyen

Hi together,

I'm happy to mention, that I found a solution:
I added a TCP server. The ESPAsyncWebServer seems not to be able to handle data send by a client. Please share the sketch, when you found a better solution :slight_smile:

Server sketch:
AsyncWebServer (with or without authentifikation; index.html is stored on 'SPIFFS'). Listening to client data at "TCP_PORT". Just reply to client with the same data (for commit on client side).

//AsyncWebServer
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <DNSServer.h>
#include <vector>

//WLAN SSID & Password
char* wlan_ssid     = "YOURSSID";
char* wlan_password = "YOURPW";
//Access Point (on ESP8266)
#define SSID "TestDataServer"
#define PASSWORD "ESP8266xx" //Min. 8 chars, if used

#define SERVER_HOST_NAME "DataCollectionServer"

#define TCP_PORT 7050
#define DNS_PORT 53

static DNSServer DNS;

static std::vector<AsyncClient*> clients; // a list to hold all clients

String vClientData;

/* clients events */
static void handleError(void* arg, AsyncClient* client, int8_t error) {
  Serial.printf("\n connection error %s from client %s \n", client->errorToString(error), client->remoteIP().toString().c_str());
}

static void handleData(void* arg, AsyncClient* client, void *data, size_t len) {
  //  Serial.printf("\n Data received from client %s \n", client->remoteIP().toString().c_str());
  //  Serial.write((uint8_t*)data, len);
  Serial.println("");
  vClientData = String((char*)data).substring(0, len);
  Serial.print("vClientData: ");
  Serial.println(vClientData);

  // reply to client
  if (client->space() > 64 && client->canSend()) {
    char reply[64];
    //sprintf(reply, "this is from %s", SERVER_HOST_NAME);
    //sprintf(reply, "Data received: %s From: %s",vClientData.c_str(), SERVER_HOST_NAME);
    //sprintf(reply, "Data received: %s",vClientData.c_str());
    sprintf(reply, "%s", vClientData.c_str());
    client->add(reply, strlen(reply));
    client->send();
  }
}

static void handleDisconnect(void* arg, AsyncClient* client) {
  Serial.printf("\n client %s disconnected \n", client->remoteIP().toString().c_str());
}

static void handleTimeOut(void* arg, AsyncClient* client, uint32_t time) {
  Serial.printf("\n client ACK timeout ip: %s \n", client->remoteIP().toString().c_str());
}


/* server events */
static void handleNewClient(void* arg, AsyncClient* client) {
  Serial.printf("\n#New client has been connected to server, ip: %s", client->remoteIP().toString().c_str());

  // add to list
  clients.push_back(client);

  // register events
  client->onData(&handleData, NULL);
  client->onError(&handleError, NULL);
  client->onDisconnect(&handleDisconnect, NULL);
  client->onTimeout(&handleTimeOut, NULL);
}



void setup() 
{
  //Debugging
  Serial.begin(115200);
  delay(200);
  Serial.println("###Setup###");

  // create access point
  while (!WiFi.softAP(SSID, PASSWORD, 6, false, 15)) {
    delay(500);
  }

  // start dns server
  if (!DNS.start(DNS_PORT, SERVER_HOST_NAME, WiFi.softAPIP()))
    Serial.printf("\n failed to start dns service \n");

  // start TCP server
  AsyncServer* TCP_server = new AsyncServer(TCP_PORT); // start listening on tcp port 7050
  TCP_server->onClient(&handleNewClient, TCP_server);
  TCP_server->begin();

  //WLAN + Web Server configuration
  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  //Set WiFi to access point (client data collection) AND station mode (html web server)
  WiFi.mode(WIFI_AP_STA); 
  WiFi.begin(ssid, password);
  
  if (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("WiFi Connect Failed! Rebooting...");
    delay(1000);
    ESP.restart();
  }

  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // Initialize SPIFFS
  if(!SPIFFS.begin()){
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }

  //With authentifikation
  HTML_server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    if(!request->authenticate(http_username, http_password))
      return request->requestAuthentication();
    request->send(SPIFFS, "/index.html");
  });

  //No authentifikation
//  // Route for root / web page
//  HTML_server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
//    request->send(SPIFFS, "/index.html");
//  });

HTML_server.onNotFound([](AsyncWebServerRequest *request){
  request->send(404);
});

// remove HTML_server.onNotFound handler
HTML_server.onNotFound(NULL);

  HTML_server.begin();

  Serial.println("###Setup end###");
}

void loop(void) 
{ 
  DNS.processNextRequest();
}

Client sketch:
The clients just sends the A0 value to the server. If the server sends back the same value, the client goes to deepsleep for 60s (connection between Pin1 and RESET necessary! Otherwise the ESP will not wake up in time :))

#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>

extern "C" {
#include <osapi.h>
#include <os_type.h>
}

#define SSID "TestDataServer"
#define PASSWORD "ESP8266xx"

#define SERVER_HOST_NAME "DataCollectionServer"

#define TCP_PORT 7050
#define DNS_PORT 53


static os_timer_t intervalTimer;

char message[64];
String vServerData = "empty";
//String vClientName = "Gurke";

static void replyToServer(void* arg) {
 AsyncClient* client = reinterpret_cast<AsyncClient*>(arg);

 // send reply
 if (client->space() > 64 && client->canSend()) {
    int sensorValue = analogRead(A0);
 sprintf(message, "A0: %s From: %s",String(sensorValue).c_str(), WiFi.localIP().toString().c_str());
 client->add(message, strlen(message));
 client->send();
 }
}

/* event callbacks */
static void handleData(void* arg, AsyncClient* client, void *data, size_t len) {
// Serial.printf("\n data received from server %s \n", client->remoteIP().toString().c_str());
// Serial.write((uint8_t*)data, len);
//  Serial.println("");
  vServerData = String((char*)data).substring(0, len);
  Serial.print("vServerData: ");
  Serial.println(vServerData);

 os_timer_arm(&intervalTimer, 2000, true); // schedule for reply to server at next 2s
}

void onConnect(void* arg, AsyncClient* client) {
 Serial.printf("\n client has been connected to %s on port %d \n", SERVER_HOST_NAME, TCP_PORT);
 replyToServer(client);
}


void setup() {
 Serial.begin(115200);
 delay(200);
  Serial.println();
  Serial.println("###Setup###");

 // connects to access point
 WiFi.mode(WIFI_STA);
 WiFi.begin(SSID, PASSWORD);
 while (WiFi.status() != WL_CONNECTED) {
 Serial.print('.');
 delay(500);
 }

  // Print local IP address and start web server
  Serial.println("");
  Serial.println("Connected to Access Point");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  AsyncClient* client = new AsyncClient;
    
  int vDataSentSuccesfully = 0;

  while(vDataSentSuccesfully == 0)
  {
    client->onData(&handleData, client);
    client->onConnect(&onConnect, client);
    client->connect(SERVER_HOST_NAME, TCP_PORT);

//    //Compare vale sent (message) with value received (vServerData)
//    Serial.print("Compare <");
//    Serial.print(message);
//    Serial.print("> with <");
//    Serial.print(vServerData);
//    Serial.println(">");
   
    if(String(message) == vServerData)
    {
      Serial.println("###vDataSentSuccesfully###");
      vDataSentSuccesfully = 1;
    }

    delay(1000);
  }

  os_timer_disarm(&intervalTimer);
  os_timer_setfn(&intervalTimer, &replyToServer, client);

  Serial.print("Uptime in ms: ");
  Serial.println(millis());
  Serial.println("###Setup end -> Deepsleep###");
  //Deepsleep
  client->stop();
  ESP.deepSleep(60e6 - millis()*1000);  //e6 = * 1.000.000
}

void loop() {

}

Please send me a PN, if you have any questions or if the code is not working.

Best regards
Noyen