ESP8266 Server/Client - Read input on server and output on client

hi all,

So i have a little experiment project where i would like to try and transmit the data from one ESP8266 to another.

The idea is:

I have a signal generator that generates a signal once every second, pulse width of the signal changes every second, and ranges from 100ms to 500ms. This signal is fed to an Arduino pin directly from a signal generator, and this works ok, BUT... What i would like to do is, instead of feeding the signal directly from signal generator to Arduino, i would feed the signal from a generator to an ESP8266 board (server) and transmit it to another ESP8266 board (client). This client board should then just pass that received signal to an output pin and then feed the signal to Arduino pin.
So basically, the same routine as feeding the signal directly from signal generator to Arduino but this time including 2xESP8266 to transfer the signal over WiFi.

Is this possible and if so, how?

Any tips would be highly appreciated.

Many thanks and Happy Easter!
Alek

Effectively, you are transmitting at the start of the "ON" period and again at the end of the "ON" period. The "OFF" period is derived from that on the receiver side.
I'm not sure about the latencies but you appear to need only an accuracy in the range of 10s of milliseconds, so an HTTP GET could do that. There may be better ways though.

I've managed to do something, basically using button/led philosophy, but instead of a button on the server side i have a signal generator and instead of a LED on the client side i have a PIN output that basically mirrors the signal of server input pin.

The problem is, i get it to work for 2 or 3 pulses and then it just stops for some reason...

Since frequency of my signal is just 1Hz, speed or latency should not be an issue at all.

Will tidy up and post my code shortly.

Many thanks,
Alek

ok, got it working, but i was wondering, is there a way to eliminate the delay in signal transmission. Image bellow shows the signal on 2 ends,

  1. Pink is signal generator output
  2. Yellow is the signal at the output pin of the ESP8266, after transmission over WiFi.

I know 30ms is not much of a latency, but can this be eliminated?

My code is bellow, for both Server and Client

Transmitter

#include <ESP8266WiFi.h>
#define btn1 5

const char *ssid = "ssid";
const char *password = "password";

int sensorValue1 = 0;        

void setup() {
  pinMode(btn1, INPUT_PULLUP);

  // set the ESP8266 to be a WiFi-client
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
}

void loop() {

if(digitalRead(btn1) == LOW) sensorValue1 = 0;

if(digitalRead(btn1) == HIGH) sensorValue1 = 1;



  // Use WiFiClient class to create TCP connections
  WiFiClient client;
  const char * host = "192.168.4.1";            //default IP address
  const int httpPort = 80;

  if (!client.connect(host, httpPort)) {
    Serial.println("connection failed");
    return;
  }

  // We now create a URI for the request. Something like /data/?sensor_reading=123
  String url = "/data/";
  url += "?sensor_reading=";
  url +=  "{\"sensor0_reading\":\"sensor0_value\",\"sensor1_reading\":\"sensor1_value\",\"sensor2_reading\":\"sensor2_value\",\"sensor3_reading\":\"sensor3_value\"}";

  url.replace("sensor1_value", String(sensorValue1));


  // This will send the request to the server
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
  unsigned long timeout = millis();
  while (client.available() == 0) {
    if (millis() - timeout > 5000) {
      Serial.println(">>> Client Timeout !");
      client.stop();
      return;
    }
  }
}

Receiver:

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

#define led1 5                //D1

const char *ssid      = "ssid";
const char *password  = "password";
     
int sensorValue1 = 0;              
String sensor_values;

ESP8266WebServer server(80);

void handleSentVar() {

  if (server.hasArg("sensor_reading"))
  {
    sensor_values = server.arg("sensor_reading");
    Serial.println(sensor_values);
    
    DynamicJsonBuffer jsonBuffer;
  }
  DynamicJsonBuffer jsonBuffer;
  JsonObject& root = jsonBuffer.parseObject(sensor_values);
  if (!root.success()) {
    Serial.println("parseObject() failed");
    return;
  }
  if (root.success())
  {
    sensorValue1          = root["sensor1_reading"].as<int>();
  }
  server.send(200, "text/html", "Data received");
}


void setup() {
  WiFi.softAP(ssid, password);
  IPAddress myIP = WiFi.softAPIP();

  pinMode(led1, OUTPUT);

  server.on("/data/", HTTP_GET, handleSentVar); // when the server receives a request with /data/ in the string then run the handleSentVar function
  server.begin();
}

void loop() {
  server.handleClient();
  if (sensorValue1 == 0)  {
      digitalWrite(led1, LOW);
    }
     if (sensorValue1 == 1)  {
      digitalWrite(led1, HIGH);
     }
}

Many thanks for any tips,
Alek

Just some ideas which could reduce the latency:

Server:
Transmit only start of press and end of press.
You are doing a lot of initialisation in the loop() which needs to be done only once at program start.
Have ready built Strings for the ON case and the OFF case.

Receiver:
Json is very heavy weight for just one value. You can do something like :
if (server.arg(“sensor_reading”).toInt() == 1 ) . . .
instead.
You can do the digitalWrite() in handleSentVar() instead of the loop()

Generally, you may be better off using udp. This example maybe has something you can use but try to find and ESP specific one: https://www.arduino.cc/en/Tutorial/WiFiSendReceiveUDPString

Edit

or look at websockets to reduce latency: A Beginner's Guide to the ESP8266

Thanks 6v6gt!

I am aware that json is heavy, as you said especially as i am transmitting basically just one bit per second. However, my knowledge on this one is very limited so i am struggling to find alternative solution.
In a meantime, i have tidied up the code a little but nothing radical that would eliminate transmission latency.

Looking for alternatives.

Many thanks,
Alek

I've commented out/replaced the main JSON code in the receiver and moved some code but otherwise tried to keep it the same. I can't test it though

// Receiver
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ArduinoJson.h>

#define led1 5                //D1

const char *ssid      = "ssid";
const char *password  = "password";

int sensorValue1 = 0;
String sensor_values;

ESP8266WebServer server(80);

void handleSentVar() {

  if (server.hasArg("sensor_reading"))
  {
    sensorValue1 = server.arg("sensor_reading").toInt() ;
    if (sensorValue1 == 0)  {
      digitalWrite(led1, LOW);
    }
    if (sensorValue1 == 1)  {
      digitalWrite(led1, HIGH);
    }
  }
  else {
    // error
    Serial.println("parseObject() failed");  // I'm not sure if this will work in a call back
    digitalWrite(led1, LOW);
  }




  /*
    if (server.hasArg("sensor_reading"))
    {
      sensor_values = server.arg("sensor_reading");
      Serial.println(sensor_values);

      DynamicJsonBuffer jsonBuffer;
    }
    DynamicJsonBuffer jsonBuffer;
    JsonObject& root = jsonBuffer.parseObject(sensor_values);
    if (!root.success()) {
      Serial.println("parseObject() failed");
      return;
    }
    if (root.success())
    {
      sensorValue1          = root["sensor1_reading"].as<int>();
    }
    server.send(200, "text/html", "Data received");
  */

}


void setup() {
  WiFi.softAP(ssid, password);
  IPAddress myIP = WiFi.softAPIP();

  pinMode(led1, OUTPUT);

  server.on("/data/", HTTP_GET, handleSentVar); // when the server receives a request with /data/ in the string then run the handleSentVar function
  server.begin();
}

void loop() {
  server.handleClient();

/*
  if (sensorValue1 == 0)  {
    digitalWrite(led1, LOW);
  }
  if (sensorValue1 == 1)  {
    digitalWrite(led1, HIGH);
  }
*/
}

nope, this does not work, no signal reception at all.
I think this is the part that makes it tick:

DynamicJsonBuffer jsonBuffer;
  JsonObject& root = jsonBuffer.parseObject(sensor_values);

Not sure if the same part causes latency though.

Many thanks for your help,
Alek

I omitted to send something back to the transmitter, failing to notice that you were testing for it. Maybe this works:

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

#define led1 5                //D1

const char *ssid      = "ssid";
const char *password  = "password";

int sensorValue1 = 0;
String sensor_values;

ESP8266WebServer server(80);

void handleSentVar() {

  if (server.hasArg("sensor_reading"))
  {
    sensorValue1 = server.arg("sensor_reading").toInt() ;
    if (sensorValue1 == 0)  {
      digitalWrite(led1, LOW);
    }
    if (sensorValue1 == 1)  {
      digitalWrite(led1, HIGH);
    }
  }
  else {
    // error
    Serial.println("parseObject() failed");  // I'm not sure if this will work in a call back
    digitalWrite(led1, LOW);
  }
  server.send(200, "text/html", "Data received");  // return to sender



  /*
    if (server.hasArg("sensor_reading"))
    {
      sensor_values = server.arg("sensor_reading");
      Serial.println(sensor_values);

      DynamicJsonBuffer jsonBuffer;
    }
    DynamicJsonBuffer jsonBuffer;
    JsonObject& root = jsonBuffer.parseObject(sensor_values);
    if (!root.success()) {
      Serial.println("parseObject() failed");
      return;
    }
    if (root.success())
    {
      sensorValue1          = root["sensor1_reading"].as<int>();
    }
    server.send(200, "text/html", "Data received");
  */

}


void setup() {
  WiFi.softAP(ssid, password);
  IPAddress myIP = WiFi.softAPIP();

  pinMode(led1, OUTPUT);

  server.on("/data/", HTTP_GET, handleSentVar); // when the server receives a request with /data/ in the string then run the handleSentVar function
  server.begin();
}

void loop() {
  server.handleClient();

/*
  if (sensorValue1 == 0)  {
    digitalWrite(led1, LOW);
  }
  if (sensorValue1 == 1)  {
    digitalWrite(led1, HIGH);
  }
*/
}

still no output from receiver...

OK. I think I see it. The transmitted data is currently a compound JSON format. The modified receiver code expects a simple format argument list consisting of only one argument.

We give it one more try together with the modified receiver code.

Replace this on the sender:

// We now create a URI for the request. Something like /data/?sensor_reading=123
  String url = "/data/";
  url += "?sensor_reading=";
  url +=  "{\"sensor0_reading\":\"sensor0_value\",\"sensor1_reading\":\"sensor1_value\",\"sensor2_reading\":\"sensor2_value\",\"sensor3_reading\":\"sensor3_value\"}";

with this:

 // We now create a URI for the request. Something like /data/?sensor_reading=123
  String url = "/data/?sensor_reading=sensor1_value";

Once that works, we can eliminate the return part where the receiver sends a message back to the sender hopefully eliminating half the latency.

Many thanks for you efforts 6v6gt!
This works now!
Latency is still the more or less the same though, it averages at 20ms.

Tidied up the code a little

receiver

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

int led1 = 5;
const char *ssid      = "ssid";
const char *password  = "password";

int sensorValue1 = 0;

ESP8266WebServer server(80);

void handleSentVar() {

  if (server.hasArg("sensor_reading"))
  {
    sensorValue1 = server.arg("sensor_reading").toInt() ;
    if (sensorValue1 == 0)  {
      digitalWrite(led1, LOW);
    }
    if (sensorValue1 == 1)  {
      digitalWrite(led1, HIGH);
    }
  }
  else {
    digitalWrite(led1, LOW);
  }
  server.send(200, "text/html", "Data received");  // return to sender
}


void setup() {
  WiFi.softAP(ssid, password);
  IPAddress myIP = WiFi.softAPIP();

  pinMode(led1, OUTPUT);

  server.on("/data/", HTTP_GET, handleSentVar); // when the server receives a request with /data/ in the string then run the handleSentVar function
  server.begin();
}

void loop() {
  server.handleClient();
}

Transmitter

#include <ESP8266WiFi.h>

const char *ssid = "ssid";
const char *password = "password";

int sensorValue1;        
int btn1 = 4;

void setup() {
  pinMode(btn1, INPUT_PULLUP);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
  }
  
}

void loop() {

  if(digitalRead(btn1) == LOW) {
    sensorValue1 = 1;
  } else  {
    sensorValue1 = 0;
  }


  WiFiClient client;
  const char * host = "192.168.4.1";
  const int httpPort = 80;
  if (!client.connect(host, httpPort)) {
    return;
  }

   // We now create a URI for the request. Something like /data/?sensor_reading=123
  String url = "/data/?sensor_reading=sensor1_value";
  url.replace("sensor1_value", String(sensorValue1));
  
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
  unsigned long timeout = millis();
  while (client.available() == 0) {
    if (millis() - timeout > 50000) {     
      client.stop();
      return;
    }
  }
}

OK. Let's try sending only if there is a change of the "button state" . This may not improve much but is a preparation to drop the part where the receiver sends an message back to the sender.

On the sender side, add this block:

// send only on change of sensorValue1
  static int sensorValueLast ;
  if ( sensorValueLast == sensorValue1 ) return ;  // leave 
  sensorValueLast = sensorValue1 ;

immediately after this existing code:

  if(digitalRead(btn1) == LOW) {
    sensorValue1 = 1;
  } else  {
    sensorValue1 = 0;
  }

This works! i am now getting random latency of more than 20ms on only a few bits out of 50-60. Average is now around 10ms. Huge improvement.
Thank you!
Removing return message should cut it further i think

OK. The last change meant a transmission occurred only on a change of state

Then this may not bring so much, because the return message should now occur only in a dead period. but let's see

On the sender, comment this out:

  unsigned long timeout = millis();
  while (client.available() == 0) {
    if (millis() - timeout > 50000) {     
      client.stop();
      return;
    }
  }

and on the receiver, comment this out:

server.send(200, "text/html", "Data received");  // return to sender

The next thing after that is to make the HTTP GET Strings constant instead of rebuilding each time. We need 2, one for sensorValue1 = 1 and the other for sensorValue1 = 0

ok, still works, latency about the same, often below 10ms now, 6-8ms most of the time, and just random jumps to ~20ms.
main loop on transmitter is still cluttered as you said, i've tried various "fixes" but so far none worked...

Many thanks for your help.
Alek

can you post the latest sender code ?

here it is:

#include <ESP8266WiFi.h>

const char *ssid = "ssid";
const char *password = "password";

int sensorValue1;        
int btn1 = 4;

void setup() {
  pinMode(btn1, INPUT_PULLUP);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
  } 
}

void loop() {

  if(digitalRead(btn1) == LOW) {
    sensorValue1 = 1;
  } else  {
    sensorValue1 = 0;
  }

// send only on change of sensorValue1
  static int sensorValueLast ;
  if ( sensorValueLast == sensorValue1 ) return ;  // leave
  sensorValueLast = sensorValue1 ;

  WiFiClient client;
  const char * host = "192.168.4.1";
  const int httpPort = 80;
  if (!client.connect(host, httpPort)) {
    return;
  }

  String url = "/data/?sensor_reading=sensor1_value";
  url.replace("sensor1_value", String(sensorValue1));
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
}

Thanks.

Here it is. I don't think there is much more scope for getting it, especially the loop(), much smaller.

#include <ESP8266WiFi.h>

const char *ssid = "ssid";
const char *password = "password";

const char * host = "192.168.4.1";
const int httpPort = 80;

int sensorValue1;
int btn1 = 4;

// built in setup()
String getStrSV0 = "" ;
String getStrSV1 = "" ;

void setup() {
  pinMode(btn1, INPUT_PULLUP);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
  }

  // build GET Strings - identical except for sensor_reading
  getStrSV0 = String("GET ") + String("/data/?sensor_reading=0 HTTP/1.1\r\n") +
              String("Host: ") + host + String("\r\nConnection: close\r\n\r\n");

  getStrSV1 = String("GET ") + String("/data/?sensor_reading=1 HTTP/1.1\r\n") +
              String("Host: ") + host + String("\r\nConnection: close\r\n\r\n");

}

void loop() {

  digitalRead(btn1) == LOW ? sensorValue1 = 1 : sensorValue1 = 0 ;

  // send only on change of sensorValue1
  static int sensorValueLast ;
  if ( sensorValueLast == sensorValue1 ) return ;  // leave
  sensorValueLast = sensorValue1 ;

  WiFiClient client;
  if (!client.connect(host, httpPort)) {
    return;
  }

  if ( sensorValue1 == 0 )  client.print( getStrSV0 ) ;
  else client.print( getStrSV1 ) ;
}

latest code makes the latency dip lower to as low as 4-5ms but also hits ~20-25ms more frequently than the last one, so i think i will test both further and see if there is anything else that can be done, although this is huge improvement and perhaps there is no point of trying to get it lower. It would be amazing to get a consistent latency below 5ms but not sure that is possible, at least with the current code or setup.

thank you for all the help and effort i really appreciate it!
Alek