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,stalestates, which vary from theshort,click,dblclickoutputs. 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 anothercaseor does nothingswitchto avoid spaghetti flow as the project grows. It's an adjustment for sure, but i loved it. ;)