Skip to content

Commit

Permalink
unified modbus readings into a single mqtt payload (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
dadna authored Aug 25, 2022
1 parent a5cbc67 commit f2b35bb
Showing 1 changed file with 44 additions and 29 deletions.
73 changes: 44 additions & 29 deletions lib/modbus.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ const { default: ModbusRTU } = require('modbus-serial');
const delay = ms => new Promise(r => setTimeout(r, ms));

class Modbus extends events.EventEmitter {
supportedFCs = [3, 4];

constructor(mqtt, modbus, device) {
super();
this.mqtt = mqtt;
Expand Down Expand Up @@ -41,38 +43,52 @@ class Modbus extends events.EventEmitter {

/**
* the modbus function code. now supports only 3 and 4. default is 4
* @param {number} functionCode default is 4
* @param {number[]} functionCodes default is [3, 4]
*/
async pollRegistersByFC(functionCode = 4) {
if (!functionCode) throw Error('function code must be provided for polling');
if (functionCode !== 3 && functionCode !== 4) throw Error('only FC3 and FC4 are supported for polling');
const descriptorRegistersProperty = functionCode === 4 ? 'input' : 'keep';
async pollRegisters(functionCodes = this.supportedFCs) {
if (!functionCodes) throw Error('function code must be provided for polling');
await this.modbus.setID(this.modbusId);
const result = {};
const input = _.get(this.descriptor, `fromModbus.${descriptorRegistersProperty}`);
const keys = Object.keys(input);
// eslint-disable-next-line
for (let key of keys) {
const addressDescriptor = _.get(input, key);
const address = _.get(addressDescriptor, 'address');
let value;
try {
const promise = functionCode === 4
? this.modbus.readInputRegisters.bind(this.modbus)
: this.modbus.readHoldingRegisters.bind(this.modbus);
// eslint-disable-next-line no-await-in-loop
value = await promise(address, 1);
// eslint-disable-next-line no-await-in-loop
await delay(300);
} catch (err) {
logger.error(err);
for (const fc of functionCodes) {
const descriptorRegistersProperty = fc === 4 ? 'input' : 'keep';
const input = _.get(this.descriptor, `fromModbus.${descriptorRegistersProperty}`);
const keys = Object.keys(input);
console.log(`reading with FC${fc}`);
if (!this.supportedFCs.includes(fc)) {
console.error(`FC${fc} not supported. skipping to next FC...`);
continue;
}
let promise;
if (fc === 3) {
promise = this.modbus.readHoldingRegisters.bind(this.modbus)
} else if (fc === 4) {
promise = this.modbus.readInputRegisters.bind(this.modbus)
} else {
console.error(`FC${fc} not supported. skipping to next FC...`);
continue;
}
// eslint-disable-next-line
for (let key of keys) {
const addressDescriptor = _.get(input, key);
const address = _.get(addressDescriptor, 'address');
let value;
const registersCountToRead = _.get(addressDescriptor, 'count') || 1;
try {
// eslint-disable-next-line no-await-in-loop
value = await promise(address, registersCountToRead);
// eslint-disable-next-line no-await-in-loop
await delay(300);
const { post } = addressDescriptor;
const interpreted = registersCountToRead === 1 ? _.get(value, 'data[0]') : value.data;
const raw = _.get(value, 'buffer');
value = (post) ? post(interpreted, raw) : interpreted;
_.set(result, key, value);
} catch (err) {
logger.error(err);
}
}
const { post } = addressDescriptor;
const interpreted = _.get(value, 'data[0]');
const raw = _.get(value, 'buffer');
value = (post) ? post(interpreted, raw) : interpreted;
_.set(result, key, value);
}

const topic = `${this.id}`;
const payload = JSON.stringify(result);
this.mqtt.publish(topic, payload);
Expand All @@ -82,8 +98,7 @@ class Modbus extends events.EventEmitter {
try {
await this.modbus.setID(this.modbusId);
// error handling is done within the invoked function so we can call both polls sequentially
await this.pollRegistersByFC(3);
await this.pollRegistersByFC(4);
await this.pollRegisters();
} catch (err) {
logger.error(err);
}
Expand Down

0 comments on commit f2b35bb

Please sign in to comment.