基于STM32及Wifi的环境监测系统(Android端APP部分)



劝君更尽一杯酒,西出阳关无故人。----王维


前言

书接上文,本设计共分为单片机部分和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资源链接
在这里插入图片描述


总结

由于水平有限,时间仓促,本APP还有许多瑕疵,希望后来人能继续改进。
  • 4
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搬砖工人_0803号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值