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

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

文章目录

  • 安卓软件开发
  • 控件及布局
  • 声音提醒
  • 无线通信
  • MQTT通信协议
  • 安卓部分
  • Arduino部分
  • 安卓软件开发

    控件及布局

    这里会介绍需要用到的控件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是否接收到该信息。

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

    物联沃分享整理
    物联沃-IOTWORD物联网 » 西电通院大二项目设计保姆式教程(上位机部分)

    发表评论