MQTT медленно подписывается и устраняет проблемы

#arduino #mqtt #mosquitto

#arduino #mqtt #mosquitto

Вопрос:

Мне нужна отчаянно необходимая помощь, пожалуйста.

Моя установка состоит из двух arduino (EtherTens) и Raspberry Pi (работает с Mosquitto MQTT broker и Home Assistant). На одном Arduino у меня есть экран датчика безопасности с двумя подключенными PIR и состояниями публикации для брокера (это работает на 100% так, как должно).

На моем другом arduino у меня есть пара экранов драйвера relay 8 (они используют I2C), подключенных к двум 8-канальным релейным платам (всего подключено 16 реле).

У меня подключены тумблеры для включения нескольких источников света в доме для активации оборудования, плюс я настроил его на активацию через Home Assistant с разделами MQTT.

Проблемы, с которыми я столкнулся, — это случайная активация реле при включении других источников света в доме. Я думаю, что где-то есть какие-то помехи в линии, и я надеюсь, что устранение сбоя программного обеспечения может решить проблему, но я запутался в коде (у меня уже есть 10-тысячные выдвижные резисторы в моей настройке тумблера).). Другая проблема заключается в том, что Arduino, работающий с экранами реле, действительно (и я имею в виду действительно) медленно подписывается на брокера…Иногда я говорю где угодно до 10 секунд.

Кто-нибудь может предложить лучший способ исправить мой код, пожалуйста?

 // This subscribes to MQTT IOT get the PIR sensor states //

#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>
#include <Wire.h>

#define SHIELD_1_I2C_ADDRESS  0x20  // 0x20 is the address with all jumpers removed
#define SHIELD_2_I2C_ADDRESS  0x21  // 0x21 is the address with a jumper on position A0
#define MAC_I2C_ADDRESS       0x50  // Microchip 24AA125E48 I2C ROM address

// Update these with values suitable for your network.
byte mac[]    = {  0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
IPAddress ip(192, 168, 2, 110);
IPAddress server(192, 168, 2, 149);

byte shield1BankA = 0; // Current status of all outputs on first shield, one bit per output
byte shield2BankA = 0; // Current status of all outputs on second shield, one bit per output

const int buttonPin2 = 2;
const int buttonPin3 = 3;
const int buttonPin4 = 4;
const int buttonPin5 = 5;
const int buttonPin6 = 6;

int buttonState2;
int buttonState3;
int buttonState4;
int buttonState5;
int buttonState6;

int lastButtonState2 = LOW;
int lastButtonState3 = LOW;
int lastButtonState4 = LOW;
int lastButtonState5 = LOW;
int lastButtonState6 = LOW;

unsigned long lastDebounceTime2 = 0;
unsigned long lastDebounceTime3 = 0;
unsigned long lastDebounceTime4 = 0;
unsigned long lastDebounceTime5 = 0;
unsigned long lastDebounceTime6 = 0;

unsigned long debounceDelay2 = 50;
unsigned long debounceDelay3 = 50;
unsigned long debounceDelay4 = 50;
unsigned long debounceDelay5 = 50;
unsigned long debounceDelay6 = 50;

EthernetClient ethClient;
PubSubClient client(server, 1883, callback, ethClient);

void callback(char* topic, byte* payload, unsigned int length) {
  // handle message arrived
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i  ) {
    Serial.print((char)payload[i]);
  }
  Serial.println();

  // Switch on the Hallway Light
  if (strcmp(topic, "left/hallwaylight") == 0) {
    if ((char)payload[0] == '1')
  {
      setLatchChannelOn(1);
      client.publish("left/hallwaylight/state", "1");
      delay(500);
    }
     else if ((char)payload[0] == '0') //Controlled from Home Assistant
    {
      setLatchChannelOff(1);
      delay(500);
      client.publish("left/hallwaylight/state", "0");
    }
}

// Switch on the Workshop Light - Sensor 2
if (strcmp(topic, "left/workshoplight") == 0) {
  if ((char)payload[0] == '1')
  {
    setLatchChannelOn(2);
    client.publish("left/workshoplight/state", "1");
    delay(500);
  }
  else if ((char)payload[0] == '0') //Controlled from Home Assistant
  {
    setLatchChannelOff(2);
    delay(500);
    client.publish("left/workshoplight/state", "0");
  }
}
/*
// Switch on the #############
if (strcmp(topic, "left/sensor2") == 0) {
  if ((char)payload[0] == '1')
  {
    setLatchChannelOn(2);
    client.publish("left/workshoplight/state", "1");
    Serial.println("Workshop Light On");
    delay(500);
  }
  else if ((char)payload[0] == '0') //Controlled from Home Assistant
  {
    setLatchChannelOff(2);
    delay(500);
    client.publish("left/workshoplight/*amp;^*amp;^amp;*", "0");
  }
}

// Switch on the ####################
if (strcmp(topic, "left/sensor2") == 0) {
  if ((char)payload[0] == '1')
  {
    setLatchChannelOn(2);
    client.publish("left/workshoplight/state", "1");
    Serial.println("Workshop Light On");
    delay(500);
  }
  else if ((char)payload[0] == '0') //Controlled from Home Assistant
  {
    setLatchChannelOff(2);
    delay(500);
    client.publish("left/workshoplight/khebfjseghf", "0");
  }
} */
// Switch on the Fountain
if (strcmp(topic, "left/fountain") == 0) {
  if ((char)payload[0] == '1')
  {
    setLatchChannelOn(8);
    client.publish("left/fountain/state", "1");
    delay(500);
  }
  else if ((char)payload[0] == '0') //Controlled from Home Assistant
  {
    setLatchChannelOff(8);
    delay(500);
    client.publish("left/fountain/state", "0");
  }
}

// Switch on the Entrance Lights
if (strcmp(topic, "left/entrancelights") == 0) {
  if ((char)payload[0] == '1')
  {
    setLatchChannelOn(7);
    client.publish("left/entrancelights/state", "1");
    delay(500);
  }
  else if ((char)payload[0] == '0') //Controlled from Home Assistant
  {
    setLatchChannelOff(7);
    delay(500);
    client.publish("left/entrancelights/state", "0");
  }
}

// Switch on the Foyer Lights
if (strcmp(topic, "left/foyerlights") == 0) {
  if ((char)payload[0] == '1')
  {
    setLatchChannelOn(6);
    client.publish("left/foyerlights/state", "1");
    delay(500);
  }
  else if ((char)payload[0] == '0') //Controlled from Home Assistant
  {
    setLatchChannelOff(6);
    delay(500);
    client.publish("left/foyerlights/state", "0");
  }
}

// Switch on the Driveway Lights - Sensor 1
if (strcmp(topic, "left/drivewaylights") == 0) {
  if ((char)payload[0] == '1')
  {
    setLatchChannelOn(9);
    client.publish("left/drivewaylights/state", "1");
    delay(500);
  }
  else if ((char)payload[0] == '0') //Controlled from Home Assistant
  {
    setLatchChannelOff(9);
    delay(500);
    client.publish("left/drivewaylights/state", "0");
  }
}

// Switch on the Spa Light
if (strcmp(topic, "left/spalight") == 0) {
  if ((char)payload[0] == '1')
  {
    setLatchChannelOn(10);
    client.publish("left/spalight/state", "1");
    delay(500);
  }
  else if ((char)payload[0] == '0') //Controlled from Home Assistant
  {
    setLatchChannelOff(10);
    delay(500);
    client.publish("left/spalight/state", "0");
  }
}

// Switch on the Pool Light
if (strcmp(topic, "left/poollight") == 0) {
  if ((char)payload[0] == '1')
  {
    setLatchChannelOn(11);
    client.publish("left/poollight/state", "1");
    delay(500);
  }
  else if ((char)payload[0] == '0') //Controlled from Home Assistant
  {
    setLatchChannelOff(11);
    delay(500);
    client.publish("left/poollight/state", "0");
  }
}

// Switch on the Tree Light
if (strcmp(topic, "left/treelight") == 0) {
  if ((char)payload[0] == '1')
  {
    setLatchChannelOn(12);
    client.publish("left/treelight/state", "1");
    delay(500);
  }
  else if ((char)payload[0] == '0') //Controlled from Home Assistant
  {
    setLatchChannelOff(12);
    delay(500);
    client.publish("left/treelight/state", "0");
  }
}

// Switch on the Fountains
if (strcmp(topic, "left/fountains") == 0) {
  if ((char)payload[0] == '1')
  {
    setLatchChannelOn(13);
    client.publish("left/fountains/state", "1");
    delay(500);
  }
  else if ((char)payload[0] == '0') //Controlled from Home Assistant
  {
    setLatchChannelOff(13);
    delay(500);
    client.publish("left/fountains/state", "0");
  }
}

// Switch on the Spa
if (strcmp(topic, "left/spa") == 0) {
  if ((char)payload[0] == '1')
  {
    setLatchChannelOn(16);
    client.publish("left/spa/state", "1");
    delay(500);
  }
  else if ((char)payload[0] == '0') //Controlled from Home Assistant
  {
    setLatchChannelOff(16);
    delay(500);
    client.publish("left/spa/state", "0");
  }
 }
}

void setup()
{
  Wire.begin(); // Wake up I2C bus
  Serial.begin(38400);
  Ethernet.begin(mac, ip);
  // Note - the default maximum packet size is 128 bytes. If the
  // combined length of clientId, username and password exceed this,
  // you will need to increase the value of MQTT_MAX_PACKET_SIZE in
  // PubSubClient.h

  /* Set up the Relay8 shields */
  initialiseShield(SHIELD_1_I2C_ADDRESS);
  sendRawValueToLatch1(0);  // If we don't do this, channel 6 turns on! I don't know why

  initialiseShield(SHIELD_2_I2C_ADDRESS);
  sendRawValueToLatch2(0);  // If we don't do this, channel 6 turns on! I don't know why

pinMode(buttonPin2, INPUT);
pinMode(buttonPin3, INPUT);
pinMode(buttonPin4, INPUT);
pinMode(buttonPin5, INPUT);
pinMode(buttonPin6, INPUT);

}

void loop()
{
if (!client.connected()) {
  reconnect();
}

int reading2 = digitalRead(buttonPin2);
int reading3 = digitalRead(buttonPin3);
int reading4 = digitalRead(buttonPin4);
int reading5 = digitalRead(buttonPin5);
int reading6 = digitalRead(buttonPin6);

if (reading2 != lastButtonState2) {
  lastDebounceTime2 = millis();
  }
if ((millis() - lastDebounceTime2) > debounceDelay2) {
  if (reading2 != buttonState2) {
    buttonState2 = reading2;

  if ((buttonPin2 == HIGH) amp;amp; What to put here????) {
    toggleLatchChannel(1);
    client.publish("left/hallwaylight/state", "1");
  }
  /*if (digitalRead(2) == HIGH) //Digital pin for Hall Light
  {
    toggleLatchChannel(1);
    delay(1000);
  }*/
 }
}

lastButtonState2 = reading2;

if (digitalRead(3) == HIGH) //Digital pin for Fountain
{
  toggleLatchChannel(6);
  delay(1000);
}

if (digitalRead(4) == HIGH) //Digital pin for Entrance Lights
{
  toggleLatchChannel(7);
  delay(1000);
}

if (digitalRead(5) == HIGH) //Digital pin for Foyer Lights
{
  toggleLatchChannel(8);
  delay(1000);
}

if (digitalRead(6) == HIGH) //Digital pin for Driveway Lights - to be removed
{
  toggleLatchChannel(9);
  delay(1000);
}

client.loop();
}

void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
  Serial.print("Attempting MQTT connection...");
  // Attempt to connect
  if (client.connect("leftArduinoClient")) {
    Serial.println("connected");
    client.subscribe("left/#");
  } else {
    Serial.print("failed, rc=");
    Serial.print(client.state());
    Serial.println(" try again in 5 seconds");
    // Wait 5 seconds before retrying
    delay(5000);
   }
 }
}

void initialiseShield(int shieldAddress)
{
// Set addressing style
Wire.beginTransmission(shieldAddress);
Wire.write(0x12);
Wire.write(0x20); // use table 1.4 addressing
Wire.endTransmission();

// Set I/O bank A to outputs
Wire.beginTransmission(shieldAddress);
Wire.write(0x00); // IODIRA register
Wire.write(0x00); // Set all of bank A to outputs
Wire.endTransmission();
}

void toggleLatchChannel(byte channelId)
{
if ( channelId >= 1 amp;amp; channelId <= 8 )
{
  byte shieldOutput = channelId;
  byte channelMask = 1 << (shieldOutput - 1);
  shield1BankA = shield1BankA ^ channelMask;
  sendRawValueToLatch1(shield1BankA);
}
else if ( channelId >= 9 amp;amp; channelId <= 16 )
{
  byte shieldOutput = channelId - 8;
  byte channelMask = 1 << (shieldOutput - 1);
  shield2BankA = shield2BankA ^ channelMask;
  sendRawValueToLatch2(shield2BankA);
  }
}

void setLatchChannelOn (byte channelId)
{
if ( channelId >= 1 amp;amp; channelId <= 8 )
{
  byte shieldOutput = channelId;
  byte channelMask = 1 << (shieldOutput - 1);
  shield1BankA = shield1BankA | channelMask;
  sendRawValueToLatch1(shield1BankA);
}
else if ( channelId >= 9 amp;amp; channelId <= 16 )
{
  byte shieldOutput = channelId - 8;
  byte channelMask = 1 << (shieldOutput - 1);
  shield2BankA = shield2BankA | channelMask;
  sendRawValueToLatch2(shield2BankA);
  }
}

void setLatchChannelOff (byte channelId)
{
if ( channelId >= 1 amp;amp; channelId <= 8 )
{
  byte shieldOutput = channelId;
  byte channelMask = 255 - ( 1 << (shieldOutput - 1));
  shield1BankA = shield1BankA amp; channelMask;
  sendRawValueToLatch1(shield1BankA);
}
else if ( channelId >= 9 amp;amp; channelId <= 16 )
{
  byte shieldOutput = channelId - 8;
  byte channelMask = 255 - ( 1 << (shieldOutput - 1));
  shield2BankA = shield2BankA amp; channelMask;
  sendRawValueToLatch2(shield2BankA);
  }
}

void sendRawValueToLatch1(byte rawValue)
{
Wire.beginTransmission(SHIELD_1_I2C_ADDRESS);
Wire.write(0x12);        // Select GPIOA
Wire.write(rawValue);    // Send value to bank A
shield1BankA = rawValue;
Wire.endTransmission();
}

void sendRawValueToLatch2(byte rawValue)
{
Wire.beginTransmission(SHIELD_2_I2C_ADDRESS);
Wire.write(0x12);        // Select GPIOA
Wire.write(rawValue);    // Send value to bank A
shield2BankA = rawValue;
Wire.endTransmission();
}

/** Required to read the MAC address ROM */
byte readRegister(byte r)
{
unsigned char v;
Wire.beginTransmission(MAC_I2C_ADDRESS);
Wire.write(r);  // Register to read
Wire.endTransmission();

Wire.requestFrom(MAC_I2C_ADDRESS, 1); // Read a byte
while (!Wire.available())
{
  // Wait
}
v = Wire.read();
return v;
}
  

Ответ №1:

Подписка на брокера может занять много времени из-за задержки в этой функции:

 void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
  Serial.print("Attempting MQTT connection...");
  // Attempt to connect
  if (client.connect("leftArduinoClient")) {
    Serial.println("connected");
    client.subscribe("left/#");
  } else {
    Serial.print("failed, rc=");
    Serial.print(client.state());
    Serial.println(" try again in 5 seconds");
    // Wait 5 seconds before retrying
    delay(5000);
   }
 }
}
  

Если вам не удастся подключиться с первой попытки, программа будет ждать 5 секунд. Вы проверили с помощью serial monitor, так ли это? Вы должны увидеть два сообщения «сбой …».

Что касается реле: отключение выполняется только для аппаратных переключателей — при нажатии кнопки происходит короткий период, когда электрические контакты в нем отскакивают назад и вперед, соединяясь и разъединяясь несколько раз. Когда вы подключаете коммутатор к микроконтроллеру, он считывает несколько переходов цифрового вывода, подключенного к кнопке. Отключение — это способ игнорировать эти короткие импульсы.

При этом нет необходимости отключать сигнал, поступающий с другого микроконтроллера или ПК — нет механических частей, которые могут колебаться, генерируя «поддельные» импульсы. Попробуйте написать короткую тестовую программу, которая просто активирует все реле последовательно, по одному за раз, без считывания каких-либо внешних входных данных. Таким образом, вы будете знать, вызвана ли проблема ошибкой в вашем коде или это аппаратная проблема.

Комментарии:

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

2. Что касается отключения и т.д., То то же самое происходит, когда я использую эскиз, который просто переключается последовательно. Должно быть, какие-то помехи, исходящие от стандартных выключателей освещения, которые передаются на Arduino и случайным образом включают реле. Возможно, грязная земля.

3. @KeiranWyllie измеряется ли время в 10 секунд с момента «Попытки подключения MQTT …» или с момента запуска системы? Что касается реле, разместите ссылку на экран, который вы используете.

4. 10 секунд — это время от последнего процесса подписки до следующего, если это имеет смысл.

5. Это ссылка на EtherTen, а это экран реле — [ссылка] ( freetronics.com.au/collections/shields/products /… )