文章目录
劝君更尽一杯酒,西出阳关无故人。----王维
前言
书接上文,本设计共分为单片机部分和Android端APP部分,本文给大家介绍一下在Android端通过继承已有的标准的工程来快速开发APP。我使用的是百度云物联网平台(其他云平台操作大体类似),单片机和APP通过MQTT协议连接云平台,设置云平台的规则引擎实现消息转发,于是APP和单片机就可以通信了,至于在MQTT协议之上的数据格式,数据的处理还是需要自定义,当然这些都是小case了。APP资源链接见文末!
一、前期准备工作
1.了解云端物联网开发文档
MQTT(Message Queuing Telemetry Transport)是一个基于二进制消息的客户端服务端架构的发布/订阅(Publish/Subscribe)模式的消息传输协议,是专为受限设备和低带宽、高延迟或不可靠的网络而设计。它体积小,功耗低,数据包小,并且可以有效地将信息分配给一个或多个接收器。
在本设计的单片机程序里使用了自定义方法实现MQTT协议的大致规范,但在APP部分,我们并不需要自己实现封装方法,只需使用已经封装好的第三方库的方法就行了。
首先要了解百度云物联网开发文档,知道自己目前需要做什么。进入百度云物联网核心套件 IoTCore开发文档,仔细通读一遍,了解如何在云平台使用MQTT协议,如何创建一个项目,如何创建一个设备,如何设置规则引擎来进行消息转发等等,开发文档很详细,而且还有配套的视频教程,手把手教学。
2.MQTT.fx客户端
工欲善其事必先利其器。
通过开发文档:快速入门>使用 MQTT 模拟器连接及收发消息。发现一个MQTT模拟器,这对我们前期测试程序很有帮助:
(1)当在百度云端成功创建单片机和APP两个设备时,得到了MQTT账户名和密码,打开两个MQTT模拟器,设置好单片机和APP的账户信息,可以测试云端的规则引擎是否已经正常工作。
(2)收发数据可视化,无论是单片机程序还是APP程序写完之后,都可以测试自定义的数据格式,由此来进行调试。
3.第三方开源MQTT-Client接入
通过开发文档:开发者指南>设备侧>通过第三方开源MQTT-Client接入物联网。发现此页面给出了基于各种语言的客服端的开源项目链接,点击Android Service开源项目进入其github地址,阅览一遍项目概述,可以看见要使用本服务的注意事项。我们可以直接拉取项目代码在其基础上改进,也可以直接新建一个空白的AS模块,然后按照其案例自己二次开发。
这里我是新建AS模块,按照其项目说明在当前工程的build.gradle添加仓库地址
maven {
url "https://repo.eclipse.org/content/repositories/paho-snapshots/"
}
在当前模块的build.gradle添加依赖
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0'
implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
等待第三方库下载完成,模块完成同步,于是就搭建完成开发环境了。
最后参照案例,在配置文件AndroidManifest.xml声明相关的权限和第三方库指定的MqttService组件!
我的模块结构预览:
显然,本模块结构还是比较简单的,大部分类都是见名知意,具体的文件说明和注释请参考工程源码。
注意:第三方库给出了两个重要的类:MqttAndroidClient 和 MqttConnectOptions。
查看MqttAndroidClient的注释:
public class MqttAndroidClient
extends android.content.BroadcastReceiver
implements org.eclipse.paho.client.mqttv3.IMqttAsyncClient
使android应用程序能够使用非阻塞方法与MQTT服务器通信。
MQTT异步接口IMqttAsyncClient的实现,使用MqttService与MQTT服务器通信。
它为android应用程序提供了MQTT 3.1版规范所有功能的简单编程接口,包括:
connect
publish
subscribe
unsubscribe
disconnect
显然,我们需要重载这些方法来实现回调。
查看MqttConnectOptions的注释:
public class MqttConnectOptions
extends Object
保存控制客户端如何连接到服务器的选项集。
显然,这就是配置MQTT账户的参数类。
二、MQTT账户的配置
1.MQTT账户需要的参数
通过百度云物联网开发文档和下载的开源案例可知,配置MqttConnectOptions这个类需要如下参数:服务端地址,MQTT账户名称,MQTT账户密码,发布的主题名称,订阅的主题名称,客户端标识。因此自定义一个参数类来表示:
public class UserInfo {
public static String host = "";
public static String userName = "";
public static String passWord = "";
public static String PUBTopic = ""; //要发布的主题
public static String SUBTopic = ""; //要订阅的主题
public static String clientId = "";//客户端标识
public static boolean userInfoIsEmpty()
{
return TextUtils.isEmpty(host);
}
}
2.规划配置MQTT账户的界面
不同的用户需要手动输入自己的MQTT账户信息,那么就设计一个简单的MQTT账户配置页面:
为了简单起见,保存/读取MQTT账户使用共享参数SharedPreferences,这里没有技术难度。
为了改善使用体验和方便前期调试,还可以在资源文件strings.xml里声明参数,在第一次打开界面时自动填充编辑框:
/**
* 根据项目资源配置文件string.xml配置MQTT账户
*/
private void initUserInfo()
{
if (UserInfo.userInfoIsEmpty()){
UserInfo.host="tcp://"+getResources().getString(R.string.host)+":1883";;
UserInfo.userName=getResources().getString(R.string.userName);
UserInfo.passWord=getResources().getString(R.string.passWord);
UserInfo.clientId=getResources().getString(R.string.clientId);
UserInfo.SUBTopic=getResources().getString(R.string.SUBTopic);
UserInfo.PUBTopic=getResources().getString(R.string.PUBTopic);
}
}
三、自定义一个服务
参考开源案例,新建一个普通服务。注意普通服务是在前台线程运行的,所以不能处理耗时的操作,否则容易导致“ANR”。当然,你也可以在普通服务里面新建线程或者使用IntentService来异步处理耗时操作。自定义的服务用于收发数据,故需要设置一个监听接口来与MainActivity通信,同时用户也可以启停此服务,故需要将使用一个ServiceConnection来实现。
1.自定义UserMqttService类
由于代码较多,请参考具体文件。说下主要流程:重载其创建,销毁方法>填充MqttAndroidClient,MqttConnectOptions的参数>设置监听接口>开始连接云端>连接成功后订阅主题>等待收发数据的回调。
2.创建一个监听接口
public interface IGetMessageCallBack {
void OnServiceConnectStatus(boolean isConnect);//监听此服务的连接状态
void onReceiveMessage(String message);//收到可识别的消息
void unknowMessage(String message);//收到未知消息
}
3.定义一个ServiceConnection实现服务绑定
private final ServiceConnection serviceConnection = new ServiceConnection() {
// 获取服务对象时的操作
public void onServiceConnected(ComponentName name, IBinder iBinder) {
Log.i(TAG, "onServiceConnected");
mService = ((UserMqttService.CustomBinder)iBinder).getService();
mService.setIGetMessageCallBack(MainActivity.this);
}
// 无法获取到服务对象时的操作
public void onServiceDisconnected(ComponentName name) {
mService = null;
Log.i(TAG, "onServiceDisconnected");
}
};
4.为启停开关设置回调
sw_BaiduIot.setOnCheckedChangeListener((compoundButton, b) -> {
if (b) {
if (UserInfo.userInfoIsEmpty()){
compoundButton.setChecked(false);
Toast.makeText(this, "请先配置MQTT账户", Toast.LENGTH_SHORT).show();
return;
}
if(mService==null){
startService(intentService);
boolean bindFlag = bindService(intentService, serviceConnection, Context.BIND_AUTO_CREATE);
Log.i(TAG, "bindFlag:" +bindFlag);
sendSimpleNotify("MQTT测试安卓端","已连接");
}
} else {
if (mService != null) {
unbindService(serviceConnection); // 解绑服务
mService.stopSelf();
mService = null;
sendSimpleNotify("MQTT测试安卓端","已断开");
Log.i(TAG, "mService:" +"解绑服务");
}
}
});
四、数据的处理和显示
设计主界面如下:
由于数据格式是APP与单片机自定义约定好的,接下来APP只需对回调的数据进行处理,然后由此更新界面就可以了。
本设计约定的数据格式为:"ESP8266,T: ,H: ,L:(两个字节数据),D: ",通过分号将字符串划分为数组,提取数据:
/**
* 将接收的消息分析,显示在列表里
* @param message 收到的开发板发来的原始的数据
*/
private void updataEnvirCaliList(String message){
//数据格式"ESP8266,T: ,H: ,L:(两个字节数据),D: "
String[] tempStr= message.split(",");
if (tempStr.length==5&&tempStr[0].equals("ESP8266")){
String temperature=tempStr[1].replace("T:","");//温度
temperature=((int)(temperature.toCharArray()[0]))+" 'C";
String humidity=tempStr[2].replace("H:","");//湿度
humidity=((int)(humidity.toCharArray()[0]))+" %";
String light=tempStr[3].replace("L:","");//光照
light=((int) light.toCharArray()[0])+((int) (light.toCharArray()[1]))
+" lx";
String LEDStatus=tempStr[4].replace("D:","");//LED状态
LEDStatus=((int)(LEDStatus.toCharArray()[0]))+"";
sw_light.setChecked(!LEDStatus.equals("1"));
Log.i(TAG,"\ntemperature:"+temperature +"\n"+"humidity:"
+humidity+"\n"+"light:"+light+"\n"+"LEDStatus:" +LEDStatus);
envirCaliList.get(0).setContentValue(light);
envirCaliList.get(1).setContentValue(temperature);
envirCaliList.get(2).setContentValue(humidity);
mEnvirCaliAdapter.notifyDataSetChanged();
}else {
Log.i(TAG,tempStr.length+"");
}
}
大部分代码见名知意,没有技术难度。至于由数据填充到列表,都是固定的编程套路,限于篇幅,这里就不展示了。
五、调试结果
(1)下载配套开发的APP到手机,开始调试。
(2)进入APP主界面。
(3)配置MQTT账户
(4)配置完成,返回主界面,打开连接开关,等会儿便可以看见列表里显示当前环境信息。
至此,分析完了本APP的主要实现。需要源码的请点击APP资源链接。