/*◄■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ LCC_ecan.c ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■►*/

// <editor-fold defaultstate="collapsed" desc="Title">
/*╔══════════════════════════════════════════════════════════════════════════╗*/
/*║                        © John William Darsow                             ║*/
/*║ Created on  : Tuesday, February 28, 2017                                 ║*/
/*║ Current Version Date: (see File Date)                                    ║*/
/*║                                                                          ║*/
/*║ File        : LCC_ecan.c                                                 ║*/
/*║  This source file provides APIs for ECAN.                                ║*/
/*║                                                                          ║*/
/*║ For         : LCC nodes                                                  ║*/
/*║ µChip       : PIC18F26K80                                                ║*/
/*║  Compiled using:     MPLAB X IDE v3.45                                   ║*/
/*║                      XC8 v1.38                                           ║*/
/*║                                                                          ║*/
/*║ Modifications,UpDates and ToDo:                                          ║*/
/*║                                                                          ║*/
/*║ JWD-NHR temporary LLC UID Range 0x08 0x01 0x00 0xBD 0x** 0x**            ║*/
/*║ Monday, May 29, 2017: After discovery of endian & register interpretation║*/
/*║   problems, major rewrite & testing of conversion functions and Rx & Tx  ║*/
/*║   routines for JMRI "bridging" on Node1. IT WORKS!! NO MIXED UP MESSAGES ║*/
/*║                                                                          ║*/
/*║                                                                          ║*/
/*║                                                                          ║*/
/*║                                                                          ║*/
/*║ This WAS a MCC generated driver implementation file for the ECAN driver  ║*/
/*║                                                                          ║*/
/*╚══════════════════════════════════════════════════════════════════════════╝*/
// </editor-fold>

/*═════════════════════════════════ INCLUDEs ═════════════════════════════════*/
#include <xc.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "LCC_ecan.h"

/*═════════════════════════════════ VARIABLES ════════════════════════════════*/
// <editor-fold defaultstate="collapsed" desc="Variables">

/*═══════════════════════ EXTERNAL VARIABLE DECLARATIONS ═════════════════════*/
extern Byte             nodesUNID[6];       // "main".c
extern uInt8            State;              // "main".c
extern uInt16           countOfmS;          // "main".c
extern uInt16           timeOut1;           // "main".c
extern Byte             ProtocolFlags_1;    // "main".c
extern Byte             ProtocolFlags_2;    // "main".c
extern Byte             ProtocolFlags_3;    // "main".c

/* Flags */
extern volatile Bit     Permitted;          // "main".c
extern volatile Bit     Verbose;            // "main".c
extern volatile Bit     EchoJMRI;           // "main".c

/*════════════════════════ LOCAL VARIABLE DEFINITIONS ════════════════════════*/
Char               UNIDstring[18];          // Unique Node ID string
Word               SourceNodeID;            // Source ID Alias, 12 lo bits of header
//Variables </editor-fold>

/*════════════════════════════ FUNCTION DEFINITIONS ══════════════════════════*/

/*════════♦ ♦ ♦ ♦ ♦═════════♦ LCC/OpenLCB Functions ♦════════♦ ♦ ♦ ♦ ♦════════*/
// <editor-fold defaultstate="collapsed" desc="LCC/OpenLCB Functions">

/*───────────────────────────── Process LCC Event ────────────────────────────*/
void processEvent(CANMsgt * pTempMsg)
{
   EventIDt   newEvent;
   Char       eString[25];
   
   memcpy(newEvent.UNID, pTempMsg->asLCC.Data, 6);                        // get Event UNID
   newEvent.Event  = (unsigned) pTempMsg->asLCC.Data[6] << 8;             // Get Event high byte (Big endian in message)
   newEvent.Event |= pTempMsg->asLCC.Data[7];                             // Get Event low byte
      
   switch (pTempMsg->asLCC.MTI)
   {
      case ETP_PCER:                                                      /*<< Producer/Consumer Event Report >>*/
         
         if (newEvent.UNID[0] == 0x01 && newEvent.UNID[1] == 0x00)        // Well-Known, Automatically-Routed
         {
            if (Verbose) printf("Event (Well-Known, Routed) %s from {%03X}.\n>",
                                Event2String(&newEvent, eString), pTempMsg->asLCC.SNID);
         }
         else if (newEvent.UNID[1] == 0x01 && newEvent.UNID[1] == 0x00)   // Well-Known, NOT Automatically-Routed
         {
            if (Verbose) printf("Event (Well-Known, NOT Routed) %s from {%03X}.\n>",
                                Event2String(&newEvent, eString), pTempMsg->asLCC.SNID);
         }
         else if (!memcmp(newEvent.UNID, nodesUNID, 6))                   // Event for this Node
         {
            if (Verbose) printf("Event %s from {%03X}.\n>",
                                Event2String(&newEvent, eString), pTempMsg->asLCC.SNID);
            consumeEventNumber(newEvent.Event);                           // Consume event for this UNID
         }
         else
         {
            if (Verbose) printf("Un-recognized Event (Not This Node) %s from {%03X}.\n>",
                                Event2String(&newEvent, eString), pTempMsg->asLCC.SNID);
            ObserveEventNumber(&newEvent);                                 // Observe events of other UNIDs
         }
         
         // Those we've learned
      // <editor-fold defaultstate="collapsed" desc="Event Utilities">
      break;
      case ETP_IC:                          /*<< Identify Consumer >>*/
      break;
      case ETP_CIV:                         /*<< Consumer Identified, Valid >>*/
      break;
      case ETP_CII:                         /*<< Consumer Identified, Identified >>*/
      break;
      case ETP_CIU:                         /*<< Consumer Identified, Unknown >>*/
      break;
      case ETP_CRI:                         /*<< Consumer Range Identified >>*/
      break;
      case ETP_IP:                          /*<< Identify Producer >>*/
      break;
      case ETP_PIV:                         /*<< Producer Identified, Valid >>*/
      break;
      case ETP_PII:                         /*<< Producer Identified, Invalid >>*/
      break;
      case ETP_PIU:                         /*<< Producer Identified, Unknown >>*/
      break;
      case ETP_PIR:                         /*<< Producer Identified Range >>*/
      break;
      case ETP_LE:                          /*<< Learn Event >>*/
      break;
      //Event Utilities </editor-fold>
      default:                              /*<< Unknown Event message >>*/
      {
         printf("Unknown Event message, ");
         printMsg(pTempMsg);                               // Print Unknown message
      }
   }
}

/*──────────────────────────── Process LCC Message ───────────────────────────*/
void processLCC(CANMsgt * pTempMsg)
{
   Word tSNIDA;
   Byte tempData[8];
   
   switch (pTempMsg->asLCC.MTI)
   {
      // <editor-fold defaultstate="collapsed" desc="Initialization Complete">
      case LCC_IC_FS:                       /*<< Initialization Complete, Full Protocol + Full Node ID >>*/
        if (Verbose) printf("Initialization Complete from %s{%03X}, Full Protocol.\n>",
                             msgUNID2String(pTempMsg, UNIDstring), pTempMsg->asLCC.SNID);
         updateNodeList(pTempMsg->asLCC.Data, pTempMsg->asLCC.SNID, 4);   // Update status in Node list
      break;
      case LCC_IC_SS:                       /*<< Initialization Complete, SS Sufficient + Full Node ID >>*/
         if (Verbose) printf("Initialization Complete from %s{%03X}, Simple Set Protocol.\n>",
                             msgUNID2String(pTempMsg, UNIDstring), pTempMsg->asLCC.SNID);
         updateNodeList(pTempMsg->asLCC.Data, pTempMsg->asLCC.SNID, 4);   // Update status in Node list
      break;
      //Initialization Complete </editor-fold>
      
      // <editor-fold defaultstate="collapsed" desc="Verify Node ID">
      case LCC_VNA:                         /*<< Verify Node ID Addressed, + fddd, Optional Full Node ID >>*/
         tSNIDA = (pTempMsg->asLCC.Data[0] & 0x0Fu) << 8;  // Get address alias from Data
         tSNIDA |= pTempMsg->asLCC.Data[1];
         if (tSNIDA == SourceNodeID)                       // If ours reply
         {
            printf("Addressed Verify Node ID from {%03X} for us{%03X} %s. ",
                                pTempMsg->asLCC.SNID, tSNIDA, msgUNID2String(pTempMsg, UNIDstring));
            sendLCC(LCC_VdN_FS, nodesUNID, 6);             // Reply with Verified Node ID FS + Full Node ID
            printf("Replied as Full protocol Node.\n>");
         }
         else
            if (Verbose) printf("Addressed Verify Node ID from {%03X} to {%03X} %s .\n>",
                                pTempMsg->asLCC.SNID, tSNIDA, msgUNID2String(pTempMsg, UNIDstring));
            
      break;
      case LCC_VNG:                         /*<< Verify Node ID, Global + Optional Full Node ID >>*/
         if (Verbose) printf("Global Verify Node ID from {%03X}, ", pTempMsg->asLCC.SNID);
         if (pTempMsg->asLCC.dlc)                          // there's a UNID
         {
            if (Verbose) printf("Looking for Node %s, ", msgUNID2String(pTempMsg, UNIDstring));
            if (!memcmp(nodesUNID, pTempMsg->asLCC.Data, 6))// Compare it
            {
               sendLCC(LCC_VdN_FS, nodesUNID, 6);          // Reply if UNID matches
               if (Verbose) printf("Match, Verified Node ID Full protocol sent.\n>");
            }
            else                                           // No reply if no match
               if (Verbose) printf("No Match.\n>");
         }
         else                                              // if NO UNID
         {
            sendLCC(LCC_VdN_FS, nodesUNID, 6);             // Reply with Verified Node ID + Full Node ID
            if (Verbose) printf("Verified Node ID Full protocol sent.\n>");
         }
      break;
      
      case LCC_VdN_FS:                      /*<< Verified Node ID Full-set protocol >>*/
         if (Verbose) printf("Verified Node ID Full protocol from {%03X}.\n>", pTempMsg->asLCC.SNID);
         
         updateNodeList(pTempMsg->asLCC.Data, pTempMsg->asLCC.SNID, 16);   // Update status in Node list
      break;
      case LCC_VdN_SS:                      /*<< Verified Node ID Simple-set protocol >>*/
         if (Verbose) printf("Verified Node ID Simple-set protocol from {%03X}.\n>", pTempMsg->asLCC.SNID);
         
         updateNodeList(pTempMsg->asLCC.Data, pTempMsg->asLCC.SNID, 8);   // Update status in Node list
      break;
      //Verify Node ID </editor-fold>

      // <editor-fold defaultstate="collapsed" desc="Simple Node Information Protocol">
      case SP_SNIRq:                         /*<< Node Information Request + fddd >*/
         tSNIDA = (pTempMsg->asLCC.Data[0] & 0x0Fu) << 8;            // Get address alias from data[]
         tSNIDA |= pTempMsg->asLCC.Data[1];
         if (tSNIDA == SourceNodeID)                                 // If ours, reply
         {
            printf("Node Information Request from {%03X} for us{%03X}.", pTempMsg->asLCC.SNID, tSNIDA);
            tSNIDA = pTempMsg->asLCC.SNID;                           // Replace with sender 
            sendNodeInfo(tSNIDA, INFO_ADDR);                         // Send Protocol Support Reply
            printf("Information sent.\n>");
         }
         else
            if (Verbose) printf("Node Information Request from {%03X} to {%03X}.\n>", pTempMsg->asLCC.SNID, tSNIDA);
            
      break;
      //Simple Node Information Protocol </editor-fold>

      // <editor-fold defaultstate="collapsed" desc="Protocol Support">
      case LCC_PSI:                         /*<< Protocol Support Inquiry + fddd >*/
         tSNIDA = (pTempMsg->asLCC.Data[0] & 0x0Fu) << 8;  // Get address alias from Data
         tSNIDA |= pTempMsg->asLCC.Data[1];
         if (tSNIDA == SourceNodeID)                       // If ours, reply
         {
            printf("Protocol Support Inquiry from {%03X} for us{%03X}. ", pTempMsg->asLCC.SNID, tSNIDA);
            memcpy(tempData, pTempMsg->asLCC.Data, 2);     // Copy SNIDA (2 bytes, f = 0)
            tempData[2] = ProtocolFlags_1;                 // Protocol byte 1
            tempData[3] = ProtocolFlags_2;                 // Protocol byte 2
            tempData[4] = ProtocolFlags_3;                 // Protocol byte 3
            sendLCC(LCC_PSR, tempData, 5);                 // Protocol Support Reply + fd, dd, Protocol flags
            printf("Reply sent.\n>");
         }
         else                                              // It was for somebody else
            if (Verbose) printf("Protocol Support Inquiry from {%3.3X} to {%3.3X}.\n>", pTempMsg->asLCC.SNID, tSNIDA);
      break;
      case LCC_PSR:                         /*<< Protocol Support Reply + fddd, Protocol flags >>*/
         tSNIDA = (pTempMsg->asLCC.Data[0] & 0x0Fu) << 8;   // Get address alias from Data
         tSNIDA = tSNIDA | pTempMsg->asLCC.Data[1];
         if (Verbose) printf("Protocol Support Reply from {%03X} to {%03X}.\n>", pTempMsg->asLCC.SNID, tSNIDA);
      break;
      //Protocol Support </editor-fold>
      
      // <editor-fold defaultstate="collapsed" desc="Others">
      case LCC_OIR:                         /*<< Optional Interaction Rejected + fddd, error, optional info + fddd,
                                             error, optional info >>*/
         if (Verbose) printf("Optional Interaction Rejected from {%03X}.\n>", pTempMsg->asLCC.SNID);
      break;
      
      case LCC_TDE:                         /*<< Terminate Due to Error + fddd, error, optional info >>*/
         if (Verbose) printf("Terminate Due to Error from {%03X}.\n>", pTempMsg->asLCC.SNID);
      break;

      case ETP_IEG:                        /*<< Identify Events, SN Global >>*/
         if (Verbose) printf("Identify Events, SN Global from {%03X}.\n>", pTempMsg->asLCC.SNID);
      break;

      case ETP_IEA:                        /*<< Identify Events, Addressed >>*/
         if (Verbose) printf("Identify Events, Addressed from {%03X}.\n>", pTempMsg->asLCC.SNID);
      break;
      //Others </editor-fold>

      default:                              /*<< Unknown message >>*/
      {
         printf("Unknown LCC message, ");
         printMsg(pTempMsg);                               // Print Unknown message
      }
   }
}

/*───────────────── Send CCM, Global & Addressed MTI Message ─────────────────*/
void sendLCC(Word MTI, Byte * pData, uInt8 dlc)
{
   CANMsgt LCCTx;
   
   if(MTI >= 0x700 && MTI <= 0x720)              // it's a CAN Control Message
      LCCTx.asLCC.PFT.byte = LCC_CAN_CTRL_PFT;   // Load Prefix & Frame Type
   else                                          // it's a Global or Addressed MTI Message
      LCCTx.asLCC.PFT.byte = LCC_GA_MTI_PFT;     // Load Prefix & Frame Type
   
   LCCTx.asLCC.MTI = MTI;                        // Load MTI passed
   LCCTx.asLCC.SNID = SourceNodeID;              // Load our SNIDA
   LCCTx.asLCC.dlc = dlc;                        // Load DLC passed
   if (dlc)                                      // If there's data copy it
      memcpy(LCCTx.asLCC.Data, pData, dlc);
   LCC_transmit(&LCCTx);                         // Send it
}

/*──────────────────────────────── Send Event ────────────────────────────────*/
void sendEvent(Byte * aUNID, uInt16 eventNo)
{
   CANMsgt LCCTx;
   
   LCCTx.asLCC.PFT.byte = LCC_GA_MTI_PFT;        // Load Prefix & Frame Type
   LCCTx.asLCC.MTI = ETP_PCER;                   // Load Producer/Consumer Event Report
   LCCTx.asLCC.SNID = SourceNodeID;              // Load our SNIDA
   LCCTx.asLCC.dlc = 8;                          // Load DLC always 8
   memcpy(LCCTx.asLCC.Data, aUNID, 6);           // Load a UNID
   LCCTx.asLCC.Data[6] = eventNo >> 8;           // Copy Event, both bytes
   LCCTx.asLCC.Data[7] = eventNo & 0x00FF;       // Big endian
   
   LCC_transmit(&LCCTx);                         // Send it
}

/*────────────────────────── Send Node Information ───────────────────────────*/
void sendNodeInfo(Word toSNIDA, Word DataAddr)
{
            
   bool done = False;
   uInt8 vCount = 0, sCount;
   Byte B0, B1, eeData;
   CANMsgt PSRp_Msg;
   
   PSRp_Msg.asLCC.MTI = SP_SNIRp;                // Set MTI
   PSRp_Msg.asLCC.SNID = SourceNodeID;           // Load our(sender) SNIDA
   PSRp_Msg.asLCC.Data[0] = toSNIDA >> 8;        // Hi nibble of receiver SNIDA for data[]
   PSRp_Msg.asLCC.Data[1] = toSNIDA & 0x00FF;    // Lo byte of receiver SNIDA for data[]
   PSRp_Msg.asLCC.Data[0] |= MFF_First;          // Set flags to first frame

   do
   {
      
      for (uInt8 i = 2; i <= 8; i++)             // Put 6 values in data array
      {
         eeData = eeprom_rd_byte(DataAddr);      // Get data
         if (eeData < 0x20 && eeData != NULL)    // is it a count
         {
            sCount = eeData;                     // Save no of strings
            vCount++;                            // 2 max
         }
         if (eeData == NULL) sCount--;           // End of String
         PSRp_Msg.asLCC.Data[i] = (unsigned) eeData;  // Put data in array
         if (vCount == 2 && sCount == 0)         // Is it the END
         {
            PSRp_Msg.asLCC.Data[0] &= 0x0F;      // Clear flags
            PSRp_Msg.asLCC.Data[0] |= MFF_Last;  // Set flags to last frame
            done = True;                         // Set done flag
            break;                               // Even if not less than 6
         }
      }
      if (PSRp_Msg.asLCC.Data[0] == MFF_First)   // After first time it's middle, else END
         PSRp_Msg.asLCC.Data[0] |= MFF_Middle;   // Set flags to middle frame
      
      LCC_transmit(&PSRp_Msg);                   // Send message part
      
   } while (!done);
}

/*─────────────────────── NO Datagram or Stream (yet) ────────────────────────*/

/*────────────────────────────── LCC Transmit ────────────────────────────────*/
void LCC_transmit(CANMsgt * pLCCMsg)
{
   CANMsgt aTxMsg;
   Char    aCANstring[29];                       //x JMRI CAN string :XhhhhhhhhNdddddddddddddddd;NULL  29 chars max
   
   aTxMsg = *CAN_LCC2CAN(pLCCMsg);               // Copy to a CAN message
   if (EchoJMRI)
      printf(CAN2String(&aTxMsg, aCANstring));   // Echo CAN Msg to Serial CAN connection (JMRI)
   CAN_transmit(&aTxMsg);                        // Send to CAN Bus second, CAN_transmit mangles message
}

//LCC/OpenLCB Functions </editor-fold>

/*══════♦ ♦ ♦ ♦ ♦═══════♦ CAN Frame Transfer Functions ♦══════♦ ♦ ♦ ♦ ♦═══════*/
// <editor-fold defaultstate="collapsed" desc="CAN Frame Transfer Functions">

/*─────────────────────────── Process CAN Message ────────────────────────────*/
void processCANMessage(CANMsgt * pCANmsg)
{
   CANMsgt            LCCMsg;                              // A Temporary LCC Message
   
   if (pCANmsg->asCAN.idType == STANDARD_CAN_ID)           //<< Process Standard (11bit ID) message
   {
      if (Verbose)
      {
         printf("Standard CAN,");                          // Get & print Standard Source ID
         for (Byte i = 0; i <= 14; i++)
            printf(" %02X",pCANmsg->asArray[i]);           // Print the data
         printf("\n>");                                    // That's it for Standard CAN Messages.
      }
   }
   else                                                    //<< Process Extended (29bit ID) message
   {
      LCCMsg = *pCANmsg;                                   // Make copy for LCC/LCCCAN Processing
      CAN_CAN2LCC(&LCCMsg);                                // Convert message to LCC message
      
      switch (LCCMsg.asLCC.PFT.Prefix)
      {
         case LCC_CAN_MESSAGE:
            processLCC_CAN(&LCCMsg);                       // Take care of LCC/CAN Alias ID Maintenence
         break;
         case LCC_MESSAGE:                                 // It's a LCC Message
            if (LCCMsg.asLCC.MTI & EVENT_BIT)              // Check for Event Present bit in MTI
               processEvent(&LCCMsg);                      //<< Process LCC Event
            // else if datagram
            else
               processLCC(&LCCMsg);                        //<< Process LCC message
         break;
         case JWD_NHR_MESSAGE:
         case OTHER_NOT_USED:
            if (Verbose)
            {
               printf("Unknown,");                         // Print Unknown Extended CAN Message
               printMsg(&LCCMsg); 
            }
         break;
      }
   }
}

/*────────────────────── Process LCC CAN Control Message ─────────────────────*/
void processLCC_CAN(CANMsgt * pLCCMsg)
{
   CANMsgt CCMsg = *pLCCMsg;                                    // make temp copy
   uInt16 i = 0;                                                // Loop counter
   
   if (Permitted)
   {
      if (CCMsg.asLCC.PFT.FrameType <= 7 && CCMsg.asLCC.PFT.FrameType >= 4) //<< CID Check ID frame
      {
         if (Verbose) printf("CID frame %1X from {%03X}.\n>",
                             CCMsg.asLCC.PFT.FrameType, CCMsg.asLCC.SNID);
         if (CCMsg.asLCC.SNID == SourceNodeID)                  // Our SNIDA! (somebody trying to get our reserved ID)
         {
            if (Verbose) printf("Somebody asking for our Node ID.\n>");
            sendLCC(CCM_RID, NULL, 0);                          // Send Reserve ID (RID) frame to stop them
         }
      }
      else if (CCMsg.asLCC.MTI == CCM_AMD)                      //<< AMD Alias Map Definition 
      {
         if (Verbose) printf("AMD from %s{%03X}.\n>", msgUNID2String(&CCMsg, UNIDstring), CCMsg.asLCC.SNID);
         updateNodeList(CCMsg.asLCC.Data, CCMsg.asLCC.SNID, 2); // Save Alias & Full address in list
      }
      else if (CCMsg.asLCC.MTI == CCM_RID)                      //<< RID Reserve ID or CIM
      {
         if(CCMsg.asLCC.SNID == SourceNodeID)
            printf("ERROR! Reserve ID RID(CIM) Received and it's OUR's.\n>");
         else
            if (Verbose) printf("RID(CIM) from {%03X}.\n>", CCMsg.asLCC.SNID);
      }
      else if (CCMsg.asLCC.MTI == CCM_AME)                      //<< AME Alias Map Enquiry
      {                                                         // Node ID Alias validation
         if(CCMsg.asLCC.dlc)                                    // If Data it's a LCCUID and only for one node
         {
            if (memcmp(nodesUNID, CCMsg.asLCC.Data, 6) == 0)    // LCCUIDs equal
            {                                                   // If so respond with AMD
               if (Verbose) printf("Specific AME from %s{%03X}, Match, Responding with AMD.\n>",
                                   msgUNID2String(&CCMsg, UNIDstring), CCMsg.asLCC.SNID);
               sendLCC(CCM_AMD, nodesUNID, 6);                  // Send Alias Map Definition frame with LCCUID
            }
            else                                                // LCCUIDs NOT equal, No Response
               if (Verbose) printf("Specific AME from %s{%03X}, No match No Response.\n>",
                                   msgUNID2String(&CCMsg, UNIDstring), CCMsg.asLCC.SNID);
         }
         else                                                   // If no Data it's General, respond with AMD
         {
            if (Verbose) printf("General AME from {%03X}, Responding with AMD.\n>", CCMsg.asLCC.SNID);
            while (i != (SourceNodeID << 4)) i++;               // Wait randomly for other nodes to respond
            sendLCC(CCM_AMD, nodesUNID, 6);                     // Send Alias Map Definition frame with LCCUID
         }
      }
                                                                //<< Add AMR & Errors
   }
   else                                                         // (!Permitted)
   {// When not permitted this is the only message allowed
      if (CCMsg.asLCC.MTI == CCM_RID && CCMsg.asLCC.SNID == SourceNodeID)      //<< RID to our CID, SNIDA already taken
      {
         if (Verbose) printf("\nNode ID already taken! Trying again.\n>");
         printMsg(&CCMsg);
         ReserveNodeIDalias(CRID_ABORT, &State);                // Abort CID sequence
         ReserveNodeIDalias(CRID_FIRST, &State);                // and try again
      }
   }
}

/*────────────────────────── Reserve a Node ID alias ─────────────────────────*/
void ReserveNodeIDalias(uInt8 CIDno, uInt8 * pState)
{                                                               // 0=Abort, 1=Generate Random ID, 2=Continue CIDs
   static uInt8 frameNo;                                        // Frame Sequence Number 0x07-0x04
   CANMsgt tmpCIDmsg;
   
   if (CIDno == CRID_FIRST)                                     // 1st time through
   {
      SourceNodeID = CAN_Generate_Alias(nodesUNID);             // Get Random SourceNodeID Alias
      frameNo = 0x07;                                           // Set Frame Sequence Number
      printf("Source Node ID Alias 0x%03X ", SourceNodeID);
   }

   if (CIDno >= CRID_FIRST)                                     // 1=Generate Random ID or 2=Continue CIDs
   {
      printf(".");
      tmpCIDmsg.asLCC.PFT.Prefix = LCC_CAN_MESSAGE;             // Put message 1of4 together
      tmpCIDmsg.asLCC.SNID = SourceNodeID;                      // SNIDA we're trying to get
      tmpCIDmsg.asLCC.dlc = 0;                                  // There are no data bytes

      if (frameNo >= 0x04)
      {                                                         //< frame 0x07-0x04 CIDs
         tmpCIDmsg.asLCC.PFT.FrameType = frameNo;               // CID frame count of 4
         tmpCIDmsg.asLCC.MTI = UNID2part(frameNo, nodesUNID);   // UID part uid of 4
         LCC_transmit(&tmpCIDmsg);                              // send it
         *pState = sCID;                                        // timeOut will make the next call
         if (frameNo == 0x04)
            timeOut1 = countOfmS + 200;                         // Wait for 200 mS on last CID frame
         else
            timeOut1 = countOfmS + 50;                          // Wait for 30 mS
         frameNo--;                                             // Next CID
      }
      else if (frameNo == 0x03)
      {                                                         //< frame 0x03 RID
         sendLCC(CCM_RID, NULL, 0);                             // Send Reserve ID (RID) frame
         printf(" Reserved.\n");
         __delay_ms(30);                                        // Inter-message timeout
         sendLCC(CCM_AMD, nodesUNID, 6);                        // Send Alias Map Definition (AMD) frame
         printf("Alias Map Definition (AMD) frame sent. Permitted.\n");
         updateNodeList(nodesUNID, SourceNodeID, 1);            // Save our new Alias, Full address & status in list
         Permitted = True;                                      // Now we are Permitted
         __delay_ms(75);                                        // Wait for previous message
         sendLCC(LCC_IC_FS, nodesUNID, 6);                      // Send Initialization Complete
         printf("Initialization Complete FS sent. From %s\n", UNID2String(nodesUNID, UNIDstring));
         __delay_ms(30);                                        // Inter-message timeout
         sendLCC(CCM_AME, NULL, 0);                             // Send Alias Map Enquiry
         printf("Alias Map Enquiry (AME) Sent.\n");
         frameNo--;                                             // finished go cleanup
      }
   }

   if (CIDno == CRID_ABORT || frameNo == 0x02)                  // Abort or End Reserve Node sequence
   {
      *pState = sIDLE;                                          // ReSet Time Out State
      timeOut1 = 0;                                             // ReSet timeOut1 timer
      frameNo = 0x07;                                           // ReSet frame sequence number
   }
}

/*────────────────────────── Generate Node ID Alias ──────────────────────────*/
Word CAN_Generate_Alias(Byte * LCCUNID)
{
   Word anAliasID;
   
   while ((anAliasID  = (unsigned) TIMER3_WORD & 0x0FFFu) == 0);     // Simple random number

   return (anAliasID);
}
  
/*─────────────────────────── Save Alias & Full ID ───────────────────────────*/
void updateNodeList(Byte * UID, Word SNIDA, Byte Status)
{              // 
   Byte tempUID[6];
   bool add = True;
   
   // check for repeat & alias reassignment
   for(Byte i = 0; i <= AFlistEnd; i++)          // Go through the list
   {
      if (memcmp(UID, AFaddr[i].Full, 6) == 0)   // UIDs equal
      {                                          // Check AID
         if (SNIDA != AFaddr[i].Alias)           // AIDs NOT equal
         { 
            AFaddr[i].Alias = SNIDA;             // Replace AID
         }
         add = False;                            // DON'T Add
         if(Status)                              // Update status
         {
            AFaddr[AFlistEnd].Stat |= Status;
         }
      }  
   }
   
   if(add == True) // if end add it
   {
      AFaddr[AFlistEnd].Alias = SNIDA;           // Copy SNIDA
      memcpy(AFaddr[AFlistEnd].Full, UID, 6);    // Copy LCCUID
      if(Status)                                 // Update status
      {
         AFaddr[AFlistEnd].Stat |= Status;
      }
      AFlistEnd++;                               // Bump End pointer
   }
}
   
/*──────────────────────────── Display Node List ─────────────────────────────*/
void displayNodeList(void)
{              // 
   for (uInt8 i = 0; i < AFlistEnd; i++)
   {
      printf("%02d) %03X = %02X:%02X:%02X:%02X:%02X:%02X, %d\n",
             i,
             AFaddr[i].Alias,
             AFaddr[i].Full[0], AFaddr[i].Full[1], AFaddr[i].Full[2],
             AFaddr[i].Full[3], AFaddr[i].Full[4], AFaddr[i].Full[5],
             AFaddr[i].Stat);
   }
   printf("\n>");
}

//CAN Frame Transfer Functions </editor-fold>

/*═══════════♦ ♦ ♦ ♦ ♦══════════♦ CAN Functions ♦═════════♦ ♦ ♦ ♦ ♦═══════════*/
// <editor-fold defaultstate="collapsed" desc="CAN Functions">

/*────────────────────────────── Initialize CAN ──────────────────────────────*/
void ECAN_Initialize(void)
{

   TRISBbits.TRISB3          = 0b1;                   // CAN Rx
   TRISBbits.TRISB2          = 0b0;                   // CAN Tx

   CANCONbits.REQOP = CAN_MODE_CONFIG;                // Requests Configuration mode 
   while (CANSTATbits.OPMODE != CAN_MODE_CONFIG);     // wait until ECAN is in config mode
    
   ECANCONbits.MDSEL = 0;                             // Mode 0 [LCC Mode 1 more buffers?][see p398]
   
  /*>>>>>>>>>>>>>>>> RXB0CONbits.RXM0  RXB0CONbits.RXM1 <<<<<<<<<<<<<<*/
 /*>>>>>>>>>>>>>>>> RXB1CONbits.RXM0  RXB1CONbits.RXM1 <<<<<<<<<<<<<<*/
 /*Mode 0:
RXM<1:0>: Receive Buffer Mode bit 1 (combines with RXM0 to form RXM<1:0> bits, see bit 5)
11 = Receive all messages (including those with errors); filter criteria is ignored
10 = Receive only valid messages with extended identifier; EXIDEN in RXFnSIDL must be ‘1’
01 = Receive only valid messages with standard identifier, EXIDEN in RXFnSIDL must be ‘0’
00 = Receive all valid messages as per EXIDEN bit in RXFnSIDL register<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  */
   
   
/*♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦ MessageID, Mask and Filter definitions ♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦*/

/**
CAN ID	            Mask		Filter		Buffer
........................................................
0x444		Acceptance Mask 0		Filter 0	RXB0
0x555		Acceptance Mask 0		Filter 0	RXB0
0x666		Acceptance Mask 0		Filter 1	RXB1
In Mode 0, acceptance filters, RXF0 and RXF1, and filter mask, RXM0, are associated with RXB0.
Filters, RXF2, RXF3, RXF4 and RXF5, and mask, RXM1, are  associated with RXB1.
 */
    
/*♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦ Initialize Receive Acceptance Masks ♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦*/
// <editor-fold defaultstate="collapsed" desc="Masks">
/*•••••• Receive Acceptance Mask 0 = 0x00000000 ••••••*/
   RXM0EIDH             = 0x00;        // Extended Identifier Mask Register, High Byte <15:8>
   RXM0EIDL             = 0x00;        // Extended Identifier Mask Register, Low Byte <7:0>
   RXM0SIDH             = 0x00;        // Standard Identifier Mask Register, High Byte <10:3> (EID<28:21>) 
   RXM0SIDL             = 0x00;        // Standard Identifier Mask Register, Low Byte <2:0> (EID<20:18><17:16>)
   // <editor-fold defaultstate="collapsed" desc="RXMnSIDL">
// RXM0SIDLbits.SID     = 0b000;    //bit 7-5 Standard <2:0> or Extended<20:18> Identifier Mask bits
                                    //bit 4 Unimplemented: Read as ‘0’
// RXM0SIDLbits.EXIDEN  = 0b0;      //bit 3 Extended Identifier Filter Enable Mask bit (only in Mode 1 & 2)
   /*                                                                    Mode 0: Unimplemented: Read as ‘0’
      1 = Messages selected by the EXIDEN bit in RXFnSIDL will be accepted
      0 = Both standard and extended identifier messages will be accepted
   */
                                    //bit 2 Unimplemented: Read as ‘0’
// RXM0SIDLbits.EID     = 0b00;     //bit 1-0 Extended Identifier Mask bits <17:16>
   //RXMnSIDL </editor-fold>
   
/*•••••• Receive Acceptance Mask 1 = 0xFFFFFFFF ••••••*/
   RXM1EIDH             = 0xFF;
   RXM1EIDL             = 0xFF;
   RXM1SIDH             = 0xFF;
   RXM1SIDL             = 0xFF;  // 0xE0?
   
//Masks </editor-fold>

/*♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦ Initialize Receive Acceptance Filters ♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦*/
// <editor-fold defaultstate="collapsed" desc="Filters">
/*•••••• Receive Acceptance Filter 0 = 0x00008880 ••••••*/
   RXF0EIDH             = 0x00;        // Extended Identifier Filter Register, High Byte <15:8>
   RXF0EIDL             = 0x00;        // Extended Identifier Filter Register, Low Byte <7:0>
   RXF0SIDH             = 0x88;        // Standard Identifier Filter Register, High Byte <10:3> (EID<28:21>)
   RXF0SIDL             = 0x80;        // Standard Identifier Filter Register, Low Byte <2:0> (EID<20:18><17:16>)
   // <editor-fold defaultstate="collapsed" desc="RXFnSIDL">
// RXF0SIDLbits.SID     = 0b000;       //bit 7-5  Standard<2:0> or Extended<20:18> Identifier Mask bits
                                       //bit 4 Unimplemented: Read as ‘0’
// RXF0SIDLbits.EXIDEN  = 0b1;         //bit 3 Extended Identifier Filter Enable bit (In Mode 0, this bit must be
   /*                                    set/cleared as required, irrespective of corresponding mask register value.)
      1 = Filter will only accept extended ID messages
      0 = Filter will only accept standard ID messages
   */
                                       //bit 2 Unimplemented: Read as ‘0’
// RXF0SIDLbits.EID     = 0b00         //bit 1-0 Extended Identifier Filter bits <17:16>
   //RXFnSIDL </editor-fold>
   RXF0SIDLbits.EXIDEN  = 0b1;         // EID Filter Enable bit (In Mode 0, this bit must be set/cleared as required

/*•••••• Receive Acceptance Filter 1 = 0x0000CCC0 ••••••*/
   RXF1EIDH             = 0x00;
   RXF1EIDL             = 0x00;
   RXF1SIDH             = 0xCC;
   RXF1SIDL             = 0xC0;

/*•••••• Receive Acceptance Filter 2 = 0x00000000 ••••••*/
   RXF2EIDH             = 0x00;
   RXF2EIDL             = 0x00;
   RXF2SIDH             = 0x00;
   RXF2SIDL             = 0x00;

/*•••••• Receive Acceptance Filter 3 = 0x00000000 ••••••*/
   RXF3EIDH             = 0x00;
   RXF3EIDL             = 0x00;
   RXF3SIDH             = 0x00;
   RXF3SIDL             = 0x00;

/*•••••• Receive Acceptance Filter 4 = 0x00000000 ••••••*/
   RXF4EIDH             = 0x00;
   RXF4EIDL             = 0x00;
   RXF4SIDH             = 0x00;
   RXF4SIDL             = 0x00;

/*•••••• Receive Acceptance Filter 5 = 0x00000000 ••••••*/
   RXF5EIDH             = 0x00; 
   RXF5EIDL             = 0x00;
   RXF5SIDH             = 0x00;
   RXF5SIDL             = 0x00;
   
/*•••••• Receive Acceptance Filters 6 to 15 in Mode 1 & 2 ••••••*/

//Filters </editor-fold>

/*♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦ Baud Rate Setting ♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦♦*/
// <editor-fold defaultstate="collapsed" desc="Baud Rate">
/**    Initialize CAN Timings  [see p446]  
	Baud rate: LCC spec 125kbs [p449]
	System frequency: 16000000 Hz
	Time quanta: 8
	Sample point: 1-2-1-4
	Sample point: 75.00% [p449 says 80%]
*/
   // <editor-fold defaultstate="collapsed" desc="BRGCON1">
// BRGCON1                    = 0x00;          // Baud Rate Control Register 1 [see p430]

// BRGCON1bits.SJW            = 0b00;          //bit 7-6 Synchronized Jump Width bits
   /*
      11 = Synchronization jump width time = 4 x TQ
      10 = Synchronization jump width time = 3 x TQ
      01 = Synchronization jump width time = 2 x TQ
      00 = Synchronization jump width time = 1 x TQ
   */
   BRGCON1bits.BRP            = 0x07;          //bit 5-0 Baud Rate Prescaler bits [for 125kbs]
   /*
      111111 = TQ = (2 x 64)/FOSC
      111110 = TQ = (2 x 63)/FOSC
      ...
      000001 = TQ = (2 x 2)/FOSC
      000000 = TQ = (2 x 1)/FOSC
   */
   //BRGCON1 </editor-fold>
   
   // <editor-fold defaultstate="collapsed" desc="BRGCON2">
// BRGCON2                    = 0x01;          // Baud Rate Control Register 2 [see p431]

   BRGCON2bits.SEG2PHTS       = 0b1;           //bit 7 Phase Segment 2 Time Select bit
   /*
      1 = Freely programmable
      0 = Maximum of PHEG1 or Information Processing Time (IPT), whichever is greater
   */
// BRGCON2bits.SAM            = 0b0;           //bit 6 Sample of the CAN bus Line bit
   /*
      1 = Bus line is sampled three times prior to the sample point
      0 = Bus line is sampled once at the sample point
   */
   BRGCON2bits.SEG1PH         = 0b010;         //bit 5-3 Phase Segment 1 bits
   /*
      111 = Phase Segment 1 time = 8 x TQ
      110 = Phase Segment 1 time = 7 x TQ
      101 = Phase Segment 1 time = 6 x TQ
      100 = Phase Segment 1 time = 5 x TQ
      011 = Phase Segment 1 time = 4 x TQ
      010 = Phase Segment 1 time = 3 x TQ
      001 = Phase Segment 1 time = 2 x TQ
      000 = Phase Segment 1 time = 1 x TQ
   */
   BRGCON2bits.PRSEG          = 0b001;         //bit 2-0 Propagation Time Select bits
   /*
      111 = Propagation time = 8 x TQ
      110 = Propagation time = 7 x TQ
      101 = Propagation time = 6 x TQ
      100 = Propagation time = 5 x TQ
      011 = Propagation time = 4 x TQ
      010 = Propagation time = 3 x TQ
      001 = Propagation time = 2 x TQ
      000 = Propagation time = 1 x TQ
   */
   //BRGCON2 </editor-fold>
 
   // <editor-fold defaultstate="collapsed" desc="BRGCON3">
// BRGCON3                    = 0x03;          // Baud Rate Control Register 3 [see p432]

// BRGCON3bits.WAKDIS         = 0b0;           //bit 7 Wake-up Disable bit
   /*
      1 = Disable CAN bus activity wake-up feature
      0 = Enable CAN bus activity wake-up feature
   */
// BRGCON3bits.WAKFIL         = 0b0;           //bit 6 Selects CAN bus Line Filter for Wake-up bit
   /*
      1 = Use CAN bus line filter for wake-up
      0 = CAN bus line filter is not used for wake-up
   */
                                               //bit 5-3 Unimplemented: Read as ‘0’
   BRGCON3bits.SEG2PH         = 0b001;         //bit 2-0 Phase Segment 2 Time Select bits(These bits are ignored if SEG2PHTS bit (BRGCON2<7>) is ‘0’.)
   /*
      111 = Phase Segment 2 time = 8 x TQ
      110 = Phase Segment 2 time = 7 x TQ
      101 = Phase Segment 2 time = 6 x TQ
      100 = Phase Segment 2 time = 5 x TQ
      011 = Phase Segment 2 time = 4 x TQ
      010 = Phase Segment 2 time = 3 x TQ
      001 = Phase Segment 2 time = 2 x TQ
      000 = Phase Segment 2 time = 1 x TQ
   */
   //BRGCON3 </editor-fold>
//Baud Rate </editor-fold>

   
   // <editor-fold defaultstate="collapsed" desc="CAN Interrupts Enaable PIE5">
// PIE5                      = 0x00;        // Peripheral Interrupt Enable Register 5
// PIE5bits.IRXIE            = 0b0;         //7 CAN Bus Error Message Received Interrupt Enable bit
// PIE5bits.WAKIE            = 0b0;         //6 CAN bus Activity Wake-up Interrupt Enable bit
// PIE5bits.ERRIE            = 0b0;         //5 CAN bus Error Interrupt Enable bit

// PIE5bits.TXB2IE           = 0b0;         //4 CAN Transmit Buffer 2 Interrupt Enable bit <MODE 0>
// PIE5bits.TXBnIE           = 0b0;         //4 CAN Transmit Buffer Interrupts Enable bit  <MODE 1 or 2>

// PIE5bits.TXB1IE           = 0b0;         //3 CAN Transmit Buffer 1 Interrupt Enable bit <Mode 1 & 2, forced to ‘0’>
// PIE5bits.TXB0IE           = 0b0;         //2 CAN Transmit Buffer 0 Interrupt Enable bit <Mode 1 & 2, forced to ‘0’>
   PIE5bits.RXB1IE           = 0b1;         //1 CAN Receive Buffer 1 Interrupt Enable bit  <MODE 0>
// PIE5bits.RXBnIE           = 0b0;         //1 CAN Receive Buffer Interrupts Enable bit   <MODE 1 or 2>

   PIE5bits.RXB0IE           = 0b1;         //0 CAN Receive Buffer 0 Interrupt Enable bit  <MODE 0>
                                            //0 Mode 1 Unimplemented: Read as 0            <MODE 1>
// PIE5bits.FIFOWMIE         = 0b0;         //0 FIFO Watermark Interrupt Enable bit        <MODE 2>

// IPR5                      = 0x00;        // Peripheral Interrupt Priority Register 5
// IPR5bits.IRXIP            = 0b0;         //7 Invalid Message Received Interrupt Priority
// IPR5bits.WAKIP            = 0b0;         //6 Bus Wake-up Activity Interrupt Priority
// IPR5bits.ERRIP            = 0b0;         //5 CAN Bus Error Interrupt Priority
// IPR5bits.TXB2IP           = 0b0;         //4 Transmit Buffer 2 Interrupt Priority
// IPR5bits.TXB1IP           = 0b0;         //3 Transmit Buffer 1 Interrupt Priority
// IPR5bits.TXB0IP           = 0b0;         //2 Transmit Buffer 0 Interrupt Priority
   IPR5bits.RXB1IP           = HIGH;        //1 Receive Buffer 1 Interrupt Priority (Mode 0)
                                            //1 Received messages Interrupt Priority (Mode 1&2)
   IPR5bits.RXB0IP           = HIGH;        //0 Receive Buffer 0 Interrupt Priority (Mode 0)
                                            //0 Unimplemented (Mode 1)
// IPR5bits.FIFOFIP          = 0b0;         //0 FIFO Full Interrupt Priority (Mode 2)

   //CAN Interrupts Enaable PIE5 </editor-fold>
   
   CANCONbits.REQOP = CAN_MODE_NORMAL;                // Requests Normal mode
   while (CANSTATbits.OPMODE != CAN_MODE_NORMAL);     // wait until ECAN is in Normal mode
}

/*─────────────────────────────── CAN Transmit ───────────────────────────────*/
Byte CAN_transmit(CANMsgt * pCanMsg)
{
   CANMsgt aTxMsg;
   Byte returnValue;

   memcpy(&aTxMsg, pCanMsg, sizeof (CANMsgt));   // Copy to mangle
                                                 // Note: SRR bit transmitted Low is Received High
   CAN_CAN2Reg(&aTxMsg);                         // Convert CAN message to Registers

   if (TXB0CONbits.TXREQ == 0)                   // if no request pending in TxBuffer 0 use it 
   {
      TXB0SIDH = aTxMsg.asRegister.XBnSIDH;      // Load temp registers to ECAN registers
      TXB0SIDL = aTxMsg.asRegister.XBnSIDL;
      TXB0EIDH = aTxMsg.asRegister.XBnEIDH;
      TXB0EIDL = aTxMsg.asRegister.XBnEIDL;
      TXB0DLC  = aTxMsg.asRegister.XBnDLC;       // Load the data registers
      TXB0D0   = aTxMsg.asRegister.XBnD0;
      TXB0D1   = aTxMsg.asRegister.XBnD1;
      TXB0D2   = aTxMsg.asRegister.XBnD2;
      TXB0D3   = aTxMsg.asRegister.XBnD3;
      TXB0D4   = aTxMsg.asRegister.XBnD4;
      TXB0D5   = aTxMsg.asRegister.XBnD5;
      TXB0D6   = aTxMsg.asRegister.XBnD6;
      TXB0D7   = aTxMsg.asRegister.XBnD7;

      TXB0CONbits.TXREQ = 1;                     //Set the buffer to transmit		
      returnValue = 1;
   }
   else if (TXB1CONbits.TXREQ == 0)              // if no request pending in TxBuffer 1 use it 
   {
      TXB1SIDH = aTxMsg.asRegister.XBnSIDH;      // Load temp registers to ECAN registers
      TXB1SIDL = aTxMsg.asRegister.XBnSIDL;
      TXB1EIDH = aTxMsg.asRegister.XBnEIDH;
      TXB1EIDL = aTxMsg.asRegister.XBnEIDL;
      TXB1DLC  = aTxMsg.asRegister.XBnDLC;       // Load the data registers
      TXB1D0   = aTxMsg.asRegister.XBnD0;
      TXB1D1   = aTxMsg.asRegister.XBnD1;
      TXB1D2   = aTxMsg.asRegister.XBnD2;
      TXB1D3   = aTxMsg.asRegister.XBnD3;
      TXB1D4   = aTxMsg.asRegister.XBnD4;
      TXB1D5   = aTxMsg.asRegister.XBnD5;
      TXB1D6   = aTxMsg.asRegister.XBnD6;
      TXB1D7   = aTxMsg.asRegister.XBnD7;

      TXB1CONbits.TXREQ = 1;                     //Set the buffer to transmit		
      returnValue = 1;
   }
   else if (TXB2CONbits.TXREQ == 0)              // if no request pending in TxBuffer 2 use it 
   {
      TXB2SIDH = aTxMsg.asRegister.XBnSIDH;      // Load temp registers to ECAN registers
      TXB2SIDL = aTxMsg.asRegister.XBnSIDL;
      TXB2EIDH = aTxMsg.asRegister.XBnEIDH;
      TXB2EIDL = aTxMsg.asRegister.XBnEIDL;
      TXB2DLC  = aTxMsg.asRegister.XBnDLC;       // Load the data registers
      TXB2D0   = aTxMsg.asRegister.XBnD0;
      TXB2D1   = aTxMsg.asRegister.XBnD1;
      TXB2D2   = aTxMsg.asRegister.XBnD2;
      TXB2D3   = aTxMsg.asRegister.XBnD3;
      TXB2D4   = aTxMsg.asRegister.XBnD4;
      TXB2D5   = aTxMsg.asRegister.XBnD5;
      TXB2D6   = aTxMsg.asRegister.XBnD6;
      TXB2D7   = aTxMsg.asRegister.XBnD7;

      TXB2CONbits.TXREQ = 1;                     //Set the buffer to transmit		
      returnValue = 1;
   }
   return (returnValue);
}

/*────────────────────────── LCC Message to CAN ID ───────────────────────────*/
CANMsgt * CAN_LCC2CAN(CANMsgt * pLCCMsg)
{
   DWord CAN_ID;                                      // is a Little Endian Double Word for 29bit CAN ID
   Word mti = pLCCMsg->asLCC.MTI;                     // is a Little Endian Word for MTI
   Word snida = pLCCMsg->asLCC.SNID;                  // is a Little Endian Word for SNIDA
   Byte tmp;

   CAN_ID = (DWord)pLCCMsg->asLCC.PFT.byte << 24;     // PFT byte to MSB byte1
   CAN_ID |= (DWord)(mti & 0x0FF0) << 12;             // MTI middle 2 nibbles shift to byte2
   tmp = (mti & 0x000F) << 4 | (snida & 0x0F00) >> 8; // Bottom nib of MTI to upper nib of byte +
                                                      // upper nib of SNID to bottom of byte
   CAN_ID |= (DWord)tmp << 8;                         // Put top/bottom combo nibble in byte3
   CAN_ID |= snida & 0x00FF;                          // Put SNID lower 2 nibs in nibbles 7-8

   pLCCMsg->asCAN.idType = EXTENDED_CAN_ID;           // Set CAN header type
   pLCCMsg->asCAN.id = CAN_ID;                        // Load id into structure
   
   return pLCCMsg;
}

/*────────────────────────── CAN ID to LCC Message ───────────────────────────*/
CANMsgt * CAN_CAN2LCC(CANMsgt * pCANMsg)
{
   DWord CAN_ID = pCANMsg->asCAN.id;                       // is a Double Word for 29bit CAN ID
                                                           // MTI & SNIDA are Words
   pCANMsg->asLCC.PFT.byte = (CAN_ID & 0x1F000000) >> 24;  // Get PFT from id
   pCANMsg->asLCC.MTI = (CAN_ID &  0x00FFF000) >> 12;      // Get MIT nibbles & position it
   pCANMsg->asLCC.SNID = CAN_ID & 0x00000FFF;              // Get SNIDA nibbles
   
   return pCANMsg;
}

/*─────────────────────────── Registers to CAN ID ────────────────────────────*/
CANMsgt * CAN_Reg2CAN(CANMsgt * pCANMsg)
{               // Needs To Be Packed with Endians Swapped
   DWord CAN_ID;                                                     // is Little Endian Double Word for 29bit CAN ID
   Byte tempByte;

   if (pCANMsg->asRegister.XBnSIDL & 0x08)                           // Check EXID=1 for Extended Message
   {                                                                 // message is extended
      pCANMsg->asCAN.idType = EXTENDED_CAN_ID;                       // Set type
      CAN_ID = (DWord)pCANMsg->asRegister.XBnEIDL;                   // Get <7:0> LSB to lo addr
      CAN_ID |= (DWord)pCANMsg->asRegister.XBnEIDH << 8;             // Get & Position <15:8> & combine
      tempByte = pCANMsg->asRegister.XBnSIDL & 0x03u;                // Get <17:16>
      tempByte |= (pCANMsg->asRegister.XBnSIDL & 0xE0u) >> 3;        // Get <20:18> shift to position & Combine <20:16>
      tempByte |= (pCANMsg->asRegister.XBnSIDH & 0x07u) << 5;        // Get SIDH <23:21> shift to position & Combine
      CAN_ID |= (DWord)tempByte << 16;                               // Position <23:16> & combine
      CAN_ID |= (DWord)(pCANMsg->asRegister.XBnSIDH & 0xF8) << 21;   // Get & Position <28:24>
   }
   else                                                              // message is standard, EXID=0
   {
      pCANMsg->asCAN.idType = STANDARD_CAN_ID;                       // Set type
      CAN_ID = (unsigned) pCANMsg->asRegister.XBnSIDL >> 5;          // Get & Position <3:0>
      CAN_ID |= (DWord)pCANMsg->asRegister.XBnSIDH << 3;             // Get & Position <10:3> & combine
   }
   pCANMsg->asCAN.id = CAN_ID;
   return pCANMsg;
}

/*─────────────────────────── CAN ID to Registers ────────────────────────────*/
CANMsgt * CAN_CAN2Reg(CANMsgt * pCANMsg)
{
   DWord CAN_ID;                                                     // is Little Endian Double Word for 29bit CAN ID
   Byte tempByte;

   CAN_ID = pCANMsg->asCAN.id;                                       // Copy to mutate from
   if (pCANMsg->asCAN.idType == EXTENDED_CAN_ID)
   {
      pCANMsg->asRegister.XBnEIDL = CAN_ID & 0x000000FF;             // EIDL<7:0> Mask out
      pCANMsg->asRegister.XBnEIDH = (CAN_ID & 0x0000FF00) >> 8;      // EIDH<15:8> Mask out & shift to byte

      tempByte =  (CAN_ID & 0x1F000000) >> 21;                       //<28:24> SIDH<28:21>, <28:24> mask & move to top of byte
      tempByte |= (CAN_ID & 0x00E00000) >> 21;                       //<23:21>
      pCANMsg->asRegister.XBnSIDH = tempByte;                        // Mask & position <23:21> & combine
      
                                                                     // Make SIDL <20:18><0, EXIDE=1, 0><17:16>
      tempByte =  (CAN_ID & 0x001C0000) >> 13;                       // get <20:18> Mask out & shift to top of byte
      tempByte |= (CAN_ID & 0x00030000) >> 16;                       // get <17:16> shift to bottom of byte & combine
      pCANMsg->asRegister.XBnSIDL = tempByte | 0x08u;                // Turn on EXIDE=1 bit
   }
   else //(pCANMsg->asCAN.idType == STANDARD_CAN_ID)
   {
      pCANMsg->asRegister.XBnEIDH = 0;                               // Reset Extended registers
      pCANMsg->asRegister.XBnEIDL = 0;
      tempByte =  (CAN_ID & 0xF8000000) >> 27;                       // Make SIDH<10:3>, Mask & shift <7:3> to bottom
      tempByte |= (CAN_ID & 0x00070000) >> 16;
      pCANMsg->asRegister.XBnSIDH = tempByte;                        // <10:8> Mask & shift to lo byte & combine
                                                                     // Make SIDL<2:0> Mask it out & put in top of byte
      pCANMsg->asRegister.XBnSIDL = (CAN_ID & 0x000E0000) << 16;     // SIDL, EXIDE & RSS=0, Turned off by mask
   }
   pCANMsg->asCAN.idType = CAN_REGISTERS;
   return pCANMsg;
}

/*────────────────────────────── String to CAN ───────────────────────────────*/
CANMsgt * String2CAN(Char * pCANstring, CANMsgt * pCanMsg)
{                      // :XhhhhhhhhNdddddddddddddddd;NULL  29 chars max
                       // 01234567890123456789012345678
                       // :ShhhNdddddddddddddddd;NULL  24 chars max
   Char tmpStr[8];
   DWord t32;
   Word t16;
   Byte t8;

   if (pCANstring[1] == 'S')                     // It's a Standard string
   {
      pCanMsg->asCAN.idType = STANDARD_CAN_ID;   // Set CAN frame type 
      for (uInt8 i = 2; i <= 4; i++)             // Copy ID hex values
         tmpStr[i - 2u] = pCANstring[i];         // to string
      t16 = xtoi((const Char *)tmpStr);          // Hex string to Int
      pCanMsg->asCAN.id = t16;                   // Set CAN frame ID (11 bit)
      
      pCanMsg->asCAN.dlc = (strlen((const Char *)pCANstring) - 7) / 2;   // sans :SiiiN;  n bytes * 2 
      for (uInt8 i = 0; i < pCanMsg->asCAN.dlc; i++)  // Put data in, if any
      {
         tmpStr[0] = pCANstring[6 + i * 2];      // Start at char 6
         tmpStr[1] = pCANstring[7 + i * 2];
         tmpStr[2] = NULL;
         t8 = xtoi((const Char *)tmpStr);        // Hex string to Int
         pCanMsg->asCAN.Data[i] = t8;
      }
   }
   else if (pCANstring[1] == 'X')                // It's an Extended string
   {
      pCanMsg->asCAN.idType = EXTENDED_CAN_ID;   // Set CAN frame type
      for (uInt8 i = 2; i <= 5; i++)             // Copy upper half ID hex values
         tmpStr[i - 2u] = pCANstring[i];         // to string (32bit xtoi() NOT supported)
      t32 = (DWord)xtoi((const Char *)tmpStr) << 16;           // Hex string to int & position
      for (uInt8 i = 6; i <= 9; i++)             // Copy lower half ID hex values
         tmpStr[i - 2u] = pCANstring[i];         // to string
      t16 = xtoi((const Char *)tmpStr);                        // Hex string to Int
      pCanMsg->asCAN.id = t32 | t16;             // Set CAN frame ID (29 bit)
      
      pCanMsg->asCAN.dlc = (strlen((const Char *)pCANstring) - 12) / 2;  // sans :XiiiiiiiiN;  n bytes * 2 
      for (uInt8 i = 0; i < pCanMsg->asCAN.dlc; i++)       // Put data in, if any
      {
         tmpStr[0] = pCANstring[11 + i * 2];     // Start at char 11    11 13 15 17 19 21 23 25
         tmpStr[1] = pCANstring[12 + i * 2];     //                     0  1  2  3  4  5  6  7
         tmpStr[2] = NULL;                       //        i * 2 + 11   11 13 15 17 19 21 23 25 
         t8 = xtoi((const Char *)tmpStr);        // Hex string to Int
         pCanMsg->asCAN.Data[i] = t8;
      }
   }
   else                                          // ERROR
   {
      pCanMsg->asCAN.idType = 0x00;
      pCanMsg->asCAN.id = 0x99999999;
      pCanMsg->asCAN.dlc = 0;
   }
   return pCanMsg;                               // Return CAN Message
}

/*────────────────────────────── CAN to String ───────────────────────────────*/
Char * CAN2String(CANMsgt * pCANmsg, Char * pCANstring)
{
   Char tmpStr[8];
   
   if(pCANmsg->asCAN.idType == STANDARD_CAN_ID)
   {
      strcpy(pCANstring, ":S");                       // Start of standard string
      sprintf(tmpStr, "%03X", (int)pCANmsg->asCAN.id);// Format to hex
      strcat(pCANstring, (const Char *)tmpStr);       // Put in string
      strcat(pCANstring, "N");                        // Put in Header Data separator
      for (uInt8 i = 0; i < pCANmsg->asCAN.dlc; i++)
      {                                               // Put data in, if any
         sprintf(tmpStr, "%02X", pCANmsg->asCAN.Data[i]);
         strcat(pCANstring, (const Char *)tmpStr);    // Concatenate data
      }
   }
   else                                               // EXTENDED_CAN_ID
   {
      strcpy(pCANstring, ":X");                       // Start of extended string
      sprintf(tmpStr, "%08lX", pCANmsg->asCAN.id);    // Format to hex
      strcat(pCANstring, (const Char *)tmpStr);       // Put in string
      strcat(pCANstring, "N");                        // Put in Header Data separator
      for (uInt8 i = 0; i < pCANmsg->asCAN.dlc; i++)
      {                                               // Put data in, if any
         sprintf(tmpStr, "%02X", pCANmsg->asCAN.Data[i]);
         strcat(pCANstring, (const Char *)tmpStr);    // Concatenate data
      }
   }
   strcat(pCANstring, ";");                           // Put in End of string
   return pCANstring;
}

/*──────────────────────── are CAN Messages In Buffer ──────────────────────?-?──*/
Byte CAN_messagesInBuffer(void)
{
   Byte messageCount = 0;
   
   if (RXB0CONbits.RXFUL)         // Check RXB0
      messageCount++;
   if (RXB1CONbits.RXFUL)         // Check RXB1
      messageCount++;
   return (messageCount);         // Returns total number of messages in all the buffers
}

/*──────────────────────────────── CAN Sleep ─────────────────────────────────*/
void CAN_sleep(void)
{
    PIR5bits.WAKIF = 0;                               // clear CAN bus wakeup interrupt
    PIE5bits.WAKIE = 1;                               // Enable CAN bus activity wakeup
    CANCONbits.REQOP = CAN_MODE_SLEEP;                // request Disable/Sleep mode
    while (CANSTATbits.OPMODE != CAN_MODE_SLEEP);     // wait until ECAN is in Disable/Sleep mode   
                                                      // Wake up from sleep should set the CAN module to Normal mode
}

/*───────────────────────────── is CAN Bus Off? ──────────────────────────────*/
Byte CAN_isBusOff(void)
{
   Byte returnValue = 0;
   // COMSTAT bit 5 TXBO: Transmitter Bus-Off bit
   // 1 = Transmit error counter > 255
   // 0 = Transmit error counter less then or equal to 255
   if (COMSTATbits.TXBO)
      returnValue = 1;
   return (returnValue);
}

/*────────────────────────── CAN is RX Error Passive ─────────────────────────*/
Byte CAN_isRXErrorPassive(void)
{
   Byte returnValue = 0;
   // COMSTAT bit 3 RXBP: Receiver Bus Passive bit
   // 1 = Receive error counter > 127
   // 0 = Receive error counter less then or equal to 127
   if (COMSTATbits.RXBP)
      returnValue = 1;
   return (returnValue);
}

/*────────────────────────── CAN is TX Error Passive ─────────────────────────*/
Byte CAN_isTXErrorPassive(void)
{
   Byte returnValue = 0;
   // COMSTAT bit 4 TXBP: Transmitter Bus Passive bit
   // 1 = Transmit error counter > 127
   // 0 = Transmit error counter less then or equal to 127
   if (COMSTATbits.TXBP)
      returnValue = 1;
   return (returnValue);
}

//CAN Functions </editor-fold>

/*══════♦ ♦ ♦ ♦ ♦═════════♦ Other & Utility Functions ♦════════♦ ♦ ♦ ♦ ♦══════*/
// <editor-fold defaultstate="collapsed" desc="Other & Utility Functions">

/*─────────────────── LCCUID 6 bytes to 1of4 1½ bytes words ──────────────────*/
Word UNID2part(uInt8 frameNo, Byte * LCCUID)
{
   Word UIDpart, Part1, Part2;
   
   switch (frameNo)
   {
      case 0x07:                // First 1½ bytes
         Part1 = LCCUID[0];
         Part2 = LCCUID[1];
         UIDpart = Part1 << 4 | (Part2 >> 4);
      break;
      case 0x06:                // Second 1½ bytes
         Part1 = LCCUID[1];
         Part2 = LCCUID[2];
         UIDpart = (Part1 & 0x0F) << 8 | Part2;
      break;
      case 0x05:                // Third 1½ bytes
         Part1 = LCCUID[3];
         Part2 = LCCUID[4];
         UIDpart = Part1 << 4 | (Part2 >> 4);
      break;
      case 0x04:                // Fourth 1½ bytes
         Part1 = LCCUID[4];
         Part2 = LCCUID[5];
         UIDpart = (Part1 & 0x0F) << 8 | Part2;
      break;
   }    
   return UIDpart;
}

/*───────────────────── UNID byte array data to String ───────────────────────*/
Char * UNID2String(Byte * aUNID, Char * IDstring)
{
   uInt8 i;
   Char tmp[3];    // don't forget the NULL
   
   IDstring[0] = NULL;
   for(i = 0; i <= 5; i++)
   {
      sprintf(tmp, "%02X", aUNID[i]);
      strcat(IDstring, tmp);
      if (i < 5) strcat(IDstring, ":");
   }
   return IDstring;
}

/*───────────────────── UNID in Message data to String ───────────────────────*/
Char * msgUNID2String(CANMsgt * pCANmsg, Char * IDstring)
{
   
   if (pCANmsg->asLCC.MTI == LCC_VNA)                                // first 2 bytes = SNIDA, last 6 bytes = UNID
      IDstring = UNID2String(pCANmsg->asLCC.Data + 2, IDstring);     // Just want UNID
   else                                                              // first 6 bytes = UNID
      IDstring = UNID2String(pCANmsg->asLCC.Data, IDstring);  

   return IDstring;
}

/*───────────────────────────── String to UNID ───────────────────────────────*/
void String2UNID(Byte * UNID, Char * str)
{                        // Convert 12 char ASCII hex string to 6 byte UNID array
   uInt8 count;
   Char Ahex[2];
   
   for (count = 0; count < 12; count += 2)
   {
      Ahex[0] = str[count];
      Ahex[1] = str[count + 1];
      UNID[count / 2] = xtoi(Ahex);
   }
}

/*────────────────────────────── Event String ────────────────────────────────*/
Char * Event2String(EventIDt * pEvent, Char * Estring)
{
   Char tmp[4];
   
   strcpy(Estring, "");
   for(uInt8 i = 0; i <= 5; i++)
   {
      sprintf(tmp, "%02X", pEvent->UNID[i]);
      strcat(Estring, tmp);
      if (i < 5) strcat(Estring, ".");
   }
      strcat(Estring, ", ");
      sprintf(tmp, "%04X", pEvent->Event);
      strcat(Estring, tmp);
   
   return Estring;
}
   
/*───────────────────────── Print LCC or CAN Message ─────────────────────────*/
void printMsg(CANMsgt * pTempMsg)
{
   if(pTempMsg->asArray[0] == STANDARD_CAN_ID)                            //<< CAN Standard message
   {
      printf(":%02X - %03X (%01X)",
             pTempMsg->asCAN.idType,
             (int)pTempMsg->asCAN.id,
             pTempMsg->asCAN.dlc);
      if (pTempMsg->asCAN.dlc && pTempMsg->asCAN.dlc <= 8)                // Check DLC size
         for (uInt8 i = 0; i < pTempMsg->asCAN.dlc; i++)
            printf(" %02X", pTempMsg->asCAN.Data[i]);                     // Print the Data string
   }
   else if(pTempMsg->asArray[0] == EXTENDED_CAN_ID)                       //<< CAN Extended message
   {
      printf(":%02X - %08lX (%01X)",
             pTempMsg->asCAN.idType,
             pTempMsg->asCAN.id,
             pTempMsg->asCAN.dlc);
      if (pTempMsg->asCAN.dlc && pTempMsg->asCAN.dlc <= 8)                // Check DLC size
         for (uInt8 i = 0; i < pTempMsg->asCAN.dlc; i++)
            printf(" %02X", pTempMsg->asCAN.Data[i]);                     // Print the Data string
   }
   else if(pTempMsg->asArray[0] == 0)                                     //<< Registers
   {
      printf(": SIDH=%02X SIDL=%02X EIDH=%02X EIDL=%02X DLC=%02X-",
             pTempMsg->asRegister.XBnSIDH,
             pTempMsg->asRegister.XBnSIDL,
             pTempMsg->asRegister.XBnEIDH,
             pTempMsg->asRegister.XBnEIDL,
             pTempMsg->asRegister.XBnDLC);
      if(pTempMsg->asRegister.XBnDLC && pTempMsg->asCAN.dlc <= 8)         // Check DLC size
         for (uInt8 i = 0; i < pTempMsg->asRegister.XBnDLC; i++)
            printf(" %02X", pTempMsg->asArray[i + 6u]);                   // Print the Data string
   }
   else if(pTempMsg->asArray[0] < 0x1F &&  pTempMsg->asArray[0] > 0x02)   //<< LCC/OpenLCB/CAN message
   {
      printf(":%02X {%03X} [%03X] (%01X)",
             pTempMsg->asLCC.PFT.byte,                                    // LCC
             pTempMsg->asLCC.MTI,
             pTempMsg->asLCC.SNID,
             pTempMsg->asCAN.dlc);                                        // Print the Data length
      if (pTempMsg->asCAN.dlc && pTempMsg->asCAN.dlc <= 8)                // Check DLC size
         for (uInt8 i = 0; i < pTempMsg->asCAN.dlc; i++)
            printf(" %02X", pTempMsg->asCAN.Data[i]);                     // Print the Data string
      }
   else                                                                   //<< Other
   {
      printf(": ");
      for (uInt8 i = 0; i <= 13; i++)
         printf("%02X ", pTempMsg->asArray[i]);                           // Print the message as an array
   }
   printf(";\n>");
}

//Other & Utility Functions </editor-fold>