Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strange interaction with PLC when writing strings #546

Open
jbockmon opened this issue Feb 15, 2024 · 2 comments
Open

Strange interaction with PLC when writing strings #546

jbockmon opened this issue Feb 15, 2024 · 2 comments

Comments

@jbockmon
Copy link

PLC: Schneider m172
String length: 32 (31 + null term)

Looking through some of the other issues here, I was able to use your help to pull the string successfully from the PLC. This is the code:

console.log("initial register read:")
let data = await modbusConnection.readHoldingRegisters(regAddress, 16);
console.log(data.buffer)
console.log((data.buffer.swap16().toString('ascii')).replace(/\0/g, ''));
console.log(Array.from(data.buffer))

console.log('Register write:')
const charCodes = Array.from(Buffer.from('abcdefghijklmnop', 'ascii'));
await modbusConnection.writeRegisters(regAddress, charCodes);
console.log(charCodes)

console.log('Second read back')
data = await modbusConnection.readHoldingRegisters(regAddress, 16);
console.log(data.buffer)
console.log((data.buffer.swap16().toString('ascii')).replace(/\0/g, ''));
console.log(Array.from(data.buffer))

The PLC contains the initial string of: "abcdefghijklmnopqrstuvwxyz" to show that it is reading properly.

Output from the code:

[2024-02-15T19:45:00.566Z] initial register read:
[2024-02-15T19:45:00.643Z] <Buffer 62 61 64 63 66 65 68 67 6a 69 6c 6b 6e 6d 70 6f 72 71 74 73 76 75 78 77 7a 00 00 00 00 00 00 00>
[2024-02-15T19:45:00.644Z] abcdefghijklmnopqrstuvwxyz
[2024-02-15T19:45:00.644Z] [
97, 98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, 110, 111, 112,
113, 114, 115, 116, 117, 118, 119, 120,
121, 122, 0, 0, 0, 0, 0, 0
]
[2024-02-15T19:45:00.645Z] Register write:
[2024-02-15T19:45:00.739Z] [
97, 98, 99, 100, 101,
102, 103, 104, 105, 106,
107, 108, 109, 110, 111,
112
]
[2024-02-15T19:45:00.740Z] Second read back
[2024-02-15T19:45:00.812Z] <Buffer 00 61 00 62 00 63 00 64 00 65 00 66 00 67 00 68 00 69 00 6a 00 6b 00 6c 00 6d 00 6e 00 6f 00 70>
[2024-02-15T19:45:00.812Z] abcdefghijklmnop
[2024-02-15T19:45:00.813Z] [
97, 0, 98, 0, 99, 0, 100, 0, 101,
0, 102, 0, 103, 0, 104, 0, 105, 0,
106, 0, 107, 0, 108, 0, 109, 0, 110,
0, 111, 0, 112, 0
]

It appears that the writeRegisters method with the PLC is being interpreted as 16 characters with terms between each character. I'm not sure if this is due to the library or the PLC. Or possibly something to do with the endianness for each character? I can't write to the individual registers because the PLC blocks all registers after the string starts for the duration of the string length.

Any help you can provide would be appreciated.

@jbockmon
Copy link
Author

After lots of shower thinking, I realized that the issue is due to the PLC packing 2 x 8 bit chars into a single 16 bit register for each register assigned to the string. Because the PLC blocks writing along the length of registers reserved for a string, the only way to get around this is bit packing each value being sent in the array with 2 chars.

Here is the code that is working for me:

async function writeStringToRegisters(regAddress, str, maxLength) {
const charCodes = Array.from(Buffer.from(str, 'ascii'));

if (charCodes.length > maxLength) {
    console.warn(`String length exceeds maximum allowed length. Truncating to ${maxLength} characters.`);
    charCodes.length = maxLength;
}

while (charCodes.length < maxLength) {
    charCodes.push(0);
}

const registerValues = [];

for (let i = 0; i < charCodes.length; i += 2) {
    const char1 = charCodes[i + 1] || 0; 
    const char2 = charCodes[i] || 0; 
    const value = (char1 << 8) | char2; 
    registerValues.push(value);
}

try {
    await modbusConnection.writeRegisters(regAddress, registerValues);
    console.log('Write successful');
} catch (error) {
    console.error('Write failed:', error);
}

}

In my packing, I had to switch endianness for my PLC, but if you want it without it swapping the packed chars, it's this:

for (let i = 0; i < charCodes.length; i += 2) {
const char1 = charCodes[i] || 0;
const char2 = charCodes[i + 1] || 0;
const value = (char1 << 8) | char2;
registerValues.push(value);
}

My issue is resolved, but I'll comment and keep this here for you folks to close should you want the code. My suggestion would be to implement a string writing function but don't want to overstep if that would be considered bloat.

@yaacov
Copy link
Owner

yaacov commented Feb 16, 2024

Hello,

Thank you for the issue, and for the work around 💚

It looks like a good example code that may help other users, can you consider adding an example for reading and writing strings to "Schneider m172" to the examples directory ?

Other examples in the example directory:
https://github.com/yaacov/node-modbus-serial/tree/master/examples

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants