zoukankan      html  css  js  c++  java
  • 【Android开发—智能家居系列】(四):UDP通信发送指令

    思路回顾

    【1】手机连接WIFI模块
    【2】UDP通信对WIFI模块发送指令,以和WIFI模块保持连接状态
    【3】UDP通信对WIFI模块发送指令,让其搜索可用的无线网,返回WIFI列表
    【4】发送指令,让WIFI模块接入指定路由
    【5】手机连接路由
    【6】发送指令,获得WIFI模块的动态IP地址

    UDP通信线程类

    package com.jczb.smartlife.common;
    
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetAddress;
    import java.net.InetSocketAddress;
    import java.net.SocketException;
    import java.net.UnknownHostException;
    
    import android.os.Handler;
    import android.os.Message;
    
    import com.jczb.smartlife.common.Tool;
    
    public class GetInfoThread extends Thread {
        private Handler handler;
        private DatagramSocket socket;
        private int msgType;
    
        private final String IP = "255.255.255.255";//广播地址
        private int PORT = 26000;
    
        /**
         * 48899端口:C32x系列的端口,用户可以用AT指令更改
         * 49000端口:除C32x系列,其他WIFI模块的端口
         * 1902端口:有人掌控宝系列产品的端口
         */
        private int targetPort = 49000 ;
    
        private boolean receive = true;
    
    
        /**
         * 
         * @param handler 传入监听此线程的Handler
         * @param intMsg 传入监听的消息类型
         */
        public GetInfoThread(Handler handler,int msgType) {
            this.handler = handler;
            this.msgType=msgType;
            init();
        }
    
        public void init(){
            try {
    
                socket = new DatagramSocket(null);
                socket.setBroadcast(true);
                socket.setReuseAddress(true);
                socket.bind(new InetSocketAddress(PORT));
            } catch (SocketException e) {
                e.printStackTrace();
                sendErrorMsg("Search Thread init fail");
                return;
            } 
        }
    
    
        public void run() {
            if (socket == null) {
                return;
            }
    
            try {
    
                byte[] data = new byte[1024];
                //创建一个空的DatagramPacket对象
                DatagramPacket revPacket = new DatagramPacket(data, data.length);
                while (receive) {
                    //服务端接收数据
                    socket.receive(revPacket);
                    if(null!=handler){
                        byte[] realData = new byte[revPacket.getLength()];
                        System.arraycopy(data, 0, realData,0, realData.length);
                        Message msg =handler.obtainMessage(msgType,realData);
                        handler.sendMessage(msg);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                socket.close();
            }
        }
    
        public void close() {
            if (socket == null)
                return;
            socket.close();
        }
    
    
        private void sendErrorMsg(String info){
    
        }
    
        /**
         * 发送数据
         * @param msg
         */
        public void sendMsg(byte[] msg) {
            if (socket != null) {
                try {
                    System.out.println("targetPort------------------->"+targetPort);
                    DatagramPacket sendPacket = new DatagramPacket(msg, msg.length,
                            InetAddress.getByName(IP), targetPort);
                    socket.send(sendPacket);
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                    System.out.println("发送失败");
                } catch (IOException e) {
                    e.printStackTrace();
                    System.out.println("发送失败");
                }
    
            }
        }
    
        public void setReceive(boolean receive) {
            this.receive = receive;
        }
    
        public void setTargetPort(int targetPort) {
            this.targetPort = targetPort;
        }
        public void setMsgType(int msgType){
            this.msgType=msgType;
        }
    }
    

    发送消息的线程类

    /**
         * 发送消息的队列,每次发送数据时,只需要调用putMsg(byte[] data)方法
         * 
         * @author usr_liujinqi
         * 
         */
        private class SendMsgThread extends Thread {
            // 发送消息的队列
            private Queue<byte[]> sendMsgQuene = new LinkedList<byte[]>();
            // 是否发送消息
            private boolean send = true;
    
            private GetInfoThread ss;
    
            public SendMsgThread(GetInfoThread ss) {
                this.ss = ss;
            }
    
            public synchronized void putMsg(byte[] msg) {
                // 唤醒线程
                if (sendMsgQuene.size() == 0)
                    notify();
                sendMsgQuene.offer(msg);
            }
    
            public void run() {
                synchronized (this) {
                    while (send) {
                        // 当队列里的消息发送完毕后,线程等待
                        while (sendMsgQuene.size() > 0) {
                            byte[] msg = sendMsgQuene.poll();
                            if (ss != null)
                                ss.sendMsg(msg);
                        }
                        try {
                            wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
    
            public void setSend(boolean send) {
                this.send = send;
            }
        }

    应用

    【举例】发送搜索WIFI模块的指令

    //1.用户获得温控器中WIFI模块的WIFI模块的IP地址,MAC地址,模块名称的指令    
        private final byte[] getInfoCode=new byte[]{(byte)0x48,0x46,0x2D,0x41,0x31,0x31,0x41,0x53,0x53,0x49,0x53,0x54,0x48,0x52,0x45,0x41,0x44};
        public static final int REC_Module=0x04;//搜索WIFI模块的信息(包括IP、Mac、名称)
    //实例化一个线程,用户获得模块信息(IP,Mac,名称)
            //参数为监听为此线程的Handler,以及接收成功后,给Handler发送的消息类型
            getInfoThread = new GetInfoThread(handler,Tool.REC_Module);
            getInfoThread.start();
            //发送消息的线程
            smt = new SendMsgThread(getInfoThread);
            smt.start();
            //设置发送的目的端口号
            getInfoThread.setTargetPort(Integer.parseInt("48899"));     
            smt.putMsg(getInfoCode);
    

    WIFI模块在接收到指令后,就会回复信息,以下的Handler就是针对回复过来的信息进行解析处理等操作。

    //处理消息的Handler
        private Handler handler= new Handler() {
            public void handleMessage(Message msg) {
                switch (msg.what) {
                case Tool.REC_Module:// 解析接收到的数据
                    //设置发送的目的端口号
                    getInfoThread.setTargetPort(Integer.parseInt("48899")); 
                    getInfoThread.setMsgType(Tool.REC_OK);
                    smt.putMsg(okCode);
                    SetServer();
                    SetDHCP();
                    byte[] data = (byte[]) msg.obj;
                    //将十进制的数据转化成十六进制数据
                    String strTemp= Tool.bytesToHexString(data);
                    //将十六进制的字符串去掉空格之后进行解析
                    String strdecode=Tool.DecodeHex(strTemp.replace(" ", ""));
                    //取出IP,Mac地址,模块名称
                    decodeIP(strdecode);
                    Toast.makeText(ConnectWifiActivity.this, "获得WIFI模块名称成功!"+ModuleName, Toast.LENGTH_SHORT).show();
    
                    break;
                case Tool.REC_Server:
                    byte[] dataServer = (byte[])msg.obj;
                    //将十进制的数据转化成十六进制数据
                    String strServer= Tool.bytesToHexString(dataServer);
                    if("2b 6f 6b 0d 0a 0d 0a ".equals(strServer)){
                        Toast.makeText(ConnectWifiActivity.this, "设置服务器成功!", Toast.LENGTH_SHORT).show();
    
                    }
                    break;
                case Tool.REC_SSID:
                    byte[] dataSSID=(byte[])msg.obj;
                    Tool.bytesToHexString(dataSSID);
                    decodeData(dataSSID);
                    break;
                case Tool.REC_AT:
                    byte[] dataID = (byte[]) msg.obj;
                    //将十进制的数据转化成十六进制数据
                    String strTempID= Tool.bytesToHexString(dataID);
                    //将十六进制的字符串去掉空格之后进行解析
                    String strdecodeID=Tool.DecodeHex(strTempID.replace(" ", ""));
                    break;
                case Tool.REC_DHCP:
                    byte[] dataDHCP = (byte[])msg.obj;
                    //将十进制的数据转化成十六进制数据
                    String strDHCP= Tool.bytesToHexString(dataDHCP);
                    if("2b 6f 6b 0d 0a 0d 0a ".equals(strDHCP)){
                        Toast.makeText(ConnectWifiActivity.this, "设置DHCP网络参数成功!", Toast.LENGTH_SHORT).show();
    
                    }
                    break;
                default:
                    byte[] data1 = (byte[]) msg.obj;
                    //将十进制的数据转化成十六进制数据
                    String strTemp1= Tool.bytesToHexString(data1);
                    Toast.makeText(ConnectWifiActivity.this, strTemp1, Toast.LENGTH_SHORT).show();
                    break;
                }
            }
        };

    总结

      凡是需要对WIFI模块发送指令的,就需要用到上述的两个线程类,还有一个对返回信息进行处理的Handler。只是发送的指令的code不一样,如上述表示的是搜索WIFI模块的十六进制的指令。不管WIFI模块在AP模式下还是STA模式,通信的最开始步骤都是先搜索模块,然后获得它的IP和Mac之后,立即回复+ok指令,就可以保持连接状态。

    Demo下载

    UDP通信发送指令Demo

  • 相关阅读:
    伯努利数学习笔记
    贝尔数学习笔记
    LuoguP5075 [JSOI2012]分零食
    LuoguP5748 集合划分计数
    LuoguP3338 [ZJOI2014]力
    LuoguP5488 差分与前缀和
    BZOJ4833 [Lydsy1704月赛]最小公倍佩尔数
    FFT&NTT学习笔记
    csp2019游记
    与图论的邂逅09:树上启发式合并
  • 原文地址:https://www.cnblogs.com/ldq2016/p/6774195.html
Copyright © 2011-2022 走看看