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

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

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

На моем другом arduino у меня есть пара щитков драйверов реле 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/*&^*&^&*", "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) && 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 && channelId <= 8 )
{
  byte shieldOutput = channelId;
  byte channelMask = 1 << (shieldOutput - 1);
  shield1BankA = shield1BankA ^ channelMask;
  sendRawValueToLatch1(shield1BankA);
}
else if ( channelId >= 9 && channelId <= 16 )
{
  byte shieldOutput = channelId - 8;
  byte channelMask = 1 << (shieldOutput - 1);
  shield2BankA = shield2BankA ^ channelMask;
  sendRawValueToLatch2(shield2BankA);
  }
}

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

void setLatchChannelOff (byte channelId)
{
if ( channelId >= 1 && channelId <= 8 )
{
  byte shieldOutput = channelId;
  byte channelMask = 255 - ( 1 << (shieldOutput - 1));
  shield1BankA = shield1BankA & channelMask;
  sendRawValueToLatch1(shield1BankA);
}
else if ( channelId >= 9 && channelId <= 16 )
{
  byte shieldOutput = channelId - 8;
  byte channelMask = 255 - ( 1 << (shieldOutput - 1));
  shield2BankA = shield2BankA & 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;
}

person Keiran Wyllie    schedule 24.10.2016    source источник


Ответы (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 секунд. Вы проверили с последовательным монитором, если это так? Вы должны увидеть два сообщения «failed...».

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

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

person mactro    schedule 24.10.2016
comment
Эй, спасибо за ответ. Я запускаю это через последовательный монитор каждый раз как часть моего собственного процесса отладки. Клиент никогда не перестает подключаться к брокеру (что хорошо), поэтому я не думаю, что задержка вообще сыграет роль. - person Keiran Wyllie; 24.10.2016
comment
Что касается устранения дребезга и т. д., то же самое происходит, когда я использую скетч, который просто переключается последовательно. Должны быть какие-то помехи, исходящие от стандартных выключателей света, которые передаются на Arduino и случайным образом включают реле. Может быть, грязная земля. - person Keiran Wyllie; 24.10.2016
comment
@KeiranWyllie измеряется в 10 секундах с момента попытки подключения MQTT ... или с момента запуска системы? Что касается реле, дайте ссылку на шилд, которым пользуетесь. - person mactro; 24.10.2016
comment
10-е — это переход от последней партии подписок к следующей, если это имеет смысл. - person Keiran Wyllie; 25.10.2016
comment
Это EtherTen — ссылка, а это экран реле — [ссылка] (freetronics.com.au/ коллекции/щиты/продукты/) - person Keiran Wyllie; 25.10.2016
comment
Я хочу проверить теорию и подключить тумблеры непосредственно к контактам Raspberry Pi GPIO, чтобы ими можно было управлять с помощью Home Assistant. - person Keiran Wyllie; 25.10.2016
comment
@KeiranWyllie Как вы питаете релейный щит? - person mactro; 25.10.2016
comment
Я удалил перемычку на контакте JD-VCC и подключил его к источнику питания 5 В с контактом GND, подключенным к тому же источнику. Контакт VCC рядом с контактами IN подключен к 5V на EtherTen. - person Keiran Wyllie; 25.10.2016
comment
Итак, у вас есть одинаковый источник питания 5 В для Arduino и релейной платы? А у тебя есть реле на 5В? - person mactro; 25.10.2016
comment
Нет, извините, я не ясно выразился. Они питания 5V из разных источников. Один от старого блока питания компьютера, а другой от коммутатора POE. - person Keiran Wyllie; 26.10.2016
comment
Хорошо, а ваши реле на 5 В постоянного тока? - person mactro; 26.10.2016
comment
Вход 5 В постоянного тока для переключения 240 В переменного тока - person Keiran Wyllie; 26.10.2016
comment
Похоже, я немного сузил проблему задержки. Кажется, что если я отключу EtherTen, на котором запущен экран датчика безопасности, то любое переключение, сделанное через Home Assistant (через брокера MQTT), произойдет сразу. Как только я снова подключаю EtherTen к своей сети, я снова получаю задержки. - person Keiran Wyllie; 29.10.2016