/* vim: sw=2:ts=2:ai */ #define YELLOW 0 #define BUTTON 1 #define M 2 #define I 3 #define U 4 #define TICKS_PER_SEC 40 #define TICK_MS (1000 / TICKS_PER_SEC) #define MILLIS(millis) ((int)((millis)/TICK_MS)) #define INACTIVE_SEC 60 #define VMAX 80 byte down; int inactive; int active; enum State { S_START, S_RECITE, S_GAP, S_RABBIT_RIGHT, S_CLICKED, S_BAD, S_MU }; byte state; int arg; // Some states use arg. int tick; // How long in state and arg. byte clicks; // Count number of button presss. byte vlen; // Length of v string. byte v[VMAX]; // Vector holding string. void setup() { // initialize the digital pin as an output. pinMode(YELLOW, OUTPUT); pinMode(BUTTON, INPUT_PULLUP); pinMode(M, OUTPUT); pinMode(I, OUTPUT); pinMode(U, OUTPUT); } void show(byte m, byte i, byte u) { digitalWrite(M, m ? HIGH : LOW); digitalWrite(I, i ? HIGH : LOW); digitalWrite(U, u ? HIGH : LOW); } #define showM() show(1,0,0) #define showI() show(0,1,0) #define showU() show(0,0,1) #define show0() show(0,0,0) void showButtonCount() { if (clicks > 5) clicks = 5; // Clamp at 5 clicks. if ((active+tick)&1) { show(0,0,0); // Make button feedback flashy. } else { show(clicks&4, clicks&2, clicks&1); } } void on() { digitalWrite(YELLOW, HIGH); } void off() { digitalWrite(YELLOW, LOW); } void bad() { tick=0,arg=MILLIS(1200),state=S_BAD; } void copy(int n, int from, int to) { for (int i = 0; i < n; i++) { v[to+i] = v[from+i]; } } void production1() { if (vlen >= VMAX) { bad(); } else if (vlen < 1) { bad(); } else if (v[vlen-1] != I) { bad(); } else { v[vlen]=U; vlen++; } } void production2() { int p; for (p=0; p VMAX) { bad(); } else { copy(c, p+1, vlen); vlen += c; } } } void production3() { int p; for (p=0; p MILLIS(2000)) { // Reset after 2 sec reset(); } } else switch (state) { case S_START: reset(); break; case S_CLICKED: #define TICKS_CLICKED MILLIS(800) if (tick < TICKS_CLICKED) { // show(clicks&4, clicks&2, clicks&1); showButtonCount(); } else { switch(clicks) { case 1: production1(); break; case 2: production2(); break; case 3: production3(); break; case 4: production4(); break; case 5: production5(); break; default: bad(); } clicks=0; if (state != S_BAD) { tick=0, arg=MILLIS(800), state=S_GAP; } } break; case S_RECITE: // arg must init to 0 #define TICKS_ON MILLIS(500) #define TICKS_OFF MILLIS(200) if (tick < TICKS_ON) { switch (v[arg]) { case M: showM(); break; case I: showI(); break; case U: showU(); break; default: show(1,1,1); // Should not happen. } } else if (tick < TICKS_ON+TICKS_OFF) { show0(); } else { tick = 0; ++arg; if (arg >= vlen) tick=0,arg=MILLIS(1500),state=S_GAP; } break; case S_GAP: // arg is how many ticks. if (tick < arg) { show0(); } else { tick=0,arg=0,state=S_RECITE; } break; case S_RABBIT_RIGHT: // arg is how many rabbits. #define TICKS_RABBIT MILLIS(100) if (tick < 1*TICKS_RABBIT) { showM(); } else if (tick < 2*TICKS_RABBIT) { showI(); } else if (tick < 3*TICKS_RABBIT) { showU(); } else if (tick < 6*TICKS_RABBIT) { show0(); } else { tick = 0; --arg; if (arg < 1) { tick=0,arg=MILLIS(1000),state=S_GAP; } } break; case S_BAD: // arg is how long. on(); #define TICKS_BEEP MILLIS(100) if (tick < arg) { if ((tick / TICKS_BEEP) & 1) { show(1,1,1); } else { show(0,0,0); } } else { tick=0,arg=MILLIS(1000),state=S_GAP; } break; case S_MU: // arg is how long. if (tick < MILLIS(700)) { if ((tick/MILLIS(50))&1) show0(); else showM(); } else if (tick < MILLIS(1000)) { show0(); } else if (tick < MILLIS(1000+700)) { if ((tick/MILLIS(50))&1) show0(); else showU(); } else { tick=0,arg=MILLIS(1200),state=S_GAP; } break; } delay(TICK_MS); ++tick; }