Устойчивость передачи данных по Serial

Всех приветствую.
Есть работающая связка на Ардуино Нано (6 аналоговых датчиков и 8 реле) и ESP-01 (на нем реализован телеграм-бот). Сейчас использую для обмена командами-данными строковые параметры. Но работает не дольше 3 суток, и не без костылей. Похоже стринги сжирают память.
Первый костыль - одиночный цикл for, без него ESP не получает ответ нанки(???). Цикл на 6 каналов работает без проблем.

Спойлер
if (text == "8")
  {
    for (int i = 0; i <= 1; i++)
    {
      esp.print("#");
      esp.print("k");
      esp.print(Channels[0].number);
      esp.print(" - ");
      esp.print(Channels[1].number);
      esp.print(" - ");
      esp.print(Channels[2].number);
      esp.print(" - ");
      esp.print(Channels[3].number);
      esp.print(" - ");
      esp.print(Channels[4].number);
      esp.print(" - ");
      esp.println(Channels[5].number);
    }
	}
	if (text == "9")
	{
		allSensorRead(); 
		for (int i = 0; i <= 5; i++)
		{
			esp.print("#");
			esp.print("i");
			esp.print(Channels[i].number);
			esp.print("/");
			esp.print((byte)(Channels[i].soilMoisturePercent));//(byte)
			esp.print("/");
			esp.print(Channels[i].soilMoisturePercentControl);
			esp.print("/");
			esp.print((byte)(Channels[i].timeOff / 3600000));
			esp.print(":");
			esp.print((byte)(((Channels[i].timeOff / 1000) % 3600) / 60));
			esp.print(":");
			esp.println((byte)(((Channels[i].timeOff / 1000) % 3600) % 60));
		}
	}

Решил попробовать пересылать структурами. И возникла проблема. Данные с нанки пропадают.
Ветка со строки с идентификатором bufOutArduino.val_id = 107 вообще не доходит до ESP (по монитору порта - команда уходит).
Ветка с bufOutArduino.val_id = 105 всегда теряет первый элемент из шести, по монитору опять-таки уходят все 6 пакетов.
Остальные отрабатываются всегда.

Код для Ардуино

Спойлер
#include <SoftwareSerial.h>
#include <avr/wdt.h>
#include <EEPROM.h>

class Channel
{
  public:
    Channel(byte _number, byte _sensorPin, byte _relayPin);  // описание конструктора
    byte number;                //номер канала
    int soilMoistureValue;   //значение с датчика
    int soilMoisturePercent; //влажность в %
    byte soilMoisturePercentControl = 70; //порог влажности в %
    long timeOff;            //длительность ожидания
    long timeOn;             //длительность полива
    byte relayPin;           //пин включения реле
    void sensorRead();       // метод проверки влажности
  private:
    byte sensorPin;        //выход датчика  
};
#define AIR 520         //сухой датчик
#define WATER 216       //мокрый датчик 

byte mode = 0;  // режим 0-ожидание, 1-блокировка, 2-замер, 3-полив

//параметры времени:
long timeOff = 5000;//время ожидания контроля
long timeOn = 5000;//время полива
unsigned long startTimeOff = 0;
unsigned long startTimeOn = 0;
#define timeOut 300000//блокировка управления за 5 минут до полива
byte ID = 0;//номер в массиве
bool waterCount = false;
bool waterSleep = false;

//пины контроллера аналоговые
#define sensor1 0
#define sensor2 1
#define sensor3 2
#define sensor4 3
#define sensor5 4
#define sensor6 5
//пины контроллера цифровые
#define sensorOnPin 4
#define pumpePin 5
#define relayPin1 6
#define relayPin2 7
#define relayPin3 8
#define relayPin4 9
#define relayPin5 10
#define relayPin6 11
//программный порт
SoftwareSerial esp(12, 13);//RX-TX 12-TX с модуля, 13-RX с модуля

Channel Channel_1 = Channel(1, sensor1, relayPin1);//5
Channel Channel_2 = Channel(2, sensor2, relayPin2);//6
Channel Channel_3 = Channel(3, sensor3, relayPin3);//7
Channel Channel_4 = Channel(4, sensor4, relayPin4);//8
Channel Channel_5 = Channel(5, sensor5, relayPin5);//10
Channel Channel_6 = Channel(6, sensor6, relayPin6);//11
Channel Channels[6] = {Channel_1, Channel_2, Channel_3, Channel_4, Channel_5, Channel_6};

struct Str {
  byte val_id = 0;
  byte val_1 = 0;
  byte val_2 = 0;
  byte val_3 = 0;
  byte val_4 = 0;
  byte val_5 = 0;
  byte val_6 = 0;
  byte crc;
};
Str bufOutArduino;

struct Cmnd {
  byte val_id;
  byte val_1;
  byte val_2;
  byte crc;
};
Cmnd bufOutESP;

void setup() 
{
	pinMode(sensorOnPin, OUTPUT);
	digitalWrite(sensorOnPin, HIGH);
	pinMode(pumpePin, OUTPUT); 
	digitalWrite(pumpePin, HIGH);

	esp.begin(9600);//115200
	Serial.begin(9600);
  //Serial.setTimeout(50);

 
	// чтение байтов из EEPROM и запись
	if (EEPROM.read(6)==1)
	{
		Channels[0].soilMoisturePercentControl = EEPROM.read(0);
	}
	if (EEPROM.read(7)==1)
	{
		Channels[1].soilMoisturePercentControl = EEPROM.read(1);  
	}
	if (EEPROM.read(8)==1)
	{
		Channels[2].soilMoisturePercentControl = EEPROM.read(2);
	}
	if (EEPROM.read(9)==1)
	{
		Channels[3].soilMoisturePercentControl = EEPROM.read(3);
	} 
	if (EEPROM.read(10)==1)
	{
		Channels[4].soilMoisturePercentControl = EEPROM.read(4);
	}
	if (EEPROM.read(11)==1)
	{
		Channels[5].soilMoisturePercentControl = EEPROM.read(5);
	}
  
	allSensorRead(); 
  	
	timeOff = Channels[0].timeOff;
	timeOn = Channels[0].timeOn;
	startTimeOff = millis();
	wdt_enable(WDTO_4S);

  bufOutArduino.val_id = 106;
  bufOutArduino.crc = crc8_bytes((byte*)&bufOutArduino, sizeof(bufOutArduino) - 1);
  esp.write((byte*)&bufOutArduino, sizeof(bufOutArduino));
  delay(2000); 
  bufOutArduino.val_id = 0;
  bufOutArduino.crc = 0;
}

void loop() 
{
	if (esp.available())
	{
    if (esp.readBytes((byte*)&bufOutESP, sizeof(bufOutESP)))
      {
        byte crc = crc8_bytes((byte*)&bufOutESP, sizeof(bufOutESP));
        if (crc == 0)
        {
          Serial.print(F("Comand - ")); 
          Serial.println(bufOutESP.val_id);
        }
        else
        {
          Serial.print(F("data is damaged\n"));
        }
      }
     
	}
     
	if (bufOutESP.val_id == 6)
	{
    if (!waterSleep)
    {
      waterSleep = true;
      Serial.println("waterSleep = true");
    }
    else
    {
      waterSleep = false;
      Serial.println("waterSleep = false");
    }
    clearComand();
	}
	if (bufOutESP.val_id == 1)
	{
    if (mode == 0)
    {
  		for (int i = 0; i <= 5; i++)
      {
        if (Channels[i].number == bufOutESP.val_1)
        { 
          ID = i;
          timeOn = 60000;
          startTimeOn = millis();
          waterCount = true;
          mode = 3;// ПРОВЕРКА ПОЛИВА
        }
      }
      bufOutArduino.val_id = 119;//'w'
      bufOutArduino.val_1 = Channels[ID].number;
      bufOutArduino.val_2 = (byte)(timeOn / 1000);
      bufOutArduino.val_3 = 0;
      bufOutArduino.val_4 = 0;
      bufOutArduino.val_5 = 253;
      bufOutArduino.val_6 = 0;
      sendData();
      clearComand();
      //delay(200);
      Serial.println("mode 0");
    }
    else
    {
      bufOutArduino.val_id = 108;//'l'
      bufOutArduino.val_1 = 0;
      bufOutArduino.val_2 = 0;
      bufOutArduino.val_3 = 0;
      bufOutArduino.val_4 = 0;
      bufOutArduino.val_5 = 0;
      bufOutArduino.val_6 = 0;
      sendData();
      clearComand();
      Serial.println(mode);
    }
	}
  if (bufOutESP.val_id == 7)
  {
    if (mode == 0)
    {
      if(60 <= bufOutESP.val_2 && bufOutESP.val_2 < 100)
      {
        for (int i = 0; i <= 5; i++)
        {
          if (Channels[i].number == bufOutESP.val_1)
          { 
            Channels[i].soilMoisturePercentControl = bufOutESP.val_2;
            bufOutArduino.val_id = 108;//'l'
            bufOutArduino.val_1 = bufOutESP.val_1;
            bufOutArduino.val_2 = Channels[i].soilMoisturePercentControl;
            bufOutArduino.val_3 = 0;
            bufOutArduino.val_4 = 0;
            bufOutArduino.val_5 = 0;
            bufOutArduino.val_6 = 0;
            sendData();
  
            EEPROM.write(bufOutESP.val_1 - 1, bufOutESP.val_2);
            EEPROM.write(bufOutESP.val_1 + 5, 1);
            
            clearComand();
            Serial.println("%%");
          }
        }
      }
    }
    else
    {
      bufOutArduino.val_id = 108;//'l'
      bufOutArduino.val_1 = 0;
      bufOutArduino.val_2 = 0;
      bufOutArduino.val_3 = 0;
      bufOutArduino.val_4 = 0;
      bufOutArduino.val_5 = 0;
      bufOutArduino.val_6 = 0;
      sendData();
      clearComand();
      Serial.println("No %%");
    }
  }
  if (bufOutESP.val_id == 8)
  {
    bufOutArduino.val_id = 107;//'k'
    bufOutArduino.val_1 = Channels[0].number;
    bufOutArduino.val_2 = Channels[1].number;
    bufOutArduino.val_3 = Channels[2].number;
    bufOutArduino.val_4 = Channels[3].number;
    bufOutArduino.val_5 = Channels[4].number;
    bufOutArduino.val_6 = Channels[5].number;
    sendData();
    clearComand();
    Serial.println("Rate");
	}
	if (bufOutESP.val_id == 9)
	{
		allSensorRead(); 
		for (int i = 0; i <= 5; i++)
		{
      bufOutArduino.val_id = 105;//'i'
      bufOutArduino.val_1 = Channels[i].number;
      bufOutArduino.val_2 = (byte)(Channels[i].soilMoisturePercent);
      bufOutArduino.val_3 = Channels[i].soilMoisturePercentControl;
      bufOutArduino.val_4 = (byte)(Channels[i].timeOff / 3600000);
      bufOutArduino.val_5 = (byte)(((Channels[i].timeOff / 1000) % 3600) / 60);
      bufOutArduino.val_6 = (byte)(((Channels[i].timeOff / 1000) % 3600) % 60);
      sendData();
      //delay(1000);
      Serial.println("Info");
      wdt_reset();
		}
    clearComand();
	}
	


	if (mode == 0)
    //------------ ОЖИДАНИЕ ---------------
	{
		if (millis() - startTimeOff > (timeOff - timeOut) || timeOff < timeOut)
		{
      mode = 1;
		}
		else
		{
		  mode = 0;
		}
	}
	else if  (mode == 1)
	//------------ БЛОКИРОВКА ---------------
	{
		if (millis() - startTimeOff > timeOff || timeOff < timeOut)
		{
			startTimeOff = millis();
			mode = 2;
		}
		else
		{
			mode = 1;
		}
	}
	else if  (mode == 2)
    //------------ КОНТРОЛЬ ---------------
	{
		allSensorRead();
		if (Channels[0].soilMoisturePercent < Channels[0].soilMoisturePercentControl) 
		{
      ID = 0;
      startTimeOn = millis();
      timeOn = Channels[0].timeOn;
      waterCount = true;
      mode = 3;// ПОЛИВ
		}
		else 
		{
      mode= 0; // ОЖИДАНИЕ
		}
	}
	else if  (mode == 3)
    //------------ ПОЛИВ ---------------
	{
    if (!waterSleep)
    {
  		if (!relayOn(ID))
  		{   
  			allSensorRead();
        mode= 0; // ОЖИДАНИЕ
  		}
      else
      {
        mode= 3;
      }
      if (waterCount)
      {
        Serial.println("Полив по времени");
        bufOutArduino.val_id = 119;//'w'
        bufOutArduino.val_1 = Channels[ID].number;
        bufOutArduino.val_2 = (byte)(timeOn / 1000);
        bufOutArduino.val_3 = 0;
        bufOutArduino.val_4 = 0;
        bufOutArduino.val_5 = 0;
        bufOutArduino.val_6 = 255;
        sendData();
        clearComand();
        waterCount = false;
      }
    }
    else
    {
      bufOutArduino.val_id = 108;//'l'
      bufOutArduino.val_1 = 10;
      bufOutArduino.val_2 = 0;
      bufOutArduino.val_3 = 0;
      bufOutArduino.val_4 = 0;
      bufOutArduino.val_5 = 0;
      bufOutArduino.val_6 = 0;
      sendData();
      clearComand();
      Serial.println("mode 3");
    }
	}
	else  mode= 0;
  wdt_reset();
}



bool relayOn(int i)
{
  if (millis() - startTimeOn > timeOn)
  {
    digitalWrite(pumpePin, HIGH);// ВЫКЛ насос
    digitalWrite(Channels[i].relayPin, HIGH);// ВЫКЛ клапан    
    return false;
  }
  else
  {
    digitalWrite(Channels[i].relayPin, LOW);// ВКЛ клапан
    digitalWrite(pumpePin, LOW);// ВКЛ насос
    return true;
  }
}

// отправка сообщения
void  sendData()
{
  bufOutArduino.crc = crc8_bytes((byte*)&bufOutArduino, sizeof(bufOutArduino) - 1);
  delay(500);
  esp.write((byte*)&bufOutArduino, sizeof(bufOutArduino));  
  delay(1500);
  Serial.print(bufOutArduino.val_id);
  Serial.print(" ");
  Serial.print(bufOutArduino.val_1);
  Serial.print(" ");
  Serial.print(bufOutArduino.val_2);
  Serial.print(" ");
  Serial.print(bufOutArduino.val_3);
  Serial.print(" ");
  Serial.print(bufOutArduino.val_4);
  Serial.print(" ");
  Serial.print(bufOutArduino.val_5);
  Serial.print(" ");
  Serial.print(bufOutArduino.val_6);
  Serial.print(" ");
  Serial.println(bufOutArduino.crc);
  bufOutArduino.val_id = 0;
  bufOutArduino.val_1 = 0;
  bufOutArduino.val_2 = 0;
  bufOutArduino.val_3 = 0;
  bufOutArduino.val_4 = 0;
  bufOutArduino.val_5 = 0;
  bufOutArduino.val_6 = 0;
  bufOutArduino.crc = 0;
}

// очистка буфера команд
void  clearComand()
{
  bufOutESP.val_id = 0;
  bufOutESP.val_1 = 0;
  bufOutESP.val_2 = 0;
  bufOutESP.crc = 0;
}

// опрос всех датчиков
void  allSensorRead()
{
	digitalWrite(sensorOnPin, LOW);
	delay(1000);
	for (int i = 0; i <= 5; i++)
	{
		Channels[i].sensorRead();
	}
	digitalWrite(sensorOnPin, HIGH);
  bubbleSort(6);
  timeOn = Channels[0].timeOn;
  timeOff = Channels[0].timeOff;
}

// конструктор
Channel::Channel(byte _number, byte _sensorPin, byte _relayPin) {
  number = _number;
  sensorPin = _sensorPin;
  relayPin = _relayPin;
  pinMode(relayPin, OUTPUT); 
  digitalWrite(relayPin, HIGH); 
}
void Channel::sensorRead()
{
  soilMoistureValue = analogRead(sensorPin);
  soilMoisturePercent = map(soilMoistureValue, AIR, WATER, 0, 100);
  soilMoisturePercent = constrain(soilMoisturePercent, 0, 100);
  timeOn = (110L - soilMoisturePercent) * 250L;  //  * 1000L / 4 
  timeOff = pow(2500.0, (soilMoisturePercent - soilMoisturePercentControl) * 0.01) * 8000L * 1000L;
  timeOff = constrain(timeOff, 1600000, 83700000);//
}
void bubbleSort(int _n)
{
	int n = _n;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n - 1; j++)
		{
			if (Channels[j].timeOff > Channels[j + 1].timeOff)
			{
			Channel b = Channels[j];
			Channels[j] = Channels[j + 1]; 
			Channels[j + 1] = b;
			}
		}
	}
}
// функция для расчёта crc
byte crc8_bytes(byte *buffer, byte size)
{
  byte crc = 0;
  for (byte i = 0; i < size; i++)
  {
    byte data = buffer[i];
    for (int j = 8; j > 0; j--)
    {
      crc = ((crc ^ data) & 1) ? (crc >> 1) ^ 0x8C : (crc >> 1);
      data >>= 1;
    }
  }
  return crc;
}

Код для ESP

Спойлер
#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <WiFiClientSecure.h> 
#include <UniversalTelegramBot.h>
#include <WiFiUdp.h> 
 
const char ssid[] = "ASUS_2";
const char password[] = "xxx";
const String CHAT_ID = "xx";
const String keyboardJson = "[[\"/Info\", \"/Rate\", \"/help\"]]";
#define BOTtoken "xxxx" //AutoPoliv_2
#define reStartPin 2

WiFiClientSecure client;
UniversalTelegramBot bot(BOTtoken, client);
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org");

bool WiFiOn = false;
bool sleepOn;//для спящего режима
byte sleepStart;
byte sleepEnd;

struct Str {
  byte val_id;
  byte val_1;
  byte val_2;
  byte val_3;
  byte val_4;
  byte val_5;
  byte val_6;
  byte crc;
};
Str bufOutArduino;

struct Cmnd {
  byte val_id;
  byte val_1;
  byte val_2;
  byte crc;
};
Cmnd bufOutESP;
 
void setup()
{
  pinMode(reStartPin, OUTPUT);
  digitalWrite(reStartPin, HIGH);
  Serial.begin(9600);
  client.setInsecure();
  timeClient.begin();
  timeClient.setTimeOffset(10800);// GMT +3 = 10800
}
 
void loop()
{
  String messg = "";
	if(wifiConnected())
	{
                timeClient.update();  
		int numNewMessages = bot.getUpdates(bot.last_message_received + 1);
		handleNewMessages(numNewMessages);
      if (Serial.readBytes((byte*)&bufOutArduino, sizeof(bufOutArduino)))
      {
        byte crc = crc8_bytes((byte*)&bufOutArduino, sizeof(bufOutArduino));
        delay(2);
        if (crc == 0)
        {
          messg = F("Ответ получен\n");
        }
        else
        {
          messg = F("data is damaged\n");
        }
           
      if(bufOutArduino.val_id == 105)//'i'://105
      {
				messg += F("Канал ");
        messg += bufOutArduino.val_1;
        messg += F(": Влажность - ");
        messg += bufOutArduino.val_2;
        messg += F("%. Порог - ");
        messg += bufOutArduino.val_3;
        messg += F("%. Ожидание - ");
        messg += bufOutArduino.val_4;
        messg += F(":");
        messg += bufOutArduino.val_5;
        messg += F(":");
        messg += bufOutArduino.val_6;
      }
      else if(bufOutArduino.val_id == 107)//'k'
      {
        messg += bufOutArduino.val_1;
        messg += F(" - ");
        messg += bufOutArduino.val_2;
        messg += F(" - ");
        messg += bufOutArduino.val_3;
        messg += F(" - ");
        messg += bufOutArduino.val_4;
        messg += F(" - ");
        messg += bufOutArduino.val_5;
        messg += F(" - ");
        messg += bufOutArduino.val_6;
      }
      else if(bufOutArduino.val_id == 106)//'j'
      {
        messg = F("Ардуино перезагружен");
      }
      else if(bufOutArduino.val_id == 119)//'w'://119
      { 
				messg += F("Полив: канал ");
        messg += bufOutArduino.val_1;
        messg += F(", длительность ");
        messg += bufOutArduino.val_2;
        messg += F(" сек.");
      }
      else if(bufOutArduino.val_id == 108)//'l'://108
      {
				if(bufOutArduino.val_1 == 0)
				{
         messg = F("Система занята, повторите через 5 минут.");
				}
				else
				{
          if(bufOutArduino.val_1 == 10)
          {
            messg = F("Включен ночной режим.");
          }
          else
          {
           messg += F("Порог влажности канала ");
           messg += bufOutArduino.val_1;
           messg += F(": ");
           messg += bufOutArduino.val_2;
           messg += F("%");
          }
				}
      }
		}
    int currentHour = timeClient.getHours();
		if(messg != "")
		{
			String chat_id = String(bot.messages[numNewMessages].chat_id);
      messg += F(" Hour: ");
      messg += currentHour;
			bot.sendMessage(chat_id, messg, "");
			messg = "";
		}
	}
	// работа без WiFi
}
 
void handleNewMessages(int numNewMessages)
{
	for (int i = 0; i < numNewMessages; i++)
	{
		String chat_id = String(bot.messages[i].chat_id);
		String text = bot.messages[i].text;
		if (text == F("/help"))
		{
			bufOutESP.val_id = 0;
			String welcome = F("Используемые команды:\n");
			welcome += F("/Rate : Порядок контроля каналов\n");
			welcome += F("/Info : Информация с каждого канала\n");
			welcome += F("/Water : Принудительный полив\n");
			welcome += F("/Limit : Изменение порога влажности\n");
      welcome += F("/SleepOn : Включение спящего режима\n");//limit += F("/SleepOn XX YY\n");
      welcome += F("/SleepOff : Отключение спящего режима\n");
      welcome += F("/Restart : Перезагрузка Ардуино\n");
			bot.sendMessage(chat_id, welcome, "Markdown");     
		}
		if (text == F("/start"))
		{
			bufOutESP.val_id = 0;
			bot.sendMessageWithReplyKeyboard(chat_id, F("Выбор команды"), "", keyboardJson, true);
		}
		if (text == F("/Rate"))
		{
      bufOutESP.val_id = 8;
			bot.sendMessage(chat_id, F("Порядок каналов "), "");
      sendComand();
		}
   if (text == F("/SleepOff"))
   {
      bufOutESP.val_id = 0;
      sleepOn = false;
      bot.sendMessage(chat_id, F("Выход из спящего режима"), "");
    }
    if (text == F("/SleepOn"))
   {
      bufOutESP.val_id = 0;
      sleepOn = true;
      bot.sendMessage(chat_id, F("Переход в спящий режим"), "");
    }
    if (text == F("/Restart"))
    {
      if(chat_id != CHAT_ID)
      {
        bot.sendMessage(chat_id, F("Доступ запрещен."), "");//Unauthorized user
        bot.sendMessage(CHAT_ID, F("Попытка перезагрузки."), "");
      }
      else
      {
        bufOutESP.val_id = 0;
        bot.sendMessage(chat_id, F("Перезапуск Ардуино\n"), "Markdown");
        digitalWrite(reStartPin, LOW);
        delay(100);
        digitalWrite(reStartPin, HIGH);
      }
    }
		if (text == F("/Water"))
		{
			if(chat_id != CHAT_ID)
			{
				bot.sendMessage(chat_id, F("Доступ запрещен."), "");
				bot.sendMessage(CHAT_ID, F("Попытка полива."), "");
			}
			else
			{
				bufOutESP.val_id = 0;
				String water = F("Принудительный полив:\n");
				water += F("/Poliv1 : Канал 1\n");
				water += F("/Poliv2 : Канал 2\n");
				water += F("/Poliv3 : Канал 3\n");
				water += F("/Poliv4 : Канал 4\n");
				water += F("/Poliv5 : Канал 5\n");
				water += F("/Poliv6 : Канал 6\n");
				bot.sendMessage(chat_id, water, "Markdown");
			}
		}
		if (text == F("/Poliv1"))
		{
      bufOutESP.val_id = 1;
      bufOutESP.val_1 = 1;
			bot.sendMessage(chat_id, F("Проверка полива канала 1"), "");
      sendComand();
		}
		if (text == F("/Poliv2"))
		{
      bufOutESP.val_id = 1;
      bufOutESP.val_1 = 2;
			bot.sendMessage(chat_id, F("Проверка полива канала 2"), "");
      sendComand();
		}
		if (text == F("/Poliv3"))
		{
      bufOutESP.val_id = 1;
      bufOutESP.val_1 = 3;
			bot.sendMessage(chat_id, F("Проверка полива канала 3"), "");
      sendComand();
		}
		if (text == F("/Poliv4"))
		{
      bufOutESP.val_id = 1;
      bufOutESP.val_1 = 4;
			bot.sendMessage(chat_id, F("Проверка полива канала 4"), "");
      sendComand();
		}
		if (text == F("/Poliv5"))
		{
                        bufOutESP.val_id = 1;
                         bufOutESP.val_1 = 5;
			bot.sendMessage(chat_id, F("Проверка полива канала 5"), "");
      sendComand();
		}
		if (text == F("/Poliv6"))
		{
      bufOutESP.val_id = 1;
      bufOutESP.val_1 = 6;
			bot.sendMessage(chat_id, F("Проверка полива канала 6"), "");
      sendComand();
		}

    if(bufOutESP.val_id == 7)
    {
      String chanel = "";
      byte newLimit = 0;
      chanel = text.substring(0, 5);
      newLimit = (text.charAt(6) - '0') * 10 + (text.charAt(7) - '0') * 1;
      if(60 <= newLimit && newLimit < 100)
      {
        bufOutESP.val_2 = newLimit;
        if (chanel == F("/Lim1"))
        {
          bufOutESP.val_1 = 1;
          sendComand();
        }
        if (chanel == F("/Lim2"))
        {
          bufOutESP.val_1 = 2;
          sendComand();
        }
        if (chanel == F("/Lim3"))
        {
          bufOutESP.val_1 = 3;
          sendComand();
        }
        if (chanel == F("/Lim4"))
        {
          bufOutESP.val_1 = 4;
          sendComand();
        }
        if (chanel == F("/Lim5"))
        {
          bufOutESP.val_1 = 5;
          sendComand();
        }
        if (chanel == F("/Lim6"))
        {
          bufOutESP.val_1 = 6;
          sendComand();
        }
      }
      else
      {
        bot.sendMessage(chat_id, F("Недопустимый порог влажности, 60 <= XX < 100"), "");
      }
    }
		if (text == F("/Limit"))
		{
			if(chat_id != CHAT_ID)
			{
				bot.sendMessage(chat_id, F("Доступ запрещен."), "");
				bot.sendMessage(CHAT_ID, F("Попытка смены порога влажности."), "");
			}
			else
			{
				bufOutESP.val_id = 7;
				bot.sendMessage(chat_id, F("Изменение порога влажности:\n /LimN XX : Канал N\n"), "Markdown");
			}
		}

		if (text == F("/Info"))
		{
			//Serial.println(9);
      bufOutESP.val_id = 9;
			bot.sendMessage(chat_id, F("Запрос..."), "");
      sendComand();
		}
    text = "";
	}
}

// отправка команды
void  sendComand()
{
  bufOutESP.crc = crc8_bytes((byte*)&bufOutESP, sizeof(bufOutESP) - 1);
  Serial.write((byte*)&bufOutESP, sizeof(bufOutESP));  
  delay(2000);
  bufOutESP.val_id = 0;
  bufOutESP.val_1 = 0;
  bufOutESP.val_2 = 0;
  bufOutESP.crc = 0;
}

bool wifiConnected()
{
	if(WiFi.status() != WL_CONNECTED)
	{
		//WiFi.mode(WIFI_STA);
		WiFi.begin(ssid, password);
		int i = 0;
		while (WiFi.status() != WL_CONNECTED)
		{
			i++;
			if(i > 60)
			{
				return false;// Connect fall
			}
			delay(500);
		}
    WiFiOn = true;
		//Connect OK
	}
	return true;
}
// функция для расчёта crc
byte crc8_bytes(byte *buffer, byte size)
{
  byte crc = 0;
  for (byte i = 0; i < size; i++)
  {
    byte data = buffer[i];
    for (int j = 8; j > 0; j--)
    {
      crc = ((crc ^ data) & 1) ? (crc >> 1) ^ 0x8C : (crc >> 1);
      data >>= 1;
    }
  }
  return crc;
}

Как можно повысить надежность передачи?

заменить софтовый сериал на хардовый, он там есть, придётся правда пару резисторов оборвать от USB-UART

Ёмаё, это просто шедевр (я пока без подколов, но с сарказмом).

Подключиться к Нано через 0 и 1 пины?

да, зашить, оторвать от юарта, подключить к ESP

Одного не хватит?

Стоит ли отрубать резисторы, если они используются только при прошивке?
Прошить, вытащить USB и переставить нанку в действующий агрегат - недостаточно?

В вашей программе отсутствуют комментарии, поэтому чтобы понять ошибка или нет надо просматривать весь код.
Пока на вскидку для avr

  • Как обрабатывается ситуация “data is damaged” ?
  • После первого 137 if (esp.available()) вся последующая обработка ведется в любом случае - приняты данные или нет, есть ошибки или нет. Т.е. 155 if (bufOutESP.val_id == 6) выполняется всегда.
1 лайк

Если данные есть - заполняется структура. Дальше в структуре проверяется идентификатор и выполняется нужная ветвь кода. Кусочек с if сделан только для контроля через монитор порта: доходят ли данные от ESP к Ардуино. Данные доходят всегда. И Ардуино их обрабатывает всегда, видно по монитору. Если данных нет или повреждены, то со 155 строки никакого выполнения не будет.
От ESP к Ардуино проблем нет. Но когда со 155 строки идет отработка команд, в мониторе видно, что ответные данные отправляются, а вот ESP принимает не все и не всегда.

У вас фактически отсутствует контроль протокола обмена.
Надежность в таком случае низкая.
Я комментировал вопрос - “как повысить надежность передачи”.

Написать нормально программу. Например, не кидаться спорить с @Upper, а попытаться понять, что он говорит и переделать программу.

Но, как бы Вы её не переписывали, Вы должны понимать, что стопудовой надёжности от UART Вы не добьётесь никогда. Дело в том, что этот протокол, по своей природе, относится к ненадёжным протоколам (см. определение надёжных и ненадёжных протоколов)

1 лайк

Я не спорил. Ответил на вопросы, прокомментировал работу скетча.

И вопрос как раз в добавлении контроля обмена. Если не все данные доходят, то дойдет ли подтверждение.
И чем можно заменить-улучшить протокол.

Вы сами писали код?

Для Ардуино весь код сам. Для бота на ESP переделывал пример с включением-выключением светодиода, собственно этот код тоже практически мой.

Функция для контрольного байта чужая. Нашел в сети.

Не совсем полно ответил на Ваш первый вопрос про “data is damaged”. Описал только скетч для ардуино.
На ESP в Телеграме должно прийти сообщение “data is damaged”. В процессе работы оно не появляется.
Но иногда при старте системы приходит 2-8 таких сообщений, хотя ардуино в это время ничего не должно было отправлять.

Начал писать вам ответ, но понял, что не могу дать вам решения.
Убрал под спойлер, т.к. не уверен в ценности советов.

Спойлер

По поводу протоколов готовый совет дать трудно, т.к. везде есть свои плюсы и минусы.
Например при передаче в текстовом виде легко задать уникальный стартовый символ и ждать его появления для начала считывания строки. Но потом строку надо будет разбирать.
Если передавать как у вас двоичные данные, то с уникальным стартовым байтом могут быть трудности. В таком случае - возможна синхронизация по паузам в передаче.

Как у вас очистится буфер в случае ошибки (если в нем окажутся лишние байты)? Самостоятельно за счет следующего чтения или лучше программно очистить?

Общий совет - все надо проверять, предусматривать поведение при сбоях.
Повторюсь - и в ESP и в AVR вы ведете обработку не зависимо от
if (Serial.readBytes((byte*)&bufOutArduino, sizeof(bufOutArduino)))
возможно это не причина ошибок в вашем случае, но это неправильно. Т.к. возможна обработка недостоверных данных.

Когда исправите явные ошибки (типа тех, о которых Вам говорят - читаете на проверяя пришёл ли байт), то надёжность передачи можете попробовать повысить вот таким способом. Но! При аккуратном программировании сам по себе UART вполне себе неплох и ошибки передачи должны быть редкостью.

В текстовом виде как раз все устойчиво заработало, когда перед командой стал отправлять стартовый символ. Но от строк хочется уйти.
Буфер структуры очищается после правильно полученной, проверенной контрольным байтом и обработанной команды. Это код clearComand() в каждом блоке. Очистки буфера при сбое нет, попробую подумать в эту сторону.

Несколько удивляет, что при одинаковом алгоритме от ESP к ардуино проходит всегда, а обратно - нет.