I did something like this for a project we built. Devices looked for events published from other devices; we queued them to evaluate what actions to take based on who was in the queue.
We solved the problem using a vector to build a list of events with the originator and a timestamp stored (similar to what @Ric described) each time an event was detected (using publish/subscribe paradigm).
I adapted what I did for your problem. The trigger for the LED to illuminate is either 1) a sensor (which then publishes an event to its peers) or 2) the evaluation of all the events, looking for the particular sequence of events and timing you wish to employ.
publish two events sequentially like this:
and then this:
will trigger the led to light…
code (it looks scarier than it is):
#define makeString(s) str(s)
#define str(s) #s
#include <vector>
#include <algorithm>
#define THIS_DEVICE_ID 1 // This is used as a string and a number in the code below
#define LED_ON_INTERVAL 15*1000 // 15 seconds
#define EVENT_KEEP_ALIVE_TIME 60 // one minutes
#define EVENT_SPACING 10 // 10 seconds
enum SensorState{
NOT_TRIPPED,
TRIPPED,
};
struct ActiveCaller{
int deviceId;
uint32_t timeStamp;
bool operator == (const ActiveCaller& ac) {
return ac.deviceId == deviceId;
}
};
std::vector<ActiveCaller> activeCallers;
const int sensorPin = D6;
const int outputPin = D7;
Timer ledTimer(LED_ON_INTERVAL, [](){
digitalWrite(outputPin, LOW);
}, true);
void setup()
{
Serial.begin(9600);
pinMode(sensorPin, INPUT_PULLUP);
pinMode(outputPin, OUTPUT);
Particle.subscribe("myEventName", handleMessages, MY_DEVICES);
}
void loop()
{
if (sensorTripped())
{
digitalWrite(outputPin, HIGH);
ledTimer.start();
notifyAll();
}
removeOldEvents();
if (activeCallers.size())
{
if (checkEvents())
{
digitalWrite(outputPin, HIGH);
ledTimer.start();
notifyAll();
activeCallers.clear(); // OPTIONAL: you trapped the event you were looking for so clear the vector
}
}
static uint32_t lastMillis = 0;
if (millis() - lastMillis > 5000)
{
lastMillis += 5000;
Particle.publish("active events", String(activeCallers.size()), 60, PRIVATE);
}
}
/*
Trigger events...
1 -> 2, 3 // a 2 is followed by a three in the vector
2 -> 3, 4
3 -> 2, 1
4 -> 3, 2
*/
bool checkEvents()
{
switch (THIS_DEVICE_ID)
{
case 1:
if(checkForSequence(2, 3) < EVENT_SPACING)
{
return true;
}
break;
case 2:
if(checkForSequence(3, 4) < EVENT_SPACING)
{
return true;
}
break;
case 3:
if(checkForSequence(2, 1) < EVENT_SPACING)
{
return true;
}
break;
case 4:
if(checkForSequence(2, 3) < EVENT_SPACING)
{
return true;
}
break;
}
return false;
}
uint32_t checkForSequence(int x, int y)
{
ActiveCaller a;
a.deviceId = x;
ActiveCaller b;
b.deviceId = y;
// look for x in the vector
std::vector<ActiveCaller>::iterator itrX = std::find(activeCallers.begin(), activeCallers.end(), a);
if (itrX == activeCallers.end())
{
return EVENT_SPACING + 1;
}
// then look for y following x in the vector
std::vector<ActiveCaller>::iterator itrY = std::find(itrX, activeCallers.end(), b);
if(itrY == activeCallers.end())
{
return EVENT_SPACING + 1;
}
//return the elapsed time between the events
uint32_t timeBetweenEvents = (*itrX).timeStamp - (*itrY).timeStamp;
return timeBetweenEvents;
}
void removeOldEvents()
{
if(activeCallers.size())
{
if (Time.now() - activeCallers.back().timeStamp > EVENT_KEEP_ALIVE_TIME)
{
activeCallers.pop_back();
}
}
}
void handleMessages(const char* event, const char* data)
{
if(strstr(data, "device")) // {"device":2}
{
char mssg[strlen(data) + 1];
strcpy(mssg, data);
strtok(mssg, ":");
int peerId = atoi(strtok(NULL,":"));
if(peerId == THIS_DEVICE_ID)
return;
ActiveCaller newCaller;
newCaller.deviceId = peerId;
newCaller.timeStamp = Time.now();
activeCallers.insert(activeCallers.begin(), newCaller);
Particle.publish("debug", String(newCaller.deviceId ) + ": " + String(newCaller.timeStamp), 60, PRIVATE);
}
}
void notifyAll()
{
char message[32];
strcpy(message, "{\"device\":");
strcat(message, makeString(THIS_DEVICE_ID));
strcat(message, "}");
Particle.publish("myEventName", message, 60, PRIVATE);
}
bool sensorTripped()
{
static SensorState lastState = TRIPPED;
static uint32_t lastTripped = 0; // timeout
SensorState currentState = static_cast<SensorState>(digitalRead(sensorPin));
if (lastState != currentState and currentState == TRIPPED and millis() - lastTripped > 200)
{
lastState = currentState;
lastTripped = millis();
return true;
}
lastState = currentState;
return false;
}
this was written for 4 devices and you identify them with a unique DEVICE_ID of 1 to 4