Page 1 of 1

MQTT (JSON) Client - Arduino I/O to Domoticz MQTT gateway (Mosquitto)

Posted: Wednesday 02 March 2022 17:19
by mjchrist
In response to the Domoticz WIKI "MQTT - Arduino, Needed! Please Help." I'm using the sketch below.
It's a project that is still growing, but I hope it helps users along the way.

The advantage of the sketch below is that you only need Mosquitto as a broker and that NodeRED does not have to be installed.

The project uses the ArduinoJson library. On the ArduinoJson site you can find more information about the library and how to use it.

Code: Select all

/**
 * Arduino MEGA 2560 with Ethernet shield
 * MQTT MEGA I/O for Domoticz (JSON)
 * Created by M.J. Christ
 */

#include <SPI.h>
#include <Ethernet.h>
#include <ArduinoMqttClient.h>
#include <ArduinoJson.h>
#include <Bounce2.h>

#include "arduino_secrets.h" // Header file containing settings for sketch

// Network settings
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // This must be unique
EthernetClient ethernetClient;
MqttClient mqttClient(ethernetClient);

char broker_user[] = BROKER_USER;
char broker_pass[] = BROKER_PASS;

// MQTT settings
const char broker[]   = "192.168.10.30";
int port = 1883;
const char inTopic[]  = "domoticz/in";  // Default incoming topic in Domoticz is domoticz/in
const char outTopic[] = "domoticz/out"; // Default outgoing topic in Domoticz is domoticz/in
char myOutput[512];

// Interval settings
unsigned long previousMillis = 0;     // Store last time
const long interval          = 60000; // interval (milliseconds)

// Input settings
#define numberInputs 2
const int idx_inputArray[numberInputs] = {18, 19};
const int inputArray[8]                = {22, 23, 24, 25, 26, 27, 28, 29}; // GPIO input pins
const int invertArray[8]               = {0,  1,  0,  0,  0,  0,  0,  0 }; // Invert input pins

#define inputOn 0  // GPIO value input on
#define inputOff 1 // GPIO value input off

// Debouncer settings
Bounce debouncer[numberInputs];
bool lastCycle[numberInputs]; // = {false};
unsigned long debounceDelay = 50; // Debounce time (milliseconds)

// Output settings
#define numberOutputs 4
const char switchType[] = "On/Off";
const int idx_outputArray[numberOutputs] = {18, 19, 20, 21};
const int outputArray[8]                 = {30, 31, 32, 33, 34, 35, 36, 37}; // GPIO output pins
#define outputOn 0  // GPIO value output on
#define outputOff 1 // GPIO value output off


void setup() {
  // Initialize serial for debug
  Serial.begin(9600);
  
  // Set input pins
  for (int pin = 0; pin < numberInputs; pin++) {
    pinMode(inputArray[pin], INPUT_PULLUP);
    debouncer[pin] = Bounce();
    debouncer[pin].attach(inputArray[pin]);
    debouncer[pin].interval(debounceDelay);
    lastCycle[pin] = debouncer[pin].read() ? inputOn : inputOff; 
  }
    
  // Set output pins
  for (int pin = 0; pin < numberOutputs; pin++) {
    pinMode(outputArray[pin], OUTPUT);
    digitalWrite(outputArray[pin], outputOff);
  }
      
  // Set MQTT client
  mqttClient.setUsernamePassword(broker_user, broker_pass);
  mqttClient.onMessage(onMqttMessage);
  mqttClient.subscribe(outTopic); 
}


void loop(){
  // If not connected to the broker, try to connect
  while (!mqttClient.connected()) {
    startBroker();
  }

  // Interval request all output states
  unsigned long currentMillis = millis();
  bool requestStates = false;
  DynamicJsonDocument doc(256);
  if (currentMillis - previousMillis >= interval) {
    // Save the last time
    previousMillis = currentMillis;
    requestStates = true;
    Serial.println("Request all states");
      for (int pin = 0; pin < numberOutputs; pin++) {
      // Serialize the message
      doc.clear();
      doc["command"] = "getdeviceinfo";
      doc["idx"] = idx_outputArray[pin];
      // Send the message
      mqttClient.beginMessage(inTopic);
      serializeJson(doc, mqttClient);
      mqttClient.endMessage();
      // show serialization message
      serializeJsonPretty(doc, myOutput);
      Serial.println(myOutput);
    }
  }
    
  // (Interval) request all input states
  bool thisCycle[numberInputs];
  for (int pin = 0; pin < numberInputs; pin++) {
    debouncer[pin].update() ;
    thisCycle[pin] = debouncer[pin].read() ? inputOn : inputOff;
    if (((requestStates) && (thisCycle[pin] != lastCycle[pin]))|| (thisCycle[pin] != lastCycle[pin])) {
      lastCycle[pin] = thisCycle[pin];
      // Invert input
      if (invertArray[pin]) {
        thisCycle[pin] = !thisCycle[pin];
      }
      // Serialize the message (input state)
      doc.clear();
      doc["command"] = "switchlight";
      doc["idx"] = idx_inputArray[pin];
      if (thisCycle[pin]) {
        doc["switchcmd"] = "On";
      } else {
        doc["switchcmd"] = "Off";
      }
      // Send the message   
      mqttClient.beginMessage(inTopic);
      serializeJson(doc, mqttClient);
      mqttClient.endMessage();
      // show serialization message
      serializeJsonPretty(doc, myOutput);
      Serial.println(myOutput);
      // Serialize the message (output state) 
      doc.clear();
      doc["command"] = "getdeviceinfo";
      doc["idx"] = idx_inputArray[pin];
      // Send the message        
      mqttClient.beginMessage(inTopic);
      serializeJson(doc, mqttClient);
      mqttClient.endMessage();
      // show serialization message
      serializeJsonPretty(doc, myOutput);
      Serial.println(myOutput);
    }
  }
  mqttClient.poll();
}


void onMqttMessage(int messageSize) {
  // we received a message
  Serial.print("Received a message with topic: '");
  Serial.print(mqttClient.messageTopic());
  Serial.print("', length: ");
  Serial.print(messageSize);
  Serial.println(" bytes:");
  // deserialize the message
  DynamicJsonDocument doc(messageSize);
  DeserializationError err = deserializeJson(doc, mqttClient);
  if (err) {
    Serial.print("deserializeJson() failed with code: ");
    Serial.println(err.f_str());
  } else {
    // show deserialization message
    serializeJsonPretty(doc, myOutput);
    Serial.println(myOutput);
    // change output state
    for (int pin = 0; pin < numberOutputs; pin++) {
      if ((idx_outputArray[pin] == doc["idx"]) and (doc["switchType"] == switchType)) {
        Serial.print("Incoming change for: ");
        Serial.print((char*)doc["name"]);
        Serial.print(" (idx: ");
        Serial.print((int)doc["idx"]);
        Serial.print("), new status: ");
        Serial.println((int)doc["nvalue"]);
        digitalWrite(outputArray[pin], doc["nvalue"] ? outputOn : outputOff);
        break;
      }
    }
  }
}


void startEthernet() {
  // Initialize network
  ethernetClient.stop();
  Serial.println("Attempting to connect to network: ");
  while (Ethernet.begin(mac) == 0) {
    Serial.print(".");
    delay(5000); // Wait 5 seconds, and try again
  }
  Serial.println("Connected to network");
  Serial.print("IP address: ");
  Serial.println(Ethernet.localIP());
}

void startBroker () {
  // Initialize broker
  mqttClient.stop();
  Serial.println("Attempting to connect to broker");
  unsigned int mqttConnection = 0;
  while (!mqttClient.connect(broker, port)) {
    Serial.print(".");
    delay(5000); // Wait 5 seconds, and try again
    mqttConnection +=1;
    if (mqttConnection >= 5) {
      Serial.print("MQTT connection failed! Error code = ");
      Serial.println(mqttClient.connectError());
      startEthernet();
      return false;
    }    
  }
  Serial.println("Connected to broker");
  mqttClient.subscribe(outTopic);
  return true;
}

Re: MQTT (JSON) Client - Arduino I/O to Domoticz MQTT gateway (Mosquitto)

Posted: Tuesday 15 March 2022 13:45
by waltervl
Thank you. I have added this topic to the Wiki https://www.domoticz.com/wiki/MQTT#Arduino
Also added some additional examples.