/*
Washer: An emulation of a washing machine ladder-logic program
Copyright (C) 2008 Richard Heurtley
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
/**---------------------------------------------------------------------------*/
// Developed with the Open Watcom C compiler
// http://www.openwatcom.org
#include
#include
#include
#include
#include
/**---------------------------------------------------------------------------*/
/*
This program simulates a ladder-logic program for the
automation of a simple washing machine. The five input
buttons tell the PLC to operate for one, two, three, or four
minutes; or to stop immediately. The five outputs indicate
the number of minutes remaining.
Each minute of washing consists of two cycles of:
14 seconds of forward rotation
1 second pause
14 second of reverse rotation
1 second pause
The C program runs at 10x real time.
Comments beginning with slash-slash-dot are the ladder logic program.
Comments beginning with slash-slash-pound are scan-and-replace terms.
If the ladder logic program is extracted and processed with
the scan-and-replace terms then it can be imported into a
PLC compiler like DirectSoft 5.
The target PLC is an Automation Direct D0-05DD-D.
*/
/**---------------------------------------------------------------------------*/
// Timer structure
typedef struct
{
int e; // enable
int o; // output
int *timep;
int timemax;
} TIMER;
// The complete state of the machine.
typedef struct
{
int b0, b1, b2, b3, b4; // button inputs
int l0, l1, l2, l3, l4; // LED outputs
int gx, g1, g2, g3, g4; // goto state
int mp, m0, m1, m2, m3, m4; // state
int e1, e2; // end of cycle
int c1, c2; // cycle
int fx, fp, rx, rp; // forward/reverse
TIMER tp;
TIMER tf; // forward pause timer
TIMER tr; // reverse pause timer
TIMER t4;
int fo, ro; // forward/reverse outputs
} STATE;
// Timer counters. These are not included in the timer structure
// because they change with every tick and the Delta() function
// compares two state structures with memcmp().
static int tp;
static int tf;
static int tr;
static int t4;
static int tenths;
static STATE s; // current state
/**---------------------------------------------------------------------------*/
// Enable or disable a timer.
void TimerEnable(TIMER *tp, int e)
{
if (tp->e && !e) // if enable is going from on to off
{
tp->o = 0; // reset output
*tp->timep = 0; // set counter to zero
}
tp->e = e; // save value
return;
}
/**---------------------------------------------------------------------------*/
// Note elapsed time.
void TimerTick(TIMER *tp)
{
if (tp->e) // if timer is enabled
{
(*tp->timep)++; // increment time
if (*tp->timep >= tp->timemax) // if time >= max
tp->o = 1; // set output
}
return;
}
/**---------------------------------------------------------------------------*/
// If something changed then print the current state in a concise form.
int Delta(STATE *sp)
{
int rv = 0;
static STATE prev;
if (memcmp(sp, &prev, sizeof(STATE))) // if something changed
{
printf
(
// "tick b01234 g1234 mp01234 c12 fprp tpeo tfeo treo t4eo e12 ofr l01234"
// "XXXXXXXX XXXXX XXXXX XXXXXX XX XXXX XX XX XX XX XX XX XXXXX"
"%8d"
" %d%d%d%d%d" // b01234
" %d%d%d%d%d" // g1234
" %d%d%d%d%d%d" // mp01234
" %d%d" // c12
" %d%d%d%d" // fprp
" %d%d" // tpe0
" %d%d" // tfeo
" %d%d" // treo
" %d%d" // t4eo
" %d%d" // e12
" %d%d" // ofr
" %d%d%d%d%d" // l012345
"\n",
tenths,
sp->b0, sp->b1, sp->b2, sp->b3, sp->b4,
sp->gx, sp->g1, sp->g2, sp->g3, sp->g4,
sp->mp, sp->m0, sp->m1, sp->m2, sp->m3, sp->m4,
sp->c1, sp->c2,
sp->fx, sp->fp, sp->rx, sp->rp,
sp->tp.e, sp->tp.o,
sp->tf.e, sp->tf.o,
sp->tr.e, sp->tr.o,
sp->t4.e, sp->t4.o,
sp->e1, sp->e2,
sp->fo, sp->ro,
sp->l0, sp->l1, sp->l2, sp->l3, sp->l4
);
fflush(stdout);
}
memcpy(&prev, sp, sizeof(STATE));
return(rv);
}
/**---------------------------------------------------------------------------*/
int main(void)
{
int rv = 0;
int c;
int e;
s.tp.timep = &tp; // initialize timer structures
s.tf.timep = &tf;
s.tr.timep = &tr;
s.t4.timep = &t4;
s.tp.timemax = 10; // tenths of a second
s.tf.timemax = 10;
s.tr.timemax = 10;
s.t4.timemax = 140;
printf("tick b01234 g1234 mp01234 c12 fprp tpeo tfeo treo t4eo e12 ofr l01234\n");
for (tenths = 0; ; tenths++)
{
// delay(100); // 0.1s
delay(10); // 0.01s fast emulation
TimerTick(&s.tp);
TimerTick(&s.tf);
TimerTick(&s.tr);
TimerTick(&s.t4);
s.b0 = 0;
s.b1 = 0;
s.b2 = 0;
s.b3 = 0;
s.b4 = 0;
if (kbhit())
{
c = getch();
switch (c)
{
case '0': s.b0 = 1; break;
case '1': s.b1 = 1; break;
case '2': s.b2 = 1; break;
case '3': s.b3 = 1; break;
case '4': s.b4 = 1; break;
case ' ': goto EGRESS;
}
}
/**---------------------------------------------------------------------------*/
// Search and replace strings
// to convert meaningful symbols
// into cryptic ladder logic symbols.
//# washer.txt
//#
//# /b0/X0/
//# /b1/X1/
//# /b2/X2/
//# /b3/X3/
//# /b4/X4/
//# /g1/C0/
//# /g2/C1/
//# /g3/C2/
//# /g4/C3/
//# /gx/C4/
//# /mp/C5/
//# /m0/C6/
//# /m1/C7/
//# /m2/C10/
//# /m3/C11/
//# /m4/C12/
//# /e1/C13/
//# /e2/C14/
//# /c1/C15/
//# /c2/C16/
//# /fx/C17/
//# /fp/C20/
//# /rx/C21/
//# /rp/C22/
//# /tp/T0/
//# /tf/T1/
//# /tr/T2/
//# /t4/T3/
//# /l1/Y0/
//# /l2/Y1/
//# /l3/Y2/
//# /l4/Y3/
//# /fo/Y4/
//# /ro/Y5/
//# /PY5/Pro/
//# /Y4r/for/
//# /T2a/tra/
//# /T0u/tpu/
/**---------------------------------------------------------------------------*/
// Beginning of ladder logic program
//. PLC 05
/**---------------------------------------------------------------------------*/
// Buttons
s.g1 = (!s.b0 && s.b1 && !s.b2 && !s.b3 && !s.b4);
s.g2 = (!s.b0 && !s.b1 && s.b2 && !s.b3 && !s.b4);
s.g3 = (!s.b0 && !s.b1 && !s.b2 && s.b3 && !s.b4);
s.g4 = (!s.b0 && !s.b1 && !s.b2 && !s.b3 && s.b4);
//. #BEGIN COMMENT
//. "Process the minute button inputs."
//. "Change minute state only if a single minute button is pressed."
//. #END
//. STRN b0
//. AND b1
//. ANDN b2
//. ANDN b3
//. ANDN b4
//. OUT g1
//. STRN b0
//. ANDN b1
//. AND b2
//. ANDN b3
//. ANDN b4
//. OUT g2
//. STRN b0
//. ANDN b1
//. ANDN b2
//. AND b3
//. ANDN b4
//. OUT g3
//. STRN b0
//. ANDN b1
//. ANDN b2
//. ANDN b3
//. AND b4
//. OUT g4
s.gx = s.g1 || s.g2 || s.g3 || s.g4;
Delta(&s);
//. #BEGIN COMMENT
//. "Calculate if a minute state change is requested."
//. #END
//. STR g1
//. OR g2
//. OR g3
//. OR g4
//. OUT gx
/**---------------------------------------------------------------------------*/
// Stop pause
s.mp = // stop pause
(
s.b0 // enter: button 0
||
(s.mp && !s.tp.o) // maintain: 1s stop pause timer
);
Delta(&s);
//. #BEGIN COMMENT
//. "Calculate the stop pause state."
//. "Enter stop pause if button 0 is pressed."
//. "Stay in stop pause for the duration of the 1s stop pause timer."
//. #END
//. STR mp
//. ANDN tp
//. OR b0
//. OUT mp
e = s.mp; // 1s stop pause timer enable
TimerEnable(&s.tp, e);
Delta(&s);
//. #BEGIN COMMENT
//. "Enable the 1s stop pause timer during the stop pause state."
//. #END
//. STR mp
//. TMR tp K10
/**---------------------------------------------------------------------------*/
// Cycles
s.e1 = s.c1 && s.tr.o; // end of cycle 1
s.e2 = s.c2 && s.tr.o; // end of cycle 2
Delta(&s);
//. #BEGIN COMMENT
//. "Calculate end-of-cycle flags."
//. "A cycle ends when the reverse pause timer elapses."
//. #END
//. STR c1
//. AND tr
//. OUT e1
//. STR c2
//. AND tr
//. OUT e2
s.c2 = // cycle 2
(
!s.mp // not stop pause
&& // and
(
(s.c1 && s.e1) // enter: end of cycle 1
||
(s.c2 && !s.e2) // maintain: not end of cycle 2
)
);
Delta(&s);
//. #BEGIN COMMENT
//. "Calculate cycle 2 state."
//. "Enter cycle 2 at the end of cycle 1."
//. "Stay in cycle 2 until the reverse pause timer elapses."
//. "Exit cycle 2 if stop pause."
//. #END
//. STR c2
//. ANDN e2
//. STR c1
//. AND e1
//. ORSTR
//. ANDN mp
//. OUT c2
s.c1 = !s.mp && // cycle 1
(
(s.m0 && s.gx) // enter: idle and any button
||
(s.c1 && !s.e1) // maintain: not end of cycle 1
||
(!s.m1 && s.e2) // restart: more to do and end of cycle 2
);
Delta(&s);
//. #BEGIN COMMENT
//. "Calculate cycle 1 state."
//. "Enter cycle 1 if idle and a minute button is pressed."
//. "Stay in cycle 1 until the reverse pause timer elapses."
//. "Re-enter cycle 1 if cycle 2 ends and there's more to do."
//. "Exit cycle 1 if stop pause."
//. #END
//. STRN m1
//. AND e2
//. STR c1
//. ANDN e1
//. ORSTR
//. STR m0
//. AND gx
//. ORSTR
//. ANDN mp
//. OUT c1
/**---------------------------------------------------------------------------*/
// Phases
s.rp = !s.mp && // reverse pause
(
(s.rx && s.t4.o) // enter: reverse timeout
||
(s.rp && !s.tr.o) // maintain: 1s pause timer
);
Delta(&s);
//. #BEGIN COMMENT
//. "Calculate reverse pause state."
//. "Enter reverse pause at the end of reverse."
//. "Stay in reverse pause until the 1s reverse pause timer elapses."
//. "Exit reverse pause if stop pause."
//. #END
//. STR rp
//. ANDN tr
//. STR rx
//. AND t4
//. ORSTR
//. ANDN mp
//. OUT rp
s.rx = !s.mp && // reverse
(
(s.fp && s.tf.o) // enter: forward pause timeout
||
(s.rx && !s.t4.o) // maintain: 14s timer
);
Delta(&s);
//. #BEGIN COMMENT
//. "Calculate reverse state."
//. "Enter reverse at the forward pause timeout."
//. "Stay in reverse until the 14s reverse timer elapses."
//. "Exit reverse if stop pause."
//. #END
//. STR rx
//. ANDN t4
//. STR fp
//. AND tf
//. ORSTR
//. ANDN mp
//. OUT rx
s.fp = !s.mp && // forward pause
(
(s.fx && s.t4.o) // enter: forward timeout
||
(s.fp && !s.tf.o) // maintain: 1s pause timer
);
Delta(&s);
//. #BEGIN COMMENT
//. "Calculate forward pause state."
//. "Enter forward pause at the end of forward."
//. "Stay in forward pause until the 1s forward pause timer elapses."
//. "Exit forward pause if stop pause."
//. #END
//. STR fp
//. ANDN tf
//. STR fx
//. AND t4
//. ORSTR
//. ANDN mp
//. OUT fp
s.fx = !s.mp && // forward
(
(s.m0 && s.gx) // enter: idle and any button
||
(s.fx && !s.t4.o) // maintain: 14s timer
||
s.e1 // restart: end of cycle 1
||
(!s.m1 && s.e2) // restart: more to do and end of cycle 2
);
Delta(&s);
//. #BEGIN COMMENT
//. "Calculate forward state."
//. "Enter forward if idle and a minute button is pressed."
//. "Stay in forward until the 14s forward timer elapses."
//. "Re-enter forward at the end of cycle 1."
//. "Re-enter forward at the end of cycle 2 if there's more to do."
//. "Exit forward if stop pause."
//. #END
//. STRN m1
//. AND e2
//. OR e1
//. STR fx
//. ANDN t4
//. ORSTR
//. STR m0
//. AND gx
//. ORSTR
//. ANDN mp
//. OUT fx
/**---------------------------------------------------------------------------*/
// Minutes
s.m1 = // minute 1 state
(
!s.mp // not in stop pause
&& // and
(!s.gx || s.g1) // not transferring or transferring to self
&& // and
(
s.g1 // enter: go to this state
||
(s.m1 && !s.e2) // maintain: not end of cycle 2
||
(s.m2 && s.e2) // enter: from end of m2
)
);
Delta(&s);
//. #BEGIN COMMENT
//. "Calculate 1 minute state."
//. "Enter 1 minute if button 1 is pressed."
//. "Enter 1 minute if the 2 minute state ends."
//. "Stay in 1 minute until the end of cycle 2."
//. "Exit 1 minute if stop pause or transferring to another minute state."
//. #END
//. STR m2
//. AND e2
//. STR m1
//. ANDN e2
//. ORSTR
//. OR g1
//. STRN gx
//. OR g1
//. ANDSTR
//. ANDN mp
//. OUT m1
s.m2 = // minute 2 state
(
!s.mp // not in stop pause
&& // and
(!s.gx || s.g2) // not transferring or transferring to self
&& // and
(
s.g2 // enter: go to this state
||
(s.m2 && !s.e2) // maintain: not end of cycle 2
||
(s.m3 && s.e2) // enter: from end of m3
)
);
Delta(&s);
//. #BEGIN COMMENT
//. "Calculate 2 minute state."
//. "Enter 2 minute if button 2 is pressed."
//. "Enter 2 minute if the 3 minute state ends."
//. "Stay in 2 minute until the end of cycle 2."
//. "Exit 2 minute if stop pause or transferring to another minute state."
//. #END
//. STR m3
//. AND e2
//. STR m2
//. ANDN e2
//. ORSTR
//. OR g2
//. STRN gx
//. OR g2
//. ANDSTR
//. ANDN mp
//. OUT m2
s.m3 = // minute 3 state
(
!s.mp // not in stop pause
&& // and
(!s.gx || s.g3) // not transferring or transferring to self
&& // and
(
s.g3 // enter: go to this state
||
(s.m3 && !s.e2) // maintain: not end of cycle 2
||
(s.m4 && s.e2) // enter: from end of m4
)
);
Delta(&s);
//. #BEGIN COMMENT
//. "Calculate 3 minute state."
//. "Enter 3 minute if button 3 is pressed."
//. "Enter 3 minute if the 4 minute state ends."
//. "Stay in 3 minute until the end of cycle 2."
//. "Exit 3 minute if stop pause or transferring to another minute state."
//. #END
//. STR m4
//. AND e2
//. STR m3
//. ANDN e2
//. ORSTR
//. OR g3
//. STRN gx
//. OR g3
//. ANDSTR
//. ANDN mp
//. OUT m3
s.m4 = // minute 4 state
(
!s.mp // not in stop pause
&& // and
(!s.gx || s.g4) // not transferring or transferring to self
&& // and
(
s.g4 // enter: go to this state
||
(s.m4 && !s.e2) // maintain: not end of cycle 2
)
);
Delta(&s);
//. #BEGIN COMMENT
//. "Calculate 4 minute state."
//. "Enter 4 minute if button 4 is pressed."
//. "Stay in 4 minute until the end of cycle 2."
//. "Exit 4 minute if stop pause or transferring to another minute state."
//. #END
//. STR m4
//. ANDN e2
//. OR g4
//. STRN gx
//. OR g4
//. ANDSTR
//. ANDN mp
//. OUT m4
s.m0 = (!s.mp && !s.m1 && !s.m2 && !s.m3 && !s.m4); // idle state
Delta(&s);
//. #BEGIN COMMENT
//. "Calculate idle state."
//. "Enter idle if not in any other minute state."
//. #END
//. STRN mp
//. ANDN m1
//. ANDN m2
//. ANDN m3
//. ANDN m4
//. OUT m0
/**---------------------------------------------------------------------------*/
// Timers
e = s.fp; // 1s pause timer enable
TimerEnable(&s.tf, e);
Delta(&s);
//. #BEGIN COMMENT
//. "Enable the 1s forward pause timer when in the forward pause state."
//. #END
//. STR fp
//. TMR tf K10
e = s.rp; // 1s pause timer enable
TimerEnable(&s.tr, e);
Delta(&s);
//. #BEGIN COMMENT
//. "Enable the 1s reverse pause timer when in the reverse pause state."
//. #END
//. STR rp
//. TMR tr K10
e = // 14s motor timer enable
(
s.fx // forward
||
s.rx // reverse
);
TimerEnable(&s.t4, e);
Delta(&s);
//. #BEGIN COMMENT
//. "Enable the 14s motor timer when in the forward or reverse states."
//. #END
//. STR fx
//. OR rx
//. TMR t4 K140
/**---------------------------------------------------------------------------*/
// Outputs
s.l4 = s.m4;
s.l3 = s.m4 || s.m3;
s.l2 = s.m4 || s.m3 || s.m2;
s.l1 = s.m4 || s.m3 || s.m2 || s.m1;
s.l0 = s.m4 || s.m3 || s.m2 || s.m1 || s.m0; // no PLC output
//. #BEGIN COMMENT
//. "LED outputs are cummulative."
//. #END
//. STR m4
//. OUT l4
//. STR m4
//. OR m3
//. OUT l3
//. STR m4
//. OR m3
//. OR m2
//. OUT l2
//. STR m4
//. OR m3
//. OR m2
//. OR m1
//. OUT l1
s.fo = s.fx;
s.ro = s.rx;
//. #BEGIN COMMENT
//. "Drive the motor forward in the forward state."
//. #END
//. STR fx
//. OUT fo
//. #BEGIN COMMENT
//. "Drive the motor reverse in the reverse state."
//. #END
//. STR rx
//. OUT ro
Delta(&s);
} // for (tenths = 0; ; tenths++)
EGRESS:
return(rv);
//. END
}
/**---------------------------------------------------------------------------*/
//. #BEGIN ELEMENT_DOC
//. "b0","Button 0","","Stop"
//. "b1","Button 1","","1 minute"
//. "b2","Button 2","","2 minutes"
//. "b3","Button 3","","3 minutes"
//. "b4","Button 4","","4 minutes"
//. "g1","Goto 1","","1 minute"
//. "g2","Goto 2","","2 minutes"
//. "g3","Goto 3","","3 minutes"
//. "g4","Goto 4","","4 minutes"
//. "gx","Goto","","A button is pressed"
//. "mp","Stop pause","","1s pause before idle"
//. "m0","Idle","","Motor off"
//. "m1","1 minute","",""
//. "m2","2 minutes","",""
//. "m3","3 minutes","",""
//. "m4","4 minutes","",""
//. "e1","End of cycle 1","",""
//. "e2","End of cycle 2","",""
//. "c1","Cycle 1","","1st 30 seconds"
//. "c2","Cycle 2","","2nd 30 seconds"
//. "fx","Forward","","Motor on 14s"
//. "fp","Forward pause","","Motor off 1s"
//. "rx","Reverse","","Motor on 14s"
//. "rp","Reverse pause","","Motor off 1s"
//. "tp","1s stop pause","","Motor off"
//. "tf","1s fwd pause","","Motor off"
//. "tr","1s rev pause","","Motor off"
//. "t4","14s motor timer","","Motor on"
//. "l1","LED 1","","1 minute"
//. "l2","LED 2","","2 minutes"
//. "l3","LED 3","","3 minutes"
//. "l4","LED 4","","4 minutes"
//. "fo","Motor forward","",""
//. "ro","Motor reverse","",""
//. #END