【智能电表数据接入物联网平台实践】

news2024/11/25 6:39:07

智能电表数据接入物联网平台实践

  • 设备接线准备
  • 设备调试
  • 代码实现
    • 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);

效果预览
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1029614.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

抄写Linux源码(Day12:从 MBR 到 C main 函数 (1) )

回忆我们需要做的事情&#xff1a; 为了支持 shell 程序的执行&#xff0c;我们需要提供&#xff1a; 1.缺页中断(不理解为什么要这个东西&#xff0c;只是闪客说需要&#xff0c;后边再说) 2.硬盘驱动、文件系统 (shell程序一开始是存放在磁盘里的&#xff0c;所以需要这两个东…

极客时间:数据结构与算法之美【文章笔记 实践 总结】

原文链接:https://time.geekbang.org/column/intro/100017301 27 | 递归树&#xff1a;如何借助树来求解递归算法的时间复杂度&#xff1f;如何借助树来分析归并排序算法的时间复杂度&#xff1f;如何借助树来分析快速排序算法的时间复杂度&#xff1f;如何借助递归树来分析斐波…

macos (M2芯片)搭建flutter环境

安装的版本3.13.4、电脑上没有安装过android studio、安装过brew 1.在终端运行sudo softwareupdate --install-rosetta --agree-to-license&#xff0c;下图展示安装成功的效果 2.下载以下安装包来获取最新的 stable Flutter SDK 3.解压&#xff0c;⚠️注意下载安装sdk的包名…

沉积物微体古生物鉴定

声明 本文是学习GB-T 42629.4-2023 国际海底区域和公海环境调查规程 第4部分&#xff1a;海洋沉积物物理特性调查. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件规定了国际海底区域和公海环境调查中的沉积物组成、物理力学性质、生物…

第二章 进程与线程 十三、进程互斥的软件实现方法(单标志法、双标志先检查、双标志后检查、Peterson算法)

目录 一、单标志法 1、算法思想 2、具体逻辑 二、双标志先检查 1、算法思想 2、具体逻辑 三、双标志后检查 1、算法思想 2、具体逻辑 四、Peterson算法 1、算法思想 2、具体逻辑 五、总结 一、单标志法 1、算法思想 两个进程在访问完临界区后会把使用临界区的权限…

as-if-serial与happens-before原则详解

文章目录 前言详解解决多线程下的问题 Happens-before原则总结as-if-serial语义happens-before的例子 前言 "as-if-serial"原则是Java内存模型中的一个重要概念。该规则规定&#xff1a;不管怎么重排序&#xff08;编译期间的重排序&#xff0c;指令级并行的重排序&…

MYSQL存储引擎基础知识介绍

下面重点介绍几种常用的存储引擎,并对比各个存储引擎之间的区别&#xff0c;以帮助读者理解 不同存储引擎的使用方式。 MyISAM MyISAM是 MySQL的默认存储引擎。MyISAM不支持事务、也不支持外键&#xff0c;其优势是访 问的速度快&#xff0c;对事务完整性没有要求或者以 SEL…

怒刷LeetCode的第11天(Java版)

目录 第一题 题目来源 题目内容 解决方法 方法一&#xff1a;迭代 方法二&#xff1a;递归 方法三&#xff1a;指针转向 第二题 题目来源 题目内容 解决方法 方法一&#xff1a;快慢指针 方法二&#xff1a;Arrays类的sort方法 方法三&#xff1a;计数器 方法四…

【Java 基础篇】Java线程组详解

Java线程组是一种用于管理线程的机制&#xff0c;它允许你将线程组化为一个单元&#xff0c;并对组内的线程进行一些操作和控制。本文将详细介绍Java线程组的概念、如何创建和管理线程组&#xff0c;以及线程组的一些常见用法。 什么是线程组&#xff1f; 线程组是一个用于组…

python生成PDF报告

前言 最近接到了一个需求-将项目下的样本信息汇总并以PDF的形式展示出来&#xff0c;第一次接到这种PDF的操作的功能&#xff0c;还是有点慌的&#xff0c;还好找到了reportlab这个包&#xff0c;可以定制化向PDF写内容&#xff01; 让我们由简入深进行讲解 一、reportlab是…

2023年腾讯云轻量服务器测评:16核 32G 28M 配置CPU测试

腾讯云轻量应用服务器16核32G28M配置优惠价3468元15个月&#xff08;支持免费续3个月/送同配置3个月&#xff09;&#xff0c;轻量应用服务器具有100%CPU性能&#xff0c;系统盘为380GB SSD盘&#xff0c;28M带宽下载速度3584KB/秒&#xff0c;月流量6000GB&#xff0c;折合每天…

由于找不到d3dx9_43.dll,无法继续执行代码要怎么解决

D3DX9_43.dll是一个动态链接库文件&#xff0c;它是DirectX的一个组件&#xff0c;主要用于支持一些旧版本的游戏和软件。当电脑缺少这个文件时&#xff0c;可能会导致这些游戏和软件无法正常运行。例如&#xff0c;一些老游戏可能需要D3DX9_43.dll来支持图形渲染等功能。此外&…

二分类问题的解决利器:逻辑回归算法详解(一)

文章目录 &#x1f34b;引言&#x1f34b;逻辑回归的原理&#x1f34b;逻辑回归的应用场景&#x1f34b;逻辑回归的实现 &#x1f34b;引言 逻辑回归是机器学习领域中一种重要的分类算法&#xff0c;它常用于解决二分类问题。无论是垃圾邮件过滤、疾病诊断还是客户流失预测&…

git安装配置教程

目录 git安装配置1. 安装git2. git 配置3.生成ssh key:4. 获取生产的密钥3. gitee或者github添加ssh-key4.git使用5. git 使用-本地仓库与远程仓库建立连接第一步&#xff1a;进入项目文件夹&#xff0c;初始化本地仓库第二步&#xff1a;建立远程仓库。 建立远程连接的小技巧 …

strcpy常见的错误

char* arr "handsome"; strcpy(arr, "pretty"); printf("%s\n", arr); 这个程序在编译器上是运行不出来的&#xff0c;因为arr是一个字符串指针&#xff0c;它的值不可以被修改&#xff0c;可以将arr改成字符数组

录屏没有声音怎么办,3个方法教你解决

随着科技的不断发展&#xff0c;人们越来越依赖电子设备进行工作和学习。在这个过程中&#xff0c;录屏已经成为了一种必要的技能。无论是手机还是电脑&#xff0c;我们都可以通过录屏来记录重要的信息。但是&#xff0c;有时候我们在录屏时会发现声音无法正常录制&#xff0c;…

前后端分离的低代码快速开发框架

低代码开发正逐渐成为企业创新的关键工具。通过提高开发效率、降低成本、增强灵活性以及满足不同用户需求&#xff0c;低代码开发使企业能够快速响应市场需求&#xff0c;提供创新解决方案。选择合适的低代码平台&#xff0c;小成本组建一个专属于你的应用。 项目简介 这是一个…

竞赛 基于深度学习的人脸识别系统

前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的人脸识别系统 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-senior/…

spring security教程(一)--认证

零.简介 【1】简介 【2】登录校验流程 【3】原理&#xff08;入门的时候先了解一下就好&#xff09; 一.思路分析 二.建表 确保你已经建立好一张用户表&#xff0c;并且引入springboot&#xff0c;mybatis&#xff0c;mp,slf4j等基础依赖。 即使你有多个角色你也可以将他们的…

安装社区版本OB

获取一键安装包 https://www.oceanbase.com/softwarecenter 离线安装 [admintest001 ~]$ tar -xzf oceanbase-all-in-one-*.tar.gz [admintest001 ~]$ cd oceanbase-all-in-one/bin/ [admintest001 bin]$ ./install.sh [admintest001 bin]$ source ~/.oceanbase-all-in-one/…