MQTT Не отрабатывается подписка на один из топиков

zztop1967
Offline
Зарегистрирован: 02.01.2017

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

Строка 088, с подпиской на топик /Dimmer02/ работает(Строки 296-303). Данные прилетают и отрабатываются. Строка 089 с подпиской на топик /Dimmer05/ не работает(Строки 306-313). При этом, если в строке 088 подписаться на топик /Dimmer05/, то все прекрасно отрабатывается. Прошу прощения, за большущую портянку листинга, возможно где то есть некий не очевидный для меня косяк, влияющий на эту проблему. Что я делаю не так?

//  Контроллер для управления нагрузками в комнате и подвале
//  на основе контроллера mqtt_ethernet_Pervushino-03
/**************************************************** 
Управление
1. Вытяжка на форточке в комнате. ШИМ (Pin 3)
2. Люстра 1 в комнате. Вкл/Выкл (Включатель-кнопка без фиксации)(Pin 2)
3. Люстра 2 в комнате. Вкл/Выкл (Включатель-кнопка без фиксации)(Pin 6)
4. Вытяжка подвал ШИМ (Pin 5)
5. Форточка в комнате. Открыть/закрыть (Датчики Открыто и Закрыто) (Pin 7)
6. Штора на окне в комнате. Окткрыть/закрыть (Датчики Открыто и Закрыто) (Pin 8)

Датчики
1. Датчик движения в комнате (Pin A0)
2. Датчик концентрации Газ/Дым в комнате (Pin A1)
3. Включатель освещения в комнате (Pin A2)
   Первое нажатие - включение Люстры №1
   Второе нажатие - включение Люстры №2
   Третье нажатие - Выключение Люстры №1
   Четвртое нажатие - Выключение Люстры №2
4. Датчик двери на веранду (Геркон) (Pin A3)
5. Датчик концентрации Газ/Дым в подвале
6. Датчик положения форточки (Закрыто)
7. Датчик положения форточки (Окрыто)
8. Датчик положения шторы (Закрыто)
9. Датчик положения шторы (Открыто)

****************************************************/
//#include <SPI.h>
#include <Wire.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
#include <Ethernet.h>
#include <EthernetClient.h>
//#include <Dns.h>
#include <Dhcp.h>
/************************* Ethernet Client Setup *****************************/
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x1E};// 
//byte mac[] = {0x04, 0xA0, 0x98, 0x3C, 0x37, 0xFE}; 
//Uncomment the following, and set to a valid ip if you don't have dhcp available.
//IPAddress iotIP (192, 168, 0, 42);
//Uncomment the following, and set to your preference if you don't have automatic dns.
//IPAddress dnsIP (8, 8, 8, 8);
//If you uncommented either of the above lines, make sure to change "Ethernet.begin(mac)" to "Ethernet.begin(mac, iotIP)" or "Ethernet.begin(mac, iotIP, dnsIP)"
/************************* Описание параметров брокера *********************************/
#define AIO_SERVER      "192.168.1.1" //Адресс брокера
#define AIO_SERVERPORT  1883              //Порт брокера
#define AIO_USERNAME    "user"            //Пользователь брокера
#define AIO_KEY         "111"      //Пароль пользователя брокера
/************ Global State (you don't need to change this!) ******************/
//  Реле управления начало
#define RelayPin2 2    // пин реле управления Люстра 1 комната
#define RelayPin6 6    // пин реле управления Люстра 2 комната
#define RelayPin7 7    // пин реле управления Форточка комната
#define RelayPin8 8    // пин реле управления Штора комната
//  Реле управления конец
//  ШИМ управление начало
//#define PWM3 3         // Вытяжка на форточке в комнате. ШИМ (Pin 3)
//int PWM3 = 3;         // Вытяжка на форточке в комнате. ШИМ (Pin 3)
//int PWM5 = 5;         // Вытяжка подвал ШИМ (Pin 5)
#define PWM3 3         // Вытяжка на форточке в комнате. ШИМ (Pin 3)
#define PWM5 5         // Вытяжка подвал ШИМ (Pin 5)
//  ШИМ управление конец
//  Датчики и кнопки начало
int Move0    = A0;        // Датчик движения комната
int GazPin1  = A1;        // Датчик Газ/Дым(Концентрация) комната
int Buttons2 = A2;        // Кнопка управления люстрами комната
int Buttons3 = A3;        // Датчик открытия/закрытия двери комната (Геркон)
//  Датчики и кнопки конец
//Set up the ethernet client
EthernetClient client;
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY); //Соединение с брокером
// You don't need to change anything below this line!
#define halt(s) { Serial.println(F( s )); while(1);  }
/****************************** Feeds ***************************************/
//Создаем топики датчиков
Adafruit_MQTT_Subscribe relay02 = Adafruit_MQTT_Subscribe(&mqtt, "/Switch2/"); // Создаем подписку на топик Люстра 1 Комната
Adafruit_MQTT_Subscribe relay06 = Adafruit_MQTT_Subscribe(&mqtt, "/Switch3/"); // Создаем подписку на топик Люстра 2 Комната

Adafruit_MQTT_Subscribe relay07 = Adafruit_MQTT_Subscribe(&mqtt, "/Openable02/"); // Создаем подписку на топик Форточка комната
Adafruit_MQTT_Subscribe relay08 = Adafruit_MQTT_Subscribe(&mqtt, "/Openable07/"); // Создаем подписку на топик Штора комната

Adafruit_MQTT_Publish TopStatusRelay2 = Adafruit_MQTT_Publish(&mqtt,  "/SSwitch2/");     // Создаем топик статуса реле №2
Adafruit_MQTT_Publish TopStatusRelay6 = Adafruit_MQTT_Publish(&mqtt,  "/SSwitch3/");     // Создаем топик статуса реле №6

Adafruit_MQTT_Publish TopStatusRelay7 = Adafruit_MQTT_Publish(&mqtt,  "/SOpenable02/");     // Создаем топик статуса Форточка комната
Adafruit_MQTT_Publish TopStatusRelay8 = Adafruit_MQTT_Publish(&mqtt,  "/SOpenable07/");     // Создаем топик статуса Штора комната

Adafruit_MQTT_Subscribe Dimmer02 = Adafruit_MQTT_Subscribe(&mqtt, "/Dimmer02/"); // Создаем подписку на топик вытяжка комната
Adafruit_MQTT_Subscribe Dimmer05 = Adafruit_MQTT_Subscribe(&mqtt, "/Dimmer05/"); // Создаем подписку на топик свет комната

//Adafruit_MQTT_Publish TopStatusDimmer02 = Adafruit_MQTT_Publish(&mqtt,  "/SDimmer02/");     // Создаем топик статуса Вытяжка комната
//Adafruit_MQTT_Publish TopStatusDimmer05 = Adafruit_MQTT_Publish(&mqtt,  "/SDimmer05/");     // Создаем топик статуса реле №8

Adafruit_MQTT_Publish Topg1 = Adafruit_MQTT_Publish(&mqtt,  "/SS02/");     // Создаем топик датчика газа/дыма №2 комната
Adafruit_MQTT_Publish Topm0 = Adafruit_MQTT_Publish(&mqtt,  "/SM03/");     // Создаем топик датчика Движения  №3 комната 
Adafruit_MQTT_Publish TopSt3 = Adafruit_MQTT_Publish(&mqtt,  "/SSt03/");     // Создаем топик датчика Движения  №3 комната 
/*************************** Sketch Code ************************************/
// Объявляем глобальные переменные
float dhtx   ;        //Переменная почти для всех датчиков
char  temp[8];        //Переменная почти для всех датчиков
int   temp2  ;        //Переменная для управления ШИМ
void setup() {
  Serial.begin(115200);

//Определяем подключение по LAN Начало
  Serial.println(F("Ждем подключения по LAN"));
  // this check is only needed on the Leonardo:
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  // start the Ethernet connection:
  if (Ethernet.begin(mac) == 0) {
    Serial.println(F("Failed to configure Ethernet using DHCP"));
    // no point in carrying on, so do nothing forevermore:
    for (;;)
      ;
  }
  // print your local IP address:
  printIPAddress();
//Определяем подключение по LAN Конец  
  Serial.println(F("Adafruit MQTT demo"));

  // Initialise the Client
  Serial.print(F("\nInit the Client..."));
  Ethernet.begin(mac);
  delay(1000); //give the ethernet a second to initialize
//Проверяем подключение LAN Начакло
  switch (Ethernet.maintain())
  {
    case 1:
      //renewed fail
      Serial.println(F("Error: renewed fail"));
      break;

    case 2:
      //renewed success
      Serial.println(F("Renewed success"));

      //print your local IP address:
      printIPAddress();
      break;

    case 3:
      //rebind fail
      Serial.println(F("Error: rebind fail"));
      break;

    case 4:
      //rebind success
      Serial.println(F("Rebind success"));

      //print your local IP address:
      printIPAddress();
      break;

    default:
      //nothing happened
      break;
  }

//Проверяем подключение LAN Конец  

// Подготовка пинов управления реле начало
  pinMode(RelayPin2, OUTPUT); 
  pinMode(RelayPin6, OUTPUT); 
  pinMode(RelayPin7, OUTPUT);    
  pinMode(RelayPin8, OUTPUT);    

  digitalWrite(RelayPin2, LOW);
  digitalWrite(RelayPin6, LOW);
  digitalWrite(RelayPin7, LOW);
  digitalWrite(RelayPin8, LOW);

// Подготовка пинов управления реле конец
// Подготовка пинов управления PWM начало
  pinMode(PWM3,OUTPUT) ;         // Вытяжка на форточке в комнате. ШИМ (Pin 3)
  pinMode(PWM5,OUTPUT) ;         // Вытяжка подвал ШИМ (Pin 5)

  analogWrite(PWM3,0) ;
  analogWrite(PWM5,0) ;   
// Подготовка пинов управления PWM конец
  
  mqtt.subscribe(&relay02);
  mqtt.subscribe(&relay06);
  mqtt.subscribe(&relay07);
  mqtt.subscribe(&relay08);
  mqtt.subscribe(&Dimmer02);  
  mqtt.subscribe(&Dimmer05);

}
// Конец секции Setup
boolean k = false ;                // Переменная для первоночального состояния реле после старта контроллера
                                   // нужно для того, что бы в Majordomo правильно отражалось состояние реле
void(* resetFunc) (void) = 0;      //Объявляем функцию reset 
String StatusRelay02,StatusRelay06,StatusRelay07,StatusRelay08 = "Off" ;
void loop() {     // Начало главного цикла программы

  // Ensure the connection to the MQTT server is alive (this will make the first
  // connection and automatically reconnect when disconnected).  See the MQTT_connect
  // function definition further below.
//  delay(10000); // Пауза отправки данных в брокер 10 сек
  
  MQTT_connect();
  // ping the server to keep the mqtt connection alive
  if(! mqtt.ping()) {
    mqtt.disconnect();
  }
// Блок обработки реле и PWM начало
  Adafruit_MQTT_Subscribe *subscription;
  if (k == false){                                               // Проверяем первый ли это цикл программы
    k = true;                                                    // 
    dhtx = 0;
    TopStatusRelay2.publish(dhtx);                               // Отправляем в топик актуальное состояние реле №2 после старта контроллера
    TopStatusRelay6.publish(dhtx);                               // Отправляем в топик актуальное состояние реле №6 после старта контроллера
    TopStatusRelay7.publish(dhtx);                               // Отправляем в топик актуальное состояние реле №7 после старта контроллера
    TopStatusRelay8.publish(dhtx);                               // Отправляем в топик актуальное состояние реле №8 после старта контроллера

  }

  while ((subscription = mqtt.readSubscription(10000))) {         // Запускаем цикл ожидания прихода управляющих топиков. Он же пауза отправки данных с датчиков
    if (subscription == &relay02) {                               // Если пришёл топик на реле №2 то выполняем действие
      Serial.print(F("relay02: "));                               // Печатаем в порт заголовок для реле №2
      Serial.println((char *)relay02.lastread);                   // Печатаем в порт значение управляющего топика для реле №2
      StatusRelay02 = ((char *)relay02.lastread);                 // Присваиваем в переменную значение управляющего топика для реле №2
      if (StatusRelay02 == "Off") {                               // Если значение пришедшего топика равно Off
      digitalWrite(RelayPin2, LOW);                               // Выключаем реле №2
      Serial.println (F("Выключено"));                            // Печатаем в порт сообщение
      dhtx = 0;                                                   // 
      TopStatusRelay2.publish(dhtx);                              // Отправляем в брокер текущее состояние реле №2
      }
      if (StatusRelay02 == "On") {                                // Если значение пришедшего топика равно On
      digitalWrite(RelayPin2, HIGH);                              // Включаем реле №2
      Serial.println (F("Включено"));                             // Печатаем в порт сообщение
      dhtx = 1;                                                   // 
      TopStatusRelay2.publish(dhtx);                              // Отправляем в брокер текущее состояние реле №2
      }      
    }
    if (subscription == &relay06) {
      Serial.print(F("relay06: "));
      Serial.println((char *)relay06.lastread);
      StatusRelay06 = ((char *)relay06.lastread);
      if (StatusRelay06 == "Off") {
      digitalWrite(RelayPin6, LOW);
      Serial.println (F("Выключено"));
      dhtx = 0;
      TopStatusRelay6.publish(dhtx);
      }
      if (StatusRelay06 == "On") {
      digitalWrite(RelayPin6, HIGH);
      Serial.println (F("Включено"));
      dhtx = 1;
      TopStatusRelay6.publish(dhtx);      
      }      
    }
    if (subscription == &relay07) {
      Serial.print(F("relay07: "));
      Serial.println((char *)relay07.lastread);
      StatusRelay07 = ((char *)relay07.lastread);
      if (StatusRelay07 == "Off") {
      digitalWrite(RelayPin7, LOW);
      Serial.println (F("Выключено"));
      dhtx = 0;
      TopStatusRelay7.publish(dhtx);      
      }
      if (StatusRelay07 == "On") {
      digitalWrite(RelayPin7, HIGH);
      Serial.println (F("Включено"));
      dhtx = 1;
      TopStatusRelay7.publish(dhtx);
      
      }
            
    }
    if (subscription == &relay08) {
      Serial.print(F("relay08: "));
      Serial.println((char *)relay08.lastread);
      StatusRelay08 = ((char *)relay08.lastread);
      if (StatusRelay08 == "Off") {
      digitalWrite(RelayPin8, LOW);
      Serial.println (F("Выключено"));
      dhtx = 0;
      TopStatusRelay8.publish(dhtx);      
      }
      if (StatusRelay08 == "On") {
      digitalWrite(RelayPin8, HIGH);
      Serial.println (F("Включено"));
      dhtx = 1;
      TopStatusRelay7.publish(dhtx);
      
      }
            
    }
// Блок обработки PWM начало
// Блок PWM Dimmer02 начало
    if (subscription == &Dimmer02) {
      Serial.print(F("Dimmer02: "));
      Serial.println((char *)Dimmer02.lastread);
      temp2 = atoi((char *)Dimmer02.lastread); // преобразуем строковое значение топика в цифровое
      Serial.print(F("temp2: "));
      Serial.println(temp2*255/100);           // Преобразуем диапазон от 0 до 100 в от 0 до 255
      analogWrite(PWM3,temp2*255/100);         // Преобразуем диапазон от 0 до 100 в от 0 до 255 и отправляем в ШИМ
    }
// Блок PWM Dimmer02 конец
// Блок PWM Dimmer05 начало
    if (subscription == &Dimmer05) {
      Serial.print(F("Dimmer05: "));
      Serial.println((char *)Dimmer05.lastread);
      temp2 = atoi((char *)Dimmer05.lastread); // преобразуем строковое значение топика в цифровое
      Serial.print(F("temp2: "));
      Serial.println(temp2*255/100);           // Преобразуем диапазон от 0 до 100 в от 0 до 255
      analogWrite(PWM5,temp2*255/100);         // Преобразуем диапазон от 0 до 100 в от 0 до 255 и отправляем в ШИМ
    }
// Блок PWM Dimmer05 конец
// Блок обработки PWM конец  
  }
// Блок обработки реле и PWM конец
//------------- Датчик Газа и дыма комната начало процедуры

  int GazLevel = analogRead(GazPin1);
      itoa(GazLevel, temp, 10);
      Serial.print(F("  Газ/Дым Комната: "));
      Serial.println (temp);
      Topg1.publish(temp);                           // Отправляем значения с датчика комната  в брокер
  
//------------- Датчик Газа и дыма комната конец процедуры
//------------- Датчик движения комната начало процедуры

  int Move0Level = analogRead(Move0);
      itoa(Move0Level, temp, 10);
      Serial.print(F("  Движение комната: "));
      Serial.println (temp);
      Topm0.publish(temp);                           // Отправляем значения с датчика комната  в брокер
  
//------------- Датчик движения комната конец процедуры
//------------- Датчик статуса двери комната-веранда начало процедуры

  int Buttons3Status = analogRead(Buttons3);
      itoa(Buttons3Status, temp, 10);
      Serial.print(F("  Статус двери комната-веранда: "));
      Serial.println (temp);
      TopSt3.publish(temp);                           // Отправляем значения с датчика комната  в брокер
  
//------------- Датчик статуса двери комната-веранда конец процедуры




} 
// Конец главного цикла программы

// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
  int8_t ret;

  // Stop if already connected.
  if (mqtt.connected()) {
    return;
  }

  Serial.print(F("Connecting to MQTT... "));

  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
       Serial.println(mqtt.connectErrorString(ret));
       Serial.println(F("Retrying MQTT connection in 5 seconds... and restart"));
       mqtt.disconnect();
       delay(5000);  // wait 5 seconds
       resetFunc();  // reset system
  }
  Serial.println(F("MQTT Connected!"));
}

void printIPAddress()
{
  Serial.print(F("My IP address: "));
  for (byte thisByte = 0; thisByte < 4; thisByte++) {
    // print the value of each byte of the IP address:
    Serial.print(Ethernet.localIP()[thisByte], DEC);
    Serial.print(F("."));
  }

  Serial.println();
}

 

rkit
Offline
Зарегистрирован: 23.11.2016

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

zztop1967
Offline
Зарегистрирован: 02.01.2017

rkit пишет:

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

Скорее нет, чем да. Пробовал исключить подписку на /Dimmer02/, оставив только подписку /Dimmer05/. Все равно не работает. Так же пробовал менять очерёдность подписок в скетче. Безрезультатно. Если бы дело было в лимитах на подписки или нехватке памяти, то работа топиков поменялась местами.

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

UpDate: Всё таки дело в лимитах. Убрал все упоминания на подписку /Dimmer02/ и подписка /Dimmer05/ заработала. Буду выяснять что именно мешает. 

UpDate2: Пробовал залить скетч в Mega 2560. Тоже не работает. Похоже дело не в нехватке памяти.

Выяснилось, что таких конструкций может быть не более пяти. Шестая строчка всегда не работает.(Данные топика не прилетают в скетч): 

  mqtt.subscribe(&relay02);
  mqtt.subscribe(&relay06);
  mqtt.subscribe(&relay07);
  mqtt.subscribe(&relay08);
  mqtt.subscribe(&Dimmer02);  
  mqtt.subscribe(&Dimmer05);

Интересно, но нигде про это информацию не нашел.

Спасибо!

sadman41
Offline
Зарегистрирован: 19.10.2016

Информация находится в https://github.com/adafruit/Adafruit_MQTT_Library/blob/master/Adafruit_MQTT.h


// how much data we save in a subscription object
// and how many subscriptions we want to be able to track.
#if defined  (__AVR_ATmega32U4__) || defined(__AVR_ATmega328P__)
  #define MAXSUBSCRIPTIONS 5
  #define SUBSCRIPTIONDATALEN 20
#else
  #define MAXSUBSCRIPTIONS 15
  #define SUBSCRIPTIONDATALEN 100
#endif

 

zztop1967
Offline
Зарегистрирован: 02.01.2017

sadman41 пишет:

Информация находится в https://github.com/adafruit/Adafruit_MQTT_Library/blob/master/Adafruit_MQTT.h

// how much data we save in a subscription object
// and how many subscriptions we want to be able to track.
#if defined  (__AVR_ATmega32U4__) || defined(__AVR_ATmega328P__)
  #define MAXSUBSCRIPTIONS 5
  #define SUBSCRIPTIONDATALEN 20
#else
  #define MAXSUBSCRIPTIONS 15
  #define SUBSCRIPTIONDATALEN 100
#endif

Спасибо!

Простите великодушно за чайниковский вопрос. А это можно победить? Или это как то связано с аппаратными ограничениями? Ну ладно еще Arduino UNO. Там не так много портов и памяти. А вот Arduino Mega 2560, так там есть где развернуться! Ан нет, фигушки... Просветите плиз!  Попробую почитать доку, но я еще весьма слаб в своих познаниях ардуино и его программирования. 

sadman41
Offline
Зарегистрирован: 19.10.2016

Насчёт Меги не знаю - по логике вещей на ней должно быть доступно 15 топиков.

Связано с ограничениями по размеру RAM в МК. Если есть запас свободной - исправляйте дефайн. Можно там же с длиной топика поэкспериментировать.