西电通院大二项目设计保姆式教程(上位机部分)

前言:
本文相关:西安电子科技大学通信工程学院大二项目设计
内容:安卓上位机软件开发、使用MQTT通信协议进行上位机与wifi模块之间的无线通信、wifi模块与下位机f411实现串口通信。
上位机部分:Android Studio、Arduino、友善之臂smart4418、esp8266无线通信模块
使用无线通信可以避免使用官方提供的接口,提高代码的复用性,毕竟不同的开发板厂商提供的接口也不同。同时,提高作品的便携程度。

安卓软件开发

控件及布局

这里会介绍需要用到的控件Button、TextView、EditView(已button为例)和两种布局:线性布局、约束布局
Button

//声明变量
public Button btn_getcolor;
//绑定.xml中的id
btn_getcolor=findViewById(R.id.button_get_TCS_data);
//设置点击事件
btn_getcolor.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                publishmessageplus(mqtt_pub_topic, "THREE");
                if(flag_c==0){
                    flag_c=1;
                }else if (flag_c==1){
                    flag_c=0;
                }
            }
        });

点击一次按钮,上位机会向wifi模块发送字符串THREE,并且改变标志位,以方便消息回传时的接收操作

LinearLayout 又称线性布局。该布局可以使放入其中的组件以水平方式或者垂直方式整齐排列,通过 android:orientation 属性指定具体的排列方式,通过 weight 属性设置每个组件在布局中所占的比重。该布局是安卓开发中最常用的布局,但是嵌套较多,代码书写较为麻烦。

我更喜欢约束布局,ConstraintLayout(约束布局)的出现是为了在Android应用布局中保持扁平的层次结构,减少布局的嵌套,为应用创建响应快速而灵敏的界面。Android Studio可以自由拖动控件,并在右侧的attributes中调节组件的位置、大小。控件需要先与四个边都进行约束,如果不这么做,会出现运行后偏在一侧的情况,当然与其他控件约束也是可以的。

声音提醒

// 声音提醒
public void showSound(int raw) {
        MediaPlayer mediaPlayer = null;
        mediaPlayer = MediaPlayer.create(getApplicationContext(), raw);
        mediaPlayer.setVolume(1, 1);
        mediaPlayer.start();
    }

这里使用的是安卓原生框架的MediaPlayer类,在res文件下创建raw文件,将录音文件更改为.wmv格式放在raw里,用create方法创建实例,调节音量大小。这些代码封装在showSound方法里,用时直接调用即可

无线通信

MQTT通信协议

MQTT是一种基于TCP/IP协议的轻量级通信协议,在物联网、小型设备、移动应用等方面有较广泛的应用。该协议基于发布pub和订阅sub模式,连接同一后台服务器的两个对象,订阅他人后可以接收到别人发布的信息,类似于订阅报刊杂志,这个消息的传播可以是单向的也可以是双向。

MQTT传输的消息分为:主题(Topic)和负载(payload)两部分,Topic可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload)这在Adruino代码中会体现出来

安卓部分

首先在libs中导入mqtt包 :org.eclipse.paho.client.mqttv3-1.2.0.jar
点击add as library添加到库中,添加后可以从build.gradle中看到

implementation files('libs\\org.eclipse.paho.client.mqttv3-1.2.0.jar')

然后就可以调用接口啦!

// mqtt初始化
public void Mqtt_init()
    {
        try {
            //host为主机名,test为clientid即连接MQTT的客户端ID,一般以客户端唯一标识符表示,MemoryPersistence设置clientid的保存形式,默认为以内存保存
            client = 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);
            //设置回调
            client.setCallback(new MqttCallback() {
                @Override
                public void connectionLost(Throwable cause) {
                    //连接丢失后,一般在这里面进行重连
                    System.out.println("connectionLost----------");
                    /artReconnect();
                }
                @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("messageArrived----------");
                    Message msg = new Message();
                    msg.what = 3;   //收到消息标志位
                    msg.obj = topicName + "---" + message.toString();
                    handler.sendMessage(msg);    // hander 回传
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public void Mqtt_connect() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    if(!(client.isConnected()) )  //如果还未连接
                    {
                        client.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();
    }
    public void startReconnect() {
        scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                if (!client.isConnected()) {
                    Mqtt_connect();
                }
            }
        }, 0 * 1000, 10 * 1000, TimeUnit.MILLISECONDS);
    }

这里涉及到多线程,因为安卓中必须遵循单线程模式,所有耗时操作必须在子线程中进行,否则会导致app闪退崩溃。通过msg.what定义标志位,在handler里进行分类操作
消息回传处理

//
handler = new Handler() {
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case 1: //开机校验更新回传
                        break;
                    case 2:  // 反馈回传

                        break;
                    case 3:  //MQTT 收到消息回传   UTF8Buffer msg=new UTF8Buffer(object.toString());
                        //String C_val = msg.obj.toString().substring(msg.obj.toString).indexOf("color:") + 6,msg.obj.toString().indexOf("}"));
                        //String D_val = msg.obj.toString().substring(msg.obj.toString().indexOf("distance:") + 9,msg.obj.toString().indexOf("]"));
                        String C_val = msg.obj.toString().substring(12,20);
                        String D_val = msg.obj.toString().substring(20,31);
                        //String val = msg.obj.toString();
                        if(flag_c==1){
                            text_color.setText(C_val);
                        }else {text_color.setText("空");}
                       if(flag_d==1){
                           text_distance.setText(D_val);
                       }else{text_distance.setText("空");}
                        break;
                    case 30:  //连接失败
                        Toast.makeText(MaintenanceActivity.this, "连接失败", Toast.LENGTH_SHORT).show();
                        break;
                    case 31:   //连接成功
                        //Toast.makeText(MaintenanceActivity.this, "连接成功", Toast.LENGTH_SHORT).show();
                        try {
                            client.subscribe(mqtt_sub_topic, 1);
                        } catch (MqttException e) {
                            e.printStackTrace();
                        }
                        break;
                    default:
                        break;
                }
            }
        };

该代码会爆黄,可能是内存泄漏导致,不过并不影响,可以通过加@SuppressLint(“HandlerLeak”)消除

发送消息,相当于重写了client.publish方法,设置message的payload属性为字节数组,方便Arduino操作

// 发送消息的方法
public void publishmessageplus(String topic,String message2)
    {
        if (client == null || !client.isConnected()) {
            return;
        }
        MqttMessage message = new MqttMessage();
        message.setPayload(message2.getBytes());
        try {
            client.publish(topic,message);
        } catch (MqttException e) {

            e.printStackTrace();
        }
    }

需要注意的是:在其他界面不能声明通过类然后调用该界面的方法,需要重新定义handler和方法

Arduino部分

arduino不显式地使用main作为入口函数,基本的函数时setup()和loop(),在setup中执行初始化,再进入loop中循环,除此以外可以自定义函数或者使用库函数,本例中接收消息并通过串口发送给411就是在PubSubClient.h库中的回调函数callback中执行的。

// 接收信息
void callback(char* topic, byte* payload, unsigned int length) {
  String msg="";
  String LED_set = "";
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    msg+= (char)payload[i];
  }
  Serial.println(msg);
  if(msg=="ONE"){
   Serial.print("ONE");
  }else if(msg=="TWO"){
     Serial.print("TWO");//发送数据给411
  }else if(msg=="THREE"){
    //Serial.print(sendJson("THREE"));
    Serial.print("THREE");
  }else if(msg=="FOUR"){
    Serial.print("FOUR");
  }else if(msg=="FIVE"){
    Serial.print("FIVE");
  }else if(msg=="SIX"){
    Serial.print("SIX");
  }else if(msg=="SEVEN"){
    Serial.print("SEVEN");
  }
}

将收到的信息存储到msg中,判断后执行if中语句

回传信息

// 
void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  if(Serial.available())
  {
       comdata = "";//缓存清零
      while (Serial.available() > 0)//循环串口是否有数据
      {
        comdata += char(Serial.read());//叠加数据到comdata
        delay(2);//延时等待响应
      } 
        if(comdata!=""){
          mark=1;
        //Serial.println(comdata);
        }
        else{mark=0;}
      }
   if(mark==1)
  {
    Serial.println(comdata);
    String json = ""+comdata;
    client.publish(Mqtt_pub_topic,json.c_str());
      digitalWrite(2,HIGH);
      delay(500);
      digitalWrite(2,LOW);
      delay(500);
   } 
}

接收到411传来的信息并通过publish发送给上位机,使用板载小灯的亮灭来提醒8266是否接收到该信息。

到这里就结束了,理解比较浅陋,如果文章中有什么不准确或者需要改进的地方,还请大佬不吝赐教。
在这里插入图片描述

  • 10
    点赞
  • 74
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值