/* 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