// PinChangeIntTest
//
// See the Wiki at http://code.google.com/p/arduino-pinchangeint/wiki for more information.
// This sketch requires the ByteBuffer library, which is found in the PinChangeInt zipfile.
// for vim editing: :set et ts=2 sts=2 sw=2
//-------- define these in your sketch, if applicable ----------------------------------------------------------
//-------- This must go ahead of the #include statement --------------------------------------------------------
// You can reduce the memory footprint of this handler by declaring that there will be no pin change interrupts
// on any one or two of the three ports.  If only a single port remains, the handler will be declared inline
// reducing the size and latency of the handler.
// #define NO_PORTB_PINCHANGES // to indicate that port b will not be used for pin change interrupts
// #define NO_PORTC_PINCHANGES // to indicate that port c will not be used for pin change interrupts
// #define NO_PORTD_PINCHANGES // to indicate that port d will not be used for pin change interrupts
// You can reduce the code size by 20-50 bytes, and you can speed up the interrupt routine
// slightly by declaring that you don't care if the static variables PCintPort::pinState and/or
// PCintPort::arduinoPin are set and made available to your interrupt routine.
// #define NO_PIN_STATE        // to indicate that you don't need the pinState
// #define NO_PIN_NUMBER       // to indicate that you don't need the arduinoPin
// if there is only one PCInt vector in use the code can be inlined
// reducing latency and code size
// define DISABLE_PCINT_MULTI_SERVICE below to limit the handler to servicing a single interrupt per invocation.
// #define       DISABLE_PCINT_MULTI_SERVICE
// The following is intended for testing purposes.  If defined, then a variable PCintPort::pinMode can be read
// in your interrupt subroutine.  It is not defined by default:
// #define PINMODE
//-------- define the above in your sketch, if applicable ------------------------------------------------------
#define PINMODE
#define FLASH
#include <ByteBuffer.h>
#include <PinChangeInt.h>

#define NEWLINE "\r\n"  // Programs like "screen" in Linux don't return with a "\n" character.

// This example demonstrates a configuration of 6 interrupting pins and 3 interrupt functions.
// A variety of interrupting pins have been chosen, so as to test all PORTs on the Arduino.
// The pins are as follows:
// quicfunc0 is attached to tPIN1-4.
// quicfunc1 is attached to tPIN5.
// quicfunc2 is attached to tPIN6.
// Pins tPIN1 and tPIN6 interrupt on FALLING.
// tPIN2 and tPIN4 interrupt on RISING.
// tPIN3 and tPIN5 interrupt on CHANGE.
// NOTE:
// For the Analog Input pins used as digital input pins, you can use numbers such as 14, 15, 16, etc.
// or you can use A0, A1, A2, etc. (the Arduino code comes with #define's for the Analog Input pin
// names and will properly recognize e.g., pinMode(A0, INPUT_PULLUP));
#if defined __AVR_ATmega2560__ || defined __AVR_ATmega1280__ || defined __AVR_ATmega1281__ || defined __AVR_ATmega2561__ || defined __AVR_ATmega640__
#define tPIN1 14  // port J
#define tPIN2 15
#define tPIN3 A8 // Port K
#define tPIN4 A12
#define tPIN5 SS   // Port B, also can be given as "57"
#define tPIN6 MOSI // This pin starts and stops the count
#else
// These only work for ATMega328-compatibles; ie, Leonardo is not covered here.
#define tPIN1 2  // port D
#define tPIN2 3
#define tPIN3 11 // Port B
#define tPIN4 12
#define tPIN5 A3 // Port C, also can be given as "17"
#define tPIN6 A4 // This pin starts and stops the count
#endif

// HOW IT WORKS (ATmega328-specific; replace the references with the proper pins for the other chip types)
// The interrupt on Arduino pin A4 (tPIN6) will, when triggered, start the counting of interrupts.  
// The array interrupt_count0[20] is updated in the interrupts; each cell keeps track of the number
// of interrupts on one of the 20 available interrupt pins on the Arduino.  Every second in the main
// loop the array is scanned and registered interrupts are reported for all pins interrupted since
// the previous second.  If no interrupts, the output is quiet.

// tPIN6 is special.  Not only does it start the counting of the interrups, but it turns on and off
// interrupts on pins 2, 11, and A3/17 (tPIN1, tPIN3, tPIN5).  All pins start by interrupting, but after
// the count is turned on and then turned off, the 3 pins are detached from interrupts.
// Everytime thereafter when the count is turned off the 3 pins are detached.  They are reattached
// when turned on.

// Output is copied to a buffer, because we can't do a Serial.print() statement in an interrupt
// routine.  The main loop checks  for entries in the buffer and prints them if found.
// Output looks like this:
// -F- - an interrupt triggered by a falling signal occurred.
// +R+ - an interrupt triggered by a rising signal occurred.
// *C* - an interrupt triggered by a change in signal occurred.
// f#p#-P# - f# shows the interrupt subroutine that was called: 0, 1, or 2
//         - p# shows the pin number that triggered the interrupt
//         - P# shows the port that this pin number is attached to. 2 is PORTB, 3 is PORTC, 4 is PORTD

// HOW TO CONNECT
// Each pin gets a momentary contact switch connected to it.  One side of the switch should connect
// to ground.  The other side of the switch connects to the Arduino pin.  For my purposes, I am using
// two rotary encoders.  Each encoder contains 3 switches.  But 6 regular pushbuttons would work, too.

/* WHAT TO LOOK FOR
 Output is sent to the serial line, so the Arduino IDE's serial terminal should be opened.
 Upon startup, press tPINS1-5.  You will see output like this:
-F-f0p2-P4 (counting off)
..*C*f0p11-P2 (counting off)
+R+f0p3-P4 (counting off)
 This shows that
 1. an interrupt was triggered on a falling signal (*F*).  It called (f0) function 0, which is quicfunc0.
    The triggering pin was (p2) Arduuino pin 2, which is on (P4) Port 4 (PORTD).  Counting of this interrupt is
    off, so you will not see any output from the main loop.
 2. Two dots appeared.  Dots came from iterations of loop(), so these 2 dots show that the two interrupts happened 2 seconds apart.
 3. an interrupt was triggered on a change in signal (*C*).  It called quicfunc0, from Arduino pin 11, on Port 2 (PORTB).
    The interrupt was not counted.
 4. an interrupt was triggered on a rising signal (+R+).  It called quicfunc0, from Arduino pin 3, on Purt 4 (PORTD).
    The pin should have started out at the high level, so likely the signal fell during onother interrupt, and now
    the rise has been caught.
    
 Now press the button attached to tPIN6 (in our case, A4 or D18).  You will see something like this:
-F-START! f2p18-P3
.Count for pin A4 is 1
 This shows that
 1. The counting machanism (START!) was triggered by a folling signal (-F-) on pin 18 (p18) which is in Port 3 (P3) (which == PORTC) and
    function f2 was called (f2).
 2. A dot appeared, which came from loop() because a second passed.
 3. The count for p18 or A4 was displayed.
 
 Now you will see messages for all the pins that you manipulate, for example:
*C*f0p11-P2
+R+f0p3-P4
*C*f0p11-P2
+R+f0p3-P4
*C*f0p11-P2
.Count for pin D3 is 6
Count for pin D11 is 9
.+R+f0p3-P4
-F-f0p2-P4
.Count for pin D2 is 1
Count for pin D3 is 1
 These codes reflect the interrupts, as described above.  This output will take place until you press tPIN6:
-F-f2: STOP! Counting off.
Interrupt OFF on tPIN1 (2) tPIN3 (11) tPIN5 (17)
 Then you will see output like this:
.....................+R+f0p12-P2 (counting off)
.+R+f0p12-P2 (counting off)
+R+f0p12-P2 (counting off)
+R+f0p12-P2 (counting off)
 and tPIN1, tPIN3, and tPIN5 will not trigger interrupts.
*/
// NOTES
// Output overwrites:
// It's possible during moderately fast interrupts to see your print output get garbled; eg,
// +R+f0p12-P2 (+R+f0p12-P2 (counting +R+f0p12-P2 (cou+R+f0p12-P+R+f0p12
// This is because the print of the buffer takes place inside a while loop, and it can
// be interrupted and new data inserted into the buffer at a midpoint of the buffer's text.
// Just by spinning my rotary encoders I can readily generate over 200 interrupts per second
// on a pin, which is easily fast enough to overrun Serial output at 115,200 bps.
// The lesson here?  ...Interrupts are tricky, and interrupt service routines should be fast.
// Just sayin'.

// Pins:
// We want to use pins from each of ports B, C and D.  So choose wisely.  Ports are shown in
// this diagram of the ATmega328P chip.  PD0 means "Port D, pin 0".  PC3 means "Port C, Pin 3",
// PB2 means "Port B, pin 2" and so on.  The corresponding Arduino pins are in parentheses.
// So PB2 is Arduino pin D 10, for example.
/*
                  +-\/-+
            PC6  1|    |28  PC5 (AI 5)
      (D 0) PD0  2|    |27  PC4 (AI 4)
      (D 1) PD1  3|    |26  PC3 (AI 3)
      (D 2) PD2  4|    |25  PC2 (AI 2)
 PWM+ (D 3) PD3  5|    |24  PC1 (AI 1)
      (D 4) PD4  6|    |23  PC0 (AI 0)
            VCC  7|    |22  GND
            GND  8|    |21  AREF
            PB6  9|    |20  AVCC
            PB7 10|    |19  PB5 (D 13)
 PWM+ (D 5) PD5 11|    |18  PB4 (D 12)
 PWM+ (D 6) PD6 12|    |17  PB3 (D 11) PWM
      (D 7) PD7 13|    |16  PB2 (D 10) PWM
      (D 8) PB0 14|    |15  PB1 (D 9) PWM
                  +----+
*/

uint8_t pins[6]={ tPIN1, tPIN2, tPIN3, tPIN4, tPIN5, tPIN6 };
uint8_t ports[6]={ 0, 0, 0, 0, 0, 0 };

uint8_t latest_interrupted_pin;
uint8_t interrupt_count[20]={0}; // 20 possible arduino pins
uint8_t port;
uint8_t mode;

ByteBuffer printBuffer(80);
char charArray[16];
char numBuffer[4] = { 0, 0, 0, 0 };
uint8_t printFull=0;

volatile boolean start=0;
volatile boolean initial=true;
long begintime=0;
long now=0;

void uint8ToString(char *outString, uint8_t number) {
  uint8_t hundreds=0;
  uint8_t tens=0;
  uint8_t ones=0;

  while (number >= 100 ) {
    hundreds++;
    number-=100;
  }
  while (number >= 10 ) {
    tens++;
    number-=10;
  }
  ones=number;
  ones+=48;
  if (hundreds > 0) { hundreds+=48; tens+=48; outString[0]=hundreds; outString[1]=tens; outString[2]=ones; outString[3]=0; }
  else if (tens > 0) {  tens+=48; outString[0]=tens; outString[1]=ones; outString[2]=0; }
  else { outString[0]=ones; outString[1]=0; };
}

void showMode() {
  switch (mode) {
  case FALLING:
    printBuffer.putString((char *) "-F-");
  break;
  case RISING:
    printBuffer.putString((char *) "+R+");
  break;
  case CHANGE:
    printBuffer.putString((char *) "*C*");
  break;
  }
}

void quicfunc0() {
  latest_interrupted_pin=PCintPort::arduinoPin;
  mode=PCintPort::pinmode;
  showMode();
  if (start==1) {
    interrupt_count[latest_interrupted_pin]++;
  }
  uint8ToString(numBuffer, latest_interrupted_pin);
  printBuffer.putString((char *) "f0p"); printBuffer.putString(numBuffer); printBuffer.putString((char *) "-P");
  uint8ToString(numBuffer, digitalPinToPort(latest_interrupted_pin));
  printBuffer.putString(numBuffer);
  if (start !=1) printBuffer.putString((char *) " (counting off)");
  printBuffer.putString((char *) NEWLINE);
};

void quicfunc1() {
  latest_interrupted_pin=PCintPort::arduinoPin;
  mode=PCintPort::pinmode;
  showMode();
  if (start==1) {
    interrupt_count[latest_interrupted_pin]++;
  }
  uint8ToString(numBuffer, latest_interrupted_pin);
  printBuffer.putString((char *) "f1p"); printBuffer.putString(numBuffer); printBuffer.putString((char *) "-P");
  uint8ToString(numBuffer, digitalPinToPort(latest_interrupted_pin));
  printBuffer.putString(numBuffer);
  if (start !=1) printBuffer.putString((char *) " (counting off)");
  printBuffer.putString((char *) NEWLINE);
};

void quicfunc2() {
  latest_interrupted_pin=PCintPort::arduinoPin;
  mode=PCintPort::pinmode;
  showMode();
  if (start == 1) {
    printBuffer.putString((char *) "f2: STOP! Counting off.\n");
    printBuffer.putString((char *) "Interrupt OFF on tPIN1 ("); uint8ToString(numBuffer, tPIN1), printBuffer.putString(numBuffer);
    printBuffer.putString((char *) ") tPIN3 (");uint8ToString(numBuffer, tPIN3), printBuffer.putString(numBuffer);
    printBuffer.putString((char *) ") tPIN5 (");uint8ToString(numBuffer, tPIN5), printBuffer.putString(numBuffer);
    printBuffer.putString((char *) ")");
    printBuffer.putString((char *) NEWLINE);
    PCintPort::detachInterrupt(tPIN1); PCintPort::detachInterrupt(tPIN3); PCintPort::detachInterrupt(tPIN5); 
    start=0;
  } else {
    start=1;
    interrupt_count[latest_interrupted_pin]++;
    printBuffer.putString((char *) "START! f2p");
    uint8ToString(numBuffer, latest_interrupted_pin);
    printBuffer.putString(numBuffer); printBuffer.putString((char *) "-P");
    uint8ToString(numBuffer, digitalPinToPort(latest_interrupted_pin));
    printBuffer.putString(numBuffer); printBuffer.putString((char *) NEWLINE);
    if (! initial) {
      PCintPort::attachInterrupt(tPIN1, &quicfunc0, FALLING);
      PCintPort::attachInterrupt(tPIN3, &quicfunc0, CHANGE);
      PCintPort::attachInterrupt(tPIN5, &quicfunc1, CHANGE);
    } else {
      initial=false;
    }                                                                                                
  }
};

uint8_t i;
void setup() {
  Serial.begin(115200);
  delay(250);
  Serial.println("Test");
  for (i=0; i < 6; i++) {
    pinMode(pins[i], INPUT_PULLUP);
    ports[i]=digitalPinToPort(pins[i]);
    switch (pins[i]) {
    case tPIN1:
        PCintPort::attachInterrupt(pins[i], &quicfunc0, FALLING);
    break;
    case tPIN3:
        PCintPort::attachInterrupt(pins[i], &quicfunc0, CHANGE);
    break;
    case tPIN2:
    case tPIN4:
        PCintPort::attachInterrupt(pins[i], &quicfunc0, RISING);
    break;
    case tPIN5:
        PCintPort::attachInterrupt(pins[i], &quicfunc1, CHANGE);
    break;
    case tPIN6:
        attachPinChangeInterrupt(pins[i], quicfunc2, FALLING); // attachPinChangeInterrupt is a #define
    break;
    }
  }
  //Serial.println(printBuffer.getCapacity(), DEC);
  //Serial.println("*---------------------------------------*");
  Serial.print("*---*");
  delay(250);
  begintime=millis();
}

void loop() {
  now=millis();
  uint8_t count;
  char outChar;
  // uint8_t bufsize;
  //if (printBuffer.getSize() != 0) { Serial.print("SZ:"); Serial.println (printBuffer.getSize(), DEC); };
  //bufsize=printBuffer.getSize();
  //if (bufsize > 0) { Serial.print("S:"); Serial.println(bufsize); }
  while ((outChar=(char)printBuffer.get()) != 0) Serial.print(outChar);
  if ((now - begintime) > 1000) {
    Serial.print(".");
    if (printBuffer.checkError()) {
      Serial.println("NOTICE: Some output lost due to filled buffer.");
    }
    for (i=0; i < 20; i++) {
      if (interrupt_count[i] != 0) {
        count=interrupt_count[i];
        interrupt_count[i]=0;
        Serial.print("Count for pin ");
        if (i < 14) {
          Serial.print("D");
          Serial.print(i, DEC);
        } else {
          Serial.print("A");
          Serial.print(i-14, DEC);
        }
        Serial.print(" is ");
        Serial.println(count, DEC);
      }
    }
    begintime=millis();
  }
}

