MQTT / Ethernet problem

I'm trying to get an arduino setup with MQTT and don't seem to be able to get it to connect. The mqtt server is running fine and is working between another pc on the network.

This is what I get from the serial monitor:
21:11:23.757 -> Connecting to network
21:11:30.195 -> Connected! IP address: 192.168.0.100
21:11:30.195 -> 1,1,1,1,
21:11:30.195 -> Attempting to connect to MQTT broker at 192.168.0.101
21:11:46.795 -> Connection to MQTT server failed, rc=19716 trying again in 5 seconds
21:11:51.802 -> Attempting to connect to MQTT broker at 192.168.0.101
21:12:16.928 -> Connection to MQTT server failed, rc=-2 trying again in 5 seconds
21:12:21.980 -> Attempting to connect to MQTT broker at 192.168.0.101
21:12:47.100 -> Connection to MQTT server failed, rc=-2 trying again in 5 seconds

I've tried with an uno and a nano and the result seems to be the same.

I've found comments that the rc=-2 could be some kind of network/router issue, but I can't find anything that could be causing a problem there. Plus, as far as I can see it does connect initially - the arduino comes up in the client list on the router settings. I can't find anything about what the rc=19716 might be though, and suspect whatever it is may be causing it to lose the connection straight away.

I've got the ENC28J60 ethernet modules, so I'm trying to use the UIPEthernet library with the PubSubClient library as described in this guide: https://www.instructables.com/id/A-Simple-MQTT-PubSub-Node-With-Arduino-UNO-and-ENC/ But with code I have got from another guide.

The ENC28J60 is connected as follows:
5v - 5v
gnd - gnd
rst - rst
cs - 10
st - 11
so - 12
sck - 13

And this is my code - ethernet/mqtt setup part:

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

const byte mac[] = {0xC0, 0xA8, 0x00, 0x01, 0x07, 0x7B};
const IPAddress deviceIP(192, 168, 0, 106);
const IPAddress mqttServerIP(192, 168, 0, 101);
const char* deviceID = "Switches";

EthernetClient ethernetClient;
PubSubClient MQTTclient(ethernetClient);
long lastMsgTime = 0;
char msg[64];
char topic[32];
int pulseCount = 0;

void mqttCallback(char* topic, byte* payload, unsigned int length) {
  memcpy(msg, payload, length);
  msg[length] = '\0';

  Serial.print("Message received in topic [");
  Serial.print(topic);
  Serial.print("] ");
  Serial.print(msg);

  if (strcmp(msg, "SOLVE") == 0) {
    onSolve();
  }
  else if (strcmp(msg, "RESET") == 0) {
    onReset();
  }
}

void ethernetSetup() {
  if (!Serial) {
    Serial.begin(9600);
  }
  Serial.println("Connecting to network");

  if (Ethernet.begin(mac) == 0) {
    Ethernet.begin(mac, deviceIP);
  }
  delay(2000);

  Serial.print("Connected! IP address: ");
  Serial.println(Ethernet.localIP());
}


void mqttSetup() {
  MQTTclient.setServer(mqttServerIP, 1883);
  MQTTclient.setCallback(mqttCallback);
}

void mqttLoop() {
  while (!MQTTclient.connected()) {

    Serial.print("Attempting to connect to MQTT broker at ");
    Serial.println(mqttServerIP);

    if (MQTTclient.connect(deviceID)) {
      Serial.println("Connected to MQTT broker");

      snprintf(topic, 32, "ToHost/%s", deviceID);
      snprintf(msg, 64, "CONNECT", deviceID);
      MQTTclient.publish(topic, msg);

      snprintf(topic, 32, "ToDevice/%s", deviceID);
      MQTTclient.subscribe(topic);
      MQTTclient.subscribe("ToDevice/All");
    }
    else {
      Serial.print("Connection to MQTT server failed, rc=");
      Serial.print(MQTTclient.state());
      Serial.println(" trying again in 5 seconds");

      delay(5000);
    }
  }
  MQTTclient.loop();
}

void publish(char* message) {
  snprintf(topic, 32, "ToHost/%s, deviceID");
  MQTTclient.publish(topic, message);
}

And the main "puzzle control" part, if it helps:

const byte numInputs = 4;
const byte inputPins[numInputs] = {2, 3, 4, 5};
const byte outputPin = 8;

bool lastInputState[numInputs];
bool solution[numInputs] = {true, false, false, true};
bool hasChanged = false;
char receivedChar;
bool newData = false;
char remoteSolve = 'S';
char remoteReset = 'R';

unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;


bool isPuzzleSolved() {
  bool solved = true;
  for (int i = 0; i < numInputs; i++) {
    if (lastInputState[i] != solution[i]) {
      solved = false;
    }
  }
  return solved;
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  for (int i = 0; i < numInputs; i++) {
    pinMode(inputPins[i], INPUT_PULLUP);
  }
  pinMode(outputPin, OUTPUT);
  digitalWrite(outputPin, LOW);

  ethernetSetup();
  mqttSetup();
}

void loop() {
  // put your main code here, to run repeatedly:
  if (Serial.available() > 0) {
    receivedChar = Serial.read();
    newData = true;
  }
  if (newData == true) {
    if (receivedChar == remoteSolve) {
      onSolve();
    }
    if (receivedChar == remoteReset) {
      onReset();
    }
    newData = false;
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    for (int i = 0; i < numInputs; i++) {
      int currentInputState = digitalRead(inputPins[i]);
      if (currentInputState != lastInputState[i]) {
        lastDebounceTime = millis();
        hasChanged = true;
        lastInputState[i] = (bool)currentInputState;
      }
      bool puzzleIsSolved = isPuzzleSolved();
      if (puzzleIsSolved) {
        onSolve();
      }
    }
    digitalWrite (outputPin, LOW);
    if (hasChanged) {
      sendState();
      hasChanged = false;
    }
  }
  mqttLoop();
}

void sendState() {
  for (int i = 0; i < numInputs; i++) {
    Serial.print(lastInputState[i]);
    if (i < numInputs) {
      Serial.print(",");
    }
  }
  Serial.println("");
}


void onReset () {
  asm volatile ("jmp 0");
  publish("Puzzle reset");
}


void onSolve() {
  digitalWrite (outputPin, HIGH);
  publish("Puzzle solved");
}

Here are a few things I noted while looking at your code.

  • The code seems incomplete. The issue is often in the part not shown.

  • You use delay() which is not a good idea. It just wastes processor cycles that could be used to do something useful. Especially with communication stacks, they tend to require regular attention.

  • Your functions do not do what they claim in the name. e.g.
    -- ethernetSetup first configures Serial
    -- mqttSetup() stops half way through the MQTT setup
    -- mqttLoop - What is that supposed to do? I know what your code does, but what does the name imply?

  • You publish a MQTT message before you subscribe. You should be able to listen before you "speak". Generally, a good advice. :slight_smile:

  • In your callback function you call even more functions. Because you do not know how often the callback is called, you should be swift. Store the data, set some flags and move on. Handle the data in the main part of your code.

Some tiny stuff:

  • topic and msg do not need to be global variables. Use local variables instead.

  • To make your code easier to read I would recommend using CAPITAL_WITH_UNDERSCORE for const values. e.g. MQTT_SERVER_IP, DEVICE_ID. Then everyone reading your codes knows the value is just some fixed value that does not get changed without looking through all of the code.

Question:

  • Is your code able to restart the Ethernet and/or MQTT connection if something goes wrong?
  • Can the rest of the application continue while your communication stack tries to reconnect?

I do not have a ENC28J60, but multiple boards running MQTT on WiFi. I found it useful to bring all the communication code into a function with a state machine. From the main loop I just call the task/function on a regular basis. The task only does a little bit at a time and then returns. This allows the main loop to be executed many thousands of times a second, while the connection is restarted automatically when necessary.

I found that in solving MQTT connection issues, to use Node-Red to workout connection issues.

Thank you for all the help so far. I should have also pointed out that the biggest problem with this code is the person trying to write it! :cold_sweat: I'm very new to all of this and have to admit, I don't really know what I'm doing!

I've manage to make sense of some of what you said, and have changed some stuff around. I now have the connection working, so have made some progress. :smiley: But, now it appears to be blocking the main code from running.

So in answer to some of your comments:

Klaus_K:

  • You use delay() which is not a good idea.
    I've got rid of those, although I have added another, just to keep an LED on for a second to help see if things are working.

-- ethernetSetup first configures Serial
I've fixed that.

-- mqttSetup() stops half way through the MQTT setup
Seem to have fixed that too. I had most of it in the mqtt loop for some reason.

-- mqttLoop - What is that supposed to do? I know what your code does, but what does the name imply?
Isn't this what is supposed to process messages being sent and received?

  • Is your code able to restart the Ethernet and/or MQTT connection if something goes wrong?
    No, I don't think so, but would be good if it could!

  • Can the rest of the application continue while your communication stack tries to reconnect?
    Apparently not, no

  • In your callback function you call even more functions. Because you do not know how often the callback is called, you should be swift. Store the data, set some flags and move on. Handle the data in the main part of your code.
    The callbacks should only be needed very rarely. The only time a message will be sent to the arduino is if we need to trigger it remotely or reset it after have been triggered.

This is my modified code. Main tab, I think is still the same:

const byte numInputs = 4;
const byte inputPins[numInputs] = {2, 3, 4, 5};
const byte outputPin = 8;

bool lastInputState[numInputs];
bool solution[numInputs] = {true, false, false, true};
bool hasChanged = false;
char receivedChar;
bool newData = false;
char remoteSolve = 'S';
char remoteReset = 'R';

unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;


bool isPuzzleSolved() {
  bool solved = true;
  for (int i = 0; i < numInputs; i++) {
    if (lastInputState[i] != solution[i]) {
      solved = false;
    }
  }
  return solved;
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  for (int i = 0; i < numInputs; i++) {
    pinMode(inputPins[i], INPUT_PULLUP);
  }
  pinMode(outputPin, OUTPUT);
  digitalWrite(outputPin, LOW);

  ethernetSetup();
  mqttSetup();
}

void loop() {
  // put your main code here, to run repeatedly:
  if (Serial.available() > 0) {
    receivedChar = Serial.read();
    newData = true;
  }
  if (newData == true) {
    if (receivedChar == remoteSolve) {
      onSolve();
    }
    if (receivedChar == remoteReset) {
      onReset();
    }
    newData = false;
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    for (int i = 0; i < numInputs; i++) {
      int currentInputState = digitalRead(inputPins[i]);
      if (currentInputState != lastInputState[i]) {
        lastDebounceTime = millis();
        hasChanged = true;
        lastInputState[i] = (bool)currentInputState;
      }
      bool puzzleIsSolved = isPuzzleSolved();
      if (puzzleIsSolved) {
        onSolve();
      }
    }
    digitalWrite (outputPin, LOW);
    if (hasChanged) {
      sendState();
      hasChanged = false;
    }
  }
  mqttLoop();
}

void sendState() {
  for (int i = 0; i < numInputs; i++) {
    Serial.print(lastInputState[i]);
    if (i < numInputs) {
      Serial.print(",");
    }
  }
  Serial.println("");

  for (int i = 0; i < numInputs; i++) {
    byte mqttmsg[] = {lastInputState[i]};
    publish(mqttmsg);
  }
}


void onReset () {
  asm volatile ("jmp 0");
  publish("reset");
}


void onSolve() {
  digitalWrite (outputPin, HIGH);
  publish("solved");
}

And the ethernet/mqtt tab:

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

const byte mac[] = {0xC0, 0xA8, 0x00, 0x01, 0x07, 0x7B};
const IPAddress deviceIP(192, 168, 0, 106);
const IPAddress mqttServerIP(192, 168, 0, 101);
const char* deviceID = "Switches";
const char* game = "FC";

EthernetClient ethernetClient;
PubSubClient MQTTclient(ethernetClient);
long lastMsgTime = 0;
char msg[64];
char topic[32];
int pulseCount = 0;


void mqttCallback(char* topic, byte* payload, unsigned int length) {
  memcpy(msg, payload, length);
  msg[length] = '\0';

  Serial.print("Message received in topic [");
  Serial.print(topic);
  Serial.print("] ");
  Serial.print(msg);

  if (strcmp(msg, "SOLVE") == 0) {
    onSolve();
    delay(1000);
  }
  else if (strcmp(msg, "RESET") == 0) {
    onReset();
  }
}

void ethernetSetup() {
  Serial.println("Connecting to network");

  if (Ethernet.begin(mac) == 0) {
    Ethernet.begin(mac, deviceIP);
  }

  Serial.print("Connected! IP address: ");
  Serial.println(Ethernet.localIP());
}


void mqttSetup() {
  MQTTclient.setServer(mqttServerIP, 1883);
  MQTTclient.setCallback(mqttCallback);

  if (MQTTclient.connect(deviceID)) {
    Serial.println("Connected to MQTT broker");
    boolean r = MQTTclient.subscribe("test");
    Serial.print("Subscribed: ");
    Serial.println(r);
  }
  else {
    Serial.print("Connection to MQTT server failed, rc=");
    Serial.println(MQTTclient.state());
  }
}

void mqttLoop() {

  //while (true) {

  //snprintf(topic, 32, "FC/%s", deviceID);
  MQTTclient.subscribe(game, deviceID);
  MQTTclient.subscribe(game, "/All");

  //snprintf(topic, 32, "FC/%s", deviceID);
  //snprintf(msg, 64, "CONNECT", deviceID);
  MQTTclient.publish(topic, msg);

  MQTTclient.loop();

}

void publish(char* message) {
  snprintf(topic, 32, "FC/%s, deviceID");
  MQTTclient.publish(topic, message);
}

If it helps, below is my original code for the base puzzle. This works just fine, and I can track inputs and over ride it remotely via serial. I'm just trying to replicate this via mqtt as I will end up connecting at least 6 different puzzles to the same network. So I guess I should also ask, do I need to bother doing all this? I figured ethernet would be a better way to connect everything, but could I just do it over usb connections? The puzzles will be spread out over 3 different rooms though, so cables will have to be quite long.

const byte numInputs = 4;
const byte inputPins[numInputs] = {2, 3, 4, 5};
const byte outputPin = 8;

bool lastInputState[numInputs];
bool solution[numInputs] = {true, false, false, true};
bool hasChanged = false;
char receivedChar;
bool newData = false;
char remoteSolve = 'S';
char remoteReset = 'R';

unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;


bool isPuzzleSolved() {
  bool solved = true;
  for (int i = 0; i < numInputs; i++) {
    if (lastInputState[i] != solution[i]) {
      solved = false;
    }
  }
  return solved;
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  for (int i = 0; i < numInputs; i++) {
    pinMode(inputPins[i], INPUT_PULLUP);
  }
  pinMode(outputPin, OUTPUT);
  digitalWrite(outputPin, LOW);
}

void loop() {
  // put your main code here, to run repeatedly:
  if (Serial.available() > 0) {
    receivedChar = Serial.read();
    newData = true;
  }
  if (newData == true) {
    if (receivedChar == remoteSolve) {
      onSolve();
      delay(1000);
    }
    if (receivedChar == remoteReset) {
      reset();
    }
    newData = false;
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    for (int i = 0; i < numInputs; i++) {
      int currentInputState = digitalRead(inputPins[i]);
      if (currentInputState != lastInputState[i]) {
        lastDebounceTime = millis();
        hasChanged = true;
        lastInputState[i] = (bool)currentInputState;
      }
      bool puzzleIsSolved = isPuzzleSolved();
      if (puzzleIsSolved) {
        onSolve();
      }
    }
    digitalWrite (outputPin, LOW);
    if (hasChanged) {
      sendState();
      hasChanged = false;
    }
  }
}

void sendState() {
  for (int i = 0; i < numInputs; i++) {
    Serial.print(lastInputState[i]);
    if (i < numInputs) {
      Serial.print(",");
    }
  }
  Serial.println("");
}


void reset () {
  asm volatile ("jmp 0");
}


void onSolve() {
  digitalWrite (outputPin, HIGH);
}

Idahowalker:
I found that in solving MQTT connection issues, to use Node-Red to workout connection issues.

Node-red is the end goal for this. I didn't really see the point until I'd at least got a connection working though. I'm just using a command prompt for now to see if anything is being sent.

Have a look at the example in reply #30 in the following post.

https://forum.arduino.cc/index.php?topic=664834.30

It uses a state machine inside a task function that handles all the communication. The example uses a different MQTT library but the overall functionality would be the same. I have used it with the PubSubClient as well.

You can move the MQTT subscription to the setup. It only needs to be done once.

If you do not want to use long cables. Did you consider WiFi?

Thanks Klaus. I've got a bit busy with other stuff at the moment, but I'll take a look at that when I get chance.

It's not that I don't want to use long cables, it's just that not only do I already have ethernet cables in place, I just wasn't sure what USB was like over that length and if I could just plug 4 or 5 arduinos into a usb hub.

I think I got the network connection part working anyway, so presumably I'd still have the current problem with the mqtt over wifi.

OK, I'm getting there! I've tried to simplify it a bit and put it all back onto one tab.

I now have the mqtt connecting fine and the arduino is sending messages. I just have two little problems now. One is that it doesn't seem to be receiving messages, and the other is just in formatting the message it sends.

For the message, this serial print code sent out "1,0,0,1,", which I then split in node red to show input states on a dashboard. How can I convert this into a message that I can send via mqtt?

for (int i = 0; i < numInputs; i++) {
    Serial.print(lastInputState[i]);
    if (i < numInputs) {
      Serial.print(",");
    }
  }
  Serial.println("");

Here is the full code. Is there something wrong here that's stopping it from receiving messages?

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

const byte mac[] = {0xC0, 0xA8, 0x00, 0x01, 0x07, 0x7B};
const IPAddress deviceIP(192, 168, 0, 106);
const IPAddress mqttServerIP(192, 168, 0, 101);

// Callback function header
void callback(char* topic, byte* payload, unsigned int length);

EthernetClient ethernetClient;
PubSubClient MQTTclient(ethernetClient);

const byte numInputs = 4;
const byte inputPins[numInputs] = {2, 3, 4, 5};
const byte outputPin = 8;

bool lastInputState[numInputs];
bool solution[numInputs] = {true, false, false, true};
bool hasChanged = false;
char receivedChar;
bool newData = false;
char remoteSolve = 'S';
char remoteReset = 'R';

unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;


bool isPuzzleSolved() {
  bool solved = true;
  for (int i = 0; i < numInputs; i++) {
    if (lastInputState[i] != solution[i]) {
      solved = false;
    }
  }
  return solved;
}


void reconnect() {
  // Loop until we're reconnected
  while (!MQTTclient.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (MQTTclient.connect("switches")) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      MQTTclient.publish("fc/switches", "connected");
      // ... and resubscribe
      MQTTclient.subscribe("fc/switches");
      MQTTclient.subscribe("fc/all");
    }
    else {
      Serial.print("failed, rc=");
      Serial.print(MQTTclient.state());
      Serial.println(" try again in 5 seconds");
      MQTTclient.publish("fc/switches", "connection failed");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}


void mqttCallback(char* topic, byte* payload, unsigned int length) {
  byte* p = (byte*)malloc(length);
  memcpy(p, payload, length);

  if (strcmp(p, "SOLVE") == 0) {
    onSolve();
  }
  else if (strcmp(p, "RESET") == 0) {
    onReset();
  }
  free(p);
}


void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  for (int i = 0; i < numInputs; i++) {
    pinMode(inputPins[i], INPUT_PULLUP);
  }
  pinMode(outputPin, OUTPUT);
  digitalWrite(outputPin, LOW);

  Serial.println("Connecting to network");
  if (Ethernet.begin(mac) == 0) {
    Ethernet.begin(mac, deviceIP);
  }
  Serial.print("Connected! IP address: ");
  Serial.println(Ethernet.localIP());

  MQTTclient.setServer(mqttServerIP, 1883);
  MQTTclient.setCallback(mqttCallback);
}


void loop() {
  // put your main code here, to run repeatedly:
  if (!MQTTclient.connected()) {
    reconnect();
  }
  MQTTclient.loop();

  if (Serial.available() > 0) {
    receivedChar = Serial.read();
    newData = true;
  }
  if (newData == true) {
    if (receivedChar == remoteSolve) {
      onSolve();
    }
    if (receivedChar == remoteReset) {
      onReset();
    }
    newData = false;
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    for (int i = 0; i < numInputs; i++) {
      int currentInputState = digitalRead(inputPins[i]);
      if (currentInputState != lastInputState[i]) {
        lastDebounceTime = millis();
        hasChanged = true;
        lastInputState[i] = (bool)currentInputState;
      }
      bool puzzleIsSolved = isPuzzleSolved();
      if (puzzleIsSolved) {
        MQTTclient.publish("fc/switches", "solved");
        Serial.println("solved");
        onSolve();
      }
    }
    digitalWrite (outputPin, LOW);
    if (hasChanged) {
      sendState();
      hasChanged = false;
    }
  }
}

void sendState() {
  for (int i = 0; i < numInputs; i++) {
    Serial.print(lastInputState[i]);
    if (i < numInputs) {
      Serial.print(",");
    }
  }
  Serial.println("");

  for (int i = 0; i < numInputs; i++) {
    char(mqttmsg) = lastInputState[i];
    MQTTclient.publish("fc/switches", mqttmsg);
  }
}


void onReset() {
  asm volatile ("jmp 0");
  MQTTclient.publish("fc/switches", "reset");
}


void onSolve() {
  bool puzzleIsSolved = isPuzzleSolved();
  while (puzzleIsSolved) {
    digitalWrite (outputPin, HIGH);
    //delay(1000);

    if (Serial.available() > 0) {
      receivedChar = Serial.read();
      newData = true;
    }
    if (newData == true) {
      if (receivedChar == remoteReset) {
        onReset();
      }
      newData = false;
    }
  }
}

Which topic have you coded to subscribe?

What do you think is going to happen here?

void onReset() {
  asm volatile ("jmp 0");
  MQTTclient.publish("fc/switches", "reset");
}

How about this one?

  for (int i = 0; i < numInputs; i++) {
    char(mqttmsg) = lastInputState[i];
    MQTTclient.publish("fc/switches", mqttmsg);
  }

Idahowalker:
Which topic have you coded to subscribe?

The arduino subscribes to topics: "fc/switches" and "fc/all"
I have node red subscribed to the same which receives the messages fine but they don't seem to be working the other way.

Oi! I must have missed the line, in the posted code, with the MQTTclient.subscribe (topic, [qos]) method.

pubsubclient API Arduino Client for MQTT

Klaus_K:
What do you think is going to happen here?

void onReset() {

asm volatile ("jmp 0");
 MQTTclient.publish("fc/switches", "reset");
}

Ok, yes, I guess there's no point trying to send a message after it resets! lol!

Klaus_K:
How about this one?

  for (int i = 0; i < numInputs; i++) {

char(mqttmsg) = lastInputState[i];
   MQTTclient.publish("fc/switches", mqttmsg);
 }

Yeah, this is the part I can't figure out. As far as I can make out, the mqtt publish can only send a string, and I can't work out how to convert the sequence of ints to a string.

Neither of these have anything to do with it receiving the messages though right?
If I send the message "SOLVE" from node red, nothing seems to happen.

What did you find when you did an internet search using the words "arduino int to string"?

To be honest, I haven't tried google! ::slight_smile: I was trying to search on here, but without much luck so far. Will try the rest of the web...

Nope, I give up!

I've tried various things with String(), and char, and byte. Whatever I do either comes up with errors, or just sends a blank message.

Did not look at the API to see how to correctly publish?

boolean publish (topic, payload, [length], [retained])
Publishes a message to the specified topic.

Parameters
topic const char[] - the topic to publish to
payload const char[], byte[] - the message to publish
length unsigned int (optional) - the length of the payload. Required if payload is a byte[]
retained boolean (optional) - whether the message should be retained
false - not retained
true - retained
Returns
false - publish failed, either connection lost or message too large
true - publish succeeded

void onReset() {
  asm volatile ("jmp 0");
  MQTTclient.publish("fc/switches", "reset");
}

Perhaps this will be the clue.

const char* topicInsideTemp = "Home/iTemperature";
 MQTTclient.publish( topicInsideTemp, String(ePtr[0]).c_str(), true );

Next time be a bit more forthcoming with the errors and this could have been solved a long time ago.

The API, read it.

I found another issue.

void onSolve() {
  bool puzzleIsSolved = isPuzzleSolved();
  while (puzzleIsSolved) {
    digitalWrite (outputPin, HIGH);
    //delay(1000);

    if (Serial.available() > 0) {
      receivedChar = Serial.read();
      newData = true;
    }
    if (newData == true) {
      if (receivedChar == remoteReset) {
        onReset();
      }
      newData = false;
    }
  }
}

You create a local variable and then use that for a while loop without updating the variable anywhere.

Additionally that code is in two locations (loop and onSolve). Especially with code that accesses peripherals that is usually calling for trouble.

It looks like you are trying to solve and learn a couple of things in your sketch all at the same time. I would recommend you create a few small sketches that you can use to try one thing at a time.

Hi, I'm back again!

Thanks for putting up with me! I am trying to learn as I go, and I have been trying to read all the appropriate documents and examples.

I now have everything working as far as the network and sending and receiving messages, and acting upon the messages received, so thank you!

I do still have one problem though with formatting a message.

When I set this up over serial originally, I used this little bit of code to send the input reads back to node-red:

  for (int i = 0; i < numInputs; i++) {
    Serial.print(lastInputState[i]);
    if (i < numInputs) {
      Serial.print(",");
    }
  }
  Serial.println("");

This sent "1,1,1,1," to node-red that I then split to show a dashboard readout.

I can't seem to replicate this in an mqtt message.

If I use this:

for (int i = 0; i < numInputs; i++) {
    MQTTclient.publish("toHost/switches", String(lastInputState[i]).c_str());
  }

It sends the correct values, but each one as a separate message. I've tried to form them into strings with the following couple of examples (along with many others, which I'm sure were even worse!), but these, although only sending the one message, it's just a blank message. Comes into node-red as "".

String message = String();
  for (int i = 0; i < numInputs; i++) {
    message = message + String(lastInputState[i])+ ",";
  }
    MQTTclient.publish("toHost/switches", message.c_str());
  String message;
  for (int i = 0; i < numInputs; i++) {
    String(lastInputState[i]);
  }
  MQTTclient.publish("toHost/switches", message.c_str());

If someone can help with this last little thing, I can finally get this up and running and leave you all in peace!

Do these switches belong together from an application point of view and could never be useful when you only had one of them?

Only then it would be useful to send them together. Otherwise create a message with a separate topic for each one of them. This will allow you to extend the application with other nodes (with more or less switches or receiver/sinks) without changing the message format. It could also make your node-RED logic simpler.