智能电表数据接入物联网平台实践
- 设备接线准备
- 设备调试
- 代码实现
- Modbus TCP Client 读取电表数据
- 读取寄存器数据转成32bit Float格式
- 然后使用modbusTCP Client 读取数据
- 使用mqtt协议接入物联网平台
- 最终代码实现
设备接线准备
设备调试
代码实现
Modbus TCP Client 读取电表数据
读取寄存器数据转成32bit Float格式
原理
/**
*
* 17038, 7864
参考 https://blog.csdn.net/qq_36270361/article/details/115823294
SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM
0100 0010 1000 1110 0000 1111 0101 1100
s = 0
e = (1000 0101)转10进制 133 - 127 = 6
尾数
000 1110 0000 1111 0101 1100
4#在尾数的左边有一个省略的小数点和1,这个1在浮点数的保存中经常省略,
1.000 1110 0000 1111 0101 1100
指数值e = 6,因此小数点向右移动6位,得到尾数值如下:
1000111.0 0000 1111 0101 1100
整数
1 *2^6 + 0 *2^5 + 0 *2^4 + 0 *2^3 + 1* 2^2 + 1 *2^1 + 1*2^0
64 + 0+0+0 + 4 + 2 + 1
71
小数部分前面0太多可以忽略不记了
0 * 2^-1 + 0 * 2^-2 + 0 * 2^-3 + 0 * 2^-4 + 0 * 2^-5 ....
浮点数值 = 整数 + 小数 = 71 + 0 = 71
*/
function toFloat(s1, s2)
{
// s1:第一个寄存器地址数据,s2:第二个寄存器地址数据
//将输入数值short转化为无符号unsigned short
const us1 = s1, us2 = s2; // int
if (s1 < 0) us1 += 65536;
if (s2 < 0) us2 += 65536;
//sign: 符号位, exponent: 阶码, mantissa:尾数
let sign, exponent; // int
let mantissa; // float
//计算符号位
sign = parseInt(us1 / 32768); // js中只需要整数
//去掉符号位
let emCode = us1 % 32768; // int
//计算阶码
exponent = parseInt(emCode / 128);
//计算尾数
mantissa = (emCode % 128 * 65536 + us2) / 8388608; // float
//代入公式 fValue = (-1) ^ S x 2 ^ (E - 127) x (1 + M)
const S = Math.pow(-1, sign)
const E = Math.pow(2, exponent - 127)
const M = (1 + mantissa)
return S * E * M;
}
然后使用modbusTCP Client 读取数据
// create an empty modbus client
const ModbusRTU = require("modbus-serial");
const client = new ModbusRTU();
// open connection to a tcp line
client.connectTCP("10.0.0.251", { port: 24 });
client.setID(1);
// read the values of 10 registers starting at address 0
// on device number 1. and log the values to the console.
setInterval(() => {
console.log('-----read-----')
client.readHoldingRegisters(4157, 10, (err, data) =>{
// data: {
// data: [17038, 7864]
// buffer // buffer 数据 实际上转换出来就是data数组
// }
if (data?.data){
console.log(data.data);
const powerData = toFloat(data.data[0], data.data[1])
console.log('-------powerData------', powerData)
}
});
}, 3000);
使用mqtt协议接入物联网平台
const mqtt = require("mqtt");
const md5 = require('js-md5');
const secureId = "admin";
const secureKey = "adminkey";
const timestamp = new Date().getTime()
const username = `${secureId}|${timestamp}`
const password = md5(username + "|" + secureKey)
const config = {
url: "mqtt://10.0.0.108:1883",
productId: '1696816545212956672',
clientId: "1704681506453053440", // 电表设备id
host: '10.0.0.108',
port: 1883
}
const mqttClient = mqtt.connect({
clientId: config.clientId,
username,
password,
host: config.host,
port: config.port,
protocol: 'mqtt'
}); //指定服务端地址和端口
// 推送数据
function publishData (key, value) {
const msg = {
"deviceId": config.clientId,
"properties": {
[key]: value
}
}
mqttClient.publish(`/${config.productId}/${config.clientId}/properties/report`,JSON.stringify(msg))
}
//连接成功
mqttClient.on("connect", function() {
console.log("服务器连接成功");
publishData('online_time', new Date().getTime()) // 上报一条上线的消息
});
最终代码实现
function toFloat(s1, s2)
{
// s1:第一个寄存器地址数据,s2:第二个寄存器地址数据
//将输入数值short转化为无符号unsigned short
const us1 = s1, us2 = s2; // int
if (s1 < 0) us1 += 65536;
if (s2 < 0) us2 += 65536;
//sign: 符号位, exponent: 阶码, mantissa:尾数
let sign, exponent; // int
let mantissa; // float
//计算符号位
sign = parseInt(us1 / 32768); // js中只需要整数
//去掉符号位
let emCode = us1 % 32768; // int
//计算阶码
exponent = parseInt(emCode / 128);
//计算尾数
mantissa = (emCode % 128 * 65536 + us2) / 8388608; // float
//代入公式 fValue = (-1) ^ S x 2 ^ (E - 127) x (1 + M)
const S = Math.pow(-1, sign)
const E = Math.pow(2, exponent - 127)
const M = (1 + mantissa)
return S * E * M;
}
// create an empty modbus client
const ModbusRTU = require("modbus-serial");
const client = new ModbusRTU();
// open connection to a tcp line
client.connectTCP("10.0.0.251", { port: 24 });
client.setID(1);
const mqtt = require("mqtt");
const md5 = require('js-md5');
const secureId = "admin";
const secureKey = "adminkey";
const config = {
url: "mqtt://10.0.0.108:1883",
productId: '1696816545212956672',
clientId: "1704681506453053440", // 电表设备id
host: '10.0.0.108',
port: 1883
}
let mqttClient = null
let reconnectInterval = 1000;
let reconnectTimer = null;
// 推送数据
function publishData (key, value) {
const msg = {
"deviceId": config.clientId,
"properties": {
[key]: value
}
}
mqttClient?.publish(`/${config.productId}/${config.clientId}/properties/report`,JSON.stringify(msg))
}
function createClient() {
if(mqttClient){
return;
}
const timestamp = new Date().getTime()
const username = `${secureId}|${timestamp}`
const password = md5(username + "|" + secureKey)
mqttClient = mqtt.connect({
clientId: config.clientId,
username,
password,
host: config.host,
port: config.port,
protocol: 'mqtt',
}); //指定服务端地址和端口
//连接成功
mqttClient?.on("connect", function() {
console.log("服务器连接成功");
publishData('online_time', new Date().getTime())
});
// 断线重连
mqttClient.on('error', (error) => {
console.log('error:',new Date().getTime(), error);
reconnect();
});
mqttClient.on('end', () => {
console.log('end-------:', new Date().getTime());
reconnect();
});
}
function reconnect() {
console.log(`reconnecting in ${reconnectInterval}ms...`);
reconnectTimer = setTimeout(createClient, reconnectInterval);
reconnectInterval = Math.min(reconnectInterval * 2, 30000);
}
// 创建链接
createClient()
// read the values of 10 registers starting at address 0
// on device number 1. and log the values to the console.
setInterval(() => {
console.log('-----read-----')
client.readHoldingRegisters(4157, 2, (err, data) =>{
if (data?.buffer){
console.log(data.data);
const powerData = toFloat(data.data[0], data.data[1])
console.log('------powerData-------', powerData)
publishData('total_working_energy', powerData)
}
});
},5 * 60 * 1000);
效果预览