States derived from analogue values edge retention

Hey

I have found a million and one ways to get this to do the same thing but unfortunately it is not what I am looking for.

Use Case: PIR Sensor interface to MQTT. Through a series of resistors the circuit is capable of determining what state the PIR sensor is in using a single pair of wires.

The analogue value is then split into “states” to publish a MQTT message: state 0 <400 Tamper, state 1 >500 < 590 Normal, state 2 >590 < 800 Motion, state 3 Else Tamper. The hardware and MQTT side off things is fine and works as expected.

The issue I am having is that the messages are being continually broadcast due to my edge retention being inoperable. I am expecting the arduino only to publish a message on change of state, not continually.

Below is a copy of the latest code, this is about version 12 of code that continuously publishes and may be further away than the original works but still explains what I am experiencing. Please note it is setup for 4 channels with 3 off them commented out due to hardware being incomplete and unconnected channels causing interference.

Any help would be much appreciated.

#include <SPI.h>                  // For networking
#include <Ethernet.h>             // For networking
#include <PubSubClient.h>         // For MQTT

/*--------------------------- Configuration ------------------------------*/
/* Network Settings */
#define ENABLE_DHCP                 false   // true/false
#define ENABLE_MAC_ADDRESS_ROM      true   // true/false
#define MAC_I2C_ADDRESS             0x50   // Microchip 24AA125E48 I2C ROM address
static uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };  // Set if no MAC ROM
IPAddress ip(192, 168, 10, 193);             // Default if DHCP is not used

/* MQTT Settings */
IPAddress broker(192, 168, 10, 20);     // MQTT broker
const char* statusTopic  = "events";    // MQTT topic to publish status reports
char messageBuffer[100];
char topicBuffer[100];
char clientBuffer[50];

/*------------------------------------------------------------------------*/

/**
   MQTT callback
*/
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++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}

// Instantiate MQTT client
//PubSubClient client(broker, 1883, callback);
EthernetClient ethclient;
PubSubClient client(ethclient);

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    String clientString = "Reconnecting Arduino-" + String(Ethernet.localIP());
    clientString.toCharArray(clientBuffer, clientString.length() + 1);
    if (client.connect(clientBuffer)) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      clientString.toCharArray(clientBuffer, clientString.length() + 1);
      client.publish(statusTopic, clientBuffer);
      // ... and resubscribe
      //client.subscribe("inTopic");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

/* ************************************************************************************* */
/* IO setup */

// Use analog inputs 0 through 3 for sensor connections A through D
byte channelAInput = 0;
//byte channelBInput = 1;
//byte channelCInput = 2;
//byte channelDInput = 3;

// Use digital outputs 4 through 7 for status indicator LEDs A through D
byte channelALed = 4;
//byte channelBLed = 5;
//byte channelCLed = 6;
//byte channelDLed = 7;

/* ************************************************************************************* */
/**
   Initial configuration
*/

void setup()
{
  Serial.begin(9600);  // Use the serial port to report back readings

  // Print the IP address
  char tmpBuf[17];
  sprintf(tmpBuf, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  Serial.println(tmpBuf);


  pinMode(channelALed, OUTPUT);   // Set up channel A
  digitalWrite(channelALed, LOW);
  //  pinMode(channelBLed, OUTPUT);   // Set up channel B
  //  digitalWrite(channelBLed, LOW);
  //  pinMode(channelCLed, OUTPUT);   // Set up channel C
  //  digitalWrite(channelCLed, LOW);
  //  pinMode(channelDLed, OUTPUT);   // Set up channel D
  //  digitalWrite(channelDLed, LOW);

  // Set up the Ethernet library to talk to the Wiznet board
  if ( ENABLE_DHCP == true )
  {
    Ethernet.begin(mac);      // Use DHCP
  } else {
    Ethernet.begin(mac, ip);  // Use static address defined above
  }

  /* Connect to MQTT broker */
  Serial.println("connecting...");
  client.setServer(broker, 1883);
  client.setCallback(callback);
  String clientString = "Starting Arduino-" + Ethernet.localIP();
  clientString.toCharArray(clientBuffer, clientString.length() + 1);
  client.publish(statusTopic, clientBuffer);

  Serial.println("Ready.");

}



/* ************************************************************************************* */
/**
   Main program loop
*/
void loop()
{
  if (!client.connected()) {
    reconnect();
  }

  client.loop();

  byte sensorStatus;
  sensorStatus = checkSensor(channelAInput, channelALed);
  //  sensorStatus = checkSensor(channelBInput, channelBLed);
  //  sensorStatus = checkSensor(channelCInput, channelCLed);
  //  sensorStatus = checkSensor(channelDInput, channelDLed);

  Serial.println("");
  delay(500);   // Wait half a second before reading all channels again
}

/**
   Checks the state of a sensor and reports it to the connected host
*/
boolean checkSensor( byte sensorInput, byte statusOutput )
{
  byte state;
  byte lastState;
  int sensorReading = analogRead(sensorInput);
  if ( sensorReading < 400 ) {
    state = 0;                        // Wire shorted. Possible tampering.
    digitalWrite(statusOutput, HIGH); // Turn the associated status LED on
  } else if ( sensorReading >= 400 && sensorReading < 590 ) {
    state = 1;                        // Normal state, sensor not triggered
    digitalWrite(statusOutput, LOW); // Turn the associated status LED off
  } else if ( sensorReading >= 590 && sensorReading < 800 ) {
    state = 2;                        // Sensor triggered.
    digitalWrite(statusOutput, HIGH); // turn the associated status LED on
  } else {
    state = 3;                        // Open circuit. Cut or tamper triggered.
    digitalWrite(statusOutput, HIGH); // Turn the associated status LED on
  }

  if ( state != lastState)
    if (state == 0) {
      String messageString = String(sensorInput, DEC) + " Tamper " + String(state, DEC);
      messageString.toCharArray(messageBuffer, messageString.length() + 1);
      String topicString = "events";
      topicString.toCharArray(topicBuffer, topicString.length() + 1);
      client.publish("events", messageBuffer);
    } else if (state == 1) {
      String messageString = String(sensorInput, DEC) + " Normal " + String(state, DEC);
      messageString.toCharArray(messageBuffer, messageString.length() + 1);
      String topicString = "events";
      topicString.toCharArray(topicBuffer, topicString.length() + 1);
      client.publish("events", messageBuffer);
    } else if (state == 2) {
      String messageString = String(sensorInput, DEC) + " Motion " + String(state, DEC);
      messageString.toCharArray(messageBuffer, messageString.length() + 1);
      String topicString = "events";
      topicString.toCharArray(topicBuffer, topicString.length() + 1);
      client.publish("events", messageBuffer);
    } else if (state == 3) {
      String messageString = String(sensorInput, DEC) + " Tamper " + String(state, DEC);
      messageString.toCharArray(messageBuffer, messageString.length() + 1);
      String topicString = "events";
      topicString.toCharArray(topicBuffer, topicString.length() + 1);
      client.publish("events", messageBuffer);
    }
  lastState = state;
}
//    String messageString = String(sensorInput, DEC) + " Tamper";
//    messageString.toCharArray(messageBuffer, messageString.length()+1);
//    String topicString = "events";
//    topicString.toCharArray(topicBuffer, topicString.length()+1);
//    client.publish("events", messageBuffer);
//// Output the current reading to the host via the serial connection
//Serial.print(sensorInput, DEC);
//Serial.print(": ");
//Serial.print(sensorReading, DEC);
//Serial.print(" (");
//Serial.print(state, DEC);
//Serial.print(") ");
//
//// Pass the current state back to the calling function
//return state;

Try declaring 'lastState' as static. And, initialize it to an unused state number so the inequality comparison with 'state' will be true the first time loop() runs.

I'll add a little why to that.

Since lastState is local to the checkSensor function, it is destroyed and recreated when the function exits and gets called again. So it doesn't remember the last state. It's always 0 at the beginning of the function. Making it static or global will fix that.

gfvalvo:
Try declaring 'lastState' as static. And, initialize it to an unused state number so the inequality comparison with 'state' will be true the first time loop() runs.

Superb, simple

static byte lastState;

Works like a charm... 2 Dyas and 12 versions of code later only a word was missing...

Thank you very much

Delta_G:
I'll add a little why to that.

Since lastState is local to the checkSensor function, it is destroyed and recreated when the function exits and gets called again. So it doesn't remember the last state. It's always 0 at the beginning of the function. Making it static or global will fix that.

Thank you for explanation, this is the first bit of code I have ever written on Arduino. Ecstatic that it is working now.

Delta_G:
So it doesn't remember the last state. It's always 0 at the beginning of the function.

I think unless explicitly initialized, a local (stack) variable's value will be indeterminate until set. It will simply be whatever happens to be in the memory location(s) at the time.

OP, as I mentioned, a better declaration would be something like:

static byte lastState = 100;

That way,

if ( state != lastState)

will be TRUE the first time loop() runs.

gfvalvo:
I think unless explicitly initialized, a local (stack) variable's value will be indeterminate until set. It will simply be whatever happens to be in the memory location(s) at the time.

Oh, my bad I could have sworn I saw "= 0" on the end of that line. I guess I need glasses.

  char tmpBuf[17];
  sprintf(tmpBuf, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

6 2 digit sets + 5 separators + a terminating NULL will NOT fit into a 17 element array.