Reading html values

I am working with a group of High School students on an engineering project. We have two ESP32 Microprocessors running and we read data via WiFi. However what we would like to do is use the WiFi data to control the second ESP32. Here is a more in depth explanation.

ESP 1 reads temp and moisture and makes this information available via web server.

ESP 2 controls a motor.

At present we read the data from ESP 1 and based on that data decide to rotate or not rotate the motor using the web interface to ESP2

What we want is to have ESP2 automatically read the data from ESP1, then if the data meets certain parameters start the motor for a specified amount of time.

I haven’t done any web programming in a really long time so my skill set if virtually nil. I used to be a web site developer about 15-20 years ago but have forgotten most of what I used, Java and html. I am sure the technology is way different today, so any help or links to a good learning/refresher page would be greatly appreciated.

Thanks.

are the ESP32s in WiFi range of each other - if so look at ESP-NOW
if not could they connect to a local WiFi network and communicate using a TCP or UDP client/server?

If in anyway connected to the same network (either peer to peer or LAN) the can access each others webserver though it's network IP.

If one acts as an AP and the other in STA (connecting to that AP) the addresses will be 192.168.4.1 & 192.168.4.2 (or higher if more devices are connected)

Yes, they are on the same network and within about 10-15 feet if each other. ESP1 serves as the wifi server and ESP2 as the client. So both on the same network. That was intentional and ESP1 has a static IP address so simplifies access via web browser. I’ll have to look at ESP-Now a little more closely, but it might be what we need, if not it definitely looks interesting.

As a matter of fact that’s exactly the way they are connected in a Client/Server arrangement. So ESP1 is the Server and ESP2 is the client. That was easy, I remembered how to do that pretty straight forward. Getting data from 1 to 2 in a form that I can use to make decisions with is not so easy. I need the data in integer or float format and html (as I recall) is text based. Its been a very long time.

A dive into the past.

example TCP client/server transferring a data structure of integers and floats

server receiving data from client

// ESP32 open server on port 10000 to receive data structure

#include <WiFi.h>

// Replace with your network credentials
const char* ssid = "xxxxx";
const char* password = "xxxxxx";

// test structure
struct __attribute__((packed)) Data {
  int16_t seq;  // sequence number
  int32_t distance;
  float voltage;
  char text[50];
} data;

WiFiServer server(10000);  // server port to listen on

void setup() {
  Serial.begin(115200);
  // setup Wi-Fi network with SSID and password
  Serial.printf("Connecting to %s\n", ssid);
  Serial.printf("\nattempting to connect to WiFi network SSID '%s' password '%s' \n", ssid, password);
  // attempt to connect to Wifi network:
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(500);
  }
  server.begin();
  // you're connected now, so print out the status:
  printWifiStatus();
  Serial.println(" listening on port 10000");
}

boolean alreadyConnected = false;  // whether or not the client was connected previously

void loop() {
  static WiFiClient client;
  static int16_t seqExpected = 0;
  if (!client)
    client = server.available();  // Listen for incoming clients
  if (client) {                   // if client connected
    if (!alreadyConnected) {
      // clead out the input buffer:
      client.flush();
      Serial.println("We have a new client");
      alreadyConnected = true;
    }
    // if data available from client read and display it
    int length;
    if ((length = client.available()) > 0) {
      //str = client.readStringUntil('\n');  // read entire response
      Serial.printf("Received length %d - ", length);
      // if data is correct length read and display it
      if (length == sizeof(data)) {
        client.readBytes((char*)&data, sizeof(data));
        Serial.printf("seq %d distaance %ld voltage %f text '%s'\n",
                      (int)data.seq, (long)data.distance, data.voltage, data.text);
        if (data.seq != seqExpected)  // check sequence number received
          Serial.printf("Error! seq expected %d received %d\n", seqExpected, data.seq);
        seqExpected = data.seq;  // set sequence number ready for next data
        seqExpected++;
      } else
        while (client.available()) Serial.print((char)client.read());  // discard corrupt packet
    }
  }
}


void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("\nSSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

client connects to server and transmits data

// ESP32 client - connect to Access point server on port 10000 - transmit data structure

#include <WiFi.h>

// access point SSID and password
char ssid[] = "xxxx";  //  network SSID (name)
char password[] = "xxxxxxxx";       // network password
IPAddress server(192, 168, 1, 211);  //  IP address

// test structure
struct __attribute__((packed)) Data {
  int16_t seq;  // sequence number
  int32_t distance;
  float voltage;
  char text[50];
} data = { 0, 56, 3.14159, "hello test" };  // sample data

void setup() {
  //Initialize serial and wait for port to open:
  Serial.begin(115200);
  Serial.printf("\nattempting to connect to WiFi network SSID '%s' password '%s' \n", ssid, password);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(500);
  }
  Serial.print("connected to SSID: ");
  Serial.println(ssid);
}

boolean alreadyConnected = false;  // whether or not the client was connected previously

void loop() {
  static WiFiClient client;
  if (!client) {  // connect to server on AP
    if (client.connect(server, 10000))
      Serial.println("Connected to AP server");
  }
  // if clength connected send data
  if (client) {
    if (!alreadyConnected) {
      client.flush();
      Serial.println("client connected to server OK");
      alreadyConnected = true;
    }
    // transmit data structure to server
    Serial.printf("seq %d distance %ld voltage %f text '%s'\n",
                  (int)data.seq, (long)data.distance, data.voltage, data.text);
    client.write((char*)&data, sizeof(data));
    delay(1000);
    data.seq++;           // update data ready for next transmission
    data.distance += 10;
    data.voltage += 2.5;
    data.text[9]++;
  }
}

server serial output

server receiving data
attempting to connect to WiFi network SSID 'xxxx' password 'zzzzzz' 
....................... listening on port 10000
SSID: xxxxxxxxxxxxxxxxxx
IP Address: 192.168.1.211
signal strength (RSSI):-48 dBm
We have a new client
Received length 60 - seq 1 distaance 66 voltage 5.641590 text 'hello tesu'
Error! seq expected 0 received 1
Received length 60 - seq 2 distaance 76 voltage 8.141590 text 'hello tesv'
Received length 60 - seq 3 distaance 86 voltage 10.641590 text 'hello tesw'
Received length 60 - seq 4 distaance 96 voltage 13.141590 text 'hello tesx'
Received length 60 - seq 5 distaance 106 voltage 15.641590 text 'hello tesy'
Received length 60 - seq 6 distaance 116 voltage 18.141590 text 'hello tesz'
Received length 60 - seq 7 distaance 126 voltage 20.641590 text 'hello tes{'
Received length 60 - seq 8 distaance 136 voltage 23.141590 text 'hello tes|'
Received length 60 - seq 9 distaance 146 voltage 25.641590 text 'hello tes}'
Received length 60 - seq 10 distaance 156 voltage 28.141590 text 'hello tes~'
Received length 60 - seq 11 distaance 166 voltage 30.641590 text 'hello tes'
Received length 60 - seq 12 distaance 176 voltage 33.141590 text 'hello tes '

client transmitting to server

client transmitting data
attempting to connect to WiFi network SSID 'xxxxt' password 'xxxxxx' 
........connected to SSID:zzzzz
Connected to AP server
client connected to server OK
seq 0 distaance 56 voltage 3.141590 text 'hello test'
seq 1 distaance 66 voltage 5.641590 text 'hello tesu'
seq 2 distaance 76 voltage 8.141590 text 'hello tesv'
seq 3 distaance 86 voltage 10.641590 text 'hello tesw'
seq 4 distaance 96 voltage 13.141590 text 'hello tesx'
seq 5 distaance 106 voltage 15.641590 text 'hello tesy'
seq 6 distaance 116 voltage 18.141590 text 'hello tesz'
seq 7 distaance 126 voltage 20.641590 text 'hello tes{'
seq 8 distaance 136 voltage 23.141590 text 'hello tes|'
seq 9 distaance 146 voltage 25.641590 text 'hello tes}'
seq 10 distaance 156 voltage 28.141590 text 'hello tes~'
seq 11 distaance 166 voltage 30.641590 text 'hello tes'
seq 12 distaance 176 voltage 33.141590 text 'hello tes '
seq 13 distaance 186 voltage 35.641590 text 'hello tes '
seq 14 distaance 196 voltage 38.141590 text 'hello tes '

the transmitted structures contain a sequence number to check for lost or duplicate packets
a CRC check could be added

if transmitting between server/clients of the same type (e.g. both ESP32s)

 __attribute__((packed)) 

is not required as data types are the same size etc

Many thanks. I need to study this code, but I see where it will do exactly what I want. Thanks again.

You can certainly send and receive raw bytes, so that the numbers are not text. If you want a medium ground for usability and debugging, the way to do it with a "web interface" is for the client to declare what they will accept for a given URL. This is done via HTTP headers. By default a browser will send something like

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

"I prefer HTML, then XHTML, and even XML; but will accept anything" (that */* at the end). Servers are then supposed to honor this.

So in this case, ESP2 as the client can send instead, for the same URL, e.g. 192.168.1.234/data

GET /data HTTP/1.0
Accept: application/json

expecting JSON in return. ESP1 as the server, having gotten a request on the path "/data", can check for the Accept header. How easy that is depends on exactly what you're using as a web server.

The JSON response is text. But that part is not necessarily too complicated

  client.print(R"({"distance":)");  // raw string so you don't have to escape quotes
  client.print(distance);  // maybe int
  client.print(R"(,"voltage":)");
  client.print(voltage, 2);  // float, two decimal places
  client.println("}");

The client then needs to parse JSON, which is a lot easier than parsing HTML. If it's simple enough, you can do it manually as well. And there are simpler data-as-text formats.

One advantage of this approach is that you can test ESP1 using web development tooling, like curl

$ curl 192.168.1.234/data -H "Accept: application/json"
{"distance":56,"voltage":7.89}

ESP-NOW transmit a structure

// ESP-NOW transmit a structure

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp-now-esp32-arduino-ide/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <esp_now.h>
#include <WiFi.h>

// REPLACE WITH YOUR RECEIVER MAC Address
uint8_t broadcastAddress[] = {0x24,0x62,0xAB,0xE0,0x64,0x54}; //{ 0x84, 0xCC, 0xA8, 0x7A, 0x56, 0x6C };  //{0x24,0x62,0xAB,0xE0,0x64,0x54};

esp_now_peer_info_t peerInfo;

// test structure
struct __attribute__((packed)) Data {
  int16_t seq;  // sequence number
  int32_t distance;
  float voltage;
  char text[50];
} data = { 0, 56, 3.14159, "hello test" };  // sample data

// callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("Last Packet Send Status:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

void setup() {
  // Init Serial Monitor
  Serial.begin(115200);
  Serial.println("ESP-NOW transmitting a structure");
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);
  Serial.print("ESP32 Board MAC Address:  ");
  Serial.println(WiFi.macAddress());
  // Init ESP-NOW
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // Once ESPNow is successfully Init, we will register for Send CB to
  // get the status of Trasnmitted packet
  esp_now_register_send_cb(OnDataSent);

  // Register peer
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.channel = 0;
  peerInfo.encrypt = false;

  // Add peer
  if (esp_now_add_peer(&peerInfo) != ESP_OK) {
    Serial.println("Failed to add peer");
    return;
  }
}

void loop() {
  // Send data structure via ESP-NOW
  Serial.printf("seq %d distaance %ld voltage %f text '%s'\n",
                (int)data.seq, (long)data.distance, data.voltage, data.text);
  esp_now_send(broadcastAddress, (uint8_t *)&data, sizeof(data));
  delay(1000);
  data.seq++;  // update data ready for next transmission
  data.distance += 10;
  data.voltage += 2.5;
  data.text[9]++;
}

esp-now receive a structure

// ESP-NOW receive a structure

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp-now-esp32-arduino-ide/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <esp_now.h>
#include <WiFi.h>

// test structure
struct __attribute__((packed)) Data {
  int16_t seq;  // sequence number
  int32_t distance;
  float voltage;
  char text[50];
} data;

// callback function that will be executed when data is received
void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) {
  static int16_t seqExpected = 0, errors=0;
  Serial.printf("Bytes received: %d ", len);
  if (len == sizeof(data)) {
    memcpy((void *)&data, (void *)incomingData, len);
    Serial.printf("seq %d distaance %ld voltage %f text '%s' errors %d\n",
                  (int)data.seq, (long)data.distance, data.voltage, data.text, errors);
    if (data.seq != seqExpected)  // check sequence number received
      Serial.printf("Error! seq expected %d received %d errors %d\n", seqExpected, data.seq, ++errors);
    seqExpected = data.seq;  // set sequence number ready for next data
    seqExpected++;
  } else
    Serial.printf("ERROR! packek size expected %d receive %d errors %d\n", sizeof(data), len, ++errors);
}


void setup() {
  // Initialize Serial Monitor
  Serial.begin(115200);
  Serial.println("ESP-NOW receive a structure");
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);
  Serial.print("ESP32 Board MAC Address:  ");
  Serial.println(WiFi.macAddress());

  // Init ESP-NOW
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // Once ESPNow is successfully Init, we will register for recv CB to
  // get recv packer info
  esp_now_register_recv_cb(OnDataRecv);
}

void loop() {
}

transmitter serial monitor output

ESP-NOW transmitter
ESP-NOW transmitting a structure
ESP32 Board MAC Address:  08:3A:F2:B7:F8:F4
seq 0 distaance 56 voltage 3.141590 text 'hello test'
Last Packet Send Status:	Delivery Fail
seq 1 distaance 66 voltage 5.641590 text 'hello tesu'
Last Packet Send Status:	Delivery Fail
seq 2 distaance 76 voltage 8.141590 text 'hello tesv'
Last Packet Send Status:	Delivery Success
seq 3 distaance 86 voltage 10.641590 text 'hello tesw'
Last Packet Send Status:	Delivery Success
seq 4 distaance 96 voltage 13.141590 text 'hello tesx'
Last Packet Send Status:	Delivery Success
seq 5 distaance 106 voltage 15.641590 text 'hello tesy'
Last Packet Send Status:	Delivery Success


receiver serial monitor output

ESP-NOW receive a structure
ESP32 Board MAC Address:  84:CC:A8:7A:56:6C
Bytes received: 60 seq 2 distaance 76 voltage 8.141590 text 'hello tesv'
Error! seq expected 0 received 2
Bytes received: 60 seq 3 distaance 86 voltage 10.641590 text 'hello tesw'
Bytes received: 60 seq 4 distaance 96 voltage 13.141590 text 'hello tesx'
Bytes received: 60 seq 5 distaance 106 voltage 15.641590 text 'hello tesy'
Bytes received: 60 seq 6 distaance 116 voltage 18.141590 text 'hello tesz'
Bytes received: 60 seq 7 distaance 126 voltage 20.641590 text 'hello tes{'


the above worked OK at 5 to 6 metres thru a foil backed plasterboard wall
failed at 6/7 metres thru two foil backed pasterbaord walls, e.g.

Bytes received: 60 seq 8 distaance 136 voltage 23.141590 text 'hello tes|' errors 3
Bytes received: 60 seq 9 distaance 146 voltage 25.641590 text 'hello tes}' errors 3
Bytes received: 60 seq 10 distaance 156 voltage 28.141590 text 'hello tes~' errors 3
Bytes received: 60 seq 12 distaance 176 voltage 33.141590 text 'hello tes ' errors 3
Error! seq expected 11 received 12 errors 4
Bytes received: 60 seq 13 distaance 186 voltage 35.641590 text 'hello tes ' errors 4
Bytes received: 60 seq 14 distaance 196 voltage 38.141590 text 'hello tes ' errors 4
Bytes received: 60 seq 15 distaance 206 voltage 40.641590 text 'hello tes ' errors 4
Bytes received: 60 seq 16 distaance 216 voltage 43.141590 text 'hello tes ' errors 4

Wow. Things have changed. I have a lot of work to do. Curl is new to me as is json. So I get to do some reading over the next few days. Thanks for the insight and information.

Everyone, thank you. I have a lot of work to do over the next few days to get “up to speed” on these new technologies (at least new to me). I guess that’s the fun of this business, always something new to learn and try.