Disconnect спустя 5 секунд после подключения (uno+pzem004+blynk)

Добрый день, уважаемое сообщество, прошу вашей помощи. Небольшая предыстория. Для контроля за температурой в загородном доме с газовым котлом решил собрать небольшой проект, разобравшись на сколько смог получилась такая схема Arduino uno+SIM800L+18b20+Blynk и такой скетч собранный из кусков:


#define BLYNK_PRINT Serial
#define TINY_GSM_MODEM_SIM800
#include <TinyGsmClient.h>
#include <BlynkSimpleTinyGSM.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 7

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

char auth[] = "11111111111111111111111111111";
char apn[] = "internet.beeline.ru";
char user[] = "beeline";
char pass[] = "beeline";
float temp = 0;

#include <SoftwareSerial.h>
SoftwareSerial SerialAT(8, 9); // RX, TX 
TinyGsm modem(SerialAT);
BlynkTimer timer;

void sendSensor()
{
 sensors.requestTemperatures();
 delay(1000);
 Blynk.virtualWrite(V1, sensors.getTempCByIndex(0));
}

void setup()
{
 Serial.begin(9600);
 delay(1000);
 SerialAT.begin(9600); 
 delay(1000);
 Serial.println("Initializing modem...");
 modem.restart();
 //modem.init();
 delay(5000);
 Blynk.begin(auth, modem, apn, user, pass);
 timer.setInterval(5000L, sendSensor);
}
void loop()
{
 Blynk.run();
 timer.run();
}

Как бы не ужасно это выглядело со стороны профессионалов arduino и программистов эта сборка более менее работает.
Собственно к проблеме, возникла необходимость мониторить напряжение и потребление для чего к выше упомянутой схеме был добавлен модуль PZEM004T и опять же добавлены куски кода найденые на форумах (да я знаю что никто не любит халявщиков, я старался в течении месяца найти причину, но видимо программирование это вообще не мое)
и тут возникла проблема упомянутая в названии этого топика. В мониторе порта я вижу как происходит подключение в приложении появляется надпись online проходит 5-10 секунд вылазит disconnect, при этом никакие данные в приложении не обновляются. Вариантов этого скетча было с десяток различных, это последний открытый, у всех одна и та же проблема. Надеюсь на вашу помощь. Заранее огромная благодарность все кто сможет указать на ошибку или хотя бы ссылки где подглядеть варианты скетчей, просто сборок arduino+pzem+blynk крайне мало, все что было на поверхности опробовано.

#define BLYNK_PRINT Serial
#define TINY_GSM_MODEM_SIM800
#include <TinyGsmClient.h>
#include <BlynkSimpleTinyGSM.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SimpleTimer.h>
#include <PZEM004T.h>
#include <SoftwareSerial.h>
PZEM004T pzem(12,13);
IPAddress ips(192,168,1,1);

#define ONE_WIRE_BUS 7

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

char auth[] = "1111111111111111111111";
char apn[] = "internet.beeline.ru";
char user[] = "beeline";
char pass[] = "beeline";
   float temp = 0;
SimpleTimer timer;

SoftwareSerial SerialAT(8, 9); // RX, TX
TinyGsm modem(SerialAT);

void pzm()
{
  float v = pzem.voltage(ips);
  if (v < 0.0) v = 0.0;
  Blynk.virtualWrite(V2, v);
  Serial.print(v);Serial.print("V; ");
  float i = pzem.current(ips);
  if(i >= 0.0){ Blynk.virtualWrite(V3, i);
  Serial.print(i);Serial.print("A; "); }
  float p = pzem.power(ips);
  if(p >= 0.0){ Blynk.virtualWrite(V4, p);
  Serial.print(p);Serial.print("W; ");}
  float e = pzem.energy(ips);
  if(e >= 0.0){ Blynk.virtualWrite(V5, e);
  Serial.print(e);Serial.print("Wh; ");}
  Serial.println();
  pzem.setAddress(ips);
}

void sendSensor()
{
 sensors.requestTemperatures();
 delay(1000);
 Blynk.virtualWrite(V1, sensors.getTempCByIndex(0));
}

void setup()
{
 Serial.begin(9600);
 delay(1000);
  SerialAT.begin(9600); 
 delay(1000);
  Serial.println("Initializing modem...");

 //modem.restart();
 modem.init();
 delay(5000);
 Blynk.begin(auth, modem, apn, user, pass);
  timer.setInterval(5000L, pzm);
  timer.setInterval(5000L, sendSensor);
}

void loop()
{
 Blynk.run();
 timer.run();
}

Возможно 1сек delay вызывает дисконект

And both of these functions are trying to run at the exact same time. Either stagger the timers, or put a small delay in between the initialization.

timer.setInterval(5000L, pzm);  //  Start 5 second long repeating timer
timer.setInterval(5100L, sendSensor);  // Start 5.1 second long repeating timer

or…

timer.setInterval(5000L, pzm);  // Start first 5 second timer
delay(100);
timer.setInterval(5000L, sendSensor);  // Start second 5 second timer 100ms after the first.

Ничего ужасного в вашем коде нет, все когда-то начинают :wink:
PZEM не обрабатывает запросы чаще двух раз в секунду (500 мс на каждый).

Каждая команда вида - это запрос:

  float v = pzem.voltage(ips);
  ...
  float i = pzem.current(ips);
  ...

Попробуйте все запросы к PZEM повесть на таймеры. И избавитесь от delay() в теле loop()…

P.S. Вообще сами данные он конвертирует и выдает быстро ~25 мс на одно значение, но потом видимо между ними нужна задержка.

Спасибо за советы! Первые два варианта опробованы, к сожалению никаких улучшений в работе мной не замечено. Вечером опробую вариант AlexArGC, единственное что меня смущает, что в скетче для pzem который выдает значение просто в монитор порта (без блинка) нету таймеров между запросами и он работает стабильно. И на счет delay(), я так понимаю нужно заменить\убрать их из функций pzm и sendSensor? Подойдет ли с помощью SimpleTimer заменить delay() на setTimeout()?

Я лично не особо понимаю смысл опроса так часто, это счётчик энергии? Если нет то и время опроса можно увеличить на несколько секунд

По итогу скорее всего время опроса будет раз в минуту, а возможно и раз в 5 минут, чаще оно не нужно, данное время опроса выставлено для тестирования. Вы говорите о времени опроса датчиков вообще или о времени между запросами к pzem именно?

к pzem, сам pzem живёт своей жизнью хз можно ли его настраивать

В общем опробовав все варианты таймеров, в конечном итоге убрав вообще все паузы и таймеры из скетча, работоспособность осталась ровно такая же, все прекрасно подключается и через секунду выкидывает. И решил я пойти возможно дурацким путем, но кое что мне удалось выяснить. Я закомментировал все строки что относятся к pzem (остался только ds18b20) и залил этот скетч, предсказуемо все прекрасно работает, далее я раскомментировал

PZEM004T pzem(12,13);
IPAddress ips(192,168,1,1); 

залил скетч, все работает. Затем я раскомментировал

pzem.setAddress(ips);

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

не открывать я просто тупой

есть конечно, похоже конфликт библиотек я посмотрел быстро на библиотеку pzem она использует другую библиотеку ipaddress она относится к самому ядру есп и похоже когда ты пишешь в адрес то происходит цирк с конями. Делай так закоментируй строку IPAddress, а сам адрес пропиши такой uint8_t 0xB4 в set.Address.

всё фигня просто в библиотеке while использует, вот и рвёт соединение а раз у тебя нет кода на реконект то она так и зависает. Стоит проверить библиотеку и добавить во все циклы yield()

в PZEM004T.cpp нашел два куска с while первый

void PZEM004T::send(const IPAddress &addr, uint8_t cmd, uint8_t data)
{
    PZEMCommand pzem;

    pzem.command = cmd;
    for(int i=0; i<sizeof(pzem.addr); i++)
        pzem.addr[i] = addr[i];
    pzem.data = data;

    uint8_t *bytes = (uint8_t*)&pzem;
    pzem.crc = crc(bytes, sizeof(pzem) - 1);

    while(serial->available())
        serial->read();

    serial->write(bytes, sizeof(pzem));
}

и второй

bool PZEM004T::recieve(uint8_t resp, uint8_t *data)
{
    uint8_t buffer[RESPONSE_SIZE];

#ifdef PZEM004_SOFTSERIAL    
    if(_isSoft)
        ((SoftwareSerial *)serial)->listen();
#endif

    unsigned long startTime = millis();
    uint8_t len = 0;
    while((len < RESPONSE_SIZE) && (millis() - startTime < _readTimeOut))
    {
        if(serial->available() > 0)
        {
            uint8_t c = (uint8_t)serial->read();
            if(!c && !len)
                continue; // skip 0 at startup
            buffer[len++] = c;
        }
       yield(); // do background netw tasks while blocked for IO (prevents ESP watchdog trigger)
    }

    if(len != RESPONSE_SIZE)
        return false;

    if(buffer[6] != crc(buffer, len - 1))
        return false;

    if(buffer[0] != resp)
        return false;

    if(data)
    {
        for(int i=0; i<RESPONSE_DATA_SIZE; i++)
            data[i] = buffer[1 + i];
    }

    return true;
}

При этом во втором уже есть yield(). А в первом должно получится так?

...
while(serial->available())
serial->read();
    serial->write(bytes, sizeof(pzem));
yield();
...

А если добавить код на реконект есть вероятность что это решит проблему? А то я сильно сомневаюсь что смогу что-то адекватно поправить в библиотеке=)