New to FSM, can someone explain how the flow works? If I use a case scenario in a loop, how is an external change detected and brought back to the loop? Is all of the code/inputs, etc to be included within the case statements?
Still having a few problems. I have modified my code to include some of the suggestions. You can see that I added some print statements to allow me to monitor execution, but I can't get it to obey, such as print "trigger" when the PIR sensor is HIGH. Anyone available to explain my error(s)? I added a void check() just to bypass the keypad (not connected).
//////////////////////////// Initialize & Includes //////////////////////////
#include "SIM900.h"
#include "sms.h"
#include "Keypad.h"
#include <GSM.h>
SMSGSM sms;
//To change pins for Software Serial, use the two lines in GSM.cpp.
int PIR_SensorPin = 2;
int LED_OutPin = 11; //green
int Alarm_OutPin = 12; //red
int alarm_count = 0;
int switchPIN = 3; // alarm on test
char keypadPIN;
int z = 0;
int i = 0;
int waitTime = 5000;
//////////////////////////// Setup Keypad //////////////////////////
const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
char keys[ROWS][COLS] =
{
{
'1', '2', '3', 'A'
}
,
{
'4', '5', '6', 'B'
}
,
{
'7', '8', '9', 'C'
}
,
{
'*', '0', '#', 'D'
}
};
byte rowPins[ROWS] = {
6, 7, A2, A3 // row pin# on keypad to Arduino pin#, ie Row Pin #1 goes to Arduino Pin #6, etc.
}; // connect to the row pinouts of the keypad
byte colPins[COLS] = {
3, A0, A1, A4
}; // connect to the column pinouts of the keypad
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS );
const char PIN[5] = {
'9', '6', '7', '9', '#'
}; // PIN number
char key_input[5] = {
0, 0, 0, 0, 0
}; // used for comparison
//////////////////////////////// Setup ///////////////////////////////////
void setup() {
Serial.begin (9600); // Establish Serial connection;
pinMode (LED_OutPin, OUTPUT); // Set pinMode.
pinMode (Alarm_OutPin, OUTPUT); // Set pinMode.
pinMode (PIR_SensorPin, INPUT); // Set pinMode.
digitalWrite (LED_OutPin, LOW); // Set output pins to LOW for start.
digitalWrite (Alarm_OutPin, LOW); // Set output pins to LOW for start.
digitalWrite (PIR_SensorPin, LOW);
Serial.println ("Studio Alarm starting up."); // Serial message that GSM shield is starting up
if (gsm.begin(4800)) { // Set GSM shield to recommended 4800 baud rate.
Serial.println ("Status = Network READY");
digitalWrite (LED_OutPin, HIGH);
}
attachInterrupt (digitalPinToInterrupt (2), trigger, CHANGE); // PIR as interrupt
// Could make use of the interrupts by checking in the interrupt if the state is "Ready" and
// then setting the state to "Alarm".
Serial.println ("System Ready");
keypadPIN = HIGH;
}
//////////////////////////// State Setup //////////////////////////
enum STATE {
Ready, //system acknowledges trigger
Unarmed, //system unarmed via keypad entry setting keypadPIN = LOW
JustActivated, //PIR-triggered interrupt
Alarm // confirm system unarmed then goto unarmed state else start alarm
};
STATE systemState = Unarmed;
long timeGettingArmed;
/////////////////////////// Trigger ///////////////////////////////
void trigger() {
systemState = JustActivated;
}
//////////////////////////// State Machine //////////////////////////
void loop() {
check ();
switch (systemState) {Serial.println("switch");
case Ready:
if (PIR_SensorPin == HIGH) {Serial.println("PIR_Sensor");
Serial.println("triggered");
systemState = Alarm;
} else if (keypadPIN == LOW) {
systemState = Unarmed;
}
break;
case Unarmed:
if (keypadPIN == HIGH) {Serial.println("keypadPIN=HIGH");
// Serial.println("Unarmed");
timeGettingArmed = millis();Serial.println(timeGettingArmed);
systemState = JustActivated;
}
break;
case JustActivated:Serial.println("JustActivated");
if (timeGettingArmed + waitTime < millis()) {
Serial.println("Ready");
systemState = Ready;
}
break;
case Alarm:
if (keypadPIN == LOW) {Serial.println("keypadPIN=LOW");
systemState = Unarmed;
} else {
beep();
}
break;
}
}
//////////////////////////// Alarm //////////////////////////
void beep() {
Serial.println("at beep");
// (sms.SendSMS("8656171435", "*** Motion Detected in Studio! ***"));
Serial.println ("MOTION detected: SMS Sent");
for (alarm_count = 0; alarm_count <= 5; alarm_count++) { // Cycle outputs if triggered
digitalWrite (Alarm_OutPin, HIGH); delay (1000); // On/Off/On/Off/Off
digitalWrite (Alarm_OutPin, LOW); delay (500);
digitalWrite (LED_OutPin, HIGH); // delay (1000);
Serial.println (alarm_count);
}
systemState = Ready;
}
void check() {
if (switchPIN == HIGH) {
keypadPIN = LOW; // Unarmed
}
else {
keypadPIN = HIGH; // Ready/Armed
}
}
ready,1stDown,1stUp,2ndDown,stale
states, which vary from theshort,click,dblclick
outputs. when in one state, check if you need to change to another state, and if not, do nothing. usually it's nothing.case
, if needed, sets the state to anothercase
or does nothingswitch
to avoid spaghetti flow as the project grows. It's an adjustment for sure, but i loved it. ;)