// start with iZYX_3q from _the_ CardKB deployed iZYX_3q
// add display.cpp from _the_ TFT/touch deployed zyx3TFTtouch
// and
//... needed # define setLED setLED_CKB in display.cpp
// otherwise identical
// handy test - but wokwi drops chars? Yeth.
// ... zyxwvutsrqponmlkjihgfedcba
const unsigned long ackTime = 120;
const unsigned long goodTime = 1000; // later, adjustable. solid green during performance a goal
const unsigned long badTime = 1333; // also. how long she has to enter the correct character
const unsigned long rewardTime = 2600; // look at all coexistant states.
const unsigned long restTime = 2777;
extern char getEvent();
extern void setupCardKB(); // forgot this
extern void scanLoop();
extern void setupDisplay();
extern void setLED(unsigned long);
// pasted from wokwi of same name and only changed these two lines
# define DEBUG 1 // set to 0 when the CardKB ATMEGA8A is the target
# define WOKWI 1 // the CardKB is hjella bright, so we need two color tables
# if DEBUG
# define dBegin(baud) Serial.begin(baud)
# define dPrint(x) Serial.print(x)
# define dPrintHex(x, y) Serial.print(x, y)
# define dPrintln(x) Serial.println(x)
# else
# define dBegin(baud) do {} while (0)
# define dPrint(x) do {} while (0)
# define dPrintln(x) do {} while (0)
# endif
// reverse alphabet trainer processes and, for now, output
extern void setLED(unsigned long);
// travel with displayFSM if we put display in a tab
# if WOKWI
# define BLACK 0x0
# define RED 0xff0000
# define GREEN 0x00ff00
# define BLUE 0x0000ff
# define YELLOW 0xffff00
# define WHITE 0xffffff
# else
# define BLACK 0x0
# define RED 0x100000
# define GREEN 0x001000
# define BLUE 0x000010
# define YELLOW 0x101000
# define WHITE 0x101010
# endif
unsigned long now; // always _the_ time. set by millis(), that's the only use of millis()
enum display {
DISP_OFF,
DISP_INVITE,
DISP_ACK,
DISP_GOOD,
DISP_BAD,
DISP_REWARD_SARCASTIC,
DISP_REWARD_SUCCESS,
DISP_REWARD_WOW,
DISP_SERVICE = 1000
};
extern bool displayFSM();
extern bool displayFSM(int);
void logicFSM();
void logicFSM(char);
// void setupDisplay();
void setup() {
# if WOKWI
Serial.begin(115200);
Serial.println("\nReverse Alphabet Trainer\n");
# else
dBegin(115200);
dPrintln("hi Mom!");
# endif
setupCardKB(); // call this Hardware
setupDisplay();
displayFSM(DISP_OFF);
}
// is it my getEvent thing?
void loopTestGetEvent() {
now = millis(); // only call to millis anywhere
logicFSM(); // crank 'em along
displayFSM();
scanLoop();
char theChar = getEvent();
if (theChar > 0) {
static int counter = 1;
Serial.println(counter); counter++;
Serial.println(" ");
Serial.println(theChar);
if (theChar == 'a') counter = 1;
}
}
// normal loop for target and simulation
void loop()
{
now = millis(); // only call to millis anywhere
logicFSM(); // crank 'em along
displayFSM();
scanLoop(); // once - ran scanning machine. now handles auto-off
char theChar = getEvent();
if (theChar > 0) {
if ('a' <= theChar && theChar <= 'z') {
dPrint(" key -> logic "); dPrintln(theChar);
logicFSM(theChar);
}
//... else displayFSM(DISP_ACK); // valid key, but currently meaningless here
}
}
enum logic {IDLE, AWAIT, ERROR, REST, FINI};
const char *logicTags[] = {"Idle", "Await", "Error", "Zzzz", "Fini"};
void logicFSM(char theChar)
{
static byte state = IDLE;
static bool newState = true;
static unsigned long timer;
static char expected;
static byte roundLength;
static byte rewardCommand;
# define TRANSITION(nextState) do { state = nextState; newState = true; } while (0)
if (theChar) {
dPrint("= ");
dPrintln(theChar);
}
static bool once; // gack! extra kludge
do { // switch on state until theChar is handled
// need to do this controlled printing every time the switch is entered/re-entered
{
static byte lastPrinted = -1;
if (lastPrinted != state) {
dPrint("logic: ");
dPrintln(logicTags[state]);
lastPrinted = state;
}
}
switch (state) {
case IDLE:
if (newState) {
roundLength = 0;
newState = false;
dPrint("once yes ");
once = true;
}
if (once && !displayFSM(DISP_SERVICE) && roundLength == 0) {
dPrintln("\n not busy and length is zero, so dFSM INVITE ");
displayFSM(DISP_INVITE);
dPrintln(" and done.");
once = false;
expected = 0;
}
if (theChar)
TRANSITION(AWAIT);
break; // would anyway, now AWAIT will
case AWAIT:
if (newState) {
newState = false;
}
if (theChar) {
if (!expected || theChar == expected) {
roundLength++;
expected = theChar - 1;
if (theChar == 'a') {
rewardCommand = DISP_REWARD_SARCASTIC;
if (roundLength > 1) rewardCommand = DISP_REWARD_SUCCESS;
if (roundLength == 26) rewardCommand = DISP_REWARD_WOW;
TRANSITION(FINI);
} else {
// expected--;
displayFSM(DISP_GOOD);
dPrintln(" !");
TRANSITION(AWAIT); // <- or maybe not. we are in AWAIT, so.
}
theChar = 0; // handlebarred either way
}
else
TRANSITION(ERROR);
}
break;
case ERROR:
if (newState) {
dPrint(" error kick ");
displayFSM(DISP_BAD);
timer = now;
newState = false;
theChar = 0; // handled. ERROR can be entered again with a new try
}
//... okay a broken timer. I have my hat handy
// turned out to be... something else
if (0) {
dPrint("error: theChar <");
dPrint(theChar ? theChar : '?');
dPrint("> expected <");
dPrint(expected);
dPrint("> timer "); dPrint(now - timer);
dPrintln("");
}
if (theChar && theChar == expected) TRANSITION(AWAIT); // another chance during red error feedback
if (now - timer >= badTime) {
TRANSITION(REST);
}
break;
case REST :
if (newState) {
displayFSM(DISP_OFF);
timer = now;
newState = false;
}
if (now - timer >= restTime)
TRANSITION(IDLE);
if (theChar)
TRANSITION(IDLE);
break;
case FINI:
if (newState) {
displayFSM(rewardCommand);
timer = now;
newState = false;
}
if (now - timer >= rewardTime + 333) {
TRANSITION(REST);
}
if (theChar) TRANSITION(IDLE);
break;
default:
dPrintln("Rotten Denmark!");
for (; ; );
}
} while (theChar);
# undef TRANSITION
}
// service or just call logicFSM(0)
void logicFSM()
{
logicFSM(0);
}
← ON is AUTO OFF