#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 /… )