// ------------------------------------------------------------------------
// J1939 CAN Connection
// ------------------------------------------------------------------------
#include "mcp_can.h"

#define CS_PIN 4 // Use pin 10 for Seeed Studio CAN Shield up to version 1.0
                 // Use pin 9 for Seeed Studion CAN shiled version 1.1 and higher
                 // Use pin 10 for SK Pang CAN shield
MCP_CAN CAN(CS_PIN);

// ------------------------------------------------------------------------
// CAN message ring buffer setup
// ------------------------------------------------------------------------
#define CANMSGBUFFERSIZE 10
struct CANMsg
{
  long lID;
  unsigned char pData[8];
  int nDataLen;
};
CANMsg CANMsgBuffer[CANMSGBUFFERSIZE];
int nWritePointer;
int nReadPointer;

// ------------------------------------------------------------------------
// Initialize the CAN controller
// ------------------------------------------------------------------------
int canInitialize(int nBaudRate)
{
  // Default settings
  nReadPointer = 0;
  nWritePointer = 0;

  // Initialize the CAN controller
  if (CAN.begin(nBaudRate) == 0)
    return 0;
  else
    return 1;

} // end canInitialize

// ------------------------------------------------------------------------
// Transmit CAN message
// ------------------------------------------------------------------------
byte canTransmit(long lID, unsigned char *pData, int nDataLen)
{

  if (CAN.sendMsgBuf(lID, CAN_EXTID, nDataLen, pData) == 0)
    return 0;
  else
    return 1;

} // end canTransmit

// ------------------------------------------------------------------------
// Receive CAN message
// ------------------------------------------------------------------------
byte canReceive(long *lID, unsigned char *pData, int *nDataLen)
{
  // Declarations
  byte nCnt;

  // In case there is a message, put it into the buffer
  while (CAN.checkReceive() == CAN_MSGAVAIL)
  {
    // Read the message buffer
    CAN.readMsgBuf(&nCnt, &CANMsgBuffer[nWritePointer].pData[0]);
    CANMsgBuffer[nWritePointer].nDataLen = (int)nCnt;
    CANMsgBuffer[nWritePointer].lID = CAN.getCanId();

    if (++nWritePointer == CANMSGBUFFERSIZE)
      nWritePointer = 0;

  } // end while

  // Check ring buffer for a message
  if (nReadPointer != nWritePointer)
  {
    // Read the next message buffer entry
    *nDataLen = CANMsgBuffer[nReadPointer].nDataLen;
    *lID = CANMsgBuffer[nReadPointer].lID;

    for (int nIdx = 0; nIdx < *nDataLen; nIdx++)
      pData[nIdx] = CANMsgBuffer[nReadPointer].pData[nIdx];

    if (++nReadPointer == CANMSGBUFFERSIZE)
      nReadPointer = 0;

    return 0;

  } // end if
  else
    return 1;

} // end canReceive

// ------------------------------------------------------------------------
// J1939 Check Peer-to-Peer
// ------------------------------------------------------------------------
bool j1939PeerToPeer(long lPGN)
{
  // Check the PGN
  if (lPGN > 0 && lPGN <= 0xEFFF)
    return true;

  if (lPGN > 0x10000 && lPGN <= 0x1EFFF)
    return true;

  return false;

} // end j1939PeerToPeer

// ------------------------------------------------------------------------
// J1939 Transmit
// ------------------------------------------------------------------------
byte j1939Transmit(long lPGN, byte nPriority, byte nSrcAddr, byte nDestAddr, byte *nData, int nDataLen)
{
  // Declarations
  long lID = ((long)nPriority << 26) + (lPGN << 8) + (long)nSrcAddr;

  // If PGN represents a peer-to-peer, add destination address to the ID
  if (j1939PeerToPeer(lPGN) == true)
  {
    lID = lID & 0xFFFF00FF;
    lID = lID | ((long)nDestAddr << 8);

  } // end if

  // Transmit the message
  return canTransmit(lID, nData, nDataLen);

} // end j1939Transmit

// ------------------------------------------------------------------------
// J1939 Receive
// ------------------------------------------------------------------------
byte j1939Receive(long *lPGN, byte *nPriority, byte *nSrcAddr, byte *nDestAddr, byte *nData, int *nDataLen)
{
  // Declarations
  byte nRetCode = 1;
  long lID;

  // Default settings
  *nSrcAddr = 255;
  *nDestAddr = 255;

  if (canReceive(&lID, nData, nDataLen) == 0)
  {
    long lPriority = lID & 0x1C000000;
    *nPriority = (int)(lPriority >> 26);

    *lPGN = lID & 0x00FFFF00;
    *lPGN = *lPGN >> 8;

    lID = lID & 0x000000FF;
    *nSrcAddr = (int)lID;

    if (j1939PeerToPeer(*lPGN) == true)
    {
      *nDestAddr = (int)(*lPGN & 0xFF);
      *lPGN = *lPGN & 0x01FF00;
    }

    nRetCode = 0;

  } // end if

  return nRetCode;

} // end j1939Receive
