Synchronous communication between Mega and ESP8266-01

I would like to send JSON objects from the ESP8266-01 to the Mega in the Serial1 port. If the Mega has received it, it would send another JSON object to the ESP. But i have some synchronization problem.
It starts working well, and then some “fail” comes in, later it doesn’t work at all.

How could i do that the Mega won’t send anything until the ESP doesn’t read and vice-versa?

Mega code:

#include <ArduinoJson.h>
#include <Arduino.h>
#include <DS18B20.h>

#define DS18B20WP_PIN_DQ  10
DS18B20 ds18b20wp(DS18B20WP_PIN_DQ);

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial1.begin(9600);
  delay(5000);
}
boolean state;
boolean pause;
void loop() {
  StaticJsonBuffer<100> jbFromESP;
  
  if(Serial1.availableForWrite()){
     StaticJsonBuffer<100> jbToESP;
     JsonObject& objToESP = jbToESP.createObject();

     objToESP["liquid_temp"] = ds18b20wp.readTempC();
     objToESP.printTo(Serial1);
     Serial1.println();
    }
   
    JsonObject& objFromESP = jbFromESP.parseObject(Serial1);
    if (objFromESP.success()) {
    state = objFromESP["state"];
    pause = objFromESP["pause"];
    Serial.print("state: ");
    Serial.println(state);

    Serial.print("pause: ");
    Serial.println(pause);

  } else {
  Serial.println("fail");
    
  }
}

ESP code:

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

//Firebase and Wi-Fi references
#define FIREBASE_HOST "*****"
#define FIREBASE_AUTH "*****"
#define WIFI_SSID "*****"
#define WIFI_PASSWORD "*****"

//references to firebase
#define PAUSE "DISTILLATION/pause"
#define STATE "DISTILLATION/state"

void setup() {
  Serial.begin(9600);
  // connect to wifi.
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("connecting");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println();
  Serial.print("connected: ");
  Serial.println(WiFi.localIP());
  
  Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);

}
float liquid_temp;
void loop() {
  StaticJsonBuffer<100> jbFromMega;
  JsonObject& objFromMega = jbFromMega.parseObject(Serial);

  if (objFromMega.success()) {
    liquid_temp = objFromMega["liquid_temp"];
    Firebase.setFloat(LIQUID_TEMP, liquid_temp);    
  } else {
  Serial.println("fail from ESP");
  }
  if(Serial.availableForWrite()){
     StaticJsonBuffer<100> jbToMega;
     JsonObject& objToMega = jbToMega.createObject();
     objToMega["state"] = Firebase.getBool(STATE);
     objToMega["pause"] = Firebase.getBool(PAUSE);
     objToMega.printTo(Serial);
     Serial.println();
    }
  
  delay(2000);
}

How could i do that the Mega won't send anything until the ESP doesn't read and vice-versa?

Without hardware modifications you can't. But the common way to overcome this is to send it as soon as one side is ready to send and let the receiver acknowledge the message. If you don't get the acknowledgement, simply resend the message.

Here is some code I use on an ESP32 to receive Serial:

void fReceiveSerial_LIDAR( void * parameters  )
{
  bool BeginSentence = false;
  char OneChar;
  char *str;
  str = (char *)ps_calloc(300, sizeof(char) ); // put str buffer into PSRAM
  // log_i("Free PSRAM: %d", ESP.getFreePsram());
  for ( ;; )
  {
    EventBits_t xbit = xEventGroupWaitBits (eg, evtReceiveSerial_LIDAR, pdTRUE, pdTRUE, portMAX_DELAY);
    if ( LIDARSerial.available() >= 1 )
    {
      while ( LIDARSerial.available() )
      {
        OneChar = LIDARSerial.read();
        if ( BeginSentence )
        {
          if ( OneChar == '>')
          {
            if ( xSemaphoreTake( sema_ParseLIDAR_ReceivedSerial, xSemaphoreTicksToWait10 ) == pdTRUE )
            {
               xQueueOverwrite( xQ_LIDAR_Display_INFO, ( void * ) &str );
              xEventGroupSetBits( eg, evtParseLIDAR_ReceivedSerial );
              //
            }
            BeginSentence = false;
            break;
          }
          strncat( str, &OneChar, 1 );
        }
        else
        {
          if ( OneChar == '<' )
          {
            strcpy( str, ""); // clear string buffer
            BeginSentence = true; // found beginning of sentence
          }
        }
      } //  while ( LIDARSerial.available() )
    } //if ( LIDARSerial.available() >= 1 )
    xSemaphoreGive( sema_ReceiveSerial_LIDAR );
    //        log_i( "fReceiveSerial_LIDAR " );
    //        log_i(uxTaskGetStackHighWaterMark( NULL ));
  }
  free(str);
  vTaskDelete( NULL );
} //void fParseSerial( void * parameters  )

The serial to be received has a sentence format. Each sentence begins with a ‘>’ and ends with a ‘<’. The beginning and end sentence markers allow for the initial receipt of the string being received, allows for the string to continue to be received, even if all the data is not received on one pass, and the ‘<’ allows for the sending to the parser.

Works very well, I normally run the task every milliSecond. I have ran the task as high as once every 250uSec, it works.