Продолжаю мучать машинку-управляемую через MQTT. Есть проблема: вис системы - если стучать по всем клавишам подряд
- Войдите на сайт для отправки комментариев
Всем доброго, друзья!
Может кто подскажет: мучаю свою многострадальную машинку-управляемую с сайта через MQТТ-брокер (веб интерфейс шлет сообщения на брокер-откуда их забирает и исполняет машинка).
Веб интерфейс проверен-всё ок. С брокером тоже всё ок-все работает отлично. Где то косяк на этапе принятия сообщения и его дальнейшей обработки(я так подозреваю).
В чем косяк: в целом всё работает ок. Но, в какой то момент-машинка "зависает" с включенными колесами или наоборот-выключенными. На команды с сайта не реагирует. Машинка построена на ESP32 (я уже выкладывал ее). С батареей всё ок.
Вот так вылглядит код. Может кто подскажет, где теоретически может теряться принятое сообщение, чтобы машинка "зависла" в неопределенном состоянии?
Из кода вырезал: 1) код выравнивания скоростей колес-с помощью прерываний и PID-регулятора (чтобы машинка ехала строго вперед); 2) замер напряжения батареи 18650. Так как особого отношения к делу он не имеет.
Основной код:
#include <WiFi.h>
#include <PubSubClient.h>
#include <Pangodream_18650_CL.h>
//#include <GyverPID.h>
//---------------Замер напряжения батареи-----------------
//#define ADC_PIN 34
//#define CONV_FACTOR 1.7
//#define READS 20
Pangodream_18650_CL BL;
/**
* If you need to change default values you can use it as
* Pangodream_18650_CL BL(ADC_PIN, CONV_FACTOR, READS);
*/
volatile uint32_t charge_test = 0; // время последней проверки уровня заряда батареи
//--------------------------------------------------------
// вставляем ниже SSID и пароль для своей WiFi-сети:
const char* ssid = "сюда название WIFI сети";
const char* password = "пароль WIFI сети";
const char* mqtt_server = "адрес mqtt-брокера";
#define mqtt_port 1883
#define MQTT_USER "" //сюда имя пользователя MQTT, если система должна иметь пользователя и пароль
#define MQTT_PASSWORD "" //сюда пароль пользователя MQTT, если система должна иметь пользователя и пароль
#define MQTT_SERIAL_PUBLISH_CH "vasyapupgen|moshino"
#define MQTT_SERIAL_RECEIVER_CH_1 "vasyapupgen/left"
#define MQTT_SERIAL_RECEIVER_CH_2 "vasyapupgen/right"
#define MQTT_SERIAL_RECEIVER_CH_3 "vasyapupgen/forward"
#define MQTT_SERIAL_RECEIVER_CH_4 "vasyapupgen/reverse"
#define MQTT_SERIAL_RECEIVER_CH_5 "vasyapupgen/ctrl"
// переменная для хранения HTTP-запроса:
String header;
// мотор 1:
int motor1Pin1 = 21;
int motor1Pin2 = 19;
//int enable1Pin = 14;
// мотор 2:
int motor2Pin1 = 23;
int motor2Pin2 = 22;
//int enable2Pin = 32;
// переменные для свойств широтно-импульсной модуляции (ШИМ) 1-двигателя:
const int freq = 30000;
const int pwmChannel = 0;
const int resolution = 8;
int dutyCycle = 0;
// переменные для свойств широтно-импульсной модуляции (ШИМ) 2-двигателя:
const int freq2 = 30000;
const int pwmChannel2 = 1;
const int resolution2 = 8;
int dutyCycle2 = 0;
// переменные для расшифровки HTTP-запроса GET:
String valueString = String(5);
int pos1 = 0;
int pos2 = 0;
//------------------- Работа с оптическими датчиками --------------------------
int OnOff = 0; //сюда будет писаться, нажата ли клавиша в данный момент
boolean CtrlState = false;
String state = "Stop"; //сюда будет писаться состояние, т.е. куда едет машинка в данный момент
volatile uint32_t lasttime1 = 0;
volatile uint32_t lasttime2 = 0;
volatile uint32_t last_check_time = 0; //время последнего вывода экран-результатов замера скорости вращения двигателей
volatile uint32_t ONEmotor_speed = 0; //средняя скорость вращения 1 двигателя
volatile uint32_t ONEmotor_speed_last = 0; //прежняя средняя скорость вращения 1 двигателя
volatile uint32_t TWOmotor_speed = 0; //средняя скорость вращения 2 двигателя
volatile uint32_t TWOmotor_speed_last = 0; //прежняя средняя скорость вращения 2 двигателя
volatile uint32_t impulse_counter1 = 0; //счетчик импульсов 1 колеса
volatile uint32_t impulse_counter2 = 0; //счетчик импульсов 2 колеса
// boolean LastInterruptionState = false; //значение последнего прерывания
volatile boolean InterruptionState = false; //значение последнего прерывания
volatile boolean InterruptionState2 = false; //значение последнего прерывания
String printing = "произошло прерывание1";
String printing2 = "произошло прерывание2";
volatile int PinState;
//Куда подключены оптические датчики энкодера:
int SensorPin1 = 13;
int SensorPin2 = 14;
//-----------------------------------------------------------------------------
//----------------------БЛОК РАБОТЫ С MQTT и WIFI --------------------
WiFiClient wifiClient;
PubSubClient client(wifiClient);
void setup_wifi() {
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
randomSeed(micros());
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Create a random client ID
String clientId = "ESP32Client-";
clientId += String(random(0xffff), HEX);
// Attempt to connect
if (client.connect(clientId.c_str(),MQTT_USER,MQTT_PASSWORD)) {
Serial.println("connected");
//Once connected, publish an announcement...
client.publish("/icircuit/presence/ESP32/", "hello world");
// ... and resubscribe
client.subscribe(MQTT_SERIAL_RECEIVER_CH_1);
client.subscribe(MQTT_SERIAL_RECEIVER_CH_2);
client.subscribe(MQTT_SERIAL_RECEIVER_CH_3);
client.subscribe(MQTT_SERIAL_RECEIVER_CH_4);
client.subscribe(MQTT_SERIAL_RECEIVER_CH_5);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void callback(char* topic, byte *payload, unsigned int length) {
char in[length+1]={0};
memcpy(in,payload,length); // функция копирует массв в другой - in. Это нужно, чтобы потом проще передавать этот массив, без указания его длины length
// if (!Charge_Level)//выполняем, если только батарея не разряжена
// {
Motors (topic, in);
// }
// else
// {
// Serial.println("Батарея разряжена-замените батарею!");
// }
// Serial.println("-------new message from broker-----");
// Serial.print("channel:");
// Serial.println(topic);
// Serial.print("data:");
// Serial.write(payload, length);
// Serial.println();
}
//---------- Скорее всего - лишняя функция, ее можно вырубить --------------
void publishSerialData(char *serialData){
if (!client.connected()) {
reconnect();
}
client.publish(MQTT_SERIAL_PUBLISH_CH, serialData);
}
//--------------------------------------------------------------------
//--------------------------------------------------------------------
void setup() {
Serial.begin(115200);
Serial.setTimeout(500);// Set time out for
setup_wifi();
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
reconnect();
// переключаем контакты моторов в режим «OUTPUT»:
pinMode(motor1Pin1, OUTPUT);
pinMode(motor1Pin2, OUTPUT);
pinMode(motor2Pin1, OUTPUT);
pinMode(motor2Pin2, OUTPUT);
// подключаем контакты оптических датчиков к пину, в режим «INPUT»:
pinMode (SensorPin1, INPUT);
pinMode (SensorPin2, INPUT);
// подключаем реакцию на прерывания - к пинам датчиков:
attachInterrupt(digitalPinToInterrupt(SensorPin1), sensor_test, HIGH);
attachInterrupt(digitalPinToInterrupt(SensorPin2), sensor_test2, HIGH);
// задаем настройки ШИМ-канала каждого из 2 двигателей:
ledcSetup(pwmChannel, freq, resolution); // первый двигатель
ledcSetup(pwmChannel2, freq2, resolution2); // второй двигатель
// подключаем ШИМ-канал, к контактам для управления скоростью вращения каждого из 2 моторов:
ledcAttachPin(motor1Pin1, pwmChannel); // первый двигатель
ledcAttachPin(motor2Pin1, pwmChannel2); // второй двигатель
// подаем на контакты ШИМ-сигнал с коэффициентом заполнения «0»:
ledcWrite(pwmChannel, dutyCycle);
ledcWrite(pwmChannel2, dutyCycle2);
}//setup
void loop(){
client.loop();
//-------- читаем уровень заряда батареи---------
// Charge_Level ();
//-----------------------------------------------
//--------- выводим скорости двигателей ---------
// SpeedCheck3 ();
// MotorTester ();
//-----------------------------------------------
//Motors_controller ();
if (Serial.available() > 0) {
char mun[501];
memset(mun,0, 501);
Serial.readBytesUntil( '\n',mun,500);
publishSerialData(mun);
}
}
А вот функция, для управления двигателями(ниже):
// Функция, управляющая двигателями
void Motors (char* topic, char ch [])
{
String s = topic;
char temp;
// выясняем, команда на вкл или выкл мотора
if(ch[0]=='0')
{
// Serial.println("команда: выключить двигатель");
temp = '0'; // отключить мотор
} else if (ch[0]=='1')
{
// Serial.println("команда: включить двигатель");
temp = '1'; // включить мотор
}
// volatile uint32_t stright_moving_time = 0;
volatile uint32_t turning_time = 0; //сюда пишется время, как долго нажата клавиша поворота, во время езды вперед или назад
int speed_temp_forward = 200; // переменная для ШИМ, при подруливании-во время движения вперед
if ((s.indexOf("ctrl")>0)&&(temp=='1'))
{
CtrlState =true;
Serial.println("Нажат Ctrl");
}
else if ((s.indexOf("ctrl")>0)&&(temp=='0'))
{
CtrlState =false;
Serial.println("Ctrl отпущен");
}
else if ((s.indexOf("left")>0)&&(temp=='1')) //ПОВОРОТ ВЛЕВО
{
if (state.equals ("Stop")) //выполняем поворот, только если кнопка какого-либо движения отпущена. Это нужно для подруливания-иначе не получится
{
if (CtrlState) //если нажат Ctrl-поворачиваемся медленно
{
Serial.println("LeftSlow"); // "Влево медленно"
state = "LeftSlow";
ledcWrite(pwmChannel, 20);
ledcWrite(pwmChannel2, 235);
digitalWrite(motor1Pin1, HIGH);
digitalWrite(motor1Pin2, LOW);
digitalWrite(motor2Pin1, LOW);
digitalWrite(motor2Pin2, HIGH);
}
else
{
Serial.println("Left"); // "Влево"
state = "Left";
ledcWrite(pwmChannel, 255);
ledcWrite(pwmChannel2, 0);
digitalWrite(motor1Pin1, HIGH);
digitalWrite(motor1Pin2, LOW);
digitalWrite(motor2Pin1, LOW);
digitalWrite(motor2Pin2, HIGH);
}
}
else if (state.equals ("Forward")) // ПОДРУЛИВАНИЕ ВЛЕВО
{
Serial.println("Подруливание влево");
state = "Left_Correction";
//-------подруливание происходит торможением левого двигателя, а правый крутится как и должен -----------
ledcWrite(pwmChannel, 0);
// ledcWrite(pwmChannel2, 0);
digitalWrite(motor1Pin1, LOW);
digitalWrite(motor1Pin2, LOW);
// digitalWrite(motor2Pin1, LOW);
// digitalWrite(motor2Pin2, LOW);
}
}
else if ((s.indexOf("right")>0)&&(temp=='1')) // ПОВОРОТ ВПРАВО
{
if (state.equals ("Stop")) //выполняем поворот, только если кнопка какого-либо движения отпущена. Это нужно для подруливания-иначе не получится
{
if (CtrlState) //если нажат Ctrl-поворачиваемся медленно
{
Serial.println("RightSlow"); // "Вправо медленно"
state = "RightSlow";
ledcWrite(pwmChannel, 235);
ledcWrite(pwmChannel2, 20);
digitalWrite(motor1Pin1, LOW);
digitalWrite(motor1Pin2, HIGH);
digitalWrite(motor2Pin1, HIGH);
digitalWrite(motor2Pin2, LOW);
}
else
{
Serial.println("Right"); // "Вправо"
state = "Right";
ledcWrite(pwmChannel, 0);
ledcWrite(pwmChannel2, 255);
digitalWrite(motor1Pin1, LOW);
digitalWrite(motor1Pin2, HIGH);
digitalWrite(motor2Pin1, HIGH);
digitalWrite(motor2Pin2, LOW);
}
}
else if (state.equals ("Forward")) // ПОДРУЛИВАНИЕ ВПРАВО
{
Serial.println("Подруливание вправо");
state = "Right_Correction";
//-------подруливание происходит торможением правого двигателя, а левый крутится как и должен -----------
// ledcWrite(pwmChannel, 0);
ledcWrite(pwmChannel2, 0);
// digitalWrite(motor1Pin1, LOW);
// digitalWrite(motor1Pin2, LOW);
digitalWrite(motor2Pin1, LOW);
digitalWrite(motor2Pin2, LOW);
}
}
else if ((s.indexOf("forward")>0)&&(temp=='1')) // ДВИЖЕНИЕ ВПЕРЕД
{
if (state.equals ("Stop")) //выполняем поворот, только если кнопка какого-либо движения отпущена. Это нужно для подруливания-иначе не получится
{
Serial.println("Forward"); // "Вперед"
state = "Forward";
ledcWrite(pwmChannel, 200); //0 - максимум оборотов
ledcWrite(pwmChannel2, 200); //0 - максимум оборотов
digitalWrite(motor1Pin1, LOW);
digitalWrite(motor1Pin2, HIGH);
digitalWrite(motor2Pin1, LOW);
digitalWrite(motor2Pin2, HIGH);
}
}
//
//// //подруливание осуществляется так: начинаем замедлять один из двигателей
////
//// while ( !(temp=='0') )
//// {
//// turning_time = millis (); // время, когда началось отклонение. Это нужно, для того, чтобы построить "дугу" поворота влево
////
//// while (millis() - turning_time<=50)
//// {
//// ledcWrite(pwmChannel, speed_temp_forward); //0 - максимум оборотов
//// }
////
//// speed_temp_forward = speed_temp_forward+5;
////
//// if (speed_temp_forward >=255) //не даем установить больше максимума- который поддерживает ШИМ. Принудительно ограничиваем
////
//// {
//// speed_temp_forward = 255;
//// }
//
//// }
//
// }
// else if ((s.length()==0)&&(state.equals("Left_Correction"))) // ПОДРУЛИВАНИЕ ВЛЕВО-ЕСЛИ ОНО ТОЛЬКО НАЧАЛОСЬ
// {
// Serial.println("Подруливание влево");
// state = "Left_Correction";
//// uint32_t CorrectionStartTime = millis(); // время, когда началось отклонение. Это нужно, для того, чтобы построить "дугу" поворота влево
//
// //пробуем медленный поворот влево
// ledcWrite(pwmChannel, 55);
// ledcWrite(pwmChannel2, 195);
// digitalWrite(motor1Pin1, HIGH);
// digitalWrite(motor1Pin2, LOW);
// digitalWrite(motor2Pin1, LOW);
// digitalWrite(motor2Pin2, HIGH);
//
// }
else if ((s.indexOf("reverse")>0)&&(temp=='1')) // ДВИЖЕНИЕ НАЗАД
{
if (state.equals ("Stop")) //выполняем поворот, только если кнопка какого-либо движения отпущена. Это нужно для подруливания-иначе не получится
{
Serial.println("Reverse"); // "Назад"
state = "Reverse";
ledcWrite(pwmChannel, 55); //255 - максимум оборотов (Sensor 2)
ledcWrite(pwmChannel2, 55); //255 - максимум оборотов (Sensor 1)
digitalWrite(motor1Pin1, HIGH);
digitalWrite(motor1Pin2, LOW);
digitalWrite(motor2Pin1, HIGH);
digitalWrite(motor2Pin2, LOW);
}
}
else if ( (state.equals ("Forward"))&&((s.indexOf("forward")>0)&&(temp=='0')) )// ОСТАНОВКА ПРИ ДВИЖЕНИИ ВПЕРЕД
{
Serial.println("Forward stop"); // "Стоп"
state = "Stop";
ledcWrite(pwmChannel, 0);
ledcWrite(pwmChannel2, 0);
digitalWrite(motor1Pin1, LOW);
digitalWrite(motor1Pin2, LOW);
digitalWrite(motor2Pin1, LOW);
digitalWrite(motor2Pin2, LOW);
}
else if ( (state.equals ("Reverse"))&&((s.indexOf("reverse")>0)&&(temp=='0')) )// ОСТАНОВКА ПРИ ДВИЖЕНИИ НАЗАД
{
Serial.println("Reverse Stop"); // "Стоп"
state = "Stop";
ledcWrite(pwmChannel, 0);
ledcWrite(pwmChannel2, 0);
digitalWrite(motor1Pin1, LOW);
digitalWrite(motor1Pin2, LOW);
digitalWrite(motor2Pin1, LOW);
digitalWrite(motor2Pin2, LOW);
}
else if ( ( (state.equals ("LeftSlow"))||(state.equals ("Left")) ) &&((s.indexOf("left")>0)&&(temp=='0')) )// ОСТАНОВКА ПРИ ПОВОРОТЕ ВЛЕВО
{
Serial.println("Left Stop"); // "Стоп"
state = "Stop";
ledcWrite(pwmChannel, 0);
ledcWrite(pwmChannel2, 0);
digitalWrite(motor1Pin1, LOW);
digitalWrite(motor1Pin2, LOW);
digitalWrite(motor2Pin1, LOW);
digitalWrite(motor2Pin2, LOW);
}
else if ( ( (state.equals ("RightSlow"))||(state.equals ("Right")) ) &&((s.indexOf("right")>0)&&(temp=='0')) )// ОСТАНОВКА ПРИ ПОВОРОТЕ ВПРАВО
{
Serial.println("Right Stop"); // "Стоп"
state = "Stop";
ledcWrite(pwmChannel, 0);
ledcWrite(pwmChannel2, 0);
digitalWrite(motor1Pin1, LOW);
digitalWrite(motor1Pin2, LOW);
digitalWrite(motor2Pin1, LOW);
digitalWrite(motor2Pin2, LOW);
}
else if (( state.equals ("Left_Correction")) &&((s.indexOf("left")>0)&&(temp=='0')) )// ОСТАНОВКА ПРИ ПОВОРОТЕ ВЛЕВО
{
Serial.println("Left Correction Stop"); // "Стоп"
state = "Forward";
ledcWrite(pwmChannel, 200); //0 - максимум оборотов
ledcWrite(pwmChannel2, 200); //0 - максимум оборотов
digitalWrite(motor1Pin1, LOW);
digitalWrite(motor1Pin2, HIGH);
digitalWrite(motor2Pin1, LOW);
digitalWrite(motor2Pin2, HIGH);
}
else if (( state.equals ("Right_Correction")) &&((s.indexOf("right")>0)&&(temp=='0')) )// ОСТАНОВКА ПРИ ПОВОРОТЕ ВЛЕВО
{
Serial.println("Right Correction Stop"); // "Стоп"
state = "Forward";
ledcWrite(pwmChannel, 200); //0 - максимум оборотов
ledcWrite(pwmChannel2, 200); //0 - максимум оборотов
digitalWrite(motor1Pin1, LOW);
digitalWrite(motor1Pin2, HIGH);
digitalWrite(motor2Pin1, LOW);
digitalWrite(motor2Pin2, HIGH);
}
// else if ( ((s.indexOf("right")>0)&&(temp=='0'))||((s.indexOf("left")>0)&&(temp=='0'))||((s.indexOf("forward")>0)&&(temp=='0'))||((s.indexOf("reverse")>0)&&(temp=='0')) )// ОСТАНОВКА
// {
// Serial.println("Stop"); // "Стоп"
// state = "Stop";
//
// ledcWrite(pwmChannel, 0);
// ledcWrite(pwmChannel2, 0);
// digitalWrite(motor1Pin1, LOW);
// digitalWrite(motor1Pin2, LOW);
// digitalWrite(motor2Pin1, LOW);
// digitalWrite(motor2Pin2, LOW);
// }
}
видимо надо вводить режим защиты от пропадания канала связи
Да, хорошее замечание. Эту сторону дела я как то упустил!
Пришла такая мысль:принудительно устанавливать соединие "снизу-вверх"- то есть от esp32-к роутеру. Каким образом: в коде выше -есть кусок, который постит некое сообщение -в некий топик. Сделать так: чтобы каждые 0,3 секунды или чаще- esp32 постила сообщение в топик. Если соединение отсутствует-устанавливала его принудительно.
Пришла такая мысль:принудительно устанавливать соединие "снизу-вверх"- то есть от esp32-к роутеру. Каким образом: в коде выше -есть кусок, который постит некое сообщение -в некий топик. Сделать так: чтобы каждые 0,3 секунды или чаще- esp32 постила сообщение в топик. Если соединение отсутствует-устанавливала его принудительно.
Стесняюсь спросить... А было как ?
А было как в коде выше ;-)
Установлено соединение с WIFI, MQTT-а дальше..дальше хрен бы с ними :-)))
Но, как показывает практика, видимо этого недостаточно. Соединение то ли переходит в режим пониженного энергопотребления или не знаю чего там. И свзяь становится какой то нестабильной. Хотя, может проблема и в питании системы(его недостатке).
Что конкретно вы имеете в виду говоря - "Если соединение отсутствует-устанавливала его принудительно.". А если сервер выключен, если оборван канал связи ? У вас всегда устанавливается связь снизу-вверх. Низ - клиент, верх - MQTT сервер. Причем сервер не может вам что то отправить. Вы можете только получить ответ сервера. Сервер - доска на которой можно опубликовать или прочитать. Именно поэтому эту технологию не используют для управления чем либо. Как правило ее используют там где быстрая реакция клиента не нужна - датчики, системы мониторинга и пр.
Именно поэтому эту технологию не используют для управления чем либо. Как правило ее используют там где быстрая реакция клиента не нужна - датчики, системы мониторинга и пр.
Ага, читал про это. Но в реальности, после множества тестов- могу сказать совершенно ответственно: скорость передачи данных при такой технологии -с лихвой перекрывает все потребности по управлению машинкой. В миллисекундах время прохождения данных не скажу, но субъективно, выглядит как "мгновенно".
Победить отвал сети(а возможно и просто подвисание системы из за питания от того же источника питания, что и двигатели) и всё - проблем нет.
Отвал сети у вас скорее всего из-за неправильно установленного периода жизни клиента, на самом клиенте или на сервере. Разбирайтесь.
пока временно удалось справиться так: при потере соединения с wifi: переподключиться. При потере соединения с mqtt- то же самое.
Правда это мертвому припарки пока что :-B
Ищу причину отвала. Пробовал гарантированно мощный источник питания-не в этом дело оказалось. Ищу пока что...
if (WiFi.status() != WL_CONNECTED) { Stop (); //при потере подключения-выключаем двигатели, чтобы машинка не уехала куда попало. setup_wifi(); //функция реконнекта ; } if (!client.connected()) { Stop (); //при потере подключения-выключаем двигатели, чтобы машинка не уехала куда попало. reconnect(); //функция реконнекта }Всё -проблема с отвалом сети решена! Проблема оказалась вовсе не там, где ожидалось. В чем оказалась проблема(если вкратце): приход большого количества сообщений по MQTT внутри каждого из 5 каналов - приводил к окончательному стопору всей системы и вылету.
Как это происходило: веб интерфейс на сайте был настроен так, что пока нажата какая либо из управляющих кнопок на клавиатуре:
↑↓←→ Ctrl - шлется непрерывным потоком сообщение в определенный топик MQTT. Несмотря на то, что сообщение короткое (0 или 1), - от прихода сотен сообщений по 5 каналам и их разбора - система просто умирала :-). Не исключаю, что я рукожоп и мой код кривой, а "риал труъ прогер" бы сделал как надо :-) Вот и нашел зато физические пределы esp32.
Когда переделали веб интерфейс управления на сайте, чтобы при нажатии на любую управляющую кнопку- слалось только 1 сообщение в соответствующий топик и при отжатии кнопки-тоже только 1 сообщение туда же, - система стала работать супер стабильно. Просто чётко как часы! :-)