Вот реализация, проверьте пожалуйста
// Путь к RPC
var pathRPC = "/rpc/v1/wb-mqtt-serial/port/Load/";
/*
Из мануала:
Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 Byte 7
Sync Camera Address Command 1 Command 2 Data 1 Data 2 Checksum
Byte 1 (Sync) - the synchronization byte, fixed to FF
Byte 2 (Camera Address) - logical address of the camera being controlled (Address 1 is 01)
Byte 3 & 4 (Command 1 and 2) are shown below
Byte 5 (Data 1) - pan speed, range from 00 (stop) to 3F (high speed) and FF for "turbo" speed (the maximum pan speed that the device can go)
Byte 6 (Data 2) - tilt speed, range from 00 (stop) to 3F (maximum speed)
Byte 7 (Checksum) - sum of bytes (excluding the synchronization byte), then modulo 100 (Decimal code: 256)
То есть длинfа посылки всегда 7 байт.
Первый - всегда 0xff
Второй - camID
3-4 команда
5 - скорость горизонталь
6 - скорость вертикаль
7 - crc. Вот его и реализуем в функции calcCRC
*/
// Вычисляет контрольную сумму CRC по протоколу Pelco-D
//На входе строка в которой каждый байт соответствует посылке. Если в строке есть 7 байт или больше они должны быть нулевыми.
function calcCRC(str) {
// Сумма начинается с 0
var sum = 0;
// Проходим по строке, игнорируя первый символ (FF)
for (i = 1; i < str.length; i++) {
// Получаем числовое представление текущего символа (ASCII-код)
var byteValue = str.charCodeAt(i);
sum += byteValue;
}
// Возвращаем остаток от деления суммы на 256
//return (sum % 256).toString(16);
return (sum % 256);
}
/*
//просто тест для CRC
var testStr = String.fromCharCode(0xff) +
String.fromCharCode(0x10) +
String.fromCharCode(0xf0) +
String.fromCharCode(0x80) +
String.fromCharCode(0x20) +
String.fromCharCode(0x20) +
String.fromCharCode(0x00);
log.info("calcCRC()", calcCRC(testStr)); //0xc0 норм
*/
function bytesToHexString(arr) {
var result = '';
for (var i = 0; i < arr.length; i++) {
var hex = arr.charCodeAt(i).toString(16);
//var hex = arr.charCodeAt(i);
log.info("hex", i, "=", hex)
if (hex.length < 2) {
hex = '0' + hex;
}
result += hex;
}
return result;
}
function hexStringToBytes(hex) {
var bytes = [];
for (var i = 0; i < hex.length; i += 2) {
bytes.push(parseInt(hex.substr(i, 2), 16));
}
return bytes;
}
function createCam(nameCam, portCam, speedportCam, parityCam, stopBitCam, camID) {
//log.info ("nameCam=", nameCam )
//log.info ("portCam=", portCam )
//log.info ("speedportCam=", speedportCam )
//log.info ("parityCam=", parityCam )
//log.info ("stopBitCam=", stopBitCam )
//log.info ("camID=", camID )
// Создаем виртуальное устройство
makeNewVirtualControl(nameCam, "panSpeed", { type: "range", value: 20, min: 0, max: 100, readonly: false });
makeNewVirtualControl(nameCam, "Left", { type: "pushbutton", readonly: false });
//makeNewVirtualControl(nameCam, "Up", { type: "pushbutton", readonly: false });
//makeNewVirtualControl(nameCam, "Stop", { type: "pushbutton", readonly: false });
//makeNewVirtualControl(nameCam, "Down", { type: "pushbutton", readonly: false });
// Up
defineRule(nameCam + "_rule", {
whenChanged: nameCam + "/Left",
then: function (newValue, devName, cellName) {
log.info(nameCam + "RULE!", "cellName=", cellName);
var req = camCommandMsg(camID, 0x00, 0x04, dev[nameCam+"/panSpeed"], 0x00);
log.info("Left=", req);
requestRPC(portCam, speedportCam, parityCam, stopBitCam, nameCam, 1, "HEX", req, 8);
}
});
/*
// Position slider
defineRule(nameCam + "_rule_position", {
whenChanged: nameCam + "/Position",
then: function (value) {
var is_auto_set = positionSetAuto[camID];
if (is_auto_set === 0) {
//var val = (dev[nameCam] && dev[nameCam]["Position"] !== undefined) ? dev[nameCam]["Position"] : 0;
//var req = camCommandMsg(camID, blindCh_L, blindCh_H, 0x03, 0x04, val);
//requestRPC(portCam, speedportCam, parityCam, stopBitCam, nameCam, 1, "HEX", req, 8);
} else {
//positionSetAuto[camID] = 0;
}
}
});
*/
// MQTT reply tracking
trackMqtt(pathRPC + nameCam + "/reply", function (message) {
log.info("from " + nameCam + " name: {}, value: {}".format(message.topic, message.value));
var replyObj;
try {
replyObj = JSON.parse(message.value);
} catch (e) {
log.error("JSON parse error: " + e);
return;
}
if (replyObj && replyObj.error === null) {
var hexStr = replyObj.result.response;
log.info("from " + nameCam + " replyObj.result.response: {}".format(hexStr));
}
})
return 0;
}
function requestRPC(modbusPort, modbusSpeed, modbusParity, reqStopbit, clientID, requestID, messageType, message, responseSize) {
// Формируем JSON запрос:
var strJson = JSON.stringify({
params: {
response_size: responseSize,
format: messageType,
path: modbusPort,
baud_rate: modbusSpeed,
parity: modbusParity,
data_bits: 8,
stop_bits: reqStopbit,
msg: message
},
id: requestID
});
publish(pathRPC + clientID, strJson, 2, false);
}
function camCommandMsg(camID, command1, command2, data1, data2) {
// Сформируем набор байт команды строкой.
var data = String.fromCharCode(0xFF, camID, command1, command2, data1, data2)
log.info ("datalen=",data.length)
var crc = calcCRC(data);//Рассчитаем CRC
//log.info ("crc=",crc)
//log.info ("dataHEX1=",data)
data = data+String.fromCharCode(crc); //Добавим CRC к строке
var bytesStr = bytesToHexString(data);
//log.info ("dataHEX2=",data)
return bytesStr;
}
function makeNewVirtualControl(vdName, nameControl, typeControl) {
if (getDevice(vdName) === undefined) {
log.info("Define new");
defineVirtualDevice(vdName, {
title: vdName,
cells: {},
});
}
// Проверим есть ли уже контрол и если нет - создадим.
var devObj = getDevice(vdName);
if (devObj && !devObj.isControlExists(nameControl)) {
log.debug("Контрола " + nameControl + " нет, создаем.");
devObj.addControl(nameControl, typeControl);
}
}
// Создаем виртуальное устройство, с контролами для камеры
// ID делаем уникальным
// createCam(nameCam, portCam, speedportCam, parityCam, stopBitCam, camID)
// имя порт bod parity stopbit camID
createCam("Камера1", "/dev/ttyRS485-2", 9600, "N", 1, 0x01 );