How to Calculate CRC-16 (XModem)?

G'Day Y'all!!

Does any kind soul perhaps have a function block written for the calculation of CRC-16 XModem checksum values?

I have looked at https://discourse.nodered.org/t/how-to-calculate-crc-16-modbus/48955 and it is a tad above my capabilities to modify for the XModem type requirements...

Possibly, there is even a dedicated node for it that I haven't spotted....

Many thanks
Ed

Apparently there is noo such node in Node-RED library. My second attempt would be a search in NPM library as it is very easy to use NPM modules in Node-RED. It happens that there is such module available. Perhaps you want to give this module a try ?

Last but not least there is an online CRC calculator that can help on checking if CRCs are being correctly computed.

Hi @Andrei

I saw that Library, but alas, I am clueless as to how to implement it correctly - I was hoping that there is someone out there that has the equivalent of this function node:

    {
        "id": "60f6223737445c93",
        "type": "function",
        "z": "d0924c81.d19aa",
        "name": "CRC16-modbus",
        "func": "var CRCMaster = {\n    StringToCheck: \"\",\n    CleanedString: \"\",\n    CRCTableDNP: [],\n    init: function() {\n        this.CRCDNPInit();\n    },\n    CleanString: function(inputType) {\n        if (inputType == \"ASCII\") {\n            this.CleanedString = this.StringToCheck;\n        } else {\n            if (this.StringToCheck.match(/^[0-9A-F \\t]+$/gi) !== null) {\n                this.CleanedString = this._hexStringToString(this.StringToCheck.toUpperCase().replace(/[\\t ]/g, ''));\n            } else {\n                //window.alert(\"String doesn't seem to be a valid Hex input.\");\n                node.warn(\"String doesn't seem to be a valid Hex input.\")\n                return false;\n            }\n        }\n        return true;\n    },\n    CRCDNPInit: function() {\n        var i, j, crc, c;\n        for (i = 0; i < 256; i++) {\n            crc = 0;\n            c = i;\n            for (j = 0; j < 8; j++) {\n                if ((crc ^ c) & 0x0001) crc = (crc >> 1) ^ 0xA6BC;\n                else crc = crc >> 1;\n                c = c >> 1;\n            }\n            this.CRCTableDNP[i] = crc;\n        }\n    },\n    CRC16Modbus: function() {\n        var crc = 0xFFFF;//0xFFFF\n        var str = this.CleanedString;\n        for (var pos = 0; pos < str.length; pos++) {\n            crc ^= str.charCodeAt(pos);\n            for (var i = 8; i !== 0; i--) {\n                if ((crc & 0x0001) !== 0) {//0001\n                    crc >>= 1;//crc >>= 1\n                    crc ^= 0xA001;\n                } else\n                    crc >>= 1;\n            }\n        }\n        return crc;\n    },\n    _stringToBytes: function(str) {\n        var ch, st, re = [];\n        for (var i = 0; i < str.length; i++) {\n            ch = str.charCodeAt(i); // get char\n            st = []; // set up \"stack\"\n            do {\n                st.push(ch & 0xFF); // push byte to stack\n                ch = ch >> 8; // shift value down by 1 byte\n            }\n            while (ch);\n            // add stack contents to result\n            // done because chars have \"wrong\" endianness\n            re = re.concat(st.reverse());\n        }\n        // return an array of bytes\n        return re;\n    },\n    _hexStringToString: function(inputstr) {\n        var hex = inputstr.toString(); //force conversion\n        var str = '';\n        for (var i = 0; i < hex.length; i += 2)\n            str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));\n        return str;\n    },\n    Calculate: function(str, inputType) {\n        this.StringToCheck = str;\n        if (this.CleanString(inputType)) {\n            crcinputcrc16modbus=this.CRC16Modbus().toString(16).toUpperCase();\n            crcinputcrc16modbus=crcinputcrc16modbus.substr(2) + crcinputcrc16modbus.substr(0, 2); //swap bytes\n   \n        }\n    }\n};\n\nCRCMaster.init();\n\nvar inputType = \"ASCII\";\nvar crcinputcrc16modbus;\nvar crcinput = msg.payload;\n\nCRCMaster.Calculate(crcinput, inputType);\n\nmsg.payload = crcinput + crcinputcrc16modbus;\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 380,
        "y": 800,
        "wires": [
            [
                "c41dee96.b578c"
            ]
        ]
    }
]

but calculating the CRC-16 XModem checksum...

The differences don't seem to be major between the Modbus and XModem CRC calculations, but I lack the skills to modify this function node to get it to calculate the XModem CRC...

In fact, the differences are as follows:

Length  Name            Polinomial  Initial value   Final value Input reflected Output reflected
16      CRC16_MODBUS    0x8005      0xFFFF          0x0000      true            true
16      CRC16_XMODEM    0x1021      0x0000          0x0000      false           false

If there are any guru's out there that could help, it would be awesome!!

Regds
Ed

I see. Modifying and creating a new node would take more time but If you want to try the NPM module installation, here is the step by step....

1- Enter your home Node-RED directory ( ~/.node-red) and install there the NPM module

npm i crc16-xmodem

2- Edit the functionGlobalContext in setting.js, adding line below. Restart Node-RED

    functionGlobalContext: {
	crc16xmodem:require('crc16-xmodem'),

3- Require the module in a function node:

let crc = global.get("crc16xmodem");
// Calculate checksum with 4 digit hex output
const originalStr = '00020101021129370016A000000677010111011300660000000005802TH53037646304';
msg.payload = crc.calculateChecksum(originalStr);
return msg;
1 Like

Thanks for coming back to me with those "step by step" instructions! I appreciate it!!

I will look into this way, indeed, if no-one can help with the mods to the Modbus CRC function node...

I like the "easy" way the modbus crc function works though - See below:

[
    {
        "id": "3dfff40c.7d3174",
        "type": "inject",
        "z": "d0924c81.d19aa",
        "name": "QDI 71 1B",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "QDI",
        "payloadType": "str",
        "x": 160,
        "y": 700,
        "wires": [
            [
                "60f6223737445c93"
            ]
        ]
    },
    {
        "id": "60f6223737445c93",
        "type": "function",
        "z": "d0924c81.d19aa",
        "name": "CRC16-modbus",
        "func": "var CRCMaster = {\n    StringToCheck: \"\",\n    CleanedString: \"\",\n    CRCTableDNP: [],\n    init: function() {\n        this.CRCDNPInit();\n    },\n    CleanString: function(inputType) {\n        if (inputType == \"ASCII\") {\n            this.CleanedString = this.StringToCheck;\n        } else {\n            if (this.StringToCheck.match(/^[0-9A-F \\t]+$/gi) !== null) {\n                this.CleanedString = this._hexStringToString(this.StringToCheck.toUpperCase().replace(/[\\t ]/g, ''));\n            } else {\n                //window.alert(\"String doesn't seem to be a valid Hex input.\");\n                node.warn(\"String doesn't seem to be a valid Hex input.\")\n                return false;\n            }\n        }\n        return true;\n    },\n    CRCDNPInit: function() {\n        var i, j, crc, c;\n        for (i = 0; i < 256; i++) {\n            crc = 0;\n            c = i;\n            for (j = 0; j < 8; j++) {\n                if ((crc ^ c) & 0x0001) crc = (crc >> 1) ^ 0xA6BC;\n                else crc = crc >> 1;\n                c = c >> 1;\n            }\n            this.CRCTableDNP[i] = crc;\n        }\n    },\n    CRC16Modbus: function() {\n        var crc = 0xFFFF;//0xFFFF\n        var str = this.CleanedString;\n        for (var pos = 0; pos < str.length; pos++) {\n            crc ^= str.charCodeAt(pos);\n            for (var i = 8; i !== 0; i--) {\n                if ((crc & 0x0001) !== 0) {//0001\n                    crc >>= 1;//crc >>= 1\n                    crc ^= 0xA001;\n                } else\n                    crc >>= 1;\n            }\n        }\n        return crc;\n    },\n    _stringToBytes: function(str) {\n        var ch, st, re = [];\n        for (var i = 0; i < str.length; i++) {\n            ch = str.charCodeAt(i); // get char\n            st = []; // set up \"stack\"\n            do {\n                st.push(ch & 0xFF); // push byte to stack\n                ch = ch >> 8; // shift value down by 1 byte\n            }\n            while (ch);\n            // add stack contents to result\n            // done because chars have \"wrong\" endianness\n            re = re.concat(st.reverse());\n        }\n        // return an array of bytes\n        return re;\n    },\n    _hexStringToString: function(inputstr) {\n        var hex = inputstr.toString(); //force conversion\n        var str = '';\n        for (var i = 0; i < hex.length; i += 2)\n            str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));\n        return str;\n    },\n    Calculate: function(str, inputType) {\n        this.StringToCheck = str;\n        if (this.CleanString(inputType)) {\n            crcinputcrc16modbus=this.CRC16Modbus().toString(16).toUpperCase();\n            crcinputcrc16modbus=crcinputcrc16modbus.substr(2) + crcinputcrc16modbus.substr(0, 2); //swap bytes\n   \n        }\n    }\n};\n\nCRCMaster.init();\n\nvar inputType = \"ASCII\";\nvar crcinputcrc16modbus;\nvar crcinput = msg.payload;\n\nCRCMaster.Calculate(crcinput, inputType);\n\nmsg.payload = crcinput + crcinputcrc16modbus;\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 380,
        "y": 800,
        "wires": [
            [
                "c41dee96.b578c"
            ]
        ]
    },
    {
        "id": "f759df4e.be5be",
        "type": "inject",
        "z": "d0924c81.d19aa",
        "name": "QMOD 49 C1",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "QMOD",
        "payloadType": "str",
        "x": 150,
        "y": 860,
        "wires": [
            [
                "60f6223737445c93"
            ]
        ]
    },
    {
        "id": "2d8e62bf.d8181e",
        "type": "inject",
        "z": "d0924c81.d19aa",
        "name": "QID D6 EA",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "QID",
        "payloadType": "str",
        "x": 160,
        "y": 740,
        "wires": [
            [
                "60f6223737445c93"
            ]
        ]
    },
    {
        "id": "11f7fde6.eeb442",
        "type": "inject",
        "z": "d0924c81.d19aa",
        "name": "QPIGS B7 A9",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "QPIGS",
        "payloadType": "str",
        "x": 150,
        "y": 780,
        "wires": [
            [
                "60f6223737445c93"
            ]
        ]
    },
    {
        "id": "6b83db48.87f2f4",
        "type": "inject",
        "z": "d0924c81.d19aa",
        "name": "QPIWS B4 DA",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "QPIWS",
        "payloadType": "str",
        "x": 140,
        "y": 820,
        "wires": [
            [
                "60f6223737445c93"
            ]
        ]
    },
    {
        "id": "fbe1ddb0.138e2",
        "type": "inject",
        "z": "d0924c81.d19aa",
        "name": "HELLO 58 DA",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "HELLO",
        "payloadType": "str",
        "x": 150,
        "y": 900,
        "wires": [
            [
                "60f6223737445c93"
            ]
        ]
    },
    {
        "id": "c41dee96.b578c",
        "type": "debug",
        "z": "d0924c81.d19aa",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 600,
        "y": 800,
        "wires": []
    }
]

(In the above inject nodes I have a few of the ASCII strings that I need to send with their appropriate XModem CRC's as part of their name - the Modbus CRC function node does a super job of it, but with the "Non XModem" crc's appended....)

The plan, on my side, is to hopefully get an XModem CRC function node working then pop it into a subflow.... I can then use it on ascii strings to and from the machine (Axpert Inverter) I am talking to, to verify comms integrity with minimal further fuss....

Regds
Ed

1 Like

Are you going to use qpig qmod etc... ?
I am also very interested if you find a way to calculate the CRC dynamically on demand .
2400,8,N for the speed of the rs232 port.

Indeed I am, I am intending to go a little further than that as well - I am hoping that once the CRC hurdle has been overcome, I will be able to back up parameters and include them into my dashboards/logging as well... This is particularly handy for "fine tuning" and monitoring harvest results with the associated parameters being logged as well... a further spin off is being able to back up your "working" inverter settings at a button press and then playing around with tweaked values, able to restore should they be "tweaked" in the wrong direction...

I am currently using Solpiplog, not logging directly to EmonCMS, but gathering info from the mqtt section only... I would like a bit more info than it provides in the line of parameters etc... The actual info coming back is great though for regular stuff... An additional possible benefit that I see is that if I can remove the Solpiplog from the equation, lower overheads, more complex and intensive control can be implemented for the same given amount of horsepower available on the pi!

Ed

Edit: a further benefit that I see is being able to get an "instantaneous" meaningful relationship between values - ie read the QPIGS query and interpret it as a string - why? - to get a more accurate derived value of for instance the "AC input VA" to the inverter.... This value has to be derived from: Solar harvest/AC out VA/Battery Charge W/Battery Discharge W etc...

Is this resolved?

Here is a quick way of getting it going...

8MJgexiRyK

[{"id":"4f660f011c561be5","type":"inject","z":"af952aeaa20f4f97","name":"validate 00020101021129370016A000000677010111011300660000000005802TH530376463048956","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"validate","payload":"00020101021129370016A000000677010111011300660000000005802TH530376463048956","payloadType":"str","x":1100,"y":960,"wires":[["2dc07650d2be62f3"]]},{"id":"2dc07650d2be62f3","type":"function","z":"af952aeaa20f4f97","name":"","func":"\nif(msg.topic == \"validate\") {\n    msg.payload = validateChecksum(msg.payload)\n    if (msg.payload) {\n        node.status({ fill: \"green\", shape: \"ring\", text: \"CRC valid\" });\n    } else {\n        node.status({ fill: \"red\", shape: \"ring\", text: \"CRC invalid\" });\n    }\n} else if (msg.topic = \"calculate\") {\n    msg.payload = calculateChecksum(msg.payload)\n    node.status({ fill: \"yellow\", shape: \"ring\", text: msg.payload });\n} else {\n    node.status({ fill: \"grey\", shape: \"ring\", text: \"\" });\n    return null;\n}\nreturn msg;\n\n\n\nfunction _crc16(data, crc, table) {\n    for (const i in data) {\n        const o = data.charCodeAt(i);\n        crc = ((crc << 8) & 0xff00) ^ table[((crc >>> 8) & 0xff) ^ o];\n    }\n    return crc & 0xffff;\n}\n\nfunction crc16(data, crc = 0xffff) {\n    const CRC16_XMODEM_TABLE = context.get(\"CRC16_XMODEM_TABLE\");\n    return _crc16(data, crc, CRC16_XMODEM_TABLE);\n}\n\n/**\n * calculateChecksum\n *\n * @param {String} data string input to calculate checksum\n * @param {Number} checkSumBit Default to 16 bit transform checksum\n */\nfunction calculateChecksum(data, checkSumBit = 16) {\n    const result = crc16(data);\n    return result.toString(checkSumBit);\n}\n\n/**\n * ValidateChecksum\n * \n * validate long string with default checksum hex format (will use last 4 character)\n * ** this is force to hex usage\n * @param {String} str The long string input (include checksum)\n * @param {Number} checksumBit Default to 16 bit transform checksum\n */\nfunction validateChecksum(str, checksumBit = 16) {\n    if (str.length < 5) return false;\n    const targetIndex = str.length - 4;\n    const headValue = str.slice(0, targetIndex);\n    const checkSum = str.slice(targetIndex, str.length).toLowerCase();\n    const result = crc16(headValue);\n    const newCheckSum = result.toString(checksumBit);\n    return checkSum === newCheckSum;\n}\n\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"const CRC16_XMODEM_TABLE = [\n    0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,\n    0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,\n    0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,\n    0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,\n    0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,\n    0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,\n    0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,\n    0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,\n    0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,\n    0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,\n    0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,\n    0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,\n    0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,\n    0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,\n    0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,\n    0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,\n    0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,\n    0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,\n    0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,\n    0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,\n    0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,\n    0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,\n    0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,\n    0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,\n    0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,\n    0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,\n    0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,\n    0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,\n    0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,\n    0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,\n    0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,\n    0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0,\n];\n\ncontext.set(\"CRC16_XMODEM_TABLE\", CRC16_XMODEM_TABLE);\n","finalize":"context.set(\"CRC16_XMODEM_TABLE\", null);","libs":[],"x":1580,"y":880,"wires":[["10fc096fe5e1c8aa"]]},{"id":"10fc096fe5e1c8aa","type":"debug","z":"af952aeaa20f4f97","name":"","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":1730,"y":880,"wires":[]},{"id":"3cbef086459c8790","type":"inject","z":"af952aeaa20f4f97","name":"calculate 10020101021129370016A000000677010111011300660000000005802TH53037646304","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"calculate","payload":"10020101021129370016A000000677010111011300660000000005802TH53037646304","payloadType":"str","x":1110,"y":800,"wires":[["2dc07650d2be62f3"]]},{"id":"de3f66c7d89ad155","type":"inject","z":"af952aeaa20f4f97","name":"validate 10020101021129370016A000000677010111011300660000000005802TH530376463048956","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"validate","payload":"10020101021129370016A000000677010111011300660000000005802TH530376463048956","payloadType":"str","x":1100,"y":840,"wires":[["2dc07650d2be62f3"]]},{"id":"d1dd0cb851448f72","type":"inject","z":"af952aeaa20f4f97","name":"calculate 00020101021129370016A000000677010111011300660000000005802TH53037646304","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"calculate","payload":"00020101021129370016A000000677010111011300660000000005802TH53037646304","payloadType":"str","x":1110,"y":920,"wires":[["2dc07650d2be62f3"]]}]
2 Likes

@Steve-Mcl

Dude!!!

That's awesome!!

Functionally, it's exactly what I am looking for, but the only difference being that I feed the node an ASCII string... ("QPIGS" for instance should return a CRC of B7 A9)

I understood the first 16 Lines of the function then ...... oh look, a squirrel!! (I got Lost!!)

I see in the comments below that there is reference to string or number.... (This isn't even higher grade stuff to me... This is "Doctorate Level"... Lol)

I have read the function's code through to the best of my limited understanding and cannot quite yet figure out how to "get it into ascii string input mode" for want of a better description... or... Am I, as usual, off chasing another squirrel?

Regds
Ed

thanks @Steve-Mcl for your help !
in fact, the CRC calculator gives B7A9 in hexadecimal. See below.
How to insert this QPIGS string in the inject node to make the calculation correct ?
like @eddee54455 , my javascript skills are nearly zero. So I defer to people who do.

image

Hey, just to improve a bit my last post. I learned (tks @Steve-Mcl) that is super easy to use external NPM modules in function nodes. No need to manually install the module. Testing the module crc16-xmodem seems to lead to a good result and the code in the function node is one line only.

msg.payload = crc16Xmodem.crc16(msg.payload, 0);

[{"id":"23bdfcd79aee079b","type":"tab","label":"CRC16-XMODEM","disabled":false,"info":""},{"id":"fbb253ee3c763461","type":"inject","z":"23bdfcd79aee079b","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"QPIGS","payloadType":"str","x":430,"y":80,"wires":[["18684c092912cf21"]]},{"id":"18684c092912cf21","type":"function","z":"23bdfcd79aee079b","name":"calculateChecksum","func":"msg.payload = crc16Xmodem.crc16(msg.payload, 0);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"crc16Xmodem","module":"crc16-xmodem"}],"x":620,"y":80,"wires":[["79ae8b57dae608f2"]]},{"id":"79ae8b57dae608f2","type":"debug","z":"23bdfcd79aee079b","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":820,"y":80,"wires":[]}]
4 Likes

It's worth pointing out this requires node-red v1.3.x or 2.x

1 Like

@Andrei & @Steve-Mcl
I was happy to be able to go home and try this flow.
But I was disappointed, it doesn't work.
What am I doing wrong?
I'm at the last version of NR and for conscience I installed the npm package in console mode and added section in settings.js, but :roll_eyes:

image

First thing to check is the setup tab in the function node. It should look likes below.

More ideas that comes to my mind. 1- I imagine that on the very first use it may take a while for the runtime to install the NPM module. If you run the flow before the installation finishes an error like that may occur. Try to deploy and run the flow again. 2- Check if the module "crc16-xmodem" exists in the "~/.node-red/externalModules" directory. I understand that you will get an error if the module is not there.

Well seen, your information was indeed missing. This is the first time I've filled in this tab.
And it works perfectly. The results are correct
I am very happy with it.
Thank you Andrei ! :ok_hand:

2 Likes

@Jean-Luc

Hey JL,

I am on NR 1.3.5 - I don't see the "Modules" section under the property of the node... Was yours also missing until you edited:

... or am I too far behind in the version of Node Red?

Regds
Ed

In node-red 2.x it is enabled by default.

In 1.3.x you have to enable it in settings.js and restart node-red / refresh editor.

Thanks @Steve-Mcl , Just busy backing up the system before I poke it with the engineering stick!

Regds
Ed

image

This area was indeed empty. I filled it in by hand following Andrei's instructions