Меню управления - переключатель с обратной связью по MQTT

Использование системы в различных ситуациях, вопросы программирования сценариев.

Модератор: immortal

Ответить
Sol
Сообщения: 1
Зарегистрирован: Чт ноя 10, 2016 3:15 pm
Благодарил (а): 0
Поблагодарили: 0

Меню управления - переключатель с обратной связью по MQTT

Сообщение Sol » Чт ноя 10, 2016 3:37 pm

Добрый день!

Который день не могу побороть казалось бы простую задачку со скачущими переключателями в меню управления при наличии обратной связи от исполнительного устройства по MQTT (хотя думаю так же будет и по HTTP и т.п.).

Суть проблемы: по дому разбросано несколько модулей на ESP8266 управляющих через реле светом и т.д.
В меню управления для них накиданы переключатели.

Модули с реле раз в несколько секунд посылают свой статус на сервер. В результате даже если сам majordomo был перезагружен или еще что стряслось - после приема статуса сервером в меню управления все переключатели сами встают в соответствии с актуальными состояниями реле. Удобно.

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

Как я понимаю происходит следующее:
1. Нажали на перключатель в меню управления. Он меняет свое состояние в интерфейсе (например был выключен - стал включен) и поле status.
2. На событие смены состояния переключателя генерируется посылка по MQTT на переключение нужного реле.
3. Начинается цикл обмена данными с брокером MQTT в majordomo. В этом цикле на реле посылается новый статус (включено), но до самого реле этот статус еще не дошел. В этом же цикле majordomo получает от брокера последнее состояние реле - а оно пока еще выключено. В результате у переключателя в меню управления меняется статус, и он снова получается выключен.
4. До реле дошел новый статус, оно включилось. Переключатель как был выключен, так и остался.
4. На следующем цикле обмена с брокером MQTT majordomo получает уже новый статус реле (только что включилось), и снова меняет значение переключателя в меню управления на правильное (включен).

Как это побороть?
Аватара пользователя
sergejey
Site Admin
Сообщения: 4286
Зарегистрирован: Пн сен 05, 2011 6:48 pm
Откуда: Минск, Беларусь
Благодарил (а): 76 раз
Поблагодарили: 1559 раз
Контактная информация:

Re: Меню управления - переключатель с обратной связью по MQT

Сообщение sergejey » Чт ноя 10, 2016 5:07 pm

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

Сергей Джейгало, разработчик MajorDoMo
Идеи, ошибки -- за предложениями по исправлению и развитию слежу только здесь!
Профиль Connect -- информация, сотрудничество, услуги
ElectronicsInFocus
Сообщения: 38
Зарегистрирован: Пт апр 22, 2016 6:15 pm
Откуда: Уфа / Россия
Благодарил (а): 5 раз
Поблагодарили: 6 раз
Контактная информация:

Re: Меню управления - переключатель с обратной связью по MQT

Сообщение ElectronicsInFocus » Пт ноя 11, 2016 9:34 am

Для повторения сообщений с топиками после перезагрузки брокера можно отправлять топики с флагом retained. Это заставит брокер сохранять последнее состояние такого топика и после перезагрузки брокера разослать его всем подписчикам.
Аватара пользователя
foxvlad
Сообщения: 287
Зарегистрирован: Пн сен 22, 2014 3:18 pm
Откуда: Сочи
Благодарил (а): 78 раз
Поблагодарили: 78 раз
Контактная информация:

Re: Меню управления - переключатель с обратной связью по MQT

Сообщение foxvlad » Пт ноя 11, 2016 10:34 am

ElectronicsInFocus писал(а):Для повторения сообщений с топиками после перезагрузки брокера можно отправлять топики с флагом retained. Это заставит брокер сохранять последнее состояние такого топика и после перезагрузки брокера разослать его всем подписчикам.
А вы случайно не знаете как потом после "retained" отчистить топик, а то если потом посылать без этого флага, при подключении клиента ему будет слаться значение запомненное с "retained" а не свежее без него.

Брокер Mosquitto.
GreatBAO
Сообщения: 119
Зарегистрирован: Пн авг 24, 2015 11:24 am
Откуда: Сочи
Благодарил (а): 4 раза
Поблагодарили: 14 раз

Re: Меню управления - переключатель с обратной связью по MQT

Сообщение GreatBAO » Пт ноя 11, 2016 10:50 am

буквально вчера закончил писать код для ESP с данным функционалом, но потом потестировал и понял что раз в секунду отправлять пакет в MQTT не очень правильно, особенно если модулей много
и пришел к такому варианту:
делаем две переменных для проверки, к примеру status и status_new и устанавливаем до цикла loop обоим false или 0
потом делаем проверку

Код: Выделить всё

if(если на пин пришло HIGH) {
  //делаем проверку на предмет изменения состояния пина и устанавливаем status_new в 1
  if(status = 0 && status_new = 0)  status_new = 1;
  status = 1;
} else {
  //делаем проверку на предмет изменения состояния пина и устанавливаем status_new в 1
  if(если status = 1 && status_new = 0)  status_new = 1;
  status = 0;
}

//тем самым, при проверке статуса пина, мы выставляем status_new в 1 только в случае изменения статуса пина
//дальше делаем проверку внутри цикла отправки пакетов MQTT брокеру
  if (WiFi.status() == WL_CONNECTED) {
    if (mqtt.connected()) {
      if(status_new) {
        //отправляем пакет со значением равному status
        mqtt.publish("Public", String(status));
        //сбрасываем status_new для дальнейших проверок
        status_new = false;
      }
    }
  }
при таком варианте, при каждом изменении состояния с датчика, отправляется пакет 1 или 0 в нужную структуру MQTT
все остальное время пакеты не отправляются

возможно еще пригодится мое решения отправки статуса связи от ESP до MJD
создал класс таймеров

Код: Выделить всё

class Timer {
private:
  int timer_set = 0;
  int timer_mode = 0;
  unsigned long timer_time;
    
public:

  int set(int t) {
    if(timer_mode == 0) {
      timer_mode = 1;
      timer_set = t * 1000;
      timer_time = millis();
      return 1;
    }

    if(timer_mode == 1 && (timer_time + timer_set) < millis()) timer_mode = 0;
    return 0;
  }
}; 
создается объект таймера
Timer name_timer;

И потом вызывается таким образом

Код: Выделить всё

if (WiFi.status() == WL_CONNECTED) {
  if (mqtt.connected()) {
    if(name_timer.set(30)) {
        mqtt.publish("Status", "1");
    }
  }
}
при проверке name_timer.set(30) он возвращает 1 каждые 30 секудн и происходит отправка пакета Status = 1

в итоге, свойства объектов у меня привязаны к MQTT, при изменении статуса любого свойства в MJD происходит изменение всех связанных объектов и вместе с этим отправляется пакет изменения статуса в ESP по MQTT
при изменении статуса любого управляющего устройства подключенного к ESP, ESP отправляет пакет изменения статуса по MQTT и MJD получая этот пакет, меняет свойства объекта при этом изменяя все привязанные к этому свойству элементы

обмен пакетами между MQTT брокером и ESP модулями сведен к минимуму, отправка пакетов происходит только в случае изменения свойства объекта в MJD или при изменении статуса пинов на ESP + отправка контрольного пакета status раз в 30 секунд ESP -> MJD для проверки статуса онлайн модуля ESP
все просто и надежно )

на этом же примере, можно реализовать обратную связь для выставления нужных пинов в нужное состояние к примеру так:
ESP при включении/появлении связи с MQTT брокером разово отправляет пакет online_mqtt в MJD
MJD при получении пакета обрабатывает метод в котором всем необходимым свойствам объекта, присваиваются их же значения, тем самым происходит отправка MQTT пакетов в ESP и тот получая их выставляет необходимые пины согласно полученным данным
в итоге, если связь с брокером была разорвана в случае ребута сервака или при пропадании WiFi сети или при перезагрузке модуля ESP, ESP модуль всегда будет иметь необходимые статусы пинов полученные из MJD

реализовать можно в функции setup

Код: Выделить всё

if(mqtt.connect(MQTT::Connect(esp_name).set_auth(mqtt_user, mqtt_pass))) {
  mqtt.publish("Status_online");
  mqtt.subscribe("#");
  mqtt.set_callback(callback);
} 
а в функции callback так

Код: Выделить всё

void callback(const MQTT::Publish& pub) {
  String payload = pub.payload_string();
  if(String(pub.topic()) == "Pin_rele") {
    rele_status = payload.toInt();
  }
}
Ответить