Created
June 11, 2025 08:34
-
-
Save yonatan8070/a656d4ef69cb1e4235a3a23202665341 to your computer and use it in GitHub Desktop.
CANOpen Message decoder from https://community.br-automation.com/t/canopen-decoder/1952
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<style> | |
html{ | |
height: 100%; | |
} | |
body { | |
font-family: Arial; | |
display:flex; | |
flex-direction: column; | |
justify-content: center; | |
background: linear-gradient(180deg, rgba(63,160,127,1) 0%, rgba(9,84,124,1) 100%); | |
background-repeat: no-repeat; | |
height: 100%; | |
} | |
.mainFrame{ | |
position: relative; | |
text-align: center; | |
margin: auto; | |
height: auto; | |
} | |
.subFrame{ | |
position: relative; | |
text-align: center; | |
background-color: gainsboro; | |
border-radius: 10px; | |
text-align: left; | |
margin: auto; | |
padding: 20px; | |
height: auto; | |
margin-bottom: 20px; | |
} | |
#mainHeader{ | |
position: relative; | |
text-align: center; | |
margin-top: 20px; | |
background-color: gainsboro; | |
border-radius: 10px; | |
text-align: center; | |
margin: auto; | |
text-decoration-color: azure; | |
} | |
#divError{ | |
border-style: solid; | |
border-width: 1px; | |
border-color: red; | |
text-align: center; | |
background-color: rgb(255, 188, 177); | |
padding: 5px; | |
border-radius: 10px; | |
} | |
.divReturn{ | |
border-style: solid; | |
border-width: 1px; | |
border-color: rgb(56, 235, 248); | |
background-color: rgb(192, 237, 255); | |
padding: 5px; | |
border-radius: 10px; | |
width: auto; | |
} | |
td{ | |
text-align: left; | |
padding:5px; | |
} | |
.divFooter { | |
position: fixed; | |
left: 0; | |
bottom: 0; | |
width: 100%; | |
color: rgb(165, 165, 165); | |
text-align: center; | |
} | |
</style> | |
</head> | |
<body> | |
<!-- HTML part--> | |
<!-- ------------------------ --> | |
<div class="mainFrame"> | |
<!-- Frame cobID decoding --> | |
<div class="subFrame"> | |
<!-- Header --> | |
<div id="mainHeader"> | |
<h2 style="color: rgb(99, 99, 99);">Decode CobID</h2> | |
</div> | |
<!-- COB-ID input area --> | |
<table> | |
<tr> | |
<td><label>Enter CobId [0x]</label></td> | |
<td><input id="inpCobId" type="text"></input></td> | |
<td> | |
<button onclick="fDecodeCobId()">Decode</button> | |
</td> | |
</tr> | |
</table><br/> | |
<!-- return --> | |
<div id="divType" class="divReturn" style="display:none"> | |
<table id="tabType"> | |
<tr> | |
<td><b><label>Message type</label></b></td> | |
<td><label id="lblType"></label><br/></td> | |
</tr> | |
<tr> | |
<td><b><label>Node </label></b></td> | |
<td><label id="lblNode"></label></td> | |
</tr> | |
</table> | |
</div> | |
</div> | |
<!-- Frame data decoding --> | |
<div id="divData" class="subFrame" style="display:none"> | |
<!-- Header --> | |
<div id="mainHeader"> | |
<h2 style="color: rgb(99, 99, 99);">Decode Frame data</h2> | |
</div> | |
<!-- data decode --> | |
<table id="tabData" > | |
<tr> | |
<td><label>Enter Data [0x]</label></td> | |
<td><input id="inpData" type="text"></input></td> | |
<td> | |
<button onclick="fDecodeData()">Decode</button> | |
</td> | |
</tr> | |
</table><br/> | |
<!-- return --> | |
<div id="divDataReturn" class="divReturn" style="display:none"> | |
<table id="tabDataReturn"> | |
<tbody> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
<!-- return --> | |
<div id="divError" class="subFrame" style="display:none"> | |
<label id="lblError"></label> | |
</div> | |
</div> | |
<!-- Footer --> | |
<div class="divFooter"> | |
<p><°///---<</p> | |
</div> | |
</body> | |
</html> | |
<!-- Scripts --> | |
<script> | |
// decode CobId | |
// ------------------------------------- | |
function fDecodeCobId(){ | |
// reset canvas | |
document.getElementById("divError").style.display = "none"; | |
document.getElementById("divType").style.display = "none"; | |
document.getElementById('divData').style.display = 'none'; | |
document.getElementById('divDataReturn').style.display = 'none'; | |
// get data of input field | |
szData = document.getElementById("inpCobId").value; | |
szData = szData.replace(/ /g,''); | |
// check input | |
// -------------- | |
// check empty string | |
if (!szData.length){ | |
fSetError("No data entered"); | |
return; | |
} | |
// check if data is hexadecimal | |
if ((!fIsHex(szData)) && (iDataFormat == 1)){ | |
fSetError("Entered data not hexadecimal"); | |
return; | |
} | |
// prepare number | |
iData = parseInt(szData, 16); | |
// check message type | |
// ---- | |
fGetMessageType(iData); | |
} | |
// help functions | |
// ------------------------------------- | |
// check if string is in hex | |
function fIsHex(iData) { | |
regexp = /^[0-9a-fA-F ]+$/; | |
return regexp.test(iData); | |
} | |
// set error | |
function fSetError(szErrorText){ | |
// set error | |
document.getElementById("lblError").textContent = "Error : " + szErrorText; | |
// show output | |
document.getElementById("divError").style.display = "block"; | |
} | |
// show data input fields | |
function fShowDataInput() { | |
document.getElementById('divData').style.display = 'block'; | |
} | |
// get message type | |
function fGetMessageType(iData) { | |
iNode = -1; | |
// check mnessage type | |
switch(true) { | |
case iData == 0x00: | |
// NMT command | |
szType = "NMT Command"; | |
iMsgType = 1; | |
fShowDataInput(); | |
break; | |
case iData == 0x80: | |
// Sync telegram | |
szType = "SYNC Telegram"; | |
iMsgType = 2; | |
break; | |
case iData >= 0x80 && iData <= 0xFF: | |
// Emergency telegram | |
szType = "EMCY Telegram"; | |
iNode = iData % 0x80; | |
iMsgType = 3; | |
break; | |
case iData == 0x100: | |
// Time telegram | |
szType = "TIME Telegram"; | |
iMsgType = 4; | |
break; | |
case iData >= 0x180 && iData <= 0x1FF: | |
// Transmit PDO 1 | |
szType = "Transmit PDO 1"; | |
iNode = iData % 0x180; | |
iMsgType = 5; | |
break; | |
case iData >= 0x200 && iData <= 0x27F: | |
// Recieve PDO 1 | |
szType = "Recieve PDO 1"; | |
iNode = iData % 0x201; | |
iMsgType = 6; | |
break; | |
case iData >= 0x280 && iData <= 0x2FF: | |
// Transmit PDO 2 | |
szType = "Transmit PDO 2"; | |
iNode = iData % 0x280; | |
iMsgType = 7; | |
break; | |
case iData >= 0x300 && iData <= 0x37F: | |
// Recieve PDO 2 | |
szType = "Recieve PDO 2"; | |
iNode = iData % 0x300; | |
iMsgType = 8; | |
break; | |
case iData >= 0x380 && iData <= 0x3FF: | |
// Transmit PDO 3 | |
szType = "Transmit PDO 3" | |
iNode = iData % 0x380; | |
iMsgType = 9; | |
break; | |
case iData >= 0x400 && iData <= 0x47F: | |
// Recieve PDO 3 | |
szType = "Recieve PDO 3"; | |
iNode = iData % 0x400; | |
iMsgType = 10; | |
break; | |
case iData >= 0x480 && iData <= 0x4FF: | |
// Transmit PDO 4 | |
szType = "Transmit PDO 4" | |
iNode = iData % 0x480; | |
iMsgType = 11; | |
break; | |
case iData >= 0x500 && iData <= 0x57F: | |
// Recieve PDO 4 | |
szType = "Recieve PDO 4"; | |
iNode = iData % 0x500; | |
iMsgType = 12; | |
break; | |
case iData >= 0x580 && iData <= 0x5FF: | |
// Transmit SDO | |
szType = "Transmit SDO" | |
iNode = iData % 0x580; | |
iMsgType = 14; | |
fShowDataInput(); | |
break; | |
case iData >= 0x600 && iData <= 0x67F: | |
// Recieve SDO | |
szType = "Recieve SDO"; | |
iNode = iData % 0x600; | |
iMsgType = 15; | |
fShowDataInput(); | |
break; | |
case iData >= 0x700 && iData <= 0x77F: | |
// Heartbeat | |
szType = "Heartbeat / Nodeguarding"; | |
iNode = iData % 0x700; | |
iMsgType = 16; | |
fShowDataInput(); | |
break; | |
default: | |
fSetError("Unknown Cob-Id"); | |
return; | |
} | |
// set message | |
document.getElementById("lblType").textContent = szType; | |
// append node if available | |
if (iNode >= 0){ | |
document.getElementById("lblNode").textContent = "0x" + (iNode.toString(16)).toUpperCase(); | |
} | |
else{ | |
document.getElementById("lblNode").textContent = "-"; | |
} | |
// show output | |
document.getElementById("divType").style.display = "block"; | |
} | |
// decode frame specific data | |
function fDecodeData(){ | |
// hide all elements | |
document.getElementById("divError").style.display = "none"; | |
document.getElementById('divDataReturn').style.display = 'none'; | |
// get data | |
szData = document.getElementById("inpData").value; | |
szData = szData.replace(/ /g,''); | |
// check empty string | |
if (!szData.length){ | |
fSetError("No data entered"); | |
return; | |
} | |
// check if data is hexadecimal | |
if ((!fIsHex(szData)) && (iDataFormat == 1)){ | |
fSetError("Entered data not hexadecimal"); | |
return; | |
} | |
// call depending function | |
switch (iMsgType){ | |
case 1: | |
// NMT | |
iStatus = fDecodeNmt(szData); | |
break; | |
case 14: | |
// Transmit SDO | |
iStatus = fDecodeSdoTx(szData); | |
break; | |
case 15: | |
// Recieve SDO | |
iStatus = fDecodeSdoRx(szData); | |
break; | |
break; | |
case 16: | |
// Nodeguard / Hearbeat | |
iStatus = fDecodeHeartbeat(szData); | |
break; | |
default: | |
break; | |
} | |
// show return frame | |
if (iStatus == 0){ | |
document.getElementById("divDataReturn").style.display = "block"; | |
} | |
} | |
// decode heartbeat data | |
function fDecodeHeartbeat(szData) { | |
// check length | |
if (szData.length % 2 != 0){ | |
fSetError("invalid length due to its telegram type"); | |
return 1; | |
} | |
// check length | |
if ((szData.length != 2)){ | |
fSetError("invalid length due to its telegram type"); | |
return 1; | |
} | |
// prepare table | |
var objTable = document.getElementById("tabDataReturn").getElementsByTagName('tbody')[0]; | |
fDeleteAllRows(objTable); | |
// get nmt state | |
szCmdByte = szData.substring(0, 2, 16); | |
// Nmt state | |
iNmtSts = (parseInt(szCmdByte,16) & 0b0111_1111); | |
switch(iNmtSts){ | |
case 0x00: | |
szSts = "Bootup"; | |
break; | |
case 0x04: | |
szSts = "Stopped"; | |
break; | |
case 0x05: | |
szSts = "Operational"; | |
break; | |
case 0x7F: | |
szSts = "Pre-Operational"; | |
break; | |
default: | |
fSetError("unknown state"); | |
return 1; | |
} | |
fAddRow(objTable, "Nmt-Status", szSts) | |
// toggle bit | |
iToggleBit = (parseInt("0x" + szCmdByte) & 0b1000_0000) >> 7; | |
// protocol type | |
if (iToggleBit){ | |
fAddRow(objTable, "Service", "Node-Guarding"); | |
} | |
else{ | |
fAddRow(objTable, "Service", "Heartbeat or Node-Guarding"); | |
} | |
// return no error | |
return 0; | |
} | |
// decode NMt data | |
function fDecodeNmt(szData) { | |
// check length | |
if (szData.length % 2 != 0){ | |
fSetError("invalid length due to its telegram type"); | |
return 1; | |
} | |
// check length | |
if ((szData.length != 4)){ | |
fSetError("invalid length due to its telegram type"); | |
return 1; | |
} | |
// prepare table | |
var objTable = document.getElementById("tabDataReturn").getElementsByTagName('tbody')[0]; | |
fDeleteAllRows(objTable); | |
// row command | |
iCmd = parseInt(szData.substring(0, 2),16); | |
switch(iCmd){ | |
case 0x01: | |
szCmd = "Enter Operational"; | |
break; | |
case 0x02: | |
szCmd = "Enter Stop"; | |
break; | |
case 0x80: | |
szCmd = "Enter Pre-Operational"; | |
break; | |
case 0x81: | |
szCmd = "Reset Node"; | |
break; | |
case 0x82: | |
szCmd = "Reset communication"; | |
break; | |
default: | |
fSetError("unknown command"); | |
return 1; | |
} | |
fAddRow(objTable, "Command", szCmd) | |
// row node | |
iNode = parseInt(szData.substring(2, 4), 16) | |
if (iNode == 0){ | |
fAddRow(objTable, "Node", "All"); | |
} | |
else{ | |
fAddRow(objTable, "Node", iNode); | |
} | |
// return no error | |
return 0; | |
} | |
// decode Recive SDO | |
function fDecodeSdoRx(szData){ | |
// check to long | |
if (szData.length > 16){ | |
fSetError("telegram to long"); | |
return 1; | |
} | |
// check length | |
if (szData.length % 2 != 0){ | |
fSetError("invalid length due to its telegram type"); | |
return 1; | |
} | |
// prepare table | |
var objTable = document.getElementById("tabDataReturn").getElementsByTagName('tbody')[0]; | |
fDeleteAllRows(objTable); | |
// row sdo type | |
szCmdByte= szData.substring(0, 2, 16); | |
// Ccs | |
iCcs = (parseInt(szCmdByte, 16) & 0b1110_0000) >> 5; | |
// check command | |
if (iCcs == 0b010){ | |
// read command | |
// read | |
fAddRow(objTable, "Command", "Read dictionary"); | |
// index | |
szIndex1 = szData.substring(2, 4, 16); | |
szIndex2 = szData.substring(4, 6, 16); | |
if ((szIndex1 != "") || (szIndex2 != "")){ | |
fAddRow(objTable, "Index", "0x" + szIndex2 + szIndex1); | |
} | |
// sub index | |
szSubIndex = szData.substring(6, 8, 16); | |
if (szSubIndex != ""){ | |
fAddRow(objTable, "Subindex", "0x" + szSubIndex); | |
} | |
// data | |
szData = szData.substring(8, szData.length, 16); | |
if (szData != ""){ | |
fAddRow(objTable, "Data", "0x" + szData); | |
} | |
} else if (iCcs == 0b001){ | |
// write command | |
// read | |
fAddRow(objTable, "Command", "Write dictionary"); | |
// sdo type | |
iSdoType = (parseInt(szCmdByte, 16) & 0b0000_0010) >> 1; | |
if (iSdoType == 1){ | |
// expedited mode | |
fAddRow(objTable, "Mode", "Expedited (1 frame)"); | |
} | |
// size | |
iSizeKnown = (parseInt(szCmdByte, 16) & 0b0000_0001); | |
iSize = (parseInt(szCmdByte, 16) & 0b0000_1100) >> 2; | |
iSize = (4-iSize); | |
if (iSizeKnown == 1){ | |
fAddRow(objTable, "Data size", iSize); | |
} | |
else{ | |
fAddRow(objTable, "Data size", "unknown"); | |
} | |
// index | |
szIndex1 = szData.substring(2, 4, 16); | |
szIndex2 = szData.substring(4, 6, 16); | |
if ((szIndex1 != "") || (szIndex2 != "")){ | |
fAddRow(objTable, "Index", "0x" + szIndex2.toUpperCase() + szIndex1.toUpperCase()); | |
} | |
// sub index | |
szSubIndex = szData.substring(6, 8, 16); | |
if (szSubIndex != ""){ | |
fAddRow(objTable, "Subindex", "0x" + szSubIndex.toUpperCase()); | |
} | |
// data | |
if (iSizeKnown){ | |
szData = szData.substring(8, 8 + iSize*2, 16); | |
} | |
else{ | |
szData = szData.substring(8, szData.length, 16); | |
} | |
if (szData != ""){ | |
fAddRow(objTable, "Data", "0x" + szData.toUpperCase()); | |
} | |
} | |
else{ | |
// invalid command | |
fSetError("Invalid command"); | |
return 1; | |
} | |
// return no error | |
return 0; | |
} | |
// decode Transmit SDO | |
function fDecodeSdoTx(szData){ | |
// check to long | |
if (szData.length > 16){ | |
fSetError("telegram to long"); | |
return 1; | |
} | |
// check length | |
if (szData.length % 2 != 0){ | |
fSetError("invalid length due to its telegram type"); | |
return 1; | |
} | |
// prepare table | |
var objTable = document.getElementById("tabDataReturn").getElementsByTagName('tbody')[0]; | |
fDeleteAllRows(objTable); | |
// row sdo type | |
szCmdByte= szData.substring(0, 2, 16); | |
// Ccs | |
iCcs = (parseInt(szCmdByte, 16) & 0b1110_0000) >> 5; | |
// check command | |
if (parseInt(szCmdByte, 16) == 0x80){ | |
// error response | |
// read | |
fAddRow(objTable, "Type", "Error response"); | |
// index | |
szIndex1 = szData.substring(2, 4, 16); | |
szIndex2 = szData.substring(4, 6, 16); | |
if ((szIndex1 != "") || (szIndex2 != "")){ | |
fAddRow(objTable, "Index", "0x" + szIndex2 + szIndex1); | |
} | |
// sub index | |
szSubIndex = szData.substring(6, 8, 16); | |
if (szSubIndex != ""){ | |
fAddRow(objTable, "Subindex", "0x" + szSubIndex.toUpperCase()); | |
} | |
// error | |
szError = szData.substring(8, szData.length, 16); | |
if (szError != ""){ | |
fAddRow(objTable, "Abort code", "0x" + szError.toUpperCase()); | |
} | |
} | |
else if (iCcs == 0b010){ | |
// read command | |
// read | |
fAddRow(objTable, "Command", "Read dictionary"); | |
// sdo type | |
iSdoType = (parseInt(szCmdByte, 16) & 0b0000_0010) >> 1; | |
if (iSdoType == 1){ | |
// expedited mode | |
fAddRow(objTable, "Mode", "Expedited (1 frame)"); | |
} | |
// size | |
iSizeKnown = (parseInt(szCmdByte, 16) & 0b0000_0001); | |
iSize = (parseInt(szCmdByte, 16) & 0b0000_1100) >> 2; | |
iSize = (4-iSize); | |
if (iSizeKnown == 1){ | |
fAddRow(objTable, "Data size", iSize); | |
} | |
else{ | |
fAddRow(objTable, "Data size", "unknown"); | |
} | |
// index | |
szIndex1 = szData.substring(2, 4, 16); | |
szIndex2 = szData.substring(4, 6, 16); | |
if ((szIndex1 != "") || (szIndex2 != "")){ | |
fAddRow(objTable, "Index", "0x" + szIndex2 + szIndex1); | |
} | |
// sub index | |
szSubIndex = szData.substring(6, 8, 16); | |
if (szSubIndex != ""){ | |
fAddRow(objTable, "Subindex", "0x" + szSubIndex.toUpperCase()); | |
} | |
// data | |
if (iSizeKnown){ | |
szData = szData.substring(8, 8 + iSize*2, 16); | |
} | |
else{ | |
szData = szData.substring(8, szData.length, 16); | |
} | |
if (szData != ""){ | |
fAddRow(objTable, "Data", "0x" + szData.toUpperCase()); | |
} | |
} else if (iCcs == 0b001){ | |
// write command | |
// read | |
fAddRow(objTable, "Command", "Write dictionary"); | |
// index | |
szIndex1 = szData.substring(2, 4, 16); | |
szIndex2 = szData.substring(4, 6, 16); | |
if ((szIndex1 != "") || (szIndex2 != "")){ | |
fAddRow(objTable, "Index", "0x" + szIndex2.toUpperCase() + szIndex1.toUpperCase()); | |
} | |
// sub index | |
szSubIndex = szData.substring(6, 8, 16); | |
if (szSubIndex != ""){ | |
fAddRow(objTable, "Subindex", "0x" + szSubIndex.toUpperCase()); | |
} | |
// data | |
szData = szData.substring(8, szData.length, 16); | |
if (szData != ""){ | |
fAddRow(objTable, "Data", "0x" + szData.toUpperCase()); | |
} | |
} | |
else{ | |
// invalid command | |
fSetError("Invalid command"); | |
return 1; | |
} | |
// return no error | |
return 0; | |
} | |
// delete all rows | |
function fDeleteAllRows(objTable){ | |
// delete all rows | |
while(objTable.rows.length > 0) { | |
objTable.deleteRow(0); | |
} | |
} | |
// add a new row | |
function fAddRow(objTable, szDesc, iValue){ | |
var objRow = objTable.insertRow(objTable.rows.length); | |
var objCell1 = objRow.insertCell(0); | |
var objCell2 = objRow.insertCell(1); | |
objCell1.innerHTML = "<b>" + szDesc + "</b>" | |
objCell2.innerHTML = iValue; | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment