Arduino Button to MQTT Publish

Yesterday I built a circuit and started on the programming for a project which effectively sends an MQTT topic on report of a button press.

I Built it on an ESP8266 with a view to moving it over to an Arduino as in deployment I will have several buttons (more than the ESP can handle).

I have run into trouble though and I cant seem to work my way out of it as I dont really understand the error.

I hope its a simple fix and I can be on my way again soon if someone would be kind enough to shove me the right way please.

#include <PubSubClient.h>

#include <Wire.h>

#include <Dhcp.h>
#include <Dns.h>
#include <EthernetUdp.h>
#include <EthernetServer.h>
#include <EthernetClient.h>
#include <Ethernet.h>

// Connect to the MQTT Broker
const char* mqtt_server = "myIPaddress";
const char* mqtt_username = "myusername";
const char* mqtt_password = "mypassword";
const char* clientID = "Alarm";


// this constant won't change:
const int buttonPin = 2;    // the pin that the pushbutton is attached to
const int chartsPin = 4;       // the pin that the LED is attached to

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    char receivedChar = (char)payload[i];
    Serial.print(receivedChar);
    if (receivedChar == '0')
      digitalWrite(chartsPin, HIGH);
    if (receivedChar == '1')
      digitalWrite(chartsPin, LOW);
  }
  Serial.println();
}


void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.println("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(clientID, mqtt_username, mqtt_password)) {
      Serial.println("Connected");
      // ... and subscribe to topic
      client.subscribe("ALARM/ZONE/#");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup()
{
  {

    client.setServer(mqtt_server, 1883);
    client.setCallback(callback);

    // initialize the button pin as a input:
    pinMode(buttonPin, INPUT);
    // initialize the LED as an output:
    pinMode(chartsPin, OUTPUT);
    // initialize serial communication:
    Serial.begin(9600);
  }

void loop() {

  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == HIGH) {
      client.publish("ALARM/ZONE/testzone", "OFF"); //
      // if the current state is HIGH then the button
      // went from off to on:
    } else {
      // if the current state is LOW then the button
      // went from on to off:
      client.publish("ALARM/ZONE/testzone", "ON"); //
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state,
  //for next time through the loop
  lastButtonState = buttonState;

  {
    if (!client.connected()) {
      reconnect();
    }
    client.loop();
  }
}]

and here is the error I get

/home/loki/Arduino/arduino_button_mqtt/arduino_button_mqtt.ino: In function 'void reconnect()':
arduino_button_mqtt:46: error: 'client' was not declared in this scope
   while (!client.connected()) {
           ^
/home/loki/Arduino/arduino_button_mqtt/arduino_button_mqtt.ino: In function 'void setup()':
arduino_button_mqtt:67: error: 'client' was not declared in this scope
     client.setServer(mqtt_server, 1883);
     ^
arduino_button_mqtt:78: error: a function-definition is not allowed here before '{' token
 void loop() {
             ^
arduino_button_mqtt:108: error: expected '}' at end of input
 }
 ^
exit status 1
'client' was not declared in this scope

I Built it on an ESP8266 with a view to moving it over to an Arduino as in deployment I will have several buttons (more than the ESP can handle).

Arduino has no WiFi, so you would need both esp and arduino. That makes things much more complex and difficult. Different voltages, two sketches, the list goes on. Esp can handle as many buttons as you need. For example you could make your buttons into a matrix, or add extra chips like pfc8574.

I have run into trouble though and I cant seem to work my way out of it as I dont really understand the error.

You seem to have forgotten to explain what this trouble is, and you have not shown us the error. Is it a compiler error or something that happens at runtime? Please try to put yourself in our shoes, think what we need to know to help you. EDIT: I see you have now edited your post to include the error message.

Anyway, thanks for using code tags.

Ok, I can see that you have copied this code from somewhere without understanding it. It is not suitable for esp, it is not an esp sketch. My guess is that is was originally for an Arduino with an Ethernet shield? The sketch uses all the wrong #include libraries for esp. It contains no code to connect to WiFi, for example.

I suggest you go find example code written for esp that uses mqtt and use that as your starting point.

It was an ESP code and it worked perfectly on the ESP.

I want it moved over to Arduino UNO for more pins and intend to use the Ethernet Shield not Wi-Fi

Ah, sorry, I did not get that. Maybe you should edit your original post and clarify that point, so others are not confused also.

I still think your plan to move to Uno is a backwards step. More expensive and less capable hardware. Are there other reasons for wanting to do that (other than more pins, which I have already pointed out can easily be overcome)?

Anyway, perhaps you should look for an example sketch written for an Ethernet shield using mqtt. Your attempt to translate the esp sketch seems to have been a little too much for your C coding skills at your current level of experience. The stray { and } in odd places indicates you have removed parts that may have been important, including the declaration of 'client'.

Can you post the working esp sketch for comparison?

PaulRB: Ah, sorry, I did not get that. Maybe you should edit your original post and clarify that point, so others are not confused also.

From OP ; "I Built it on an ESP8266 with a view to moving it over to an Arduino as in deployment I will have several buttons (more than the ESP can handle)."

There is no real reason not to deploy it on an ESP to be honest - although I would prefer ethernet over wifi its not critical - I thought that I was limited by the pins as I want to manage about 20 inputs but that gas also been explained that it can be overcome.

The only part I removed from the original sketch was the wifi loop but if it appears to the more seasoned eye that the sketch is now too fragmented to be viable then I will look to moving back to the ESP and taking a look at expanding the GPIO pins as suggested.

enforcer: From OP ; "I Built it on an ESP8266 with a view to moving it over to an Arduino as in deployment I will have several buttons (more than the ESP can handle)."

Yes, that's the ambiguous part. You do not say that you had a sketch working on esp. the phrase "with a view to" can be read as "something I intend to do sometime in the future" rather than something you are currently working on and having problems with. That's how I read it and how I came to the incorrect interpretation of your situation.

20 inputs, 20 buttons? If so, these could be read with 5 pins. That would be using an interesting technique called "Charlieplexing" and would require a small diode in series with each button.

Charlieplexing. Thats something I didnt expect to hear.

As with many things, it appears that there are more than one way to skin a cat (so a friend told me).

I had a look at the extension boards and it goes into hex coding and binary coding which although I get the principle behind it - I am not at that level.

As requested, here is the functional code from the ESP and the only thing missing is the #include wifi bit

#include <EEPROM.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Ethernet.h>


// Connect to the WiFi
const char* ssid = "SSID";
const char* password = "PASSWORD";
// Connect to the MQTT Broker
const char* mqtt_server = "IPADDRESS";
const char* mqtt_username = "USERNAME";
const char* mqtt_password = "PASSWORD";
const char* clientID = "Alarm";


// this constant won't change:
const int buttonPin = 2;    // the pin that the pushbutton is attached to
const int chartsPin = 4;       // the pin that the LED is attached to

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button


PubSubClient client(espClient);



void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    char receivedChar = (char)payload[i];
    Serial.print(receivedChar);
    if (receivedChar == '0')
      digitalWrite(chartsPin, HIGH);
    if (receivedChar == '1')
      digitalWrite(chartsPin, LOW);
  }
  Serial.println();
}


void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.println("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(clientID, mqtt_username, mqtt_password)) {
      Serial.println("Connected");
      // ... and subscribe to topic
      client.subscribe("ALARM/ZONE/#");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}



void setup()
{
  {

    client.setServer(mqtt_server, 1883);
    client.setCallback(callback);

    // initialize the button pin as a input:
    pinMode(buttonPin, INPUT);
    // initialize the LED as an output:
    pinMode(chartsPin, OUTPUT);
    // initialize serial communication:
    Serial.begin(9600);
  }

void loop() {

  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == HIGH) {
      client.publish("ALARM/ZONE/testzone", "OFF"); //
      // if the current state is HIGH then the button
      // went from off to on:
    } else {
      // if the current state is LOW then the button
      // went from on to off:
      client.publish("ALARM/ZONE/testzone", "ON"); //
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state,
  //for next time through the loop
  lastButtonState = buttonState;

  {
    if (!client.connected()) {
      reconnect();
    }
    client.loop();
  }
}

This worked as I had intended - via a 9v optoisolated circuit it brought the pin high and output the MQTT topic/payload which I could watch from the console, broker and on the end device (HomeAssistant).

So if this works - and there is a way to get this up to <= 20 without too much learning then perhaps I will stay the course on the ESP .

I had a look at the extension boards and it goes into hex coding and binary coding which although I get the principle behind it - I am not at that level.

No idea what “extension boards” you have been looking at, or what “hex coding” would be involved! Some binary manipulation is often involved, you have to expect that.

As requested, here is the functional code from the ESP and the only thing missing is the #include wifi bit

No, there are still important bits missing from that code, not just missing #includes. For example the WiFi ssid and password are declared but never used.

Here’s an example Charlieplexed button matrix for 20 buttons. As you can see, the buttons are organised as 5 groups of 4.

Ah yes, I sent the wrong bit.

here is what is currently running and working …

#include <EEPROM.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <ESP8266WiFi.h>




// Connect to the WiFi
const char* ssid = "************";
const char* password = "**********";
// Connect to the MQTT Broker
const char* mqtt_server = "************";
const char* mqtt_username ="************";
const char* mqtt_password = "*************";
const char* clientID = "Alarm";


// this constant won't change:
const int buttonPin = D2;    // the pin that the pushbutton is attached to
const int chartsPin = D4;       // the pin that the LED is attached to

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button


WiFiClient espClient;
PubSubClient client(espClient);



void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    char receivedChar = (char)payload[i];
    Serial.print(receivedChar);
    if (receivedChar == '0')
      digitalWrite(chartsPin, HIGH);
    if (receivedChar == '1')
      digitalWrite(chartsPin, LOW);
  }
  Serial.println();
}


void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.println("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(clientID, mqtt_username, mqtt_password)) {
      Serial.println("Connected");
      // ... and subscribe to topic
      client.subscribe("ALARM/ZONE/#");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}



void setup()
{
  {

    client.setServer(mqtt_server, 1883);
    client.setCallback(callback);

    // initialize the button pin as a input:
    pinMode(buttonPin, INPUT);
    // initialize the LED as an output:
    pinMode(chartsPin, OUTPUT);
    // initialize serial communication:
    Serial.begin(9600);
  }

// Connect to WiFinetwork
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    //Serial.begin(9600);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  //Serial.begin(9600);
  // Start the server
  // server.begin();
  // Serial.println("Server started");
  //Serial.begin(9600);
  // Print the IP address
  Serial.print("Local IP: ");
  Serial.println(WiFi.localIP());
}

void loop() {

  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == HIGH) {
      client.publish("ALARM/ZONE/testzone", "OFF"); //
      // if the current state is HIGH then the button
      // went from off to on:
    } else {
      // if the current state is LOW then the button
      // went from on to off:
      client.publish("ALARM/ZONE/testzone", "ON"); //
    }
    // Delay a little bit to avoid bouncing
    delay(100);
  }
  // save the current state as the last state,
  //for next time through the loop
  lastButtonState = buttonState;

  {
    if (!client.connected()) {
      reconnect();
    }
    client.loop();
  }
}

So each “button” in my case will be a 12v output from an alarm system - one for each “zone” on a reflex output.
Each zone will trigger a different MQTT Topic - (lounge, Garage etc)

So will charlieplexing work for this still - as each address needs to be unique or with the expansion chip (pfc8574) be the better option ?

No, Charlieplexing won't work for that, so I would recommend 3 x pcf847 I/o expanders. That would require only 2 esp pins. Alternatives would be 3 x 74hc165 shift registers (3 esp pins required) which would be faster, but I'm not sure speed matters very much. Or 3 x 74hc138 or 2 x 74hc4067 multiplexers, but that would need 6 esp pins.

Because the signals are 12V, you will need a voltage divider on each line to reduce it to 3.3V. This would simply be a pair of resistors. Can you explain what a "reflex output" is please?

Thanks, I have ordered the pcf chips.

A reflex output is for the alarm system - it basically mimics the zone that it is linked to. So for example if the front door is open and the reflex output was linked to a sounder then the alarm would sound the whole time the door was open.

So in my case, the reflex output is linked to the optoisolated circuit to take it down from 12v and when the zone is open it pulls the pin on the ESP and it sends out an MQTT topic ALARM/FRONTDOOR "open"

This allows me to make a mimic panel in HomeAssistant and then use pushbullet for notifications on certain zones.

Speed on the chips isnt really an issue as MQTT messaging is going to be pretty light.