#javascript #node.js #beagleboneblack #rs485
Вопрос:
У меня есть Beaglebone black, работающий на Linux Debian с системой связи cape 2 с подключением RS485 (устройство последовательного порта Linux находится в /dev/ttyS1).
У меня нет проблем с тем, чтобы заставить thinks взаимодействовать с устройством BMS (система управления батареей), за исключением того, что его протокол выглядит настолько странно, что я не могу безопасно синхронизировать свои запросы с его ответами.
Позвольте мне погрузиться в протокол, который я разработал: рамка для запроса напряжения, тока и SoC (состояние заряда) 0x90
, и рамка выглядит следующим образом:
<Buffer a5 40 90 08 00 00 00 00 00 00 00 00 cs>
С cs
контрольной суммой (совокупная сумма всех байтов, сохраняющая LSB).
13-байтовый ответ выглядит следующим образом:
<Buffer 69 40 86 00 84 00 00 75 30 03 e2 4c 00>
Чтобы декодировать кадр, пропуская 3 первых байта, мы делаем:
- байт 3, 16-разрядный BE: напряжение, масштаб 0,1 В: 0x0084 = 132 * 0,1 = 13,2 В
- байт 5, 16-разрядный BE: пропустить
- байт 7, 16-разрядный BE: текущий, примените смещение 30000, затем масштаб 0.1 A 0x7530 = 30000 — 30000 = 0A
- байт 8, 16-разрядный BE: SoC, примените масштаб 0,1%: 0x03e2 = 994 * 0,1 = 99,4%
Мой журнал кода выдает следующие значения, которые соответствуют ожидаемым.
Pepsr v2.2.236 4:08:15 PM 📈 [pepsr-olenlab2] -- ✅- 🚜 RS 485: voltageCurrentSoc: { voltage: 13.200000000000001, current: 0, soc: 99.4 }
Пока все хорошо.
Но теперь, поскольку мне нужно гораздо больше данных от BMS, я должен периодически запрашивать больше кадров. Я реализовал систему вопросов и ответов в setInterval()
следующем:
class Rs485 {
// … blah blah …
start() {
// == Analyze the received frames
serialport.on('data', data => {
// Append data
this.readingBytes = this.readingBytes.concat(Array.from(data))
//this.log('data received: ', data, 'as array', Array.from(data), 'length is', this.readingBytes.length)
// Data is OK?
if (this.readingBytes.length == 13) {
this.parseMessage(new Buffer.from(this.readingBytes))
this.readingBytes = []
}
})
// == Prepare the next frame to be requested
this.askFor = 0
this.askForCount = Object.keys(Rs485.FRAME_TYPE).length
const request = () => {
const key = Object.keys(Rs485.FRAME_TYPE)[this.askFor]
const type = Rs485.FRAME_TYPE[key]
//this.log('Now asking for ', this.askFor, 'th type, aka', key, ', value:', type)
this.request(type)
}
// == On open…
this.serialport.on('open', () => {
this.success('Successfully open')
request()
})
// == Periodically ask for the next frame type (1Hz)
setInterval(() => {
if (!this.serialport.isOpen) {
this.log("Port Open Status: " this.serialport.isOpen)
return
}
request()
},1e3)
}
}
parseMessage()
просто делает то, что мы сделали выше, в зависимости от типа кадра.
Как вы можете видеть, я сохраняю текущий запрошенный тип кадра в переменной и предполагаю, что следующий полученный пакет будет ответом на запрос.
Одна вещь, которую, я думаю, я должен прояснить, заключается в том, что я не нашел крючка в заголовке ответа, который я мог бы связать с запросом.
Итак, мой вопрос в том, как я могу улучшить модель коммуникации, чтобы лучше согласовать ответ с запросом?
Комментарии:
1. Какой язык вы используете?
2. Это javascript на nodejs
Ответ №1:
Если вы когда-либо отправляете запросы только одного типа (например, запрос напряжения батареи, тока и SoC), то нет практической необходимости связывать запросы с ответами. Вам может сойти с рук просто отправлять новый запрос каждую секунду, даже не глядя на ответы. Если несколько кадров пропадут или будут повреждены во время передачи, вреда не будет — пока будут поступать последующие ответы, у вас будут свежие данные.
Если вы хотите обнаружить сбой связи в этом решении, запустите таймер при получении ответа (значение таймера зависит от вашей системы — может быть, 10-60 секунд?) и объявите сбой связи, если вы не получили никаких новых ответов до истечения времени ожидания.
Если вы отправляете запросы разных типов, то да, вам нужно запомнить, какой тип запроса был отправлен в последний раз, и дождаться правильного ответа. В этом решении вам нужно запускать таймер с каждым запросом, а затем ждать, пока текущий кадр запроса либо получит соответствующий кадр ответа, либо истечет время ожидания. Не отправляйте новый запрос до того, как это произойдет. Обработайте одну транзакцию запроса-ответа до ее успешного или неудачного завершения, прежде чем начинать новую. Таким образом, любые сбои передачи на линии связи будут пойманы таймаутом, и запросы и ответы не должны выходить из синхронизации. Опять же, значения времени ожидания зависят от вашей системы.