Ардуино нано, mqtt, много топиков

yden
Offline
Зарегистрирован: 30.01.2016

Добрый.

Подскажите плиз. Есть ардуино нано, w5100 - работает климат-контролем в доме. Ардуинка шлет контроллеру данные о климате по mqtt, получает от него команды.

Суть проблемы: если ардуинка подписана\публикует много топиков, то часты зависания mqtt. Ардуинка с одним топиком работает месяцами без сбоев. 

Может где я в коде напартачил.

Код:


#include <OneWire.h>
#include <DallasTemperature.h>
#include <DHT.h>
#include <Wire.h>
#include <SPI.h>
#include <PubSubClient.h>
#include <Ethernet2.h>
#include <Bounce.h>

// =======================================================================
// Конфигурация устройства MQTT:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xE8, 0xFE, 0xE7 };
IPAddress ip(192, 168, 1, 77);
IPAddress dnServer(192, 168, 1, 1);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);

const char *mqtt_server = "192.168.1.70"; // Имя сервера MQTT
const int mqtt_port = 1883; // Порт для подключения к серверу MQTT
const char *mqtt_user = "***"; // Логи от сервер
const char *mqtt_pass = "***"; // Пароль от сервера

EthernetClient ethClient;
PubSubClient client(ethClient);

// =======================================================================

//ds18b20
OneWire oneWire(5); // вход датчиков 18b20
DallasTemperature ds(&oneWire);
DeviceAddress ds_budka = {0x28, 0x53, 0xD9, 0x1E, 0x00, 0x00, 0x80, 0x65};
//2853D91E00008065

//подсчет времени
//опрос датчиков
long previousMillis_sensor = 0;  // храним время последнего опроса датчиков
long interval_sensor = 30000;     //интервал

//mqtt
long previousMillis_mqtt_send = 0;  // храним время последнего подключения
long interval_mqtt_send = 15000;     //интервал

//mqtt broker
long previousMillis_mqtt_connect = 0;  // храним время последнего подключения
long interval_mqtt_connect = 60000;     //интервал
unsigned long currentMillis_mqtt_connect = 0;


int t_kux;
int h_kux;
int t_kux_tmp = 0;
int h_kux_tmp = 0;

int t_san;
int h_san;
int t_san_tmp = 0;
int h_san_tmp = 0;

int t_budka;
int t_budka_tmp = 0;

byte i = 0;

//геркон крышки унитаза
const byte inGorshok = 2; //вход крышка унитаза
//#define outSirena A6 //выход сирена

//создаем объект класса Bounce. Указываем пин, к которому подключена кнопка, и время дребезга в мс.
Bounce bouncer1 = Bounce(inGorshok, 50);

boolean flag_karlson_san = false; //флаг карлсона санузел
boolean flag_karlson_kux = false; //флаг карлсона кухня
boolean flag_gorshok = false; //флаг сирены крышки горшка
boolean flag_budka = false; //флаг отопление будка
boolean flag_heating_kux = false; //флаг отопление кухня

//вход для dht
const byte DHTPIN2 = 4; // датчик санузел
const byte DHTPIN3 = 3; // датчик кухня

#define DHTTYPE_2301 DHT21   // DHT 21 (AM2301) кухня + улица
#define DHTTYPE_2302 DHT22   // DHT 22 (AM2302) санузел

//выходы вентиляторов
const byte outPin1 = 6; // кухня
const byte outPin2 = 7; // санузел
const byte outPin3 = 8; // будка
const byte outSirena = 9; // сирена

//выходы отопление кухня
const byte outPin4 = A4; // кухня

DHT dht2(DHTPIN2, DHTTYPE_2302);
DHT dht3(DHTPIN3, DHTTYPE_2301);

// =======================================================================
// Функция получения данных от сервера
void callback(char* topic, byte* payload, unsigned int length)
{
  //--------------------------------------------------------------------------------------
  if (String(topic) == "ihouse/climat/san/karlson")
  {
    if ((char)payload[0] == '0') flag_karlson_san = true;
    if ((char)payload[0] == '1') flag_karlson_san = false;

    digitalWrite(outPin2, flag_karlson_san);
  }

  if (String(topic) == "ihouse/climat/kux/karlson")
  {
    if ((char)payload[0] == '0') flag_karlson_kux = true;
    if ((char)payload[0] == '1') flag_karlson_kux = false;

    digitalWrite(outPin1, flag_karlson_kux);
  }

  if (String(topic) == "ihouse/climat/budka/heating")
  {
    if ((char)payload[0] == '0') flag_budka = true;
    if ((char)payload[0] == '1') flag_budka = false;

    digitalWrite(outPin3, flag_budka);
  }

  if (String(topic) == "ihouse/climat/kux/heating")
  {
    if ((char)payload[0] == '0') flag_heating_kux = true;
    if ((char)payload[0] == '1') flag_heating_kux = false;

    digitalWrite(outPin4, flag_heating_kux);
  }

  if (String(topic) == "ihouse/gadget/gorshok")
  {
    if ((char)payload[0] == '1') flag_gorshok = true;
    if ((char)payload[0] == '0') flag_gorshok = false;
  }
}

// =======================================================================
void mqtt_reconnect()
{
  unsigned long currentMillis_mqtt_connect = millis();

  // подключаемся к MQTT серверу
  if (currentMillis_mqtt_connect - previousMillis_mqtt_connect >= interval_mqtt_connect)
  {
    String clientId = "nanoclimat-";
    clientId += String(random(0xffff), HEX);
    if (client.connect(clientId.c_str(), mqtt_user, mqtt_pass))
    {
      client.publish("ihouse/climat/kux/karlson", String(flag_karlson_kux).c_str());
      client.publish("ihouse/climat/san/karlson", String(flag_karlson_san).c_str());
      client.publish("ihouse/climat/budka/heating", String(flag_budka).c_str());
      client.publish("ihouse/climat/kux/heating", String(flag_heating_kux).c_str());

      //подписываемся по топики
      client.subscribe("ihouse/climat/kux/karlson");
      client.loop();
      client.subscribe("ihouse/climat/san/karlson");
      client.loop();
      client.subscribe("ihouse/climat/budka/heating");
      client.loop();
      client.subscribe("ihouse/climat/kux/heating");
      client.loop();
      client.subscribe("ihouse/gadget/gorshok");
      client.loop();
    }
    previousMillis_mqtt_connect = currentMillis_mqtt_connect;
  }
}

// =======================================================================
void setup()
{
  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(callback);
  delay(10);

  Ethernet.begin(mac, ip, dnServer, gateway, subnet);
  delay(10);

  dht2.begin();
  dht3.begin();
  ds.begin();

  //для релюшек
  pinMode(outPin1, OUTPUT);
  digitalWrite(outPin1, HIGH);

  pinMode(outPin2, OUTPUT);
  digitalWrite(outPin2, HIGH);

  pinMode(outPin3, OUTPUT);
  digitalWrite(outPin3, HIGH);

  pinMode(outPin4, OUTPUT);
  digitalWrite(outPin4, HIGH);

  //геркон горшка
  pinMode(inGorshok, INPUT);

  //сирена горшка
  pinMode(outSirena, OUTPUT);
  digitalWrite(outSirena, LOW);

}

// =======================================================================
// Функция отправки в топик
void MQTT_Send()
{
  unsigned long currentMillis_mqtt_send = millis();

  if (currentMillis_mqtt_send - previousMillis_mqtt_send >= interval_mqtt_send)
  {
    previousMillis_mqtt_send = currentMillis_mqtt_send;
    client.publish("ihouse/work/climat", String(random(1000)).c_str());
  }
  delay(10);
}

// =======================================================================
void loop()
{
  unsigned long currentMillis_sensor = millis();

  //--------------------------------------------------------------------------------------
  if (!client.connected())
  {
    mqtt_reconnect();
  }

  client.loop();
  MQTT_Send();

  //--------------------------------------------------------------------------------------
  //проверка состояния геркона - санузел
  if ( bouncer1.update() )
  {
    //если считано значение 1
    if (bouncer1.read() == HIGH)
    {
      client.publish("ihouse/climat/san/gorshok", String(1).c_str());
    }
    else
    {
      client.publish("ihouse/climat/san/gorshok", String(0).c_str());
    }
  }

  //--------------------------------------------------------------------------------------
  //проверка статуса крышки горшка
  if (flag_gorshok == true)
  {
    digitalWrite(outSirena, HIGH);
  }
  else
  {
    digitalWrite(outSirena, LOW);
  }

  //--------------------------------------------------------------------------------------
  //влажность+температура+давление
  if (currentMillis_sensor - previousMillis_sensor > interval_sensor)
  {
    previousMillis_sensor = currentMillis_sensor;
    i++;
  }

  //санузел
  if (i == 1)
  {
    if (dht2.readHumidity() <= 100) h_san = dht2.readHumidity();
    if (dht2.readTemperature() <= 50) t_san = dht2.readTemperature();

    if (t_san != t_san_tmp)
    {
      client.publish("ihouse/climat/san/temp", String(t_san).c_str());
      t_san_tmp = t_san;
    }
    if (h_san != h_san_tmp)
    {
      client.publish("ihouse/climat/san/hum", String(h_san).c_str());
      h_san_tmp = h_san;
    }
  }

  //кухня
  if (i == 2)
  {
    if (dht3.readHumidity() <= 100) h_kux = dht3.readHumidity();
    if (dht3.readTemperature() <= 50) t_kux = dht3.readTemperature();

    if (t_kux != t_kux_tmp)
    {
      client.publish("ihouse/climat/kux/temp", String(t_kux).c_str());
      t_kux_tmp = t_kux;
    }
    if (h_kux != h_kux_tmp)
    {
      client.publish("ihouse/climat/kux/hum", String(h_kux).c_str());
      h_kux_tmp = h_kux;
    }
  }

  //dallas подготовка
  if (i == 3)
  {
    ds.requestTemperatures(); // считываем температуру с датчиков
  }

  //dallas считывание
  if (i == 4)
  {
    if (ds.getTempC(ds_budka) <= 50) t_budka = ds.getTempC(ds_budka);

    if (t_budka != t_budka_tmp)
    {
      client.publish("ihouse/climat/budka/temp", String(t_budka).c_str());
      t_budka_tmp = t_budka;
    }

    i = 0;
  }
}
yden
Offline
Зарегистрирован: 30.01.2016

благодарю

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

yden пишет:

Суть проблемы: если ардуинка подписана\публикует много топиков, то часты зависания mqtt. Ардуинка с одним топиком работает месяцами без сбоев. 

Может где я в коде напартачил.

Принято к сведению. Спасибо, будем знать теперь.

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

yden пишет:

то часты зависания mqtt. 

тема не раскрыта, что зависает? сам скетч или внешний сервер?

Update: Ставлю на недостаток памяти МК.

Избавляйтесь от лишних библиотек DallasTemperature.h Bounce.h - переписывайте самостоятельно работу с кнопками и DS18B20.

Уменьшите пути к топикам, вместо ihouse/climat/budka/temp например сокращайте на ih/cl/bd/tm

Если не поможет  - вместо PubSubClient.h переходите на самостоятельное общение с mqtt сервером

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

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

yden
Offline
Зарегистрирован: 30.01.2016

sadman41 пишет:

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

Скетч использует 20694 байт (67%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 1261 байт (61%) динамической памяти, оставляя 787 байт для локальных переменных. Максимум: 2048 байт.
 
yden
Offline
Зарегистрирован: 30.01.2016

andycat пишет:

yden пишет:

то часты зависания mqtt. 

тема не раскрыта, что зависает? сам скетч или внешний сервер?

Update: Ставлю на недостаток памяти МК.

Избавляйтесь от лишних библиотек DallasTemperature.h Bounce.h - переписывайте самостоятельно работу с кнопками и DS18B20.

Уменьшите пути к топикам, вместо ihouse/climat/budka/temp например сокращайте на ih/cl/bd/tm

Если не поможет  - вместо PubSubClient.h переходите на самостоятельное общение с mqtt сервером

скетч.

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

yden пишет:

sadman41 пишет:

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

Скетч использует 20694 байт (67%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 1261 байт (61%) динамической памяти, оставляя 787 байт для локальных переменных. Максимум: 2048 байт.
 

Натыкайте вызов в луп, выводите в Serial, смотрите - утекает ли память:
 

inline uint32_t getRamFree(void) {
  extern uint16_t __heap_start, *__brkval;
  uint16_t v;
  return (uint32_t) &v - (__brkval == 0 ? (uint32_t) &__heap_start : (uint32_t) __brkval);
}

 

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

yden пишет:

скетч.

?

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Вместо вот такого:

 if (String(topic) == "ihouse/climat/kux/heating")

откройте для себя strcmp_P, и все строки - в PROGMEM, макросом F().

yden
Offline
Зарегистрирован: 30.01.2016

DIYMan пишет:

Вместо вот такого:

 if (String(topic) == "ihouse/climat/kux/heating")

откройте для себя strcmp_P, и все строки - в PROGMEM, макросом F().

можно пример, где посмотреть реализацию можно?

nik182
Offline
Зарегистрирован: 04.05.2015