Sending and receiving button presses via MQTT (and questions about time)


#1

Two weeks ago we had a new addition to our household - a beautiful ginger and white cat called Apricat.

Now I would love to say that it’s been plain sailing since we got her, but (possibly because she’s a rescue cat), the transition into the house hasn’t been as smooth as we’d like, resulting in some minor swiping and biting incidents.

Because of this, I’ve started project wildcat - an incident tracker similar to the “X days without accident” signs often seen in industrial workplaces:

pasted%20image%200

So, with that preamble out of the way, I’m having some trouble with a couple parts, which I’m hoping you can help me with.

The idea is simple enough - a digital display which shows the number of days since the last incident, and a little button on the side which, when pressed, resets this to zero. For better or worse, I’m going to power this with an ESP8266 (Feather Huzzah, specifically), using Adafruit IO to record every time the button is pressed, and using the data stored on IO to calculate how long it’s been since the last incident.

(I’m sure there are far better solutions than this, and I realise using a cloud service for such a thing is probably overkill - but I’m interested in playing with MQTT for learning purposes, and solves the problem of keeping a clock running, and allows for the device to lose power without losing when the last incident was)

Anyway, here’s the code I have so far:

/***************************************************
  Adafruit MQTT Library ESP8266 Example

  Must use ESP8266 Arduino from:
    https://github.com/esp8266/Arduino

  Works great with Adafruit's Huzzah ESP board:
  ----> https://www.adafruit.com/product/2471

  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Tony DiCola for Adafruit Industries.
  MIT license, all text above must be included in any redistribution
 ****************************************************/
#include <NTPClient.h>        //Used to get a timestamp
#include <ESP8266WiFi.h>      //ESP8266 Core WiFi Library
#include <DNSServer.h>        //Local DNS Server used for redirecting all requests to the configuration portal
#include <ESP8266WebServer.h> //Local WebServer used to serve the configuration portal
#include <WiFiManager.h>      //https://github.com/tzapu/WiFiManager WiFi Configuration Magic

#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
#include "config.h"

/************ Global State (you don't need to change this!) ******************/

// Create an ESP8266 WiFiClient class to connect to the MQTT server.
WiFiClient client;

// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
Adafruit_MQTT_Client mqtt(&client, MQTT_SERVER, MQTT_SERVERPORT, MQTT_USERNAME, MQTT_KEY);

/****************************** Feeds ***************************************/

Adafruit_MQTT_Subscribe input = Adafruit_MQTT_Subscribe(&mqtt, MQTT_USERNAME "/feeds/wildcat.incident");
Adafruit_MQTT_Publish output = Adafruit_MQTT_Publish(&mqtt, MQTT_USERNAME "/feeds/wildcat.incident");

/****************************** IO *****************************************/

int timestamp = 0;

#define BUTTON_PIN 2

/****************************** Timestamp ***********************************/

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000);

/*************************** Sketch Code ************************************/

void inputcallback(double x)
{
    Serial.print("Input value is: ");
    Serial.println((int)x);
    timestamp = (int)x;
}

void setup()
{
    Serial.begin(9600);
    delay(10);

    pinMode(BUTTON_PIN, INPUT_PULLUP);

    // updateStatus();

    WiFiManager wifiManager;
    wifiManager.setTimeout(180);

    if (!wifiManager.autoConnect("Wildcat"))
    {
        Serial.println("failed to connect and hit timeout");
        delay(3000);
        ESP.reset();
        delay(5000);
    }

    Serial.println("connected...yeey :)");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());

    input.setCallback(inputcallback);

    mqtt.subscribe(&input);

    timeClient.begin();
}

uint32_t x = 0;

void loop()
{
    // Ensure the connection to the MQTT server is alive (this will make the first
    // connection and automatically reconnect when disconnected).  See the MQTT_connect
    // function definition further below.
    MQTT_connect();

    timeClient.update();
    // Serial.println(timeClient.getFormattedTime());
    // Serial.println(timeClient.getEpochTime());

    // If the button is pressed
    if (digitalRead(BUTTON_PIN) == LOW)
    {
        Serial.println("Button pressed");
        // Wait for the button to be released
        while (digitalRead(BUTTON_PIN) == LOW)
        {
            delay(100);
        }
        Serial.println("Button released");
        // Publish a button pushed message to a topic
        output.publish(timeClient.getEpochTime());
    }

    // this is our 'wait for incoming subscription packets and callback em' busy subloop
    // try to spend your time here:
    mqtt.processPackets(10000);

    // updateStatus();

    // ping the server to keep the mqtt connection alive
    // NOT required if you are publishing once every KEEPALIVE seconds
    if (!mqtt.ping())
    {
        mqtt.disconnect();
    }
}

// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect()
{
    int8_t ret;

    // Stop if already connected.
    if (mqtt.connected())
    {
        return;
    }

    Serial.print("Connecting to MQTT... ");

    uint8_t retries = 3;
    while ((ret = mqtt.connect()) != 0)
    { // connect will return 0 for connected
        Serial.println(mqtt.connectErrorString(ret));
        Serial.println("Retrying MQTT connection in 10 seconds...");
        mqtt.disconnect();
        delay(10000); // wait 10 seconds
        retries--;
        if (retries == 0)
        {
            // basically die and wait for WDT to reset me
            while (1)
                ;
        }
    }
    Serial.println("MQTT Connected!");
}

Right now I have two main issues - the first is the mqtt.processPackets(10000); method inside loop is causing the loop to pause for 10000 milliseconds (I think). This would be fine, other than the fact that I need to keep the button pressed for at least ten seconds before the loop restarts.

The second issue is I want to send a unix timestamp via the MQTT publish method, but this is currently failing because it says that an unsigned long isn’t the same as an integer - but I don’t know how to make it use a long instead.

Sorry for such a long post, I hope this makes sense, and that someone can help me.

Thank you.