抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

MQTT服务端

我做的这个App是作为MQTT的一个客户端,因此必须要有一个MQTT服务端来让它连接上,之后就可以发布、订阅消息了;有一些公共MQTT服务器(可以自行百度),我在腾讯云创建了一个自己的MQTT服务端,下面介绍如何创建。

登录腾讯云。进入控制台,在搜索框搜索”物联网通信“

image-20211110222342087

点击创建新产品(可以把产品理解为一个MQTT服务器),我之前已经创建过一些了,所以下面显示有几个产品

image-20211110222552411

填写相关信息

image-20211110222826344

然后点击自己刚新建的产品,进入创建一个新设备

我们的物理设备要连接刚新建的”MQTT服务器“,需要提供一些验证信息,这些信息在我们新建设备之后可以得到

image-20211110223120954

点击我们刚新建的设备,可以查看设备的信息,其中设备密钥是我们的物理设备连接时需要的信息

image-20211110223635625

下面用桌面软件MQTT.fx尝试连接,打开软件,如左图:

5/6/7步需要自己刚才新建的产品、设备密钥信息,根据官方文档,如右图:

1
2
3
Broker Address: [产品ID].iotcloud.tencentdevices.com
Broker Port: 1883或8883
Client ID: 产品ID+设备名 (也可以直接复制新建设备时设备密钥的Client ID)
image-20211110224456603 image-20211110224935292
产品ID 设备密钥
image-20211110225634775 image-20211110223635625

接下来填写用户信息,然后点击ok保存即可

填写信息 查看自己设备的用户名、密码信息(最后2个就是)
image-20211110230019466 image-20211110223635625

接下来可以进行订阅和发布主题了,首先点击connect连接

image-20211110230459943

主题不能随便填,首先回到腾讯云刚新建的设备,点击设备进去,默认提供3个主题,其中2个一个只能发布,一个只能订阅,只有一个既能发布也能订阅,当然,可以自己新建几个主题,根据自己需要

image-20211110230839704

把发布主题粘贴到MQTT.fx软件,就可以发布主题了,发布后在产品(不是设备)的云日志可以查看刚发布的内容

image-20211110231427094 image-20211110231352083

订阅也是这样,订阅后需要在腾讯云 设备界面选择在线调试,然后选择MQTT.fx订阅的主题,发送消息后MQTT.fx即可收到消息,此处不演示

image-20211110231622518

Android MQTT客户端

代码工程已开源,GitHub请点击前去下载,Gitee请点击前去下载

使用Android Studio(自行百度下载),新建一个空白工程

添加MQTT依赖

在自己[project]下的 build.gradle添加如下代码

1
2
3
4
5
6
7
8
repositories {
google()
mavenCentral()
//添加下面这句代码
maven {
url "https://repo.eclipse.org/content/repositories/paho-releases/"
}
}

在[project]/app下的build.gradle添加如下代码

1
2
3
4
dependencies {
compile 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.1'
}

然后选择顶部弹出的 Sync Now 信息

image-20211110232735634

撸代码

修改AndroidManifest.xml,添加权限

1
2
3
4
<!--允许程序打开网络套接字-->
<uses-permission android:name="android.permission.INTERNET" />
<!--允许程序获取网络状态-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

image-20211110233017614

修改视图文件activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>

<TextView
android:layout_marginTop="20dp"
android:layout_marginLeft="20dp"
android:layout_marginBottom="10dp"
android:id="@+id/house_env"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="室内环境"
android:textStyle="bold"
android:textSize="20dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/show_temp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginLeft="20dp"
android:layout_marginBottom="10dp"

android:text="温度(°C)"
android:textSize="20dp" />

<TextView
android:id="@+id/temp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginLeft="20dp"
android:layout_marginBottom="10dp"
android:text="0"
android:textSize="20dp" />

<TextView
android:id="@+id/show_humi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

android:layout_marginTop="10dp"
android:layout_marginLeft="20dp"
android:layout_marginBottom="10dp"
android:text="湿度(%RH)"
android:textSize="20dp" />
<TextView
android:id="@+id/humi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

android:layout_marginTop="10dp"
android:layout_marginLeft="20dp"
android:layout_marginBottom="10dp"
android:text="0"
android:textSize="20dp" />

</LinearLayout>

<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<TextView
android:id="@+id/show_light"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

android:layout_marginLeft="20dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:text="光照强度"
android:textSize="20dp" />
<TextView
android:id="@+id/light"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

android:layout_marginLeft="20dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:text="0"
android:textSize="20dp" />

</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<TextView
android:id="@+id/ledsta1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

android:layout_marginLeft="20dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:text="LED状态"
android:textSize="20dp" />
<TextView
android:id="@+id/ledsta2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

android:layout_marginLeft="20dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:text="关"
android:textSize="20dp" />

<TextView
android:id="@+id/beepsta1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

android:layout_marginLeft="20dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:text="蜂鸣器状态"
android:textSize="20dp" />
<TextView
android:id="@+id/beepsta2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

android:layout_marginLeft="20dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:text="关"
android:textSize="20dp" />
</LinearLayout>
<TextView
android:id="@+id/sets"
android:layout_column="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

android:layout_marginLeft="20dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:text="设置"
android:textStyle="bold"
android:textSize="20dp" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<TextView
android:id="@+id/set_temp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:text="报警温度(当前温度超过该值蜂鸣器将报警)"
android:textSize="15dp" />

<TextView
android:id="@+id/seekbarval1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginLeft="10dp"
android:text="40"/>
</LinearLayout>
<SeekBar
android:id="@+id/seekBar1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_gravity="center"
android:max="100"
android:progress="40"/>
<!-- android:progressDrawable="@drawable/seekbar_progress" />-->

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<TextView
android:id="@+id/set_tlight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

android:layout_marginLeft="20dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:text="最低光照(当前光照小于该值将点亮led灯)"
android:textSize="15dp" />

<TextView
android:id="@+id/seekbarval2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginLeft="20dp"
android:text="12"/>
</LinearLayout>
<SeekBar
android:id="@+id/seekBar2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_gravity="center"
android:max="100"
android:progress="12"/>
<!-- android:progressDrawable="@drawable/seekbar_progress2" />-->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">


</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<Button
android:layout_marginLeft="100dp"
android:layout_marginTop="20dp"
android:id="@+id/connectbtn"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:textColor="@color/white"
android:text="连接"/>

<Button
android:layout_marginTop="20dp"
android:layout_marginRight="20dp"
android:layout_marginLeft="20dp"
android:id="@+id/exitbtn"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:textColor="@color/white"
android:text="退出"/>

</LinearLayout>
<TextView
android:layout_margin="5dp"
android:id="@+id/msgTxt"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:scrollbars="vertical"
android:text=""/>


</LinearLayout>

效果图

image-20211110233204303

接下来修改MainActivity.java,添加控制操作,上面只是完成一个界面的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
package com.ajream.mqttdemo4;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;


public class MainActivity extends AppCompatActivity {

//定义成员
private Button connectbtn;
private Button exitbtn;

private TextView temptv;
private TextView humitv;
private TextView lighttv;
private TextView ledtv;
private TextView beeptv;
private TextView bar1tv;
private TextView bar2tv;
private TextView showmsgtv;

private SeekBar tempbar;
private SeekBar lightbar;

//MQTT客户端配置所需信息
private String host = "tcp://xxxxxxx.iotcloud.tencentdevices.com:1883"; //mqtt服务器(腾讯云)地址、端口
private String userName = "xxxxxxxxxx"; //用户名(在腾讯云查看自己的设备信息)
private String passWord = "xxxxxxxxxx"; //密码(在腾讯云查看自己的设备信息)
private String mqtt_id = "xxxxxxxxxxxxxxxxxxx";
private String mqtt_sub_topic = "xxxxxxxxxx/AndroidClient/data";
private String mqtt_pub_topic = "xxxxxxxxxx/AndroidClient/data";

private ScheduledExecutorService scheduler;
private MqttClient mqttClient;
private MqttConnectOptions options;
private Handler handler;

private String msgToPublish = ""; //要发布的消息
private String alarmTempMsg = "";
private String minLightMsg = "";
JSONObject msgGet;

@SuppressLint("HandlerLeak")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//关联控件
connectbtn = findViewById(R.id.connectbtn);
exitbtn = findViewById(R.id.exitbtn);
temptv = findViewById(R.id.temp);
humitv = findViewById(R.id.humi);
lighttv = findViewById(R.id.light);
ledtv = findViewById(R.id.ledsta2);
beeptv = findViewById(R.id.beepsta2);
bar1tv = findViewById(R.id.seekbarval1);
bar2tv = findViewById(R.id.seekbarval2);
showmsgtv = findViewById(R.id.msgTxt);

tempbar = findViewById(R.id.seekBar1);
lightbar = findViewById(R.id.seekBar2);

alarmTempMsg = String.valueOf(tempbar.getProgress());
minLightMsg = String.valueOf(lightbar.getProgress());

/*点击按钮连接*/
connectbtn.setOnClickListener(view -> {
Mqtt_init();
startReconnect();
});

exitbtn.setOnClickListener(view -> {
android.os.Process.killProcess(android.os.Process.myPid());
});


tempbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
Toast.makeText(MainActivity.this, "设置报警温度为: " + progress, Toast.LENGTH_LONG).show();
bar1tv.setText(check(progress));
alarmTempMsg = check(progress);
}


@Override
public void onStartTrackingTouch(SeekBar seekBar) {

}

@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// msgToPublish = alarmTempMsg + "," + minLightMsg;
msgToPublish = "{\"atemp\":\"" + alarmTempMsg + "\",\"mlight\":\"" + minLightMsg+"\"}";
publishMsg(mqtt_pub_topic, msgToPublish);

}
});

lightbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
Toast.makeText(MainActivity.this, "设置最低光照为: " + i, Toast.LENGTH_LONG).show();
bar2tv.setText(check(i));
minLightMsg = check(i);
}

@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}

@Override
public void onStopTrackingTouch(SeekBar seekBar) {
msgToPublish = "{\"atemp\":\"" + alarmTempMsg + "\",\"mlight\":\"" + minLightMsg+"\"}";
publishMsg(mqtt_pub_topic, msgToPublish);
}
});

handler = new Handler() {
@SuppressLint("SetTextI18n")

public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1: //开机校验更新回传
break;
case 2: // 反馈回传
break;
case 3: //MQTT 收到消息回传 UTF8Buffer msg=newUTF8Buffer(object.toString());
// Toast.makeText(MainActivity.this,msg.obj.toString(),Toast.LENGTH_SHORT).show();
showmsgtv.setText(msg.obj.toString());
JSONObject msgGet = null;
try {
msgGet = new JSONObject(msg.obj.toString());
temptv.setText(msgGet.get("temp").toString());
humitv.setText(msgGet.get("humi").toString());
lighttv.setText(msgGet.get("light").toString());
if(Integer.parseInt(msgGet.get("ledsta").toString())==0) ledtv.setText("关");
else ledtv.setText("开");
if(msgGet.get("beepsta").toString().charAt(0)=='0') beeptv.setText("关");
else beeptv.setText("开");
} catch (JSONException e) {
e.printStackTrace();
}
break;

case 30: //连接失败
Toast.makeText(MainActivity.this,"连接失败" ,Toast.LENGTH_SHORT).show();
break;
case 31: //连接成功
Toast.makeText(MainActivity.this,"连接成功" ,Toast.LENGTH_SHORT).show();
try {
mqttClient.subscribe(mqtt_sub_topic,1);
}
catch (MqttException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
};
}

private String check(int progress) {
int curValue = 100 * progress/Math.abs(100);
return String.valueOf(curValue);
}

private void publishMsg(String topic, String message2) {
if (mqttClient == null || !mqttClient.isConnected()) {
return;
}
MqttMessage message = new MqttMessage();
message.setPayload(message2.getBytes());
try {
mqttClient.publish(topic, message);
} catch (MqttException e) {
e.printStackTrace();
}
}

private void Mqtt_init() {
try {
//host为主机名,test为clientid即连接MQTT的客户端ID,一般以客户端唯一标识符表示,MemoryPersistence设置clientid的保存形式,默认为以内存保存
mqttClient = new MqttClient(host, mqtt_id, new MemoryPersistence());

//MQTT的连接设置
options = new MqttConnectOptions();

//设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,这里设置为true表示每次连接到服务器都以新的身份连接
options.setCleanSession(false);

options.setUserName(userName); //设置连接的用户名

options.setPassword(passWord.toCharArray()); //设置连接的密码

// 设置超时时间 单位为秒
options.setConnectionTimeout(10);
// 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送个消息判断客户端是否在线,但这个方法并没有重连的机制
options.setKeepAliveInterval(20);

//设置回调函数
mqttClient.setCallback(new MqttCallback() {
@Override
public void connectionLost(Throwable cause) {
//连接丢失后,一般在这里面进行重连
// System.out.println("connectionLost----------");
// startReconnect();
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
//publish后会执行到这里
System.out.println("deliveryComplete---------"
+ token.isComplete());
}
@Override
public void messageArrived(String topicName, MqttMessage message)
throws Exception {
//subscribe后得到的消息会执行到这里面
System.out.println("getMsg: ");
Message msg = new Message();
msg.what = 3; //收到消息标志位
msg.obj = message.toString();
handler.sendMessage(msg); // hander 回传
}
});
} catch (Exception e) {
e.printStackTrace();
}
}

private void Mqtt_connect() {
new Thread(new Runnable() {
@Override
public void run() {
try {
if(!(mqttClient.isConnected())) {
mqttClient.connect(options);
Message msg = new Message();
msg.what = 31;
handler.sendMessage(msg);
}
} catch (Exception e) {
e.printStackTrace();
Message msg = new Message();
msg.what = 30;
handler.sendMessage(msg);
}
}
}).start();
}
private void startReconnect() {
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
if (!mqttClient.isConnected()) {
Mqtt_connect();
}
}
}, 0 * 1000, 10 * 1000, TimeUnit.MILLISECONDS);
}
}

来两张运行截图

image-20211110234502485 image-20211110234549641

评论