// automated hand car-wash
#undef MY_HW
#ifdef MY_HW
const byte PinWater = 12;
const byte PinFoam = 11;
const byte PinInp [] = { A1, A2, A3 };
enum { Water, Foam ,Coin_5, Coin_1, Coin_10}; // identifies pins
enum { OutOff = HIGH, OutOn = LOW };
enum { ButOff = HIGH, ButOn = LOW }; // depends on wiring
// ---------------------------------------------------------
#else
# include <LiquidCrystal_I2C.h>
# define I2C_ADDR 0x27
# define LCD_COLUMNS 16
# define LCD_LINES 2
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);
const byte PinWater = 9;
const byte PinFoam = 10;
const byte PinInp [] = { 2, 3, 4, 5, 6 };
enum { Coin_1, Coin_5, Coin_10, Water, Foam }; // identifies pins
enum { OutOff = LOW, OutOn = HIGH }; // depends on wiring
enum { ButOff = LOW, ButOn = HIGH }; // depends on wiring
#endif
// -----------------------------------------------------------------------------
char s [90];
// -----------------------------------------------------------------------------
// common output routine, can also be used to output to LCD
void
disp (
const char *s )
{
Serial.println (s);
#ifndef MY_HW
lcd.clear ();
lcd.print (s);
#endif
}
// -----------------------------------------------------------------------------
// monitor all inputs, return index of input when pressed
// or return NoInpt
const byte Ninp = sizeof(PinInp);
const int NoInp = -1;
byte inpState [Ninp];
int
chkInp ()
{
for (int n = 0; n < Ninp; n++) {
byte inp = digitalRead (PinInp [n]);
#if 0
sprintf (s, "inp %d %d", n, inp);
disp (s);
delay (1000);
#endif
if (inpState [n] != inp) { // state change
inpState [n] = inp;
delay (10); // debounce
if (ButOn == inp) // pressed
return n;
}
}
return NoInp;
}
// -----------------------------------------------------------------------------
// add to the balance depending on coin and return balance
const int MinBalance = 20;
int balance;
int
updateBalance (
int coin )
{
switch (coin) {
case Coin_1:
balance += 1;
break;
case Coin_5:
balance += 5;
break;
case Coin_10:
balance += 10;
break;
default: // ignore non-coin input
return balance;
break;
}
sprintf (s, " balance %3d", balance);
disp (s);
return balance;
}
// -----------------------------------------------------------------------------
// state and event (inputs and tmr) state machine
enum { Off, Idle, WaterOn, FoamOn };
int state = Off;
// timers that track system on and water/foam usage times
struct Tmr {
const unsigned long MsecPeriodMax; // max period
const char *label; // text label
bool active;
unsigned long msec;
unsigned long msecPeriod; // remaining water/foam period
}
tmr [] = {
{ 5000, "water" },
{ 2000, "foam" },
{ 30000, "system" },
};
const int Ntmr = sizeof(tmr)/sizeof(Tmr);
enum { T_Water, T_Foam, T_System }; // indices of timers
// -------------------------------------
void
loop (void)
{
// -------------------------------------
// handle timers
unsigned long msec = millis ();
for (int n = 0; n < Ntmr; n++) {
if (tmr [n].active) {
if (msec - tmr [n].msec >= tmr [n].msecPeriod) {
tmr [n].active = false; // expired
tmr [n].msecPeriod = 0; // indicates time-out
}
}
}
// -------------------------------------
// handle inputs and state
int inp = chkInp ();
// state machine containing checks for events
switch (state) {
case Off:
digitalWrite (PinWater, OutOff);
digitalWrite (PinFoam, OutOff);
if (MinBalance <= updateBalance (inp)) {
balance -= MinBalance;
// reset timers & enable system timer
for (int n = 0; n < Ntmr; n++)
tmr [n].msecPeriod = tmr [n].MsecPeriodMax;
tmr [T_System].active = true;
tmr [T_System].msec = msec;
state = Idle;
disp ("System On");
}
break;
// system on, water/foam off
case Idle:
if (0 == tmr [T_System].msecPeriod) {
state = Off;
disp ("System time-out");
}
else if (Water == inp) {
if (0 < tmr [T_Water].msecPeriod) {
digitalWrite (PinWater, OutOn);
tmr [T_Water].active = true;
tmr [T_Water].msec = msec;
state = WaterOn;
sprintf (s, "Water On - time remaining %lu",
tmr [T_Water].msecPeriod);
disp (s);
}
else
disp ("Water timed-out");
}
else if (Foam == inp && 0 < tmr [T_Foam].msecPeriod) {
if (0 < tmr [T_Foam].msecPeriod) {
digitalWrite (PinFoam, OutOn);
tmr [T_Foam].active = true;
tmr [T_Foam].msec = msec;
state = FoamOn;
sprintf (s, "Foam On - time remaining %lu",
tmr [T_Foam].msecPeriod);
disp (s);
}
else
disp ("Foam timed-out");
}
break;
// system on, foam on
case FoamOn:
if (0 == tmr [T_System].msecPeriod) {
state = Idle;
disp ("System time-out");
}
else if (0 == tmr [T_Foam].msecPeriod) {
digitalWrite (PinFoam, OutOff);
state = Idle;
disp ("Foam time-out");
}
else if (Foam == inp) {
digitalWrite (PinFoam, OutOff);
tmr [T_Foam].active = false;
tmr [T_Foam].msecPeriod -= msec - tmr [T_Foam].msec;
state = Idle;
sprintf (s, "Foam Off - time remaining %lu",
tmr [T_Foam].msecPeriod);
disp (s);
}
break;
// system on, water on
case WaterOn:
if (0 == tmr [T_System].msecPeriod) {
state = Idle;
disp ("System time-out");
}
else if (0 == tmr [T_Water].msecPeriod) {
digitalWrite (PinWater, OutOff);
state = Idle;
disp ("Water time-out");
}
else if (Water == inp) {
digitalWrite (PinWater, OutOff);
tmr [T_Water].active = false;
tmr [T_Water].msecPeriod -= msec - tmr [T_Water].msec;
state = Idle;
sprintf (s, "Water Off - time remaining %lu",
tmr [T_Water].msecPeriod);
disp (s);
}
break;
}
}
// -----------------------------------------------------------------------------
void
setup (void)
{
Serial.begin (9600);
for (int n = 0; n < Ninp; n++) {
#ifdef MyHW
pinMode (PinInp [n], INPUT_PULLUP);
#else
pinMode (PinInp [n], INPUT);
#endif
inpState [n] = digitalRead (PinInp [n]);
}
digitalWrite (PinWater, OutOff);
digitalWrite (PinFoam, OutOff);
pinMode (PinWater, OUTPUT);
pinMode (PinFoam, OUTPUT);
#ifndef MY_HW
lcd.init ();
#endif
disp ("Ready");
}