Arduino, MQTT, and openHAB Help

I am trying to setup a openHAB, MQTT mosquito and Arduino system. I have successfully connected both openHAB and the Arduino UNO to the MQTT Broker but I am having trouble getting the status of my subscribed to topic to use as a condition in a loop. I understand this has something to do with the callback function. Unfortunately, My Arduino client keeps closing it's connection after a couple seconds after connecting. I know this problem is because of my void loop() function but I do not know where to go from there. Any help would be greatly appreciated.

3lights.ino (1.73 KB)

The comment in your code that says

// Dont overload the server!
  delay(4000);

doesn't make sense as the Arduino is passively waiting for messages to arrive. That is how the subscription system works. It's like waiting for a letter to arrive in your mailbox.

To detect a newly arrived message the Arduino has to periodically check the ethernet adaptor. The 4 second delay stops it from doing that, so data piles up. The server doesn't understand why the ethernet adaptor and Arduino aren't responding with a "got that" message and assumes something is wrong.

See Arduino PubSubClient - MQTT Client Library Encyclopedia

Welcome to the forum

I also recommend you have a look at a couple of fundmental concepts to get a good working sketch. This is especially important for communication stacks.

The loop function got its name because it is supposed to loop continuously. Try to write your code in a way that loop() is run as often as possible.

Avoid the delay() function at all cost. It is the number one issue in beginner sketches in this forum. Have a look at the following example. It will teach you how to time individual tasks or functions without delay.

File -> Examples -> 02.Digital -> BlinkWithoutDelay

Avoid blocking code e.g. while loops. Make sure you write your code in a way that it is not stopping or waiting for anything. At the top of your code write functions that need to be called again and again to do something. They only do one little thing at a time and then move on. This is how you make things seem to work in parallel. e.g.

void loop()
{
  readButtons();
  computeData();
  wifiTask();
  displayTask();
}

Move most of your code from setup into these task functions and call them from loop. Right now your code has no way of recovering from failure e.g. when the network connection stops working. I like to use state machines based on switch statements to manage network connections. Your sketch should work when you remove the network cable and plug it back in.

Please try to post your code in code tags (see example above). Many users here do not like to download files and you will get less answers. Have a look at the How to get the best out of this forum

Thank you for your response and advice. Here is my code so you do not have to download it:

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

// Function prototypes
void callback(char* topic, byte* payload, unsigned int length);
 
// Set your MAC address and IP address here
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 2, 1);
 
// Make sure to leave out the http and slashes!
const char* server = "192.168.2.139";
 
// Ethernet and MQTT related objects
EthernetClient ethClient;
PubSubClient mqttClient(ethClient);

void setup() {
  // put your setup code here, to run once:
  
  // Useful for debugging purposes
  Serial.begin(9600);
  
  // Start the ethernet connection
  Ethernet.begin(mac, ip);              
  
  // Ethernet takes some time to boot!
  delay(3000);                          
 
  // Set the MQTT server to the server stated above ^
  mqttClient.setServer(server, 1883);   
 
  // Attempt to connect to the server with the ID "arduino-UNO"
  if (mqttClient.connect("arduino-UNO")) 
  {
    Serial.println("Connection Established To MQTT Broker!!!!!");
 
    // Establish the subscribe event
    mqttClient.setCallback(callback);

    // Ensure that we are subscribed to the topic "kitchen"
    mqttClient.subscribe("kitchen");
  } 
  else 
  {
    Serial.println("Connection Failed :(");
  }
}

void loop() {
  // put your main code here, to run repeatedly:

  // This is needed at the top of the loop!
  mqttClient.loop();
}

void callback(char* topic, byte* payload, unsigned int length)
{
  // Print the topic
  Serial.print("Topic: ");
  Serial.println(topic);
 
  // Print the message
  Serial.print("Message: ");
  for(int i = 0; i < length; i ++)
  {
    Serial.print((char)payload[i]);
  }
 
  // Print a newline
  Serial.println("");
}

I have removed the delay(4000) line as mikb55 had advised but the error persists. To give more detail here is the mqtt broker log of connection from the Arduino to disconnection found in the attachments as well as the serial monitor for the Arduino connecting to the broker and then changing the condition of the switch once

Screen Shot 2021-01-25 at 10.35.48 AM.png

Screen Shot 2021-01-25 at 10.35.48 AM.png

Your IP address is unusual. I generally expect the 1 suffix to be the router - are you sure that that address is available for the Arduino to use?

I was a little unsure about that part as I could not find good documentation. My understanding was that the IPAddress ip() was a fallback ip in case the ethernet did not pick up one automatically. My ethernet cable is plugged into my router so I found the ip address of my router and put it there. The server IP address is the IP address of my computer that my mqtt broker is running on!

mach6movement:
I was a little unsure about that part as I could not find good documentation. My understanding was that the IPAddress ip() was a fallback ip in case the ethernet did not pick up one automatically. My ethernet cable is plugged into my router so I found the ip address of my router and put it there. The server IP address is the IP address of my computer that my mqtt broker is running on!

That looks like your problem. Your MQTT log shows that it's getting traffic from the Arduino using the 1 address. Give it a different IP.

You are referring to this line of code correct?

IPAddress ip(192, 168, 2, 1);

Can it be any IP Address at all? I am not very familiar with how this works.

No. The first three need to be the same but you can change the last one. You would be best served looking at the configuration of your router to find out which IP addresses it gives out and therefore which are available for your use without a conflict.

Or you can guess. Try 98 for example. Ping it from your PC first to be sure it's not already in use.

Or drop the IP from that line altogether and rely on your router to give you one.

Thank you wildbill, using 98 instead of 1 did fix the problem that I was encountering. If you have a moment I was wondering if you could help me with my callback function, I need the message passed along to be a if statement condition, I have seen people do this but am unsure if it is correct? Right now the Serial Monitor is showing ON and OFF whenever the condition changes but that is being produced by the for loop in the callback!

void callback(char* topic, byte * payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  payload[length] = '\0';
  String message = String((char*)payload);

  Serial.println(message);

  if (String(topic) == inTopic) {
    if (message == "MESSAGE_TRIGGER") {
     
    }
  }
}

String objects are bad news. Keep the data in C strings and use strcmp to compare them.

Could you please provide an example of what you mean by your latest reply? Every code I view trying to accomplish this uses strings.

void callback(char* topic, byte * payload, unsigned int length) 
{
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  payload[length] = '\0';
  Serial.println((char*)payload);

  if (strcmp(topic,"kitchen")==0) 
    {
    if (strcmp(payload,"MESSAGE_TRIGGER")==0) 
      {
      // Do something
      }
  }
}

mach6movement:
Every code I view trying to accomplish this uses strings.

Just because every code does something one way, does not mean it is the best thing to do or that you must do the same. There is a lot of creativity in programming.

The strcmp example wildbill gave you is very generic and will work with all kinds of topic and payload formats.

The topic and payload have a lot of redundancy. If your code subscribes to only one topic you could do without testing the topic string. If you have multiple topics that are different in the first character you could just compare that. The same is true for the payload. This will make your code less generic and a bit harder to extend but might be an option when you need to optimize your code. e.g., save some processing cycles or reduce memory size.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.