前言
本文在Ubuntu操作系统下实现一个物联网系统,该系统能够实现生成两个随机数来模拟传感器采集到的数据,利用node-red监测emqx上的数据情况。
其主要可以完成以下
功能要求
:
- 由一个父线程创建两个子线程。
- 由父线程统一将感知 (模拟) 数据发布到 MQTT Broker。
- 温度和湿度均由随机数生成。温度范围:0-40,湿度范围:10-100。
- 由父线程统一将感知 (模拟) 数据发布到 MQTT Broker。
- 在 Node-Red 上接收数据并分别显示温度和湿度。
一、目的
- 基于MQTT通信协议实现物联网各层的数据传输。
- 理解和掌握物联网各层之间数据通信、数据处理、数据显示。
二、背景
-
理论研究基础
(1) MQTT是基于TCP/IP协议栈而构建的,已成为IoT通信的标准。
(2) MQTT的灵活性使得为IoT设备和服务的多样化应用场景提供支持成为可能。
(3) NodeRED是构建物联网应用程序的一个强大工具,其重点是简化代码块的“连接”以执行任务。使用可视化编程方法,允许开发人员将预定义的节点连接起来执行任务。 -
技术层面支持
(1) 一台装有Windows、VirtuaBox和Ubuntu虚拟机的计算机。
(2) MQTT Broker服务器——emqx服务器。
(3) NodeRED可视化程序设计。
三、主要步骤
- MQTT 服务器在 Linux 上的搭建
- 安装 MQTT 开发环境
- 安装 Node-Red
- Node-Red 可视化程序设计
- MQTT客户端程序编写
- 编译执行客户端程序,并在浏览器上查看运行结果
四、操作流程
1.emqx的安装与配置
下载 emqx-ubuntu18.04-4.3.11-amd64.zip
wget https://www.emqx.com/zh/downloads/broker/4.3.11/emqx-ubuntu18.04-4.3.11-amd64.zip
安装
unzip emqx-ubuntu18.04-4.3.11-amd64.zip
运行
./bin/emqx start
2.MQTT库安装
安装 git
∼/emqx$ sudo apt install git libssl‐dev openssl
从 Git 中下载 paho C 库。注意:该下载文件将会下载到当前的路径中。
∼/emqx$ git clone https://github.com/eclipse/paho.mqtt.c.git
安装 Paho C库
∼/emqx$ cd paho.mqtt.c
∼/emqx$ make
∼/emqx$ sudo make install
3.客户端源码
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
#include<unistd.h>
#include"MQTTClient.h"
#include <sys/wait.h>
#include <time.h>
#include <pthread.h>
#include <semaphore.h>
char buf[1024];
char suf[1024];
sem_t sem_empty;
sem_t sem_full;
pthread_mutex_t mutex_shidu;
pthread_mutex_t mutex_wendu;
void *thread_funb(void *p) {
int temperature;
while(1) {
srand((unsigned int)time(0));
temperature=(rand()%41);
sem_wait(&sem_empty);
pthread_mutex_lock(&mutex_wendu);
printf("监测到的温度是:%d\n",temperature);
sprintf(suf,"%.3d",temperature);
sleep(2);
pthread_mutex_unlock(&mutex_wendu);
sem_post(&sem_full);
}
}
void *thread_funa(void *p) {
int humidity=0;
while(1) {
srand((unsigned int)time(0));
humidity=(rand()%91)+10;
sem_wait(&sem_empty);
pthread_mutex_lock(&mutex_shidu);
printf("监测到的湿度是:%d\n",humidity);
sprintf(buf,"%.3d",humidity);
sleep(2);
pthread_mutex_unlock(&mutex_shidu);
sem_post(&sem_full);
}
}
int main(int argc,char **argv) {
sem_init(&sem_empty, 0, 2);
sem_init(&sem_full, 0, 0);
char *address="tcp://localhost:1883";
char *client_id="publish_client";
char *topic1="shidu";
char *topic2="wendu";
const int time_out=10000;
int rv;
int QOS=2;
MQTTClient client;
MQTTClient_connectOptions conn_opts=MQTTClient_connectOptions_initializer;
MQTTClient_message publish_msg=MQTTClient_message_initializer;
MQTTClient_deliveryToken token;
conn_opts.keepAliveInterval=60;
conn_opts.cleansession=1;
MQTTClient_create(&client,address,client_id,MQTTCLIENT_PERSISTENCE_NONE ,NULL);
if((rv=MQTTClient_connect(client,&conn_opts))!=MQTTCLIENT_SUCCESS) {
printf("MQTTClient_connect failure:%s\n",strerror(errno));
return 0;
}
publish_msg.qos=QOS;
publish_msg.retained=0;
pthread_t t1, t2;
pthread_create(&t1, 0, thread_funa, (void *)1);
pthread_create(&t2, 0, thread_funb, (void *)2);
while(1) {
sem_wait(&sem_full);
pthread_mutex_lock(&mutex_shidu);
pthread_mutex_lock(&mutex_wendu);
publish_msg.payload=(void *)buf;
publish_msg.payloadlen=strlen(buf);
int payloadlen;
MQTTClient_publishMessage(client,topic1,&publish_msg ,&token);
rv=MQTTClient_waitForCompletion(client,token,time_out);
printf("上传时间 %d 上传湿度 %s\n",rv,buf);
publish_msg.payload=(void *)suf;
publish_msg.payloadlen=strlen(suf);
MQTTClient_publishMessage(client,topic2,&publish_msg ,&token);
rv=MQTTClient_waitForCompletion(client,token,time_out);
printf("上传时间 %d 上传温度 %s\n",rv,suf);
sleep(1);
pthread_mutex_unlock(&mutex_wendu);
pthread_mutex_unlock(&mutex_shidu);
sem_post(&sem_empty);
}
}
4.NodeRed节点信息
[
{
"id": "f3db3f27.cba5c",
"type": "tab",
"label": "Flow1",
"disabled": false,
"info": ""
},
{
"id": "d8c8d735.0b48a",
"type": "debug",
"z": "f3db3f27.cba5c",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 440,
"y": 160,
"wires": [ ]
},
{
"id": "fa39b2f.0c3eb5",
"type": "ui_gauge",
"z": "f3db3f27.cba5c",
"name": "",
"group": "6e9b4b3d.5f0a14",
"order": 0,
"width": "8",
"height": "8",
"gtype": "donut",
"title": "温度",
"label": "℃",
"format": "{{value}}",
"min": 0,
"max": "40",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"className": "",
"x": 450,
"y": 240,
"wires": [ ]
},
{
"id": "58a15f93.3f2528",
"type": "mqtt in",
"z": "f3db3f27.cba5c",
"name": "",
"topic": "Action1",
"qos": "2",
"datatype": "auto",
"broker": "b4ba4381.73d0a8",
"x": 180,
"y": 220,
"wires": [
[
"d8c8d735.0b48a",
"fa39b2f.0c3eb5"
]
]
},
{
"id": "f66f499d.734c38",
"type": "mqtt in",
"z": "f3db3f27.cba5c",
"name": "",
"topic": "Action2",
"qos": "2",
"datatype": "auto",
"broker": "b4ba4381.73d0a8",
"x": 180,
"y": 440,
"wires": [
[
"1e52052e.58ccbb",
"18574447.fd7b1c"
]
]
},
{
"id": "1e52052e.58ccbb",
"type": "debug",
"z": "f3db3f27.cba5c",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 400,
"y": 420,
"wires": [ ]
},
{
"id": "18574447.fd7b1c",
"type": "ui_gauge",
"z": "f3db3f27.cba5c",
"name": "",
"group": "6e9b4b3d.5f0a14",
"order": 0,
"width": "10",
"height": "10",
"gtype": "gage",
"title": "湿度",
"label": "%",
"format": "{{value}}",
"min": "10",
"max": "100",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"className": "",
"x": 390,
"y": 480,
"wires": [ ]
},
{
"id": "50c44fcd.1a6d5",
"type": "ui_switch",
"z": "f3db3f27.cba5c",
"name": "",
"label": "switch",
"tooltip": "",
"group": "6e9b4b3d.5f0a14",
"order": 2,
"width": 0,
"height": 0,
"passthru": true,
"decouple": "false",
"topic": "topic",
"topicType": "msg",
"style": "",
"onvalue": "true",
"onvalueType": "bool",
"onicon": "",
"oncolor": "",
"offvalue": "false",
"offvalueType": "bool",
"officon": "",
"offcolor": "",
"animate": false,
"className": "",
"x": 250,
"y": 320,
"wires": [
[
"d8c8d735.0b48a",
"1e52052e.58ccbb"
]
]
},
{
"id": "6e9b4b3d.5f0a14",
"type": "ui_group",
"name": "Default",
"tab": "c6d87ea6.50a2",
"order": 1,
"disp": true,
"width": "20",
"collapse": false,
"className": ""
},
{
"id": "b4ba4381.73d0a8",
"type": "mqtt-broker",
"name": "Test",
"broker": "127.0.0.1",
"port": "1883",
"clientid": "",
"usetls": false,
"compatmode": false,
"keepalive": "60",
"cleansession": true,
"birthTopic": "Action1",
"birthQos": "2",
"birthPayload": "",
"closeTopic": "",
"closeQos": "0",
"closePayload": "",
"willTopic": "",
"willQos": "0",
"willPayload": ""
},
{
"id": "c6d87ea6.50a2",
"type": "ui_tab",
"name": "Tab 1",
"icon": "dashboard",
"order": 1
}]
5.调试
编译客户端程序:
gcc mqtt_client.c ‐o mqtt_publish -lpthread ‐lpaho‐mqtt3c -L ./paho .mqtt.c/ ‐I ./paho.mqtt.c/src/
执行客户端程序:
./mqtt_publish
总结
以上就是今天要讲的内容,本文仅仅简单的模拟了使用软件模拟实现物联网系统的一部分功能,而真正完成一个物联网系统,除了需要软件知识外,还需要学习相应的硬件知识,需要我们在实践的过程中一点一滴积累。