 }
 
 
+//------------------------------------------------------------------------------------------------
+
 void sendHexByte(byte val)
 {
   Serial.write(tohexch(val>>4)); // high nybble


typedef enum 
{
  STATE_NOCARD = 0,
  STATE_INSERTING,
  STATE_WAITING_ROW,
  STATE_IN_ROW,
  STATE_GAP,
  STATE_REMOVING,
  STATE_END
} STATE;

byte sticky = 0;
boolean seenReg = false;
byte row = 0;
byte card[CARD_ROWS];
unsigned int adc[NUM_CHANNELS];
STATE state = STATE_NOCARD;
STATE prev = STATE_NOCARD;


void setup()
{
  Serial.begin(115200);  
  pinMode(LED_REG, OUTPUT);
  pinMode(LED_D7,  OUTPUT);
  pinMode(LED_D6,  OUTPUT);
  pinMode(LED_D5,  OUTPUT);
  pinMode(LED_D4,  OUTPUT);
  pinMode(LED_D3,  OUTPUT);
  pinMode(LED_D2,  OUTPUT);
  pinMode(LED_D1,  OUTPUT);
  pinMode(LED_D0,  OUTPUT);
  sendCardReport(REPORT_OK_BOOT, NULL, 0);
}


void loop()
{  
  readADCs();
  
  // Generate filtered versions of registration and data
  byte freg = getData(adc[8], 0);  
  byte now  = getData(adc[7], 7) | getData(adc[6], 6) | getData(adc[5], 5) | getData(adc[4], 4)
            | getData(adc[3], 3) | getData(adc[2], 2) | getData(adc[1], 1) | getData(adc[0], 0);

  // Show live diagnostics on LEDs
  writeLEDs(freg, now);

#if defined(CFGEN_SEND_ADC_REPORTS)
  if (now != sticky)
  {
    Serial.write(':');
    Serial.write(REPORT_OK_ADC);
    for (int i=8; i>=0; i--)
    {
      //in decimal ADC values are 10 bits (3 nybbles)
      Serial.print(adc[i]);
      Serial.print(" ");
    }
    Serial.println();
  }
#endif

  
  // crank round the acquisition state machine
  switch (state)
  {
    case STATE_NOCARD:
      // stay here while freg=1 and data=0xFF, all holes (card removed)
      if ((freg != 1) || now != 0xFF)
      { // at least one sensor has seen paper
        state = STATE_INSERTING;
        //Serial.println(state);
      } 
    break;
    
    case STATE_INSERTING:
      // check for early removal (all holes)
      if ((freg == 1) && (now == 0xFF))
      {
        // bail early if all on (removed)
        state = STATE_REMOVING;
        //Serial.println(state);
      }
      // wait for all paper
      else if ((freg == 0) && (now == 0x00))
      {
        row = 0;
        state = STATE_WAITING_ROW;
        //Serial.println(state);
      }
    break;
    
    case STATE_WAITING_ROW:
      // check for early card removal (all reading holes)
      if ((freg == 1) && (now == 0xFF))
      {
        state = STATE_REMOVING;
        //Serial.println(state);
      }
      else
      { // wait for any data to start appearing (at least one hole)
        if ((freg == 1) || (now != 0x00))
        {
          sticky  = now;
          state   = STATE_IN_ROW;
          //Serial.println(state);
        }
      }
    break;
    
    case STATE_IN_ROW:
      // check for early card removal (all holes)
      if ((freg == 1) && (now == 0xFF))
      {
        state = STATE_REMOVING;
        //Serial.println(state);
      }
      else
      {
        // remember if we see the registration hole while reading a row
        // this is only punched if the row is completely unpunched
        // that way we don't miss the row
        if (freg == 1)
        {
          seenReg = true;
        }
        // keep collecting sticky bits until all go zero again
        sticky |= now;
        if ((freg == 0) && (now == 0x00))
        {
          state = STATE_GAP;
          //Serial.println(state);
        }  
      }
    break;

// if you want an empty row, punch the REG hole
// don't punch all 8+REG else it will see a card removal

#include <avr/pgmspace.h>


//===== CONFIGURATION ============================================================================

//Turn this on to turn a state change report every state change
//#define CFGEN_SEND_STATE_REPORTS

//Turn this on to send row data reports every detected row
//This is useful for continuous read strips
//Although note you would have to disable 'max len exceeded' checks for this to work.
//#define CFGEN_SEND_ROW_REPORTS

//GPIO reads low for hole
#define HOLE 0
//GPIO reads high for paper
#define PAPER 1

// Pinouts for Sparkfun ProMicro:
// https://learn.sparkfun.com/tutorials/pro-micro--fio-v3-hookup-guide/hardware-overview-pro-micro

// Note, this pin mapping is as per the original 9-analogues design that David and Gemma used in 2015
#define REG          18  // 18 A0
#define D7           19  // 19 A1
#define D6           20  // 20 A2
#define D5           21  // 21 A3
#define D4            4  // 4  A6
#define D3            6  // 6  A7
#define D2            8  // 8  A8
#define D1            9  // 9  A9
#define D0           10  // 10 A10

// Note, this pin mapping is as per the original 9-analogues design that David and Gemma used in 2015
#define LED_REG      1 // TX
#define LED_D7       0 // RX
#define LED_D6       2
