#include <16F628A.h>

#FUSES XT, WDT, NOPROTECT, NOPUT      // CCS Compiles PUT wrong - this means PUT
#FUSES NOBROWNOUT               //No brownout reset
#FUSES MCLR                     //Master Clear pin enabled
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                    //No EE protection

#ID 0x0020

#USE DELAY(CLOCK=4000000, RESTART_WDT)

#USE FAST_IO(A)
#USE FAST_IO(B)

#ZERO_RAM

#BIT mlan =      0x05.0
#BIT LEDG =      0x05.1
#BIT LEDR =      0x05.2
#BIT pulse =    0x05.3   // Low is 1 Sec, High is 10 secs
#BIT config =   0x05.4
#Bit id_out =   0x06.0

#DEFINE   MLAN_IN            0b11111001
#DEFINE   MLAN_OUT            0b11111000
#DEFINE   STORED_IDS_ADDR   0x3F   

// Map out the IDs stored in the EEPROM
byte   CONST ee_table[7] = {0,6,12,18,24,30,36};

// Globals
int id_in[6];

// Subroutines


// Flash RED
// This routine flashes the RED LED for secs/4 seconds

void flash_red(int secs) {
   int i;
   LEDG = 0; delay_cycles(1); LEDR = 1;
   for (i = 0; i < secs; i++) {
      delay_ms(250);
      LEDR = !LEDR;
   }
}

int gen_crc(int crc_in, int newdata) {
   int i;
   for(i = 0; i < 8; i++) {
      shift_right(&crc_in, 1, (bit_test(crc_in, 0) ^ shift_right(&newdata, 1, 0)));
      // Check for the extra XOR
      if (bit_test(crc_in, 7)) {
         // We need an extra XOR
         crc_in ^= 0x0C;
      }
   }
   return crc_in;
}

int read_byte() {
   int byte_in, i, bit_cnt;
   for(i = 0; i < 8; i++) {
      // Clear bit counter
      bit_cnt = 0;
      // Pulse microLAN low to clock bit
      set_tris_a(MLAN_OUT);
      mlan = 0;
      delay_us(3);
      set_tris_a(MLAN_IN);
      delay_us(3);   // Allow some pullup recovery time
      // We now have ~7us left to sample a few times
      if (!mlan) { bit_cnt++; }
      if (!mlan) { bit_cnt++; }
      if (!mlan) { bit_cnt++; }
      // Lets shoot for 2 out of 3!  Use a quickie bit test for efficiency
      shift_right(&byte_in, 1, !bit_test(bit_cnt, 1));
      // Lets allow for the microLan device to release (45us MAX) and LAN to recover (15)
      delay_us(45);
   }
   return byte_in;
}

// READ iButton
// This routine is called to read an iButtons' unique serial ID.  The following steps are taken:
// 1. Issue Read ROM command on MicroLAN (0x33)
// 2. Read in 8 byte ID
// 3. Check family code and CRC
// 4. Store 6 byte serial in id_in if valid and return TRUE OR
//      Return false for Bad CRC or family code

boolean read_ibutton() {

   int i, tmp, family, crc_in;

   // Lets write out the Read ROM command
   tmp = 0x33;
   for(i = 0; i < 8; i++) {
      // Bring micro LAN low for 7us
      set_tris_a(MLAN_OUT);
      mlan = 0;
      delay_us(7);

      // Output the bit
      if (shift_right(&tmp, 1, 0)) {
         // Output a 1 via the pullup
         set_tris_a(MLAN_IN);
      } else {
         set_tris_a(MLAN_OUT);
         mlan = 0;
      }

      delay_us(50);   // Allow iButton to read the data and complete slot

      // Now we go to tristate
      set_tris_a(MLAN_IN);

      // No need for 1us delay since loop takes care of it
   }

   // Now the iButton will start dumping data back to us synced to our pulses
   family = read_byte();

   // If the family code is bad - quit
   if (family != 0x01) {
      return FALSE;
   }

   crc_in = gen_crc(0x00, family);

   for(i = 0; i < 6; i++) {

//      id_in[i] = read_byte();
//      crc_in = gen_crc(crc_in, id_in[i]);

      // The code below is more compact (15us vs 20us)
      tmp = read_byte();
      id_in[i] = tmp;
      crc_in = gen_crc(crc_in, tmp);
   }
   if (crc_in == read_byte()) {
      // The CRC is okay!
      return TRUE;
   } else {
      return FALSE;
   }
}

// Check LAN
// This routine issues an INIT pulse on the micro LAN (480 - 960 us) and looks for a presence
// pulse from an ibutton.  If it sees one, it returns true indicating an active device is on the LAN

boolean check_lan() {
   int i, low_hit;
   low_hit = 0;
   // Bring MLAN low for at least 480us - we'll use 520 us
   set_tris_a(MLAN_OUT);
   mlan = 0;
   delay_us(520);
   // Put bus back in tristate
   set_tris_a(MLAN_IN);
   // Lets see if a pulse comes back in the proper window
   // The pulse will start 15-60 us after going high
   // The pulse will be 60us - 240us

   // The presence pulse period must be a minimum of 480us
   // So lets check for a low every 10us and if we get 4 (since pulse has to be 60us min), take it
   for (i = 0; i < 48; i++) {
      if (!mlan) { low_hit++; }   // Catch a presence pulse
      delay_us(2);   // For loop takes 9us @ 4MHz
   }      

   if (low_hit > 3) {
      return TRUE;
   } else {
      return FALSE;
   }
}

// Check ID
// This routine takes the passed ID in id_in and compares it to those IDs in EEPROM
// If it finds a match, it returns the ID number, otherwise it returns 0x00

int check_id(int num_ids) {

   int ee_addr, button_idx, cnt;
   boolean match;

   button_idx = 0;

   while(++button_idx <= num_ids) {
      // Check all stored button IDs
      cnt = 0; restart_wdt();
      ee_addr = ee_table[button_idx-1];
      match = TRUE;
      while (cnt != 6) {
         if (read_eeprom(ee_addr) != id_in[cnt]) {
            match = FALSE;   // Doesn't match - check next button
            break;
         }
         ee_addr++;
         cnt++;
      }               
      if (match) {
         // We matched everything!
         return button_idx;
      }
   }
   // No matches!
   return 0;   
}


void main() {
   int stored_ids, id_matched, temp_out, i, eeprom_addr;

   // Setup IO port directions and initialize ports
   set_tris_a(MLAN_IN);
   set_tris_b(0b00000000);
   id_out = 0;

   // Lets give some indication that we are alive
   LEDR = 0; LEDG = 1;
   delay_ms(1000);
   LEDG = 0; LEDR = 1;
   delay_ms(1000);
   LEDR = 0; LEDG = 0;
   delay_ms(1000);

   // Check config bitm - LOW means config
   if (!config) {
      // Configuration mode
      eeprom_addr = 0x00;
      stored_ids = 0;

      // We can store 7 buttons
      for(i = 0; i < 7; i++) {
         LEDG = 0; delay_cycles(1); LEDR = 1;   // Red

         // Lets wait for a button
LAN_WAIT1:
         while(!check_lan()) { restart_wdt(); }
   
         // Lets read the iButton - if we get a CRC error, lets try again (and again :) )
         while(!read_ibutton()) {
            // We didn't get a good read, lets keep trying
            restart_wdt();
            delay_ms(1);   // Give the bus a chance to stabilize
            if (!check_lan()) {
               // Bail - we can't see a device!
               goto LAN_WAIT1;
            }
         }

         // Lets make sure this ID hasn't already been stored in EEPROM
         if (check_id(stored_ids) != 0) {
            i--;   // Lets take a step back for another try
            flash_red(20);      
            continue;
         }

         // We have a valid ID in id_in.  Lets store it - all six bytes
         for(temp_out = 0; temp_out < 6; temp_out++) {
            write_eeprom(eeprom_addr, id_in[temp_out]);
            restart_wdt();
            eeprom_addr++;
         }

         // Update number of IDs - this ensures that we don't check random 
         // data bytes if they use < 7 buttons
         stored_ids++;
         write_eeprom(STORED_IDS_ADDR, stored_ids);

         // Green LED for 5 seconds
         LEDR = 0; delay_cycles(1); LEDG = 1;
         delay_ms(3000);
      
      }         

      // Okay, we've maxed out or storage - let user know
      // Blink Green until reset
      LEDR = 0; delay_cycles(1); LEDG = 1;
      while(TRUE) {
         delay_ms(250);
         LEDG = !LEDG;
      }

   } else {
      // Normal operation
      stored_ids = read_eeprom(STORED_IDS_ADDR);
      while(TRUE) {

          // Check for presence pulse and read ID if button is online
LAN_WAIT2:
         while(!check_lan()) { restart_wdt(); }

         // Something is on the micro LAN - lets read the ID
         while(!read_ibutton()) {
            // We didn't get a good read, lets keep trying
            restart_wdt();
            delay_ms(1);   // Give the bus a chance to stabilize
            if (!check_lan()) {
               // Bail - we can't see a device!
               goto LAN_WAIT2;;
            }
         }
         
         // Valid ID was received - lets check it
         id_matched = check_id(stored_ids);      
         if (id_matched == 0) {
            // ID does not match
            LEDG = 0; delay_cycles(1); LEDR = 1;   // RED
            delay_ms(2000);     
            LEDG = id_out; delay_cycles(1); LEDR = 0;
         } else {
            // Valid ID match - trigger outputs
            temp_out = ~id_out;                  
            // Output the bits
            id_out = temp_out;
            LEDR = 0; delay_cycles(1); LEDG = id_out; // Green
            delay_ms(500);
         }
      }   // while(TRUE)
   }   // if (config)
}   // End MAIN

