Here is my final state diagram, wiring schematic, and code (though I might change some threshold values). The code is excessively long and definitely not optimized; it was just the easiest way to understand what was going on so I can more easily debug. It should be relatively self-explanatory therefore. The state diagram from which the code was derived is similarly not optimal for the same reason.
And I figured out that the reason the bell & buzzer weren’t going off during the demo was because I turned on the batteries improperly, as in apparently I need to turn on the Arduino last, otherwise it will enter some strange inescapable state-change path. I don’t have it all figured out but it’s hopefully enough such that the effectiveness of Murphy’s Law won’t be consistent.
HELP REQUEST!
I’m having no luck photographing it and I honestly have no idea how to videoscenario it so if anyone has any suggestions or can offer help it would be greatly appreciated!
[State Diagram & Wiring Schematic]
[Code]
#include <Servo.h>
// PINS //
int soapLED_R = 10; // warning light
int soapLED_G = 9; // indicates soap being dispensed
int soapLED_B = 8; // standard light [GB=white]
int sinkLED = 11; // indicates running water [B]
int soapsensor = 0;
int sinksensor = 1;
int toiletswitch = 12; // indicates motion detected [into toilet!]
int flushbutton = 13;
int lockbutton = 7;
int doorswitch = 4;
int signservo = 3;
int locksolenoid = 2;
int speaker = 5;
int bell = 6;
Servo sign;
// STATES //
int State = 0;
const int Blank = 0;
const int Locked = 1;
const int Wash = 2;
const int LockWash = 3;
const int Flush = 4;
const int Toilet = 5;
const int DUnlock = 6;
const int DLeaving = 7;
const int DSoap = 8;
const int DRinse = 9;
const int Clean = 10;
const int CLeaving = 11;
const int TUnlock = 12;
const int TLeaving = 13;
const int TSoap = 14;
const int TWater = 15;
int LockState = 0;
const int Auto = 0;
const int Manual = 1;
// OUTPUT CONDITIONS //
int Reward = 0;
const int Off = 0;
const int On = 1;
int Buzzer = 0;
int Sign = 0;
const int Bl_Bl = 125; // in: blank out: blank
const int Wa_Bl = 107; // in: wash out: blank
const int Wa_Di = 87; // in: wash out: dirty
const int Ri_Bl = 68; // in: rinse out: blank
const int Fl_Bl = 51; // in: flush out: blank
const int Fl_Fl = 31; // in: flush out: flush
const int Th_Cl = 13; // in: thanks out: clean!
int Soap = 0;
const int White = 0; // standard light
const int Red = 1; // warning light
const int Green = 2; // indicates soap being dispensed
int Sink = 0;
int Lock = 0;
// ANALOG INPUT THRESHOLDS //
int soapmin = 30;
int soapmax = 100;
int sinkmin = 100;
int sinkmax = 300;
// OTHER //
int lockbuttonpress = 0;
int dooropentime = 0;
int sinkontime = 0;
void setup()
{
Serial.begin(9600);
pinMode(soapLED_R, OUTPUT); // digital
pinMode(soapLED_G, OUTPUT); // digital
pinMode(soapLED_B, OUTPUT); // digital
pinMode(sinkLED, OUTPUT); // digital
pinMode(soapsensor, INPUT); // analog
pinMode(sinksensor, INPUT); // analog
pinMode(toiletswitch, INPUT); // digital
pinMode(flushbutton, INPUT); // digital
pinMode(lockbutton, INPUT); // digital
pinMode(doorswitch, INPUT); // digital: LOW when closed, HIGH when open
pinMode(signservo, OUTPUT); // digital PWM
pinMode(locksolenoid, OUTPUT); // digital
pinMode(speaker, OUTPUT); // digital
pinMode(bell, OUTPUT); // digital
State = Blank;
LockState = Auto;
Reward = Off;
Buzzer = Off;
Sign = Bl_Bl;
Soap = White;
Sink = White;
Lock = Off;
lockbuttonpress = 0;
dooropentime = 0;
sinkontime = 0;
sign.attach(signservo);
sign.setMaximumPulse(2400);
sign.setMinimumPulse(544);
sign.write(Bl_Bl);
}
void loop()
{
// SWITCH STATES //
switch(State)
{
case Blank:
if(lockbuttonpress==0 && digitalRead(lockbutton)==HIGH && digitalRead(doorswitch)==LOW)
{ lockbuttonpress=1; State=Locked; }
if(digitalRead(lockbutton)==LOW) {lockbuttonpress=0;}
if(analogRead(soapsensor)<soapmin) {State=Wash;}
if(digitalRead(flushbutton)==HIGH) {State=Flush;}
if(digitalRead(toiletswitch)==HIGH) {State=Toilet;}
break;
case Locked:
if(lockbuttonpress==0 && digitalRead(lockbutton)==HIGH)
{ lockbuttonpress=1; State=Blank;}
if(digitalRead(lockbutton)==LOW) {lockbuttonpress=0;}
if(analogRead(soapsensor)<soapmin) {State=LockWash;}
if(digitalRead(flushbutton)==HIGH) {State=Flush;}
if(digitalRead(toiletswitch)==HIGH) {State=Toilet;}
break;
case Wash:
if(lockbuttonpress==0 && digitalRead(lockbutton)==HIGH && digitalRead(doorswitch)==LOW)
{ lockbuttonpress=1; State=LockWash; }
if(digitalRead(lockbutton)==LOW) {lockbuttonpress=0;}
if(digitalRead(doorswitch)==HIGH) {State=Blank;}
if(analogRead(sinksensor)<sinkmin) {State=Clean;}
if(digitalRead(flushbutton)==HIGH) {State=Flush;}
if(digitalRead(toiletswitch)==HIGH) {State=Toilet;}
break;
case LockWash:
if(lockbuttonpress==0 && digitalRead(lockbutton)==HIGH)
{ lockbuttonpress=1; State=Wash;}
if(digitalRead(lockbutton)==LOW) {lockbuttonpress=0;}
if(digitalRead(doorswitch)==HIGH) {State=Blank;}
if(analogRead(sinksensor)<sinkmin)
{ if(sinkontime==0) {sinkontime=millis();}
if(millis()-sinkontime>1000) {sinkontime=0; State=Clean;} }
if(analogRead(sinksensor)>sinkmax) {sinkontime=0;}
if(digitalRead(flushbutton)==HIGH) {State=Flush;}
if(digitalRead(toiletswitch)==HIGH) {State=Toilet;}
break;
case Flush:
if(lockbuttonpress==0 && digitalRead(lockbutton)==HIGH)
{ lockbuttonpress=1; State=DUnlock;}
if(digitalRead(lockbutton)==LOW) {lockbuttonpress=0;}
if(digitalRead(doorswitch)==HIGH)
{ dooropentime=millis(); State=DLeaving; }
if(analogRead(soapsensor)<soapmin) {State=DSoap;}
if(analogRead(sinksensor)<sinkmin) {State=DRinse;}
if(digitalRead(toiletswitch)==HIGH) {State=Toilet;}
break;
case Toilet:
if(lockbuttonpress==0 && digitalRead(lockbutton)==HIGH)
{ lockbuttonpress=1; State=TUnlock;}
if(digitalRead(lockbutton)==LOW) {lockbuttonpress=0;}
if(digitalRead(doorswitch)==HIGH) {dooropentime=millis(); State=TLeaving;}
if(analogRead(soapsensor)<soapmin) {State=TSoap;}
if(analogRead(sinksensor)<sinkmin) {State=TWater;}
if(digitalRead(flushbutton)==HIGH) {State=Flush;}
break;
case DUnlock:
if(lockbuttonpress==0 && digitalRead(lockbutton)==HIGH && digitalRead(doorswitch)==LOW)
{ lockbuttonpress=1; State=Flush; LockState=Manual; }
if(digitalRead(lockbutton)==LOW) {lockbuttonpress=0;}
if(digitalRead(doorswitch)==HIGH)
{ dooropentime=millis(); State=DLeaving; }
if(analogRead(soapsensor)<soapmin) {State=DSoap;}
if(analogRead(sinksensor)<sinkmin) {State=DRinse;}
if(digitalRead(flushbutton)==HIGH) {State=Flush;}
if(digitalRead(toiletswitch)==HIGH) {State=Toilet;}
break;
case DLeaving:
if(digitalRead(doorswitch)==LOW && millis()-dooropentime<1000)
{ dooropentime=0; State=Flush;}
if(millis()-dooropentime>1000) {dooropentime=0; State=Blank;}
if(analogRead(soapsensor)<soapmin) {State=DSoap;}
if(analogRead(sinksensor)<sinkmin) {State=DRinse;}
if(digitalRead(flushbutton)==HIGH) {State=Flush;}
if(digitalRead(toiletswitch)==HIGH) {State=Toilet;}
break;
case DSoap:
if(lockbuttonpress==0 && digitalRead(lockbutton)==HIGH)
{ lockbuttonpress=1; State=DUnlock;}
if(digitalRead(lockbutton)==LOW) {lockbuttonpress=0;}
if(digitalRead(doorswitch)==HIGH)
{ dooropentime=millis(); State=DLeaving; }
if(analogRead(sinksensor)<sinkmin)
{ if(sinkontime==0) {sinkontime=millis();}
if(millis()-sinkontime>1000) {sinkontime=0; State=Clean;} }
if(analogRead(sinksensor)>sinkmax) {sinkontime=0;}
if(digitalRead(flushbutton)==HIGH) {State=Flush;}
if(digitalRead(toiletswitch)==HIGH) {State=Toilet;}
break;
case DRinse:
if(lockbuttonpress==0 && digitalRead(lockbutton)==HIGH)
{ lockbuttonpress=1; State=DUnlock;}
if(digitalRead(lockbutton)==LOW) {lockbuttonpress=0;}
if(digitalRead(doorswitch)==HIGH)
{ dooropentime=millis(); State=DLeaving; }
if(analogRead(soapsensor)<soapmin) {State=DSoap;}
if(digitalRead(flushbutton)==HIGH) {State=Flush;}
if(digitalRead(toiletswitch)==HIGH) {State=Toilet;}
break;
case Clean:
if(lockbuttonpress==0 && digitalRead(lockbutton)==HIGH)
{ lockbuttonpress=1;
if(LockState==Manual) {LockState=Auto;}
if(LockState==Auto && digitalRead(doorswitch)==LOW) {State=Locked;} }
if(digitalRead(lockbutton)==LOW) {lockbuttonpress=0;}
if(digitalRead(doorswitch)==HIGH)
{ dooropentime=millis(); State=CLeaving; }
if(digitalRead(flushbutton)==HIGH) {State=Flush;}
if(digitalRead(toiletswitch)==HIGH) {State=Toilet;}
break;
case CLeaving:
if(digitalRead(doorswitch)==LOW || millis()-dooropentime>1000)
{ dooropentime=0; State=Blank;}
break;
case TUnlock:
if(lockbuttonpress==0 && digitalRead(lockbutton)==HIGH && digitalRead(doorswitch)==LOW)
{ lockbuttonpress=1; State=Toilet; LockState=Manual; }
if(digitalRead(lockbutton)==LOW) {lockbuttonpress=0;}
if(digitalRead(doorswitch)==HIGH) {dooropentime=millis(); State=TLeaving;}
if(analogRead(soapsensor)<soapmin) {State=TSoap;}
if(analogRead(sinksensor)<sinkmin) {State=TWater;}
if(digitalRead(flushbutton)==HIGH) {State=Flush;}
if(digitalRead(toiletswitch)==HIGH) {State=Toilet;}
break;
case TLeaving:
if(digitalRead(doorswitch)==LOW && millis()-dooropentime<1000)
{ dooropentime=0; State=Toilet;}
if(millis()-dooropentime>1000) {dooropentime=0; State=Blank;}
if(analogRead(soapsensor)<soapmin) {State=TSoap;}
if(analogRead(sinksensor)<sinkmin) {State=TWater;}
if(digitalRead(flushbutton)==HIGH) {State=Flush;}
if(digitalRead(toiletswitch)==HIGH) {State=Toilet;}
break;
case TSoap:
if(lockbuttonpress==0 && digitalRead(lockbutton)==HIGH)
{ lockbuttonpress=1; State=TUnlock;}
if(digitalRead(lockbutton)==LOW) {lockbuttonpress=0;}
if(digitalRead(doorswitch)==HIGH)
{ dooropentime=millis(); State=TLeaving; }
if(analogRead(sinksensor)<sinkmin) {State=TWater;}
if(digitalRead(flushbutton)==HIGH) {State=Flush;}
if(digitalRead(toiletswitch)==HIGH) {State=Toilet;}
break;
case TWater:
if(lockbuttonpress==0 && digitalRead(lockbutton)==HIGH)
{ lockbuttonpress=1; State=TUnlock;}
if(digitalRead(lockbutton)==LOW) {lockbuttonpress=0;}
if(digitalRead(doorswitch)==HIGH)
{ dooropentime=millis(); State=TLeaving; }
if(analogRead(soapsensor)<soapmin) {State=TSoap;}
if(digitalRead(flushbutton)==HIGH) {State=Flush;}
if(digitalRead(toiletswitch)==HIGH) {State=Toilet;}
break;
}
// SET CONDITIONS //
switch(State)
{
case Blank:
dooropentime = 0; sinkontime = 0;
Reward = Off; Buzzer = Off;
Sign = Bl_Bl;
Soap = White; Sink = Off;
Lock = Off; LockState = Auto;
if(analogRead(sinksensor)<sinkmin) {Sink = On;}
break;
case Locked:
Reward = Off; Buzzer = Off;
Sign = Bl_Bl;
Soap = White; Sink = Off;
Lock = On; LockState = Manual;
if(analogRead(sinksensor)<sinkmin) {Sink = On;}
break;
case Wash:
Reward = Off; Buzzer = Off;
Sign = Bl_Bl;
Soap = White; Sink = Off;
Lock = Off; LockState = Auto;
if(analogRead(soapsensor)<soapmin) {Soap = Green;}
if(analogRead(sinksensor)<sinkmin) {Sink = On;}
break;
case LockWash:
Reward = Off; Buzzer = Off;
Sign = Bl_Bl;
Soap = White; Sink = Off;
Lock = On; LockState = Manual;
if(analogRead(soapsensor)<soapmin) {Soap = Green;}
if(analogRead(sinksensor)<sinkmin) {Sink = On;}
break;
case Flush: // do not set LockState
Reward = Off; Buzzer = Off;
Sign = Wa_Bl;
Soap = Red; Sink = Off;
Lock = On;
break;
case Toilet: // do not set LockState
Reward = Off; Buzzer = Off;
Sign = Fl_Bl;
Soap = White; Sink = Off;
Lock = On;
break;
case DUnlock:
Reward = Off; Buzzer = Off;
Sign = Wa_Di;
Soap = Red; Sink = Off;
Lock = Off; LockState = Auto;
break;
case DLeaving:
Reward = Off; Buzzer = On;
Sign = Wa_Di;
Soap = Red; Sink = Off;
Lock = Off; LockState = Auto;
break;
case DSoap: // do not set LockState
Reward = Off; Buzzer = Off;
Sign = Wa_Bl;
Soap = Red; Sink = Off;
Lock = On;
if(analogRead(sinksensor)<sinkmin) {Sink = On;}
if(analogRead(soapsensor)<soapmin) {Soap = Green;}
break;
case DRinse: // do not set LockState
Reward = Off; Buzzer = Off;
Sign = Ri_Bl;
Soap = Red; Sink = Off;
Lock = On;
if(analogRead(sinksensor)<sinkmin) {Sink = On;}
break;
case Clean:
sinkontime = 0;
Reward = Off; Buzzer = Off;
Sign = Th_Cl;
Soap = White; Sink = Off;
if(analogRead(soapsensor)<soapmin) {Soap = Green;}
if(analogRead(sinksensor)<sinkmin) {Sink = On;}
if(LockState==Auto) {Lock = Off;}
else {Lock = On;}
break;
case CLeaving:
Reward = On; Buzzer = Off;
Sign = Th_Cl;
Soap = White; Sink = Off;
Lock = Off; LockState = Auto;
break;
case TUnlock:
Reward = Off; Buzzer = Off;
Sign = Fl_Fl;
Soap = White; Sink = Off;
Lock = Off; LockState = Auto;
break;
case TLeaving:
Reward = Off; Buzzer = On;
Sign = Fl_Fl;
Soap = White; Sink = Off;
Lock = Off; LockState = Auto;
break;
case TSoap: // do not set LockState
Reward = Off; Buzzer = Off;
Sign = Fl_Bl;
Soap = White; Sink = Off;
Lock = On;
if(analogRead(soapsensor)<soapmin) {Soap = Green;}
break;
case TWater: // do not set LockState
Reward = Off; Buzzer = Off;
Sign = Fl_Bl;
Soap = White; Sink = Off;
Lock = On;
if(analogRead(sinksensor)<sinkmin) {Sink = On;}
break;
}
// EXECUTE CONDITIONS //
if(Reward==On) {digitalWrite(bell, HIGH);}
else {digitalWrite(bell, LOW);}
if(Buzzer==On) {digitalWrite(speaker, HIGH);}
else {digitalWrite(speaker, LOW);}
switch(Sign)
{
case Bl_Bl: sign.write(Bl_Bl); break;
case Wa_Bl: sign.write(Wa_Bl); break;
case Wa_Di: sign.write(Wa_Di); break;
case Ri_Bl: sign.write(Ri_Bl); break;
case Fl_Bl: sign.write(Fl_Bl); break;
case Fl_Fl: sign.write(Fl_Fl); break;
case Th_Cl: sign.write(Th_Cl); break;
}
Servo::refresh();
switch(Soap)
{
case White:
digitalWrite(soapLED_R, LOW);
digitalWrite(soapLED_G, HIGH);
digitalWrite(soapLED_B, HIGH);
break;
case Red:
digitalWrite(soapLED_R, HIGH);
digitalWrite(soapLED_G, LOW);
digitalWrite(soapLED_B, LOW);
break;
case Green:
digitalWrite(soapLED_R, LOW);
digitalWrite(soapLED_B, HIGH); // B & G pins reversed?
digitalWrite(soapLED_G, LOW);
break;
}
if(Sink==On) {digitalWrite(sinkLED, HIGH);}
else {digitalWrite(sinkLED, LOW);}
if(Lock==On && digitalRead(doorswitch)==LOW) {digitalWrite(locksolenoid, HIGH);}
else {digitalWrite(locksolenoid, LOW);}
}