My goal is to make an automatic gate and garage door, and it has a servo as the lock mechanism on the gate, and alarm systems that notify when the gate or garage door is open too long, more than the interval (in my case 10 seconds) through a buzzer.
It also uses:
- Bluetooth to send a command (which is
readChar
) to open/close the gate and open/close the garage. - 4 limit switches (2 switched located at both end of gate and garage to stop its movement).
I want that the gate and garage doors can be opened/closed at the same time, so there are 2 state machines. And the alarm system (if it is activated) will stop when the gate reaches limit switch 1, and the garage reaches limit switch 3.
However with my current code (compiled), I get:
- The first state machine (stateGate) can open and close the gate, but the buzzer does not buzz when it exceeds the limit.
- The second state machine (stateGarage) did not do anything/did not operate.
Can someone point out/correct what is the mistake? Because I can't seem to find what is wrong. Thank you in advance!
Here is my current full code:
#include <Servo.h>
Servo Servokey;
// Motor for Gate (MotorA)
const byte IN1 = 9;
const byte IN2 = 8;
const byte EnableMotorA = 6;
// Motor for Garage Door (MotorB)
const byte IN3 = 4;
const byte IN4 = 3;
const byte EnableMotorB = 5;
const byte LimitSwitch1 = A1;
const byte LimitSwitch2 = A2;
const byte LimitSwitch3 = A3;
const byte LimitSwitch4 = A4;
const int piezoPin = 10; // piezo for the alarm system
int StateGate;
int StateGarage;
boolean gateClosed = false;
boolean garageClosed = false;
unsigned long GateMillis; //will hold the timer for Gate
unsigned long GarageMillis; // will hold the timer for Garage
unsigned long interval = 10000;
void setup() {
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(EnableMotorA, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
pinMode(EnableMotorB, OUTPUT);
pinMode(LimitSwitch1, INPUT);
pinMode(LimitSwitch2, INPUT);
pinMode(LimitSwitch3, INPUT);
pinMode(LimitSwitch4, INPUT);
Servokey.attach(11); // lock mechanism for Gate
Servokey.write(70);
unsigned long GateMillis = 0; //will hold the timer for Gate
unsigned long GarageMillis = 0;
Serial.begin(9600);
}
void loop() {
byte readChar = 0;
if (Serial.available() > 0)
readChar = Serial.read();
stateGate(readChar);
stateGarage(readChar);
}
void stateGate(byte readChar) {
static enum { StateGate_Idle, StateGate_Opening, StateGate_Open, StateGate_Closing, StateGate_Close, StateGate_Alarm} StateGate;
switch (StateGate) {
case StateGate_Idle:
{ // If we received a valid command, start the operation
if (readChar > 0) {
switch (readChar) {
case 'A':
StateGate = StateGate_Opening;
break;
case 'B':
StateGate = StateGate_Closing;
break;
default:
// Here you can notify that there was a bad command
break;
}
}
}
break;
case StateGate_Opening:
Servokey.write(140);
delay(50);
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
analogWrite(EnableMotorA, 250);
StateGate = StateGate_Open;
break;
case StateGate_Open:
if (digitalRead(LimitSwitch2)) {
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
analogWrite(EnableMotorA, 0);
GateMillis = millis();
gateClosed = false;
StateGate = StateGate_Idle;
}
break;
case StateGate_Closing:
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
analogWrite(EnableMotorA, 250);
StateGate = StateGate_Close;
break;
case StateGate_Close:
if (digitalRead(LimitSwitch1)) {
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
analogWrite(EnableMotorA, 0);
Servokey.write(70);
delay(50);
gateClosed = true;
StateGate = StateGate_Idle;
}
break;
case StateGate_Alarm:
if ((millis() - GateMillis) > interval && gateClosed == false) {
tone(piezoPin, 5000);
StateGate = StateGate_Idle;
} else if (gateClosed == true) {
StateGate = StateGate_Idle;
}
break;
default:
StateGate = StateGate_Idle;
break;
}
}
void stateGarage(byte readChar) {
static enum { StateGarage_Idle, StateGarage_Opening, StateGarage_Open, StateGarage_Closing, StateGarage_Close, StateGarage_Alarm} StateGarage;
switch (StateGarage) {
case StateGarage_Idle:
{ // If we received a valid command, start the operation
if (readChar > 0) {
switch (readChar) {
case 'C':
StateGarage = StateGarage_Opening;
break;
case 'D':
StateGarage = StateGarage_Closing;
break;
default:
// Here you can notify that there was a bad command
break;
}
}
}
break;
case StateGarage_Opening:
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
analogWrite(EnableMotorA, 250);
StateGarage = StateGarage_Open;
break;
case StateGarage_Open:
if (digitalRead(LimitSwitch4)) {
digitalWrite(IN3, LOW);
digitalWrite(IN4, LOW);
analogWrite(EnableMotorA, 0);
GarageMillis = millis();
garageClosed = false;
StateGarage = StateGarage_Idle;
}
break;
case StateGarage_Closing:
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
analogWrite(EnableMotorA, 250);
StateGarage = StateGarage_Close;
break;
case StateGarage_Close:
if (digitalRead(LimitSwitch3)) {
digitalWrite(IN3, LOW);
digitalWrite(IN4, LOW);
analogWrite(EnableMotorB, 0);
garageClosed = true;
StateGarage = StateGarage_Idle;
}
break;
case StateGarage_Alarm:
if ((millis() - GarageMillis) > interval && garageClosed == false) {
tone(piezoPin, 5500);
StateGarage = StateGarage_Idle;
} else if (garageClosed == true)
StateGarage = StateGarage_Idle;
break;
default:
StateGarage = StateGarage_Idle;
break;
}
}
I made another code like what @CodeGorilla had suggested, however it turned out that both state machines did not operate.... I think I am still missed out something
Here's the other code:
#include <Servo.h>
Servo Servokey;
enum StateGate { StateGate_Idle = 0, StateGate_Opening, StateGate_Open, StateGate_Closing, StateGate_Closed, StateGate_Alarm, StateGate_Last };
enum StateGarage { StateGarage_Idle = 0, StateGarage_Opening, StateGarage_Open, StateGarage_Closing, StateGarage_Closed, StateGarage_Alarm, StateGarage_Last };
struct Data1
{
byte IN1;
byte IN2;
bool Enable;
int NextState;
Data1(byte in1, byte in2, bool enable, int nextstate)
:IN1(in1), IN2(in2), Enable(enable), NextState(nextstate)
{}
};
struct Data2
{
byte IN3;
byte IN4;
bool Enable;
int NextState;
Data2(byte in3, byte in4, bool enable, int nextstate)
:IN3(in3), IN4(in4), Enable(enable), NextState(nextstate)
{}
};
struct MotorData1
{
byte IN1;
byte IN2;
byte Enable;
MotorData1(byte i1, byte i2, byte enable)
: IN1(i1), IN2(i2), Enable(enable)
{}
void pinMode() const
{
:: pinMode(IN1, OUTPUT);
:: pinMode (IN2, OUTPUT);
:: pinMode (Enable, OUTPUT);
}
int Set(const Data1& data1) const
{// Set the motors to the given state
digitalWrite(IN1, data1.IN1);
digitalWrite(IN2, data1.IN2);
analogWrite(Enable, data1.Enable?250:0);
return data1.NextState;
}
};
struct MotorData2
{
byte IN3;
byte IN4;
byte Enable;
MotorData2(byte i3, byte i4, byte enable)
: IN3(i3), IN4(i4), Enable(enable)
{}
void pinMode() const
{
:: pinMode(IN3, OUTPUT);
:: pinMode (IN4, OUTPUT);
:: pinMode (Enable, OUTPUT);
}
int Set(const Data2& data2) const
{// Set the motors to the given state
digitalWrite(IN3, data2.IN3);
digitalWrite(IN4, data2.IN4);
analogWrite(Enable, data2.Enable?250:0);
return data2.NextState;
}
};
// Define the motors
const MotorData1 MotorsA(9, 8, 6);
const MotorData2 MotorsB(4, 3, 5);
const byte LimitSwitch1 = A1;
const byte LimitSwitch2 = A2;
const byte LimitSwitch3 = A3;
const byte LimitSwitch4 = A4;
const int piezoPin = 10; // piezo for the alarm system
StateGate CurrentGateState = StateGate_Idle;
StateGarage CurrentGarageState = StateGarage_Idle;
boolean gateClosed = false;
boolean garageClosed = false;
unsigned long GateMillis(0); //will hold the timer for Gate
unsigned long GarageMillis(0); // will hold the timer for Garage
unsigned long interval = 10000;
void setup() {
MotorsA.pinMode();
MotorsB.pinMode();
pinMode(LimitSwitch1, INPUT);
pinMode(LimitSwitch2, INPUT);
pinMode(LimitSwitch3, INPUT);
pinMode(LimitSwitch4, INPUT);
Servokey.attach(11); // lock mechanism for Gate
Servokey.write(70);
Serial.begin(9600);
}
void loop()
{
byte readChar = 0;
if (Serial.available() > 0)
readChar = Serial.read();
stateGate(readChar);
stateGarage(readChar);
}
void stateGate(byte readChar)
{// Define the state machine
static const Data1 stateData1[StateGate_Last] =
{
Data1(LOW, LOW, false, StateGate_Idle) // StateGate_Idle
, Data1(LOW, HIGH, true, StateGate_Open) // StateGate_Opening
, Data1(LOW, LOW, false, StateGate_Idle) // StateGate_Open
, Data1(HIGH, LOW, false, StateGate_Closed) // StateGate_Closing
, Data1(LOW, LOW, false, StateGate_Idle) // StateGate_Closed
, Data1(LOW, LOW, false, StateGate_Idle) // StateGate_Alarm
};
switch (CurrentGateState)
{
case StateGate_Idle:
{ // If we received a valid command, start the operation
if (readChar > 0) {
switch (readChar) {
case 'A':
if (CurrentGateState != StateGate_Open)
{ // Prevent opening the gate if its already open
CurrentGateState = StateGate_Opening;
}
break;
case 'B':
if (CurrentGateState != StateGate_Closed)
{// Prevent closing the gate if its already closed.
CurrentGateState = StateGate_Closing;
}
break;
default:
// Here you can notify that there was a bad command
break;
}
}
}
break;
case StateGate_Opening:
Servokey.write(140);
delay(50);
CurrentGateState = MotorsA.Set(stateData1[StateGate_Opening]);
break;
case StateGate_Open:
if (digitalRead(LimitSwitch2))
{
CurrentGateState = MotorsA.Set(stateData1[StateGate_Open]);
GateMillis = millis();
gateClosed = false;
}
break;
case StateGate_Closing:
CurrentGateState = MotorsA.Set(stateData1[StateGate_Closing]);
break;
case StateGate_Closed:
if (digitalRead(LimitSwitch1))
{
CurrentGateState = MotorsA.Set(stateData1[StateGate_Closed]);
Servokey.write(70);
delay(50);
gateClosed = true;
}
break;
case StateGate_Alarm:
if ((millis() - GateMillis) > interval && gateClosed == false) {
tone(piezoPin, 5000);
CurrentGateState = StateGate_Idle;
}
else if (gateClosed == true) {
CurrentGateState = StateGate_Idle;
}
break;
default:
CurrentGateState = StateGate_Idle;
break;
}
}
void stateGarage(byte readChar)
{// Define the state machine
static const Data2 stateData2[StateGarage_Last] =
{
Data2(LOW, LOW, false, StateGarage_Idle) // StateGate_Idle
, Data2(LOW, HIGH, true, StateGarage_Open) // StateGate_Opening
, Data2(LOW, LOW, false, StateGarage_Idle) // StateGate_Open
, Data2(HIGH, LOW, false, StateGarage_Closed) // StateGate_Closing
, Data2(LOW, LOW, false, StateGarage_Idle) // StateGate_Closed
, Data2(LOW, LOW, false, StateGarage_Idle) // StateGate_Alarm
};
switch (CurrentGarageState)
{
case StateGarage_Idle:
{ // If we received a valid command, start the operation
if (readChar > 0) {
switch (readChar) {
case 'A':
if (CurrentGarageState != StateGarage_Open)
{ // Prevent opening the gate if its already open
CurrentGarageState = StateGarage_Opening;
}
break;
case 'B':
if (CurrentGarageState != StateGarage_Closed)
{// Prevent closing the gate if its already closed.
CurrentGarageState = StateGarage_Closing;
}
break;
default:
// Here you can notify that there was a bad command
break;
}
}
}
break;
case StateGarage_Opening:
CurrentGarageState = MotorsB.Set(stateData2[StateGarage_Opening]);
break;
case StateGarage_Open:
if (digitalRead(LimitSwitch2))
{
CurrentGarageState = MotorsB.Set(stateData2[StateGarage_Open]);
GarageMillis = millis();
garageClosed = false;
}
break;
case StateGarage_Closing:
CurrentGarageState = MotorsB.Set(stateData2[StateGarage_Closing]);
break;
case StateGarage_Closed:
if (digitalRead(LimitSwitch1))
{
CurrentGarageState = MotorsB.Set(stateData2[StateGarage_Closed]);
Servokey.write(70);
delay(50);
garageClosed = true;
}
break;
case StateGarage_Alarm:
if ((millis() - GarageMillis) > interval && garageClosed == false) {
tone(piezoPin, 5000);
CurrentGarageState = StateGarage_Idle;
}
else if (garageClosed == true) {
CurrentGarageState = StateGarage_Idle;
}
break;
default:
CurrentGarageState = StateGarage_Idle;
break;
}
}
StateGarage_Alarm
, but you have no command that actually causes the state machine to enter that state. .... your state machine has some questionable functionality ... theStateGate_Opening
state code sets the state toStateGate_Open
without checking if the gate actually opened. ... similar thing is being repeated in other parts of the code – jsotola Jan 5 '18 at 8:36StateGate_Open
is actually the problem with the code – jsotola Jan 5 '18 at 8:42{IN1State, IN2State, EnableA, EnableB, StateGArage}
.garageClosed
could be replaced with two extraStateGarage_
entriesOpened
andClosed
. – Code Gorilla Jan 5 '18 at 15:05