示例#1
0
        //-SUB------------------------------------------------------------------------
        // Event : frmMain_Activated
        //----------------------------------------------------------------------------
        private void frmMain_Activated(object sender, EventArgs e)
        {
            // Let's have it called only once...
            if (bFormActivated == false)
            {
                bFormActivated = true;

                if (COMPort.Initialize(-1) != 0)
                {
                    MessageBox.Show("No COM Port available!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
                else
                {
                    // Fill the COM combobox
                    for (int nIndex = 0; nIndex < COMPort.nPorts; nIndex++)
                    {
                        cboCOMPort.Items.Add(COMPort.sPorts[nIndex]);
                    }

                    // Select the combobox index
                    cboCOMPort.SelectedIndex = COMPort.nSelPort;

                    // Close the serial port
                    COMPort._serialport.Close();

                    // Enable the Start button
                    btnStart.Enabled = true;
                } // end else
            }     // end if
        }         // end frmMain_Activated
示例#2
0
        }// end RESET_J1939

        //-FUNCTION-----------------------------------------------------------------
        // Routine     : MM_J1939
        // Description : Set the gateway's message mode
        // Returncode  : None
        // -------------------------------------------------------------------------
        public static void MM_J1939(byte nMsgMode)
        {
            msg_SETMSGMODE[SETMSGMODE_IDX_MODE]   = nMsgMode;
            msg_SETMSGMODE[SETMSGMODE_IDX_CHKSUM] = ComputeCheckSum(msg_SETMSGMODE);

            // Transmit the message to the COM port
            COMPort.Transmit(msg_SETMSGMODE, MSG_LEN_SETMSGMODE + 3);
        }// end MM_J1939
示例#3
0
        }// end SA_J1939

        //-FUNCTION-----------------------------------------------------------------
        // Routine     : RQ_J1939
        // Description : Request info from the gateway
        // Returncode  : None
        // -------------------------------------------------------------------------
        public static void RQ_J1939(byte nID)
        {
            msg_RQ[4] = nID;
            msg_RQ[5] = ComputeCheckSum(msg_RQ);

            // Transmit the message to the COM port
            COMPort.Transmit(msg_RQ, 6);
        }// end RQ_J1939
示例#4
0
        }// end SH_J1939

        //-FUNCTION-----------------------------------------------------------------
        // Routine     : SA_J1939
        // Description : Set ACK message active (=1) / inactive (=0)
        // Returncode  : None
        // -------------------------------------------------------------------------
        public static void SA_J1939(byte nActive)
        {
            msg_SETACK[SETACK_IDX_ACTIVE] = nActive;
            msg_SETACK[SETACK_IDX_CHKSUM] = ComputeCheckSum(msg_SETACK);

            // Transmit the message to the COM port
            COMPort.Transmit(msg_SETACK, 6);
        }// end SA_J1939
示例#5
0
        }// end RQ_J1939

        //-FUNCTION-----------------------------------------------------------------
        // Routine     : RESET_J1939
        // Description : Reset the gateway
        // Returncode  : None
        // -------------------------------------------------------------------------
        public static void RESET_J1939()
        {
            msg_RESET[7] = ComputeCheckSum(msg_RESET);

            // Transmit the message to the COM port
            COMPort.Transmit(msg_RESET, 8);

            System.Threading.Thread.Sleep(100); // Time in msec
        }// end RESET_J1939
示例#6
0
        //-FUNCTION-----------------------------------------------------------------
        // Routine     : SH_J1939
        // Description : Set heartbeat frequency in milliseconds
        // Returncode  : None
        // -------------------------------------------------------------------------
        public static void SH_J1939(int nFrequency)
        {
            msg_SETHEART[SETHEART_IDX_FREQMSB] = (byte)(nFrequency >> 8);
            msg_SETHEART[SETHEART_IDX_FREQLSB] = (byte)(nFrequency & 0xFF);
            msg_SETHEART[SETHEART_IDX_CHKSUM]  = ComputeCheckSum(msg_SETHEART);

            // Transmit the message to the COM port
            COMPort.Transmit(msg_SETHEART, 7);
        }// end SH_J1939
示例#7
0
        }// end FD_J1939

        //-FUNCTION-----------------------------------------------------------------
        // Routine     : FA_FD_1939
        // Description : Creates filter message, adds PGN & checksum, and sends it
        // Returncode  : None
        // -------------------------------------------------------------------------
        public static void FA_FD_J1939(long lPGN)
        {
            // Declarations
            int nMsgLen = MSG_LEN_FA + 3;

            // Fill the port number and PGN
            msg_FA[FA_IDX_PGN_MSB] = (byte)((lPGN & 0xFF0000) >> 16);
            msg_FA[FA_IDX_PGN_2ND] = (byte)((lPGN & 0x00FF00) >> 8);
            msg_FA[FA_IDX_PGN_LSB] = (byte)(lPGN & 0x0000FF);

            // Process and fill the checksum (does not include stuff bytes)
            msg_FA[FA_IDX_CHKSUM] = ComputeCheckSum(msg_FA);

            // Determine the total message length by scanning for START and ESC tokens
            for (int nIndex = 1; nIndex < MSG_LEN_FA + 3; nIndex++)
            {
                if (msg_FA[nIndex] == MSG_TOKEN_START ||
                    msg_FA[nIndex] == MSG_TOKEN_ESC)
                {
                    nMsgLen++;
                }
            }
            if (msg_FA[FA_IDX_CHKSUM] == MSG_TOKEN_START ||
                msg_FA[FA_IDX_CHKSUM] == MSG_TOKEN_ESC)
            {
                nMsgLen++;
            }

            // Resize the message to be transmitted
            byte[] pMsg = new byte[nMsgLen];

            // Copy the message; insert stuff bytes where necessary
            pMsg[0] = MSG_TOKEN_START;  // Insert the START token
            int nPointer = 1;

            for (int nIndex = 1; nIndex < MSG_LEN_FA + 3; nIndex++)
            {
                if (msg_FA[nIndex] == MSG_TOKEN_START)
                {
                    pMsg[nPointer++] = MSG_TOKEN_ESC;
                    pMsg[nPointer++] = MSG_START_STUFF;
                }// end if
                else if (msg_FA[nIndex] == MSG_TOKEN_ESC)
                {
                    pMsg[nPointer++] = MSG_TOKEN_ESC;
                    pMsg[nPointer++] = MSG_ESC_STUFF;
                }// end else if
                else
                {
                    pMsg[nPointer++] = msg_FA[nIndex];
                }
            }// end for

            // Transmit the message to the COM port
            COMPort.Transmit(pMsg, nMsgLen);
        }// end FA_FD_J1939
示例#8
0
        }         // end frmMain_Activated

        //-SUB------------------------------------------------------------------------
        // Event : frmMain_FormClosing
        //----------------------------------------------------------------------------
        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            // Make sure the COM port is open to receive the reset command
            COMPort.Initialize(cboCOMPort.SelectedIndex);

            // Reset the gateway
            jCOM1939.RESET_J1939();

            // Close the COM port
            COMPort.Terminate();
        }// end frmMain_FormClosing
示例#9
0
        }// end frmMain_FormClosing

        //-SUB------------------------------------------------------------------------
        // Event : btnStart_Click
        //----------------------------------------------------------------------------
        private void btnStart_Click(object sender, EventArgs e)
        {
            String fileName = "logs/JcomData" + DateTime.Now.ToString("MM-dd--hh-mm") + ".csv";

            if (COMPort.Initialize(cboCOMPort.SelectedIndex) == 0)
            {
                timerLoop.Start();

                // Enable/disable start/stop buttons
                btnStart.Enabled = false;
                btnStop.Enabled  = true;

                // Enable further buttons
                btnRequestStatus.Enabled = true;
                btnClaimAddr.Enabled     = true;
                btnAddFilter.Enabled     = true;
                btnDelFilter.Enabled     = true;
                loadLog.Enabled          = true;
                TrLog.Enabled            = true;

                // Reset the gateway
                jCOM1939.RESET_J1939();
                nSrcAddr = 0;

                // Set the Request message filter
                jCOM1939.FA_J1939(PGNRequest);

                jCOM1939.FA_J1939(jCOM1939.FILTER_PASS_ALL);

                // Set the gateway mode
                jCOM1939.MM_J1939(jCOM1939.MSGMODE_GATEWAY2);
                dataFile = new StreamWriter(fileName);
                string header = "time , pgn , destination , source , priority , data,\n";
                txtGatewayLog.Text = "";
                dataFile.WriteLine(header);
                start           = DateTime.Now;
                cmdFoundCount   = 0;
                cmdsGotten.Text = "0";
                foundCmd.Text   = "0";
                addr.Text       = "0";
            }// end if
            else
            {
                MessageBox.Show("Sorry! There is a problem with the COM port.", "Attention!");
            }
        }// end btnStart_Click
示例#10
0
        }// end btnStart_Click

        //-SUB------------------------------------------------------------------------
        // Event : btnStop_Click
        //----------------------------------------------------------------------------
        private void btnStop_Click(object sender, EventArgs e)
        {
            // Enable/disable start/stop buttons
            btnStart.Enabled = true;
            btnStop.Enabled  = false;

            // Disable further buttons
            btnRequestStatus.Enabled = false;
            btnClaimAddr.Enabled     = false;
            btnAddFilter.Enabled     = false;
            btnDelFilter.Enabled     = false;
            btnTransmit.Enabled      = false;
            loadLog.Enabled          = false;
            TrLog.Enabled            = false;
            timerLoop.Stop();
            COMPort.Terminate();
            dataFile.Close();
        }// end buttonStopCOM_Click
示例#11
0
        }// end ComputeCheckSum

        //-SUB----------------------------------------------------------------------
        // Routine     : RemoveStuffBytes
        // Description : Remove stuff bytes from message
        // -------------------------------------------------------------------------
        public static void RemoveStuffBytes(ref byte[] pBuffer, ref int nBufferSize)
        {
            // Scan through the global receive buffer
            for (int nIndex = 0; nIndex < nBufferSize; nIndex++)
            {
                if (nBufferSize - nIndex >= 2)
                {
                    if (pBuffer[nIndex] == MSG_TOKEN_ESC &&
                        pBuffer[nIndex + 1] == MSG_START_STUFF)
                    {
                        pBuffer[nIndex] = MSG_TOKEN_START;
                        COMPort.ShiftBuffer(nIndex + 1, ref pBuffer, ref nBufferSize);
                    }// end if
                    else if (pBuffer[nIndex] == MSG_TOKEN_ESC &&
                             pBuffer[nIndex + 1] == MSG_ESC_STUFF)
                    {
                        pBuffer[nIndex] = MSG_TOKEN_ESC;
                        COMPort.ShiftBuffer(nIndex + 1, ref pBuffer, ref nBufferSize);
                    } // end else
                }     // end if
            }         // end for
        }             // end RemoveStuffBytes
示例#12
0
        }// end MM_J1939

        //-FUNCTION-----------------------------------------------------------------
        // Routine     : SET_J1939
        // Description : Set J1939 Parameters
        // Returncode  : None
        // -------------------------------------------------------------------------
        public static void SET_J1939(byte nSrcAddr, byte nAddrBottom, byte nAddrTop, byte nOpMode, byte[] pNAME, bool bRequestRESTATUSMessage)
        {
            if (bRequestRESTATUSMessage == true)
            {
                msg_SET1[SET_IDX_SA]     = nSrcAddr;
                msg_SET1[SET_IDX_BOTTOM] = nAddrBottom;
                msg_SET1[SET_IDX_TOP]    = nAddrTop;
                msg_SET1[SET_IDX_OPMODE] = nOpMode;

                for (int nIndex = 0; nIndex < 8; nIndex++)
                {
                    msg_SET1[nIndex + SET_IDX_NAME] = pNAME[nIndex];
                }

                msg_SET1[SET_IDX_CHKSUM] = ComputeCheckSum(msg_SET1);

                // Transmit the message to the COM port
                COMPort.Transmit(msg_SET1, MSG_LEN_SET);
            }// end if
            else
            {
                msg_SET[SET_IDX_SA]     = nSrcAddr;
                msg_SET[SET_IDX_BOTTOM] = nAddrBottom;
                msg_SET[SET_IDX_TOP]    = nAddrTop;
                msg_SET[SET_IDX_OPMODE] = nOpMode;

                for (int nIndex = 0; nIndex < 8; nIndex++)
                {
                    msg_SET[nIndex + SET_IDX_NAME] = pNAME[nIndex];
                }

                msg_SET[SET_IDX_CHKSUM] = ComputeCheckSum(msg_SET);

                // Transmit the message to the COM port
                COMPort.Transmit(msg_SET, MSG_LEN_SET);
            } // end else
        }     // end SET_J1939
示例#13
0
        //-FUNCTION-----------------------------------------------------------------
        // Routine     : RX_J1939
        // Description : Checks for received messages from the gateway
        // Returncode  : See codes above
        // -------------------------------------------------------------------------
        public static int RX_J1939(ref long lPGN, ref int nDest, ref int nSrc, ref int nPriority,
                                   ref byte[] nData, ref int nDataLen)
        {
            // Declarations
            bool bNewMsg      = false;
            int  nReceiveMode = RX_NoMessage;
            int  nIndex       = 0;
            int  nMsgLen      = 0;
            int  nPointer     = 0;
            int  nMSB         = 0;
            int  nLSB         = 0;
            int  nOffset      = 0;
            int  nDataCounter = 0;

            try
            {
                // Regardless of the current status, read the com port
                // New data will be attached to the receive buffer
                // COMPort.Receive(); // This is the call when not using the interrupt service routine
                if (frmMain.bSerialDataReceive == false && COMPort.nCOM_ReceiveBufferSize >= MSG_MINLENGTH)
                {
                    frmMain.bSerialDataProcess = true;

                    // Extract the next message
                    // ----------------------------------------------------
                    // Find MSG_TOKEN_START
                    for (nIndex = 0; nIndex < COMPort.nCOM_ReceiveBufferSize; nIndex++)
                    {
                        if (COMPort.pCOM_ReceiveBuffer[nIndex] == MSG_TOKEN_START)
                        {
                            break;
                        }
                    }

                    // In case the first byte is NOT MSG_TOKEN_START, clean the buffer
                    if (nIndex > 0)
                    {
                        COMPort.CleanReceiveBuffer(0, nIndex);
                    }

                    // Extract some message specifics from the buffer
                    nPointer = 1;   // Points to data length MSB
                    nOffset  = 1;
                    nMSB     = 0;
                    nLSB     = 0;

                    // Determine the actual data length MSB
                    if (COMPort.pCOM_ReceiveBuffer[nPointer] == MSG_TOKEN_ESC &&
                        COMPort.pCOM_ReceiveBuffer[nPointer + 1] == MSG_START_STUFF)
                    {
                        nMSB    = MSG_TOKEN_START;
                        nOffset = 2;
                    }// end if
                    else if (COMPort.pCOM_ReceiveBuffer[nPointer] == MSG_TOKEN_ESC &&
                             COMPort.pCOM_ReceiveBuffer[nPointer + 1] == MSG_ESC_STUFF)
                    {
                        nMSB    = MSG_TOKEN_ESC;
                        nOffset = 2;
                    }// end else if
                    else
                    {
                        nMSB    = COMPort.pCOM_ReceiveBuffer[MSG_IDX_MSB];
                        nOffset = 1;
                    }// end else

                    // Determine the actual data length LSB
                    nPointer = MSG_IDX_MSB + nOffset;
                    if (COMPort.pCOM_ReceiveBuffer[nPointer] == MSG_TOKEN_ESC &&
                        COMPort.pCOM_ReceiveBuffer[nPointer + 1] == MSG_START_STUFF)
                    {
                        nLSB    = MSG_TOKEN_START;
                        nOffset = 2;
                    }// end if
                    else if (COMPort.pCOM_ReceiveBuffer[nPointer] == MSG_TOKEN_ESC &&
                             COMPort.pCOM_ReceiveBuffer[nPointer + 1] == MSG_ESC_STUFF)
                    {
                        nLSB    = MSG_TOKEN_ESC;
                        nOffset = 2;
                    }// end else if
                    else
                    {
                        nLSB    = COMPort.pCOM_ReceiveBuffer[nPointer];
                        nOffset = 1;
                    }// end else
                    nPointer += nOffset; // nPointer now points to first data byte

                    // Determine the actual data length
                    nMsgLen = (nMSB << 8) + nLSB;

                    // Scan through the buffer to find the end of the message
                    nIndex       = nPointer;
                    nDataCounter = 0;
                    while (nIndex < COMPort.nCOM_ReceiveBufferSize)
                    {
                        // Check for another MSG_TOKEN_START
                        if (COMPort.pCOM_ReceiveBuffer[nIndex] == MSG_TOKEN_START)
                        {
                            // Indicate that the start of another message is in the buffer
                            bNewMsg = true;
                            break;
                        }

                        // In the following we do not check if the second stuff byte exists.
                        // This method helps to detect byte stuffing errors.
                        if (COMPort.pCOM_ReceiveBuffer[nIndex] == MSG_TOKEN_ESC)
                        {
                            nOffset = 2;
                        }
                        else if (COMPort.pCOM_ReceiveBuffer[nIndex] == MSG_TOKEN_ESC)
                        {
                            nOffset = 2;
                        }
                        else
                        {
                            nOffset = 1;
                        }

                        nIndex += nOffset;
                        nDataCounter++;

                        if (nDataCounter == nMsgLen)
                        {
                            break;
                        }
                    }// end while

                    // We have a valid message when the counted data matches the reported data
                    if (nDataCounter == 0 || nDataCounter != nMsgLen)
                    {
                        // Determine whether this is an incomplete or faulty message
                        if (bNewMsg == true)
                        {
                            // The reported message length does not match the message data,
                            // but there is already a new message in the buffer, which
                            // indicates a byte stuffing error
                            nReceiveMode = RX_FaultyMessage;

                            // Clean the receive buffer
                            COMPort.CleanReceiveBuffer(0, nDataCounter + 3);
                        }// end if
                        else
                        {
                            nReceiveMode = RX_NoMessage;
                        }
                    }
                    else
                    {
                        // nIndex now points to the first byte after the message
                        // Copy the entire message
                        COMPort.nCOM_CopyBufferSize = nIndex;
                        for (nIndex = 0; nIndex < COMPort.nCOM_CopyBufferSize; nIndex++)
                        {
                            COMPort.pCOM_CopyBuffer[nIndex] = COMPort.pCOM_ReceiveBuffer[nIndex];
                        }

                        // Clean the receive buffer
                        COMPort.CleanReceiveBuffer(0, COMPort.nCOM_CopyBufferSize);

                        // Remove stuffing bytes from copy buffer
                        RemoveStuffBytes(ref COMPort.pCOM_CopyBuffer, ref COMPort.nCOM_CopyBufferSize);

                        // Verify the checksum
                        byte nCheckSum = ComputeCheckSum(COMPort.pCOM_CopyBuffer);
                        if (nCheckSum != COMPort.pCOM_CopyBuffer[nMsgLen + 2])
                        {
                            nReceiveMode = RX_FaultyMessage;
                        }
                        else
                        {
                            // Check the message identifier
                            nMsgLen      = nMsgLen + 3; // Add overhead length
                            nReceiveMode = FilterSystemMessage(COMPort.pCOM_CopyBuffer);
                        }// end else
                    } // end else
                }     // end if

                // If this is an actual data message, copy parameters accordingly
                switch (nReceiveMode)
                {
                case RX_Message:

                    lPGN      = ((long)COMPort.pCOM_CopyBuffer[RXTX_IDX_PGNMSB]) << 16;
                    lPGN     |= ((long)COMPort.pCOM_CopyBuffer[RXTX_IDX_PGN2ND]) << 8;
                    lPGN     |= ((long)COMPort.pCOM_CopyBuffer[RXTX_IDX_PGNLSB]);
                    nPriority = (int)COMPort.pCOM_CopyBuffer[RXTX_IDX_PRIORITY];
                    nSrc      = (int)COMPort.pCOM_CopyBuffer[RXTX_IDX_SRCADDR];
                    nDest     = (int)COMPort.pCOM_CopyBuffer[RXTX_IDX_DESTADDR];
                    nDataLen  = nMsgLen - (RXTX_IDX_DATASTART + 1);    // Remove the overhead length
                    nData     = new byte[nDataLen];

                    int nIdx = 0;
                    for (nIndex = RXTX_IDX_DATASTART; nIndex < RXTX_IDX_DATASTART + nDataLen; nIndex++)
                    {
                        nData[nIdx++] = COMPort.pCOM_CopyBuffer[nIndex];
                    }

                    break;

                case RX_RS:

                    nData    = new byte[2];
                    nData[0] = COMPort.pCOM_CopyBuffer[RS_IDX_STATUS];
                    nData[1] = COMPort.pCOM_CopyBuffer[RS_IDX_SA];

                    break;

                case RX_VERSION:
                    nData    = new byte[6];
                    nData[0] = COMPort.pCOM_CopyBuffer[HEART_IDX_HW_MAJOR];
                    nData[1] = COMPort.pCOM_CopyBuffer[HEART_IDX_HW_MINOR];
                    nData[2] = COMPort.pCOM_CopyBuffer[HEART_IDX_HW_BUGFIX];
                    nData[3] = COMPort.pCOM_CopyBuffer[HEART_IDX_SW_MAJOR];
                    nData[4] = COMPort.pCOM_CopyBuffer[HEART_IDX_SW_MINOR];
                    nData[5] = COMPort.pCOM_CopyBuffer[HEART_IDX_SW_BUGFIX];

                    break;

                case RX_HEART:
                    nData    = new byte[8];
                    nData[0] = COMPort.pCOM_CopyBuffer[HEART_IDX_HW_MAJOR];
                    nData[1] = COMPort.pCOM_CopyBuffer[HEART_IDX_HW_MINOR];
                    nData[2] = COMPort.pCOM_CopyBuffer[HEART_IDX_HW_BUGFIX];
                    nData[3] = COMPort.pCOM_CopyBuffer[HEART_IDX_SW_MAJOR];
                    nData[4] = COMPort.pCOM_CopyBuffer[HEART_IDX_SW_MINOR];
                    nData[5] = COMPort.pCOM_CopyBuffer[HEART_IDX_SW_BUGFIX];
                    nData[6] = COMPort.pCOM_CopyBuffer[HEART_IDX_CHECKSUMERRORS];
                    nData[7] = COMPort.pCOM_CopyBuffer[HEART_IDX_STUFFBYTEERRORS];

                    break;
                } // end switch
            }     // end try
            catch
            {
                nReceiveMode = RX_FaultyMessage;
            };

            frmMain.bSerialDataProcess = false;

            // Return the receive mode
            return(nReceiveMode);
        }// end RX_J1939
示例#14
0
        }// end TX_J1939

        //-FUNCTION-----------------------------------------------------------------
        // Routine     : TXP_J1939
        // Description : Creates periodic transmit message, adds parameters & checksum,
        //               and sends it
        // Returncode  : None
        // -------------------------------------------------------------------------
        public static void TXP_J1939(long lPGN, int nDest, int nSrc, int nPriority, byte[] nData, int nDataLen, int nInterval, bool bLoopback)
        {
            // Declarations
            int nMsgLenUnstuffed = MSG_LEN_TXP + nDataLen;

            // Fill all parameters
            byte[] pUnstMsg = new byte[nMsgLenUnstuffed + 3]; // Add header length

            pUnstMsg[TXP_IDX_MSGSTART]  = MSG_TOKEN_START;
            pUnstMsg[TXP_IDX_MSGLENMSB] = (byte)((nMsgLenUnstuffed & 0xFF00) >> 8);
            pUnstMsg[TXP_IDX_MSGLENLSB] = (byte)(nMsgLenUnstuffed & 0xFF);

            if (bLoopback == true)
            {
                pUnstMsg[TXP_IDX_MSGID] = MSG_ID_TXPL;
            }
            else
            {
                pUnstMsg[TXP_IDX_MSGID] = MSG_ID_TXP;
            }

            pUnstMsg[TXP_IDX_PGNMSB]   = (byte)((lPGN & 0xFF0000) >> 16);
            pUnstMsg[TXP_IDX_PGN2ND]   = (byte)((lPGN & 0x00FF00) >> 8);
            pUnstMsg[TXP_IDX_PGNLSB]   = (byte)(lPGN & 0x0000FF);
            pUnstMsg[TXP_IDX_DESTADDR] = (byte)nDest;
            pUnstMsg[TXP_IDX_SRCADDR]  = (byte)nSrc;
            pUnstMsg[TXP_IDX_PRIORITY] = (byte)nPriority;
            pUnstMsg[TXP_IDX_FREQMSB]  = (byte)((nInterval & 0xFF00) >> 8);
            pUnstMsg[TXP_IDX_FREQLSB]  = (byte)(nInterval & 0x00FF);

            // Add actual data without stuff bytes
            nMsgLenUnstuffed += 3; // Add the header length
            int nPointer = TXP_IDX_DATASTART;

            for (int nIndex = 0; nIndex < nDataLen; nIndex++)
            {
                pUnstMsg[nPointer++] = nData[nIndex];
            }

            // Process and fill the checksum (does not include stuff bytes)
            pUnstMsg[nPointer++] = ComputeCheckSum(pUnstMsg);

            // Determine the total message length by scanning for START and ESC tokens
            int nMsgLenStuffed = nPointer;

            for (int nIndex = 1; nIndex < nPointer; nIndex++)
            {
                if (pUnstMsg[nIndex] == MSG_TOKEN_START ||
                    pUnstMsg[nIndex] == MSG_TOKEN_ESC)
                {
                    nMsgLenStuffed++;
                }
            }

            // Resize the message to be transmitted
            byte[] pMsg = new byte[nMsgLenStuffed];

            // Copy the message; insert stuff bytes where necessary
            pMsg[0]  = MSG_TOKEN_START; // Insert the START token
            nPointer = 1;
            for (int nIndex = 1; nIndex < nMsgLenUnstuffed; nIndex++)
            {
                if (pUnstMsg[nIndex] == MSG_TOKEN_START)
                {
                    pMsg[nPointer++] = MSG_TOKEN_ESC;
                    pMsg[nPointer++] = MSG_START_STUFF;
                }// end if
                else if (pUnstMsg[nIndex] == MSG_TOKEN_ESC)
                {
                    pMsg[nPointer++] = MSG_TOKEN_ESC;
                    pMsg[nPointer++] = MSG_ESC_STUFF;
                }// end else if
                else
                {
                    pMsg[nPointer++] = pUnstMsg[nIndex];
                }
            }// end for

            // Transmit the message to the COM port
            COMPort.Transmit(pMsg, nMsgLenStuffed);
        }// end TXP_J1939