Need help getting two ESP32-C3 to communicate through WIFI

DJETH

Well-known member
I understand this is a Teensy forum so to be clear these will be connected to a Teensy 4.1 through serial and ONLY use the ESP32-C3 for the WIFI.

The basic project at this time is to have a clock (7” display run by a Teensy 4.1) that shows time, date, temperatures, and turns on and off some lights. The temperatures and light control will be for 2 different buildings 100’ and 200’ away and will send the readings to the display in the house. I have WIFI router and extenders already in place.

Currently I have the ESP on bread boards and they are powered by the USB port

The Problems…. I don’t understand the WIFI send and receive process.

  • Main problem - I can get the client to send to the server but I CANNOT get the server to send a message to the client.
  • Secondary - The data sent from the client and received at server seems very SLOW at times. The client sends two different codes to the server. The server turns on and off an LED depending on the code received. I am running a millis timer that tracks the LED on to off and off to on times… so basically time from the end of one packet to the end of the next packet. Sometimes it runs below 50ms but other times around 200ms – 400ms. When it runs slow the times are very consistent like there is part of the WIFI driver waiting for something to clear or respond… buffer… ack…
  • I cycle the power on the ESP and it might run below 50ms till the next time I turn it off. Then turn it back on later and it will run slow. I did try two different wireless networks and no change in the pattern. I tried another pair of ESP32’s and the same. Not sure if is the client or the server causing the slowdowns
NOTES:

With minimal code and the WIFI library installed it takes up 73% of the program space and 10% of dynamic memory.

Some of the code I got from the internet and modified it somewhat.

Code:
///Server Code

/*
 * This ESP32 code is created by esp32io.com
 *
 * This ESP32 code is released in the public domain
 *
 * For more detail (instruction and wiring diagram), visit https://esp32io.com/tutorials/communication-between-two-esp32
 */

// ESP32 #2: TCP SERVER + AN LED
#include <WiFi.h>

#define LED_PIN 5  // ESP32 pin GPIO15 connected to LED Was 5 for C3 -  GPIO21 for C6
#define SERVER_PORT 4080

const char* ssid = "XXXXXXXXXXXX";  // CHANGE TO YOUR WIFI SSID 
const char* password = "XXXXXXXXXX";           // CHANGE TO YOUR WIFI PASSWORD 
IPAddress local_IP(192, 168, 1, 35);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
//WiFiServer server(80);
WiFiServer TCPserver(SERVER_PORT);


int SwitchCase = 0;
int PacketCount = -1;
uint32_t MillisTimer1 = 0;
uint32_t MillisTimer2 = 0;
uint32_t MillisToChangeLED = 0;

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);

  Serial.println("ESP32 #2: TCP SERVER + AN LED");
  WiFi.config(local_IP, gateway, subnet);
  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected to WiFi");

  // Print your local IP address:
  Serial.print("ESP32 #2: TCP Server IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println("ESP32 #2: -> Please update the serverAddress in ESP32 #1 code");

  // Start listening for a TCP client (from ESP32 #1)
  TCPserver.begin();
}

void loop() {

  switch (SwitchCase) {

    case 0:  //Basically Case 0 only runs once... then goes to case 1
      {
      
        WiFiClient client = TCPserver.accept();
                      if (client) {
          // Read the command from the TCP client:
          String command = client.readStringUntil('~');
          PacketCount = command.toInt();
          Serial.print(PacketCount);
          Serial.print(" ");
          Serial.print(command);
          Serial.print(" ");
          String command1 = client.readStringUntil('\n');
          // Serial.print("ESP32 #2: - Received command: ");
          Serial.print(command1);
TCPserver.
          if (command1 == "12345678901234567890") {
            digitalWrite(LED_PIN, HIGH);  // Turn LED on
            MillisTimer1 = millis();
          } else if (command1 == "98765432109876543210") {
            digitalWrite(LED_PIN, LOW);  // Turn LED off
            MillisTimer2 = millis();
          }  // end else if
       }
       client.stop();
        SwitchCase = 1;
        PacketCount++;
        break;
      }



    case 1:  // Runs continously in the void loop
      {
        WiFiClient client = TCPserver.accept();
             if (client) {
          // Read the command from the TCP client:
          String command = client.readStringUntil('~');
          Serial.print(PacketCount);
          Serial.print(" ");
          Serial.print(command);
          Serial.print(" ");
          String command1 = client.readStringUntil('\n');
          // Serial.print("ESP32 #2: - Received command: ");
          Serial.print(command1);

          if (command1 == "12345678901234567890") {
            digitalWrite(LED_PIN, HIGH);  // Turn LED on
            //Timer to measure LED Off to On or On to Off
            MillisToChangeLED = millis() - MillisTimer2;
            Serial.print(" ");
            Serial.print(MillisToChangeLED);
            Serial.println(" msec");
            MillisTimer1 = millis();
          } else if (command1 == "98765432109876543210") {
            digitalWrite(LED_PIN, LOW);  // Turn LED off
            //Timer to measure LED Off to On or On to Off
            MillisToChangeLED = millis() - MillisTimer1;
            Serial.print(" ");
            Serial.print(MillisToChangeLED);
            Serial.println(" msec");
            MillisTimer2 = millis();
          }  // end else if
          client.stop();
          PacketCount++;
       }
        break;

      }  // End Switch

      /*
     // I CANT GET THIS TO WORK..................... send message to client from server
        WiFiClient client = TCPserver.accept();
      if (client) {
        if (client.connected()) {
          client.println("Hello\n");
          delay(10);
        }
      }
      client.stop();
      */
  }  // End Switch case

}  // End Void Loop

Client Code
Code:
//Client Code

/*
 * This ESP32 code is created by esp32io.com
 *
 * This ESP32 code is released in the public domain
 *
 * For more detail (instruction and wiring diagram), visit https://esp32io.com/tutorials/communication-between-two-esp32
 */

// ESP32: TCP CLIENT + A BUTTON/SWITCH
#include <WiFi.h>



const char* ssid = "XXXXXXXXXX";  // CHANGE TO YOUR WIFI SSID 
const char* password = "XXXXXXXX";           // CHANGE TO YOUR WIFI PASSWORD 
const char* serverAddress = "192.168.1.35";   // CHANGE TO ESP32#2'S IP ADDRESS
const int serverPort = 4080;

IPAddress local_IP(192, 168, 1, 36);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
WiFiClient TCPclient;

int PacketCount = 1;
int PacketstoSendBeforeNextBreak = 0;
int PacketsBeforeBreak = 30;
int TimeBeforePacketSendResumes_Millis = 1000;
uint32_t PacketSendResumeTimer = 0;

void setup() {
  Serial.begin(115200);


  Serial.println("ESP32: TCP CLIENT + A BUTTON/SWITCH");
  WiFi.config(local_IP, gateway, subnet);
  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected to WiFi");

  // connect to TCP server (Arduino #2)
  if (TCPclient.connect(serverAddress, serverPort)) {
    Serial.println("Connected to TCP server");
  } else {
    Serial.println("Failed to connect to TCP server");
  }  // End else

  PacketSendResumeTimer = millis() + TimeBeforePacketSendResumes_Millis;
}  // End Void Setup

void loop() {
// The two if's below send X number of packets then waits Y time befoer sending the x number of packes again
if(PacketSendResumeTimer <= millis()) {
if(PacketCount > PacketstoSendBeforeNextBreak)  {
PacketstoSendBeforeNextBreak = PacketCount + PacketsBeforeBreak;
PacketSendResumeTimer = millis() + TimeBeforePacketSendResumes_Millis;

} else {  // Sends data to server to turn ON LED - also keeps wu wit packet count
    TCPclient.connect(serverAddress, serverPort);
  TCPclient.print(PacketCount);
  TCPclient.write('~');
  TCPclient.write("12345678901234567890\n");
  TCPclient.clear();
  TCPclient.stop();
  Serial.print(PacketCount);
  Serial.println(" 12345");  // shortened to free up the serial sooner
  PacketCount++;
 
 // Sends data to server to turn OFF LED - also keeps wu wit packet count
  TCPclient.connect(serverAddress, serverPort);
  TCPclient.print(PacketCount);
  TCPclient.write('~');
  TCPclient.write("98765432109876543210\n");
  TCPclient.clear();
  TCPclient.stop();
  Serial.print(PacketCount);
  Serial.println(" 98765");  // shortened to free up the serial sooner
  PacketCount++;

  }// End else
  }  // End if(PacketSendResumeTimer <= millis())

/*  I CANT GET THIS TO WORK..................... recieve message from server
 // get message form TCP server:
   TCPserver.begin();
        WiFiClient client = TCPserver.accept();
        // WiFiServer server = client.accept();
        if (client) {
          // Read the command from the TCP client:
          String command3 = client.readStringUntil('\n');
           Serial.print(command3);
        }  // End  if (client) 
*/

}  // End void loop


I have tried server.write and server.print but it wont compile… also not sure what to do on the client to receive the data.

Any help would be appreciated… DJETH 1000016115.jpg1000016116.jpg
 
I usually use ESP_NOW when communicating between ESPs.
Here is a tutorial that shows how to set up ESP-NOW Web Server Sensor Dashboard (ESP-NOW + Wi-Fi).
There are a few more tutorials dealing with:-
With all that information you should be able to get what you want going and understand the sequence (or the other way round!).
 
BriComp,

Thank you for the information. I had seen the ESP-Now but had not pursued it because I thought it would not use my existing WIFI and only communicated board to board (no router/network) with a limited range. I guess I misunderstood. I will check these out and report back
DJETH
 
1. Are you only using the ESP's for communications through your network? Could the units be connected wired using Ethernet (or is wireless a requirement)
2. Or are you trying to use the ESP's just for wireless between the Teensy's, meaning if you could communicate Teensy to Teensy using a wireless module, would that be acceptable?
 
Kris,
Sorry for the delay in responding...
1. currently I have WIFI near the buildings but strong enough for reliable connection. I could drop another access point at each location and use ethernet if I had too.
2. The design will be a Teensy at each building and use the ESP for WIFI communication between Teensy's. so basically every remote Teensy will have a ESP acting as a wireless module. currently two remote buildings (100' and 200' away from the main Teensy at the house). I did a BASIC preliminary test with the ESP-NOW and it works pretty good. It is Peer to Peer only(I think I have the terminology correct) and does not require a WIFI network but creates its own. I put one ESP at the building 200' away (outside the building where it will live) and the other in the house and had really fast packet transfers and no packet loss that I could see in that short test. If you have a WIFI module in mind that will work let me know but it must have a ready to go library with examples because I can just do basic programming.
FYI...I will not be connecting to the internet.

Thanks for your input... DJETH
 
Glad you got it going. I find, as you have, that ESP_NOW just works.
If distance or signal strength becomes a problem you could consider using Lora Modules, such as EBYTE E220-90T22D. They present as a serial port to the Teensy. You do need to add three other pins, but essentially the transfer is over serial UART. I have a library here that supports that item.
You can buy them here (or directly from EBYTE).

They will not be as fast as ESP_NOW but they will have a range measured in miles.

PS
I use the EBYTES as well as the ESP modules, it's horses for courses. A trade off between speed and range.
 
Last edited:
Nice... I have a solar assembly that is 900' away that I would like to monitor the battery and solar voltages from the house... The EBYTE Lora would work for the info and the Library that you and Kris worked on.
 
I use these things all the time such as my home monitoring system. EBYTE also offers a 30db unit EBYTE E220-90T30D that will give more range. Caution, you cannot power these from the onboard power regulators, you must power them from an external source.
 
Nice... I have a solar assembly that is 900' away that I would like to monitor the battery and solar voltages from the house... The EBYTE Lora would work for the info and the Library that you and Kris worked on.
Sorry I got the part number wrong, it should be EBYTE E220-900T22D.
The ..22D is good for up to 5km and the ..30D is good for 10km.
Both those distances are open space, line of sight of course.
But given that your range is only 900' (0.27km) the 5km range of the ...22D should suffice.
 
BriComp,
No worries your link had the correct part number...

Chris,

Great advice... I will use an external power source...

Thank you both for the recommendations and that you have actually used them.
 
BriComp,
No worries your link had the correct part number...

Chris,

Great advice... I will use an external power source...

Thank you both for the recommendations and that you have actually used them.
Let us know how you get on. Good luck with it.
 
Chris and BriComp,

what is the maximum reliable UART Serial transmission rate going the be between the Teensy and the Ebyte?
 
I leave mine at the default 9600, but I'm only sending 80 bytes or so every second.

Some things I've found to get longest range 1) slowest air data rate possible, 2) max transmit power 3) min of 3.5db antennas EBYTE has these

Where will your antennas be located?
 
One will be inside the house but on an outside wall. The other at the solar panel. Both about 6' off the ground. Maybe because of the trees and putting it on the furthest wall from the solar panel unit I should get the 30db... Would I get better over air data rate with more db?

Layout.jpg
 
Would I get better over air data rate with more db?
Potentially yes, but there is no point on going overboard.
Note that the lowest Air Data Rate (ADR) is 2.kbps (think of it as 240 char/s) will have the largest range.
You can monitor the rssi value to see how well you are transmitting/receiving and then decide upon upping the ADR.
The default ADR and baud rate are 2.4kbps and 9600 respectively. Obviously Ebyte have chosen this for maximum performance (distance).
The image below gives an idea of acceptable Signal Strength.
Bear in mind that with LORA there is NO handshaking. The data just gets sent and it is up to the user to determine if the data is getting through.
In a lot of cases there would be no need to have any handshaking. On the other hand simply sending a simple small ack return message would let the sender know that the data got though ok.
Acceptable Signal Strength.png
 
Agree on above, also send as little data as possible. There are some tricks you can do to trim data. For example I measure outdoor temperature, which is stored as a float which takes 4 bytes (32 bits), but I don't need 5-6 decimals of precision. I'll make a decision on how much precision and how high the value needs to be. In my case one decimal is good enough and 100-105 where I live is rare but possible. I take the float multiply by 10, recast to an int. This becomes 1050--the biggest int I need becomes 11 bits (2048). I'll bit shift that data with other data into a uint16_t and send it along. The on the receiving side bit shift that data to a float and divide by 10.0. Yea you will have round off error but this saves just under 3 bytes. There are other tricks such as subtracting a known baseline and sending only the difference. One of my projects sends some 25 floats, and 10 ints but the transmitted data is only 42 bytes (several values have small ranges).

Bit shifting is not hard, but may not be something a new programmer wants to tackle. I can write an example if you can share what data you want to send and some basic details of each.
 
Looking at the code you seem to open a TCP connection, send a single packet and then close it again.

Any reason why you can't keep the connection open? Once you have a connection from the client to the server you can send multiple packets in either direction, unless you suspect an unreliable network link there is no need to keep opening and closing the connection.

How many different devices will you have in the end? Will the communications all be to a central point? And how critical is it that every message gets through?
If you use a UDP broadcast / multicast type system then all units can listen to all transmissions without having to worry about a server/client structure. This also means that since you don't need to specify a server IP you can set all the devices to use DHCP which makes IP address management far simpler. The down side is that delivery isn't guaranteed, if the listening device isn't online or a packet gets dropped then you don't necessarily know about it. You can have everything transmit a "I'm here" type packet every few seconds to keep track of what's live and send everything a couple of times to give some basic resilience but it's no substitute for TCP if you want to ensure delivery or at least know when it fails.
 
Looking at the code you seem to open a TCP connection, send a single packet and then close it again.

Any reason why you can't keep the connection open? Once you have a connection from the client to the server you can send multiple packets in either direction, unless you suspect an unreliable network link there is no need to keep opening and closing the connection.

How many different devices will you have in the end? Will the communications all be to a central point? And how critical is it that every message gets through?
If you use a UDP broadcast / multicast type system then all units can listen to all transmissions without having to worry about a server/client structure. This also means that since you don't need to specify a server IP you can set all the devices to use DHCP which makes IP address management far simpler. The down side is that delivery isn't guaranteed, if the listening device isn't online or a packet gets dropped then you don't necessarily know about it. You can have everything transmit a "I'm here" type packet every few seconds to keep track of what's live and send everything a couple of times to give some basic resilience but it's no substitute for TCP if you want to ensure delivery or at least know when it fails.
Gone past WiFi now Andy. Using/discussing ESP_NOW on ESP32s and Ebyte Lora modules as being more appropriate.
 
In my case one decimal is good enough and 100-105 where I live is rare but possible. I take the float multiply by 10, recast to an int. This becomes 1050--the biggest int I need becomes 11 bits (2048). I'll bit shift that data with other data into a uint16_t and send it along.
That is making the assumption the lowest number you need is 0. If the minimum is less then you may need an extra bit for negative values. Or you could add an offset to shift the centre of the covered range. Similarly if you don't need to get all the way down to 0 you may be able to use less bits.

Of course if you are then using a 16 bit int to transmit the data this becomes academic. If you can keep it simple, and scaled integers in standard sizes give you what you need then they are a simple solution. But sometimes fitting 3 10 bit values into 32 bits is worth the extra effort involved. I once used a radio with a data rate of 13 bits per second, at that point you don't waste a single bit.
 
Gone past WiFi now Andy. Using/discussing ESP_NOW on ESP32s and Ebyte Lora modules as being more appropriate.
I see that but I also didn't see any meaningful suggestions on how to make the wifi option work. As with any problem there are lots of possible solutions, it doesn't hurt to offer an alternative approach. If nothing else a standard WiFi method gives a lot more flexibility for future expansion, it allows wired ethernet and other brand devices to all connect if required.
 
Chris,
Bit shifting is not hard, but may not be something a new programmer wants to tackle. I can write an example if you can share what data you want to send and some basic details of each.
I will be looking at the voltage of the solar panel (0-38VDC) and batteries (0-30 VDC). I will have one decimal accuracy. after the analog read I am using the MultiMap Library to "map" my actual voltage readings. However when I make the array for voltage readings I multiple by 100 so I don't have a decimal then before I display it I can convert it to the float


Code:
int Battery_AnalogReadValues_MultimapInputArray[21] = { 0, 16, 32, 64, 128, 256, 512, 768, 1024, 1280, 1536, 1792,
                                                          2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, 4096 };
int Battery_VoltageChart_MultimapOutputArray[21] = { 0, 200, 400, 600, 800, 1000, 1200, 1400, 1600, 1800, 2000, 2200, 2400, 2600, 2800, 3000, 3200, 3400, 3600, 3800, 4000 };

Please give an example of the bit shifting so I can understand the concept.
Also watched the video you have of the library which will help quite a bit. I did order 4 of the EBYTE E220-900T22D to try out...
 
Chris,

I will be looking at the voltage of the solar panel (0-38VDC) and batteries (0-30 VDC). I will have one decimal accuracy. after the analog read I am using the MultiMap Library to "map" my actual voltage readings. However when I make the array for voltage readings I multiple by 100 so I don't have a decimal then before I display it I can convert it to the float


Code:
int Battery_AnalogReadValues_MultimapInputArray[21] = { 0, 16, 32, 64, 128, 256, 512, 768, 1024, 1280, 1536, 1792,
                                                          2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, 4096 };
int Battery_VoltageChart_MultimapOutputArray[21] = { 0, 200, 400, 600, 800, 1000, 1200, 1400, 1600, 1800, 2000, 2200, 2400, 2600, 2800, 3000, 3200, 3400, 3600, 3800, 4000 };

Please give an example of the bit shifting so I can understand the concept.
Also watched the video you have of the library which will help quite a bit. I did order 4 of the EBYTE E220-900T22D to try out...
I hope the following makes sense. There is no need for bit shifting. The prog below converts a float to uint16_t (16 bit unsigned integer)
shifting the decimal place one place to the left.
Coming back the other way it converts the uint16_t to a float then divides by 10 to put the decimal point in the correct place.
Should you wish to preserve more decimal places then multiply and divide by 100,1000 or whatever grabs your fancy.
Code:
#include <Arduino.h>

float    floatValue;
uint16_t intValue;

uint16_t FloatToInt(float f) {
    return uint16_t(floatValue * 10 + 0.5);
}

float IntToFloat(uint16_t i) {
    return float(i) / 10.0;
}
void setup() {
    Serial.begin(9600);
    while (!Serial && millis() < 5000){}

    floatValue = 37.86;
    intValue = uint16_t(floatValue * 10 + 0.5);
    Serial.print("floatValue: "); Serial.println(floatValue);
    Serial.print("intValue:   "); Serial.println(intValue);

    /* Let me explain the conversion from float to int
    * Assuming you just wanted to preserve 1 decimal place
      most people would use  intValue = int(floatValue * 10);
      which in the example shown above would have yielded an
      integer value of 378 (37.8 multiplied by 10) but had you
      actually got an integer value of 37.86 the simple route
      would yield the same value of 378 where infact it should
      be 379 which is what my conversion does.
      float 37.86 becomes float 378.6.
      add the 0.5 and it becomes 379.1
      convert to int (drop off decimal places ) and it becomes 379
      which is 37.86 rounded to 1 dp
      
      */
    uint16_t i;
    i = FloatToInt(37.86);
    Serial.println(i);
    Serial.println(IntToFloat(i));
}

void loop() {
}
 
Andy,
I thought the WIFI through the local network was the only solution. I did get the one way communication to work through my local network however the speed was very inconsistent and I could NOT get the server to send a message to the client. I also thought that to get the range of the 200' building I HAD to go through the local network. After testing the ESP-NOW I have two way communication and WAY faster speeds.

Any reason why you can't keep the connection open? Once you have a connection from the client to the server you can send multiple packets in either direction, unless you suspect an unreliable network link there is no need to keep opening and closing the connection.
To be honest I copied a lot of this code from https://esp32io.com/tutorials/communication-between-two-esp32 and some of it I don't understand how to use it because I have no experience. After you replied I went back and move the code
TCPclient.stop();
and put it where when the
(PacketCount > PacketstoSendBeforeNextBreak)
and it works fine. originally I thought it needed to be there after every packet to send the packet... You have taught me something and I am thankful.

Still not sure how to make the server send to the client... but the ESP-NOW works great...
 
Back
Top