wifi模块 订阅
Wi-Fi模块又名串口Wi-Fi模块,属于物联网传输层,功能是将串口或TTL电平转为符合Wi-Fi无线网络通信标准的嵌入式模块,内置无线网络协议IEEE802.11b.g.n协议栈以及TCP/IP协议栈。传统的硬件设备嵌入Wi-Fi模块可以直接利用Wi-Fi联入互联网,是实现无线智能家居、M2M等物联网应用的重要组成部分。 展开全文
Wi-Fi模块又名串口Wi-Fi模块,属于物联网传输层,功能是将串口或TTL电平转为符合Wi-Fi无线网络通信标准的嵌入式模块,内置无线网络协议IEEE802.11b.g.n协议栈以及TCP/IP协议栈。传统的硬件设备嵌入Wi-Fi模块可以直接利用Wi-Fi联入互联网,是实现无线智能家居、M2M等物联网应用的重要组成部分。
信息
外文名
wifi module
主要功能
实现串口、TTL到无线网络转换
网络协议
IEEE802.11协议栈、TCP/IP协议栈
产品性质
wifi通信的传输转换产品
中文名
wifi模块
应用领域
无线网络
wifi模块主要分类
Wi-Fi模块可分为三类:1,通用Wi-Fi模块,比如手机、笔记本、平板电 脑上的USB或者SDIO接口模块,Wi-Fi协议栈和驱动是在安卓、Windows、IOS的系统里跑的,是需要非常强大的CPU来完成应用;2,路由器方案Wi-Fi模块,典型的是家用路由器,协议和驱动是借助拥有强大Flash和Ram资源的芯片加Linux操作系统;3,嵌入式Wi-Fi模块,32位单片机,内置Wi-Fi驱动和协议,接口为一般的MCU接口如UART等。适合于各类智能家居或智能硬件单品。  很多厂家已经尝试将Wi-Fi模块加入电视、空调等设备中,以搭建无线家居智能系统。实现APP的操控以及和阿里云,京东云,百度云等互联网巨头云端的对接,让家电厂家快速方便的实现自身产品的网络化智能化并和更多的其他电器实现互联互通。 [1] 
收起全文
精华内容
参与话题
问答
  • 51单片机通过WIFI模块ESP8266控制LED灯

    万次阅读 多人点赞 2018-06-16 18:47:40
    手机APP通过ESP8266 WIFI模块与51单片机通信控制LED灯的开关。下位机由单片机、ESP8266模块和LED灯组成,上位机由Android手机APP承担。我们在APP上发送LED灯的开关控制指令,ESP8266将收到的数据发送给单片机,从而...

    写在前面:

    本想将该设计全部开源,无奈理想丰满现实骨感,笔者不得不为五斗米折腰,为了小公主奶粉钱,已将全部源码设计资料和焊接成品挂在了某宝:
    https://item.taobao.com/item.htm?ft=t&id=626621440543

     

    一、系统方案

    手机APP通过ESP8266 WIFI模块与51单片机通信控制LED灯的开关。下位机由单片机、ESP8266模块和LED灯组成,上位机由Android手机APP承担。我们在APP上发送LED灯的开关控制指令,ESP8266将收到的数据发送给单片机,从而实现对LED灯进行开关控制。

     

    设计好的实物是这个样子:

    二、硬件设计

    ESP8266模块作为一个透传模块使用,RXD、TXD分别连接51单片机的TXD和RXD,VCC和CH_PD管脚接3.3V电压,GND接地,只需要连接这些管脚,ESP8266模块就可以正常工作了。在单片机P2口上连接了三个5mm的LED灯,分别是红黄蓝三种颜色,我们的目的是通过手机APP上的开关控制着三个LED灯的亮灭。

    ESP8266实物管脚图

    三、单片机软件设计

    硬件的连接不复杂,接下来主要是单片机和手机APP代码的编写。

    单片机代码主要是串口初始化、串口中断和ESP8266的初始化。首先是串口初始化:

            TMOD=0x20;		//定时器1工作在方式2
    	TH1 = 0xfd;		//波特率9600
    	TL1 = 0xfd;
    	SM0=0;                  //串口工作在方式1
    	SM1=1;
    	EA = 1;			//开总中断
    	REN = 1;		//使能串口
    	TR1 = 1;		//定时器1开始计时

    然后是ESP8266初始化:

            delayms(1000);                        //延时
    	sendString("AT+CWMODE=2\r\n");        //设置ESP8266工作在AP模式下
    	delayms(1000);	
    	sendString("AT+CIPMUX=1\r\n");        //允许多连接
    	delayms(1000);	
    	sendString("AT+CIPSERVER=1\r\n");     //建立服务器
    	delayms(1000);	
    	ES = 1;				      //esp8266初始化之后开串口中断

    贴上51单片机负责串口发送的两个函数:

    //发送一个字节
    void sendChar(uchar a)
    {
    	SBUF = a;
    	while(TI==0);
    	TI=0;
    	
    }
    
    //发送字符串
    void sendString(uchar *s)
    {
    	while(*s!='\0')
    	{
    		sendChar(*s);
    		s++;
    	}
    		
    }

    在串口中断中处理接收到的数据:

    1:打开红色灯    2:关闭红色灯

    3:打开黄色灯    4:关闭黄色灯

    5:打开蓝色灯    6:关闭蓝色灯

    esp8266在收到数据并转发给单片机时的数据格式:+IPD,<client号>,<收到的字符长度>:收到的字符,比如+IPD,0,5:hello,其中+PID是固定的;0代表的是TCP客户端编号,esp8266最多支持5个客户端同时连接,也就是说客户端编号是0到4,在本设计中由于只有一个客户端与esp8266相连,所以客户端编号是0;5代表收到的字符长度;hello是收到的字符。在本例中esp8266发送给单片机的数据是+IPD,0,1:1,我们把接收到的字符串缓存到字符数组中,所以在处理收到的数据逻辑中,首先判断是否是以'+'开始的,否则视作无效数据,然后判断数组中的第十个数据,因为第十个数据才是上位机发送过来的数据。

    void uart() interrupt 4
    {
        if(RI == 1)   
        {
            RI = 0;     //清除串口接收标志位
    	receiveTable[i]=SBUF;
    	if(receiveTable[0]=='+')
    	{
    		i++;
    	}
    	else
    	{
    		i=0;
    	}
    	if(i==10)
    	{
    		i=0;
    		switch(receiveTable[9])
    		{
    			case '1':
    				RedLED=0;
    				break;
    			case '2':
    				RedLED=1;
    				break;
    			case '3':
    				YellowLED=0;
    				break;
    			case '4':
    				YellowLED=1;
    				break;
    			case '5':
    				BlueLED=0;
    				break;
    			case '6':
    				BlueLED=1;
    				break;
    		}
    	}
        
      }
    }

     

    四、Android APP软件设计

     

    Android APP是借助Android Studio来开发的,界面比较简单,通过编辑框输入esp8266的IP地址和端口号,esp8266默认的IP地址是192.168.4.1,端口号是333,这些都可以通过AT指令进行修改。布局页面的xml代码我们就不贴了,熟悉Android开发的读者很快就能根据截图编写出来,放上一个APP界面的截图:

    我们主要看一下逻辑代码部分:

    首先是控件的初始化和按钮点击事件回调的绑定

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mBtnConnect = (Button) findViewById(R.id.btn_connect);
            mEtIP = (EditText) findViewById(R.id.et_ip);
            mEtPort = (EditText) findViewById(R.id.et_port);
            mBtnRedOn = (Button) findViewById(R.id.btn_red_on);
            mBtnRedOff = (Button) findViewById(R.id.btn_red_off);
            mBtnYellowOn = (Button) findViewById(R.id.btn_yellow_on);
            mBtnYellowOff = (Button) findViewById(R.id.btn_yellow_off);
            mBtnBlueOn = (Button) findViewById(R.id.btn_blue_on);
            mBtnBlueOff = (Button) findViewById(R.id.btn_blue_off);
            mBtnConnect.setOnClickListener(this);
            mBtnRedOn.setOnClickListener(this);
            mBtnRedOff.setOnClickListener(this);
            mBtnYellowOn.setOnClickListener(this);
            mBtnYellowOff.setOnClickListener(this);
            mBtnBlueOn.setOnClickListener(this);
            mBtnBlueOff.setOnClickListener(this);
    
        }

    按钮的点击回调方法:

        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.btn_connect:
                    //连接
                    if (mSocket == null || !mSocket.isConnected()) {
                        String ip = mEtIP.getText().toString();
                        int port = Integer.valueOf(mEtPort.getText().toString());
                        mConnectThread = new ConnectThread(ip, port);
                        mConnectThread.start();
                    }
                    if (mSocket != null && mSocket.isConnected()) {
                        try {
                            mSocket.close();
                            mBtnConnect.setText("连接");
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
    
                    break;
                case R.id.btn_red_on:         //开红灯
                    if (out != null) {
                        out.print("1");
                        out.flush();
                    }
                    break;
                case R.id.btn_red_off:        //关红灯
                    if (out != null) {
                        out.print("2");
                        out.flush();
                    }
                    break;
                case R.id.btn_yellow_on:
                    if (out != null) {
                        out.print("3");
                        out.flush();
                    }
                    break;
                case R.id.btn_yellow_off:
                    if (out != null) {
                        out.print("4");
                        out.flush();
                    }
                    break;
                case R.id.btn_blue_on:
                    if (out != null) {
                        out.print("5");
                        out.flush();
                    }
                    break;
                case R.id.btn_blue_off:
                    if (out != null) {
                        out.print("6");
                        out.flush();
                    }
                    break;
            }
        }
     

    负责连接esp8266的线程:

    private class ConnectThread extends Thread {
            private String ip;
            private int port;
    
            public ConnectThread(String ip, int port) {
                this.ip = ip;
                this.port = port;
            }
    
            @Override
            public void run() {
                try {
                    mSocket = new Socket(ip, port);
                    out = new PrintStream(mSocket.getOutputStream());
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mBtnConnect.setText("断开");
                        }
                    });
                    new HeartBeatThread().start();
                } catch (IOException e) {
                    e.printStackTrace();
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(MainActivity.this, "连接失败", Toast.LENGTH_SHORT).show();
                        }
                    });
                }
            }
        }

    本文完!

     

    展开全文
  • WIFI模块

    千次阅读 2013-10-28 15:45:20
    什么是WIFI WIFI是一种无线连接技术,可用于PC,PDA,手机等终端。WIFI的目的是改善基于IEEE802.11标准的无线网络产品...简单介绍一下,WIFI模块的基本功能: 开关WIFI 除了在WIFI设置界面可以开关WI
    1. 什么是WIFI

    WIFI是一种无线连接技术,可用于PCPDA手机等终端。WIFI的目的是改善基于IEEE802.11标准的无线网络产品之间的互通性,也就是说WIFI是基于802.11标准的,但WIFI不等同无线网络。


    1. android平台下的WIFI模块

    简单介绍一下,WIFI模块的基本功能:

    1. 开关WIFI

    除了在WIFI设置界面可以开关WIFI,还有其他的入口可以开关,要查看这些开关状态是否一致。还有就是飞行模式对WIFI开关的影响,由于WIFI开和关都有一个时间过程,而飞行模式的开关瞬间完成,所以有时会出现冲突。

    1. 开关新可用网络提醒

    新可用网络的定义是自WIFI模块开启后,从未发现过的,为加密的网络。只有满足了新可用网络的定义,才会有提醒。

    1. 连接断开网络

    连接断开各种不同加密类型的网络(具体类型下文有详解)

    1. 手动添加网络

    需要路由器关闭SIID广播。可手动输入SIID,网络加密类型,密码。对于OPAL手机来说,路由器隐藏了SSID,手动添加的网络是无法连接的。

    1. 搜索网络

    手动点击搜索按钮可以搜索网络,也可以等待WIFI模块自动搜索网络。

    1. 休眠设置

    由于WIFI模块是用电大户,所有为了省电,androidWIFI加了一个休眠策略,可以设置永远不断开,充电时不断开和锁屏时断开。要测试休眠设置是否有效,可以在路由器上PING手机的IPPING通就是连接状态。OPAL手机的休眠策略属于完全失效,现在的情况是无论选哪个都会一直保持连接,锁屏后15分钟再休眠。

    1. 设置静态IP

    Android系统里对IP设置的输入限制很有问题,我一直认为这是弱智的限制。正常IP的范围在0-255之间,androidIP输入的限制是整数0到整数255之间,也就是说0000.000200.001.001这样一个IP都能合法输入。


    1. WIFI模块深入了解一点点

    1. WIFI的基本运行流程

    【初始化】
    1
    SystemServer启动的时候,会生成一个ConnectivityService的实例

    2 ConnectivityService的构造函数会创建WifiService

    3 WifiStateTracker会创建WifiMonitor接收来自底层的事件,WifiServiceWifiMonitor是整个模块的核心。WifiService负责启动关闭wpa_supplicant、启动关闭WifiMonitor监视线程和把命令下发给wpa_supplicant,WifiMonitor则负责从wpa_supplicant接收事件通知。

    【连接AP

    1 WirelessSettings在初始化的时候配置了由WifiEnabler来处理Wifi按钮

    2当用户按下Wifi按钮后, Android会调用WifiEnableronPreferenceChange,再由WifiEnabler调用WifiManagersetWifiEnabled接口函数,通过AIDL,实际调用的是WifiServicesetWifiEnabled函数,WifiService接着向自身发送一条MESSAGE_ENABLE_WIFI消息,在处理该消息的代码中做真正的使能工作:首先装载WIFI内核模块(该模块的位置硬编码为"/system/lib/modules/wlan.ko" ),然后 启 动wpa_supplicant (配置 文 件 硬 编 码 为"/data/misc/wifi/wpa_supplicant.conf")再通过WifiStateTracker来启动WifiMonitor中的监视线程

    3当使能成功后,会广播发送WIFI_STATE_CHANGED_ACTION这个Intent通知外界WIFI已经 成 功 使 能 了 。WifiEnabler创建 的 时 候 就 会 向Android注册 接 收WIFI_STATE_CHANGED_ACTION,因此它会收到该Intent,从而开始扫描

    【查找AP

    1扫描的入口函数是WifiServicestartScan,它其实也就是往wpa_supplicant发送SCAN命令。

    2wpa_supplicant处理完SCAN命令后,它会向控制通道发送事件通知扫描完成,从而wifi_wait_for_event函数会接收到该事件,由此WifiMonitor中的MonitorThread会被执行来出来这个事件。

    3 WifiStateTracker则接着广播发送SCAN_RESULTS_AVAILABLE_ACTION这个Intent

    4 WifiLayer注册了接收SCAN_RESULTS_AVAILABLE_ACTION这个Intent,所以它的相关处理函数handleScanResultsAvailable会被调用,在该函数中,先会去拿到SCAN的结果(最终是往wpa_supplicant发送SCAN_RESULT命令并读取返回值来实现的),List<ScanResult> list = mWifiManager.getScanResults();对每一个扫描返回的AP,WifiLayer会调用WifiSettingsonAccessPointSetChanged函数,从而最终把该AP加到GUI显示列表中。

    【配置AP参数】

    当用户在WifiSettings界面上选择了一个AP,会显示配置AP参数的一个对话框。

    showAccessPointDialog(state, AccessPointDialog.MODE_INFO);

    【连接】

    当用户在AcessPointDialog中选择好加密方式和输入密钥之后,再点击连接按钮,Android就会去连接这个AP

    1 WifiLayer会先检测这个AP是不是之前被配置过,这个是通过向wpa_supplicant发送LIST_NETWORK命令并且比较返回值来实现的,

    //Need WifiConfiguration for the AP

    WifiConfiguration config = findConfiguredNetwork(state);

    如果wpa_supplicant没有这个AP的配置信息,则会向wpa_supplicant发送ADD_NETWORK命令来添加该AP

    2 ADD_NETWORK命令 会 返 回 一 个ID,WifiLayer再用 这 个 返 回 的ID作为参数向wpa_supplicant发送ENABLE_NETWORK命令,从而让wpa_supplicant去连接该AP

    【配置IP地址】

    1wpa_supplicant成功连接上AP之后,它会向控制通道发送事件通知连接上AP,从而wifi_wait_for_event函数会接收到该事件,由此WifiMonitor中的MonitorThread会被执行来出来这个事件

    2 WifiMonitor再调用WifiStateTrackernotifyStateChange,WifiStateTracker则接着会往自身发送EVENT_DHCP_START消息来启动DHCP去获取IP地址

    3然后再广播发送NETWORK_STATE_CHANGED_ACTION这个Intent

    4 WifiLayer注册了接收NETWORK_STATE_CHANGED_ACTION这个Intent,所以它的相关处理函数handleNetworkStateChanged会被调用,DHCP拿到IP地址之后,会再发送EVENT_DHCP_SUCCEEDED消息

    5 WifiLayer处理EVENT_DHCP_SUCCEEDED消息,会再 次 广 播 发 送

    至此为止,整个连接过程完成

    2. wpa_supplicant

    Android平台使用的WiFi控制框架是基于大名鼎鼎的wpa_supplicant,它是一个安全中间件,为各种无线网卡提供统一的安全机制,如下图所示:

    对应上述结构,基于Android的手机中的WiFi控制分为三大组件:
    1
    )客户端程序,包括wpa_cli命令行或java图形界面程序,通过unix本地socket
    wpa_supplicant daemon
    服务通信,发送命令并接收结果;
    2
    wpa_supplicant daemon服务,对应上述中间部分,功能是“上传下达”。所有客户端通过它控制硬件网卡,通过发送字符串命令控制是否扫描AP,提取扫描结果和是否关联AP等操作,同时将驱动的执行状态发送给用户。该服务是设计支持多种无线网卡芯片,因此各个厂商共同提供了一个通用接口给wpa_supplicant调用;
    3
    )网卡驱动;


    在手机内存的/etc/wpa_supplicant.conf中我们可以直接看到WIFI支持的网络类型,每种类型都有例子,比如:

    #Both WPA-PSK and WPA-EAP is accepted. Only CCMP is accepted as pairwise and

    # group cipher.

    #network={

    # ssid="example"

    # bssid=00:11:22:33:44:55

    # proto=WPA RSN

    # key_mgmt=WPA-PSK WPA-EAP

    # pairwise=CCMP

    # group=CCMP

    # psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb

    #}

    不同类型的网络,不同的参数等等,应有尽有。

    1. WIFI模块的LOG了解多一点点

    我们在上面已经知道WIFI的启动过程,在功能运行中也会输出相应的日志信息,下面就来详细了解一下。(请注意,WIFI开启后会更改电池状态等其他状态。关闭WIFI时,android的策略是卸载驱动来省电。如有缺失就是问题。不过下文删去了与WIFI无关的LOG!)

    1.开启WIFI&自动搜索

    E/WifiHW ( 1201): ==JOHN DEBUG==: [WIFI] Load Driver

    加载驱动

    D/SettingsWifiEnabler( 1321): Received wifi state changed from Disabled to Enabling

    接收到广播:WIFI正在开启

    D/WifiService( 1201): ACTION_BATTERY_CHANGED pluggedType: 2

    电池状态改变

    E/WifiHW(1201):==JOHNDEBUG==:moduleaddress:4b938008 filename:/system/lib/modules/dhd.ko args:firmware_path=/system/wlan/broadcom/rtecdc.bin nvram_path=/system/wlan/broadcom/nvram.txt

    WIFI硬件:加载内核模块

    I/wpa_supplicant( 2490): CTRL-EVENT-STATE-CHANGE id=-1 state=2

    wpa_supplicant发出事件通知

    V/WifiMonitor( 1201): Event [CTRL-EVENT-STATE-CHANGE id=-1 state=2]

    WifiMonitorwpa_supplicant接收事件通知

    I/wpa_supplicant( 2490): CTRL-EVENT-SCAN-RESULTS Ready

    wpa_supplicant发出事件通知:准备好开始搜索网络了

    E/wpa_supplicant( 2490): wpa_driver_priv_driver_cmd SCAN-ACTIVE len = 4096

    wpa_supplicant发出事件通知:驱动命令行.主动搜索.LEN

    E/wpa_supplicant( 2490): wpa_driver_priv_driver_cmd SCAN-ACTIVE len = 0, 11

    wpa_supplicant发出事件通知:驱动命令行.主动搜索.LEN

    E/wpa_supplicant( 2490): wpa_driver_priv_driver_cmd SCAN-PASSIVE len = 4096

    wpa_supplicant发出事件通知:驱动命令行.被动搜索.LEN

    E/wpa_supplicant( 2490): wpa_driver_priv_driver_cmd SCAN-PASSIVE len = 0, 12

    wpa_supplicant发出事件通知:驱动命令行.被动搜索.LEN=0.12

    D/SettingsWifiEnabler( 1321): Received wifi state changed from Enabling to Enabled

    接收到广播:WIFI已经开启

    E/wpa_supplicant( 2490): wpa_driver_priv_driver_cmd RSSI len = 4096

    wpa_supplicant发出事件通知:

    E/wpa_supplicant( 2490): wpa_driver_priv_driver_cmd RSSI len = 4, 4

    wpa_supplicant发出事件通知:

    E/wpa_supplicant( 2490): wpa_driver_priv_driver_cmd LINKSPEED len = 4096

    wpa_supplicant发出事件通知:

    E/wpa_supplicant( 2490): wpa_driver_priv_driver_cmd LinkSpeed 54 len = 12, 12

    wpa_supplicant发出事件通知:

    E/wpa_supplicant( 2490): wpa_driver_priv_driver_cmd MACADDR len = 4096

    wpa_supplicant发出事件通知:驱动命令行.MAC地址.LEN

    E/wpa_supplicant( 2490): wpa_driver_priv_driver_cmd Macaddr = 44:A4:2D:27:25:BE

    wpa_supplicant发出事件通知:驱动命令行.MAC地址

    E/wpa_supplicant( 2490): len = 28, 28

    wpa_supplicant发出事件通知:

    V/WifiStateTracker( 1201): Connection to supplicant established, state=SCANNING

    WIFI状态跟踪:连接请求确认,状态=搜索

    D/NetworkStateTracker( 1201): setDetailed state, ld =IDLE and new state=SCANNING

    网络状态跟踪:更新显示为搜索状态

    V/WifiStateTracker( 1201): Changing supplicant state: SCANNING ==> INACTIVE

    WIFI状态跟踪:更改请求状态:搜索中->不活动


    2.点击连接&获取状态

    E/WifiHW ( 1201): ==JOHN DEBUG==: [WIFI] Load Driver

    WIFI硬件:加载驱动

    D/SettingsWifiEnabler( 1321): Received wifi state changed from Disabled to Enabling

    收到广播,WIFI状态正在开启

    E/WifiHW(1201):==JOHNDEBUG==:moduleaddress:4b938008 filename:/system/lib/modules/dhd.ko args:firmware_path=/system/wlan/broadcom/rtecdc.bin nvram_path=/system/wlan/broadcom/nvram.txt

    WIFI硬件:加载内核模块

    E/WifiHW ( 1201): ==JOHN DEBUG==: return of insmod : ret = 0, Unknown error: 0

    WIFI硬件:返回装载模块报告:返回指令0,未知错误0

    ……

    I/wpa_supplicant( 2490): Trying to associate with 1c:bd:b9:f6:a7:9f (SSID='LosAngeles' freq=2412 MHz)

    wpa_supplicant发出事件通知:尝试连接,(SSID='LosAngeles'频段=2412 MHz

    V/WifiMonitor( 1201):Event[Trying to associate with 1c:bd:b9:f6:a7:9f (SSID='LosAngeles' freq=2412 MHz)]

    WifiMonitor接收wpa_supplicant的事件

    V/WifiMonitor( 1201): Event [CTRL-EVENT-STATE-CHANGE id=-1 state=3]

    WifiMonitor接收事件

    V/WifiStateTracker( 1201): Changing supplicant state: SCANNING ==> ASSOCIATING

    WIFI状态跟踪:更改请求状态:搜索中->匹配中

    D/NetworkStateTracker( 1201): setDetailed state, ld =SCANNING and new state=CONNECTING

    网络状态跟踪:更新显示为正在连接状态

    D/ConnectivityService( 1201): ConnectivityChange for WIFI: CONNECTING/CONNECTING

    连接管理服务:改变WIFI连接状态:正在连接/正在连接

    V/WifiStateTracker( 1201): Changing supplicant state: ASSOCIATING ==> ASSOCIATED

    WIFI状态跟踪:更改请求状态:匹配中->已匹配

    D/NetworkStateTracker( 1201): setDetailed state, ld =CONNECTING and new state=CONNECTING

    网络状态跟踪:更新显示为正在连接状态

    I/wpa_supplicant( 2490): Associated with 1c:bd:b9:f6:a7:9f

    wpa_supplicant发出事件通知:已和1c:bd:b9:f6:a7:9f匹配

    V/WifiMonitor( 1201): Event [Associated with 1c:bd:b9:f6:a7:9f]

    WifiMonitor接收wpa_supplicant的事件

    V/WifiStateTracker( 1201): Changing supplicant state: ASSOCIATED ==> FOUR_WAY_HANDSHAKE

    WIFI状态跟踪:更改请求状态:已匹配->TCP中断连接

    D/NetworkStateTracker( 1201): setDetailed state, ld =CONNECTING and new state=AUTHENTICATING

    网络状态跟踪:更新显示为鉴定中

    D/ConnectivityService( 1201): Dropping ConnectivityChange for WIFI: CONNECTING/AUTHENTICATING

    连接管理服务:抛出WIFI连接状态改变:已连接/鉴定中

    V/WifiStateTracker( 1201): Changing supplicant state: FOUR_WAY_HANDSHAKE ==> GROUP_HANDSHAKE

    WIFI状态跟踪:更改请求状态:TCP中断连接->确认标志位

    D/NetworkStateTracker( 1201): setDetailed state, ld =AUTHENTICATING and new state=AUTHENTICATING

    网络状态跟踪:更新显示为鉴定中

    I/wpa_supplicant( 2490): WPA: Key negotiation completed with 1c:bd:b9:f6:a7:9f [PTK=CCMP GTK=TKIP]

    wpa_supplicant发出事件通知:WPA:1c:bd:b9:f6:a7:9f确定标志位

    I/wpa_supplicant( 2490): CTRL-EVENT-STATE-CHANGE id=0 state=7

    wpa_supplicant发出事件通知:

    I/wpa_supplicant( 2490): CTRL-EVENT-CONNECTED - Connection to 1c:bd:b9:f6:a7:9f completed (auth) [id=0 id_str=]

    wpa_supplicant发出事件通知:连接完成

    V/WifiMonitor( 1201): Event [WPA: Key negotiation completed with 1c:bd:b9:f6:a7:9f [PTK=CCMP GTK=TKIP]]

    WifiMonitor接收wpa_supplicant事件

    V/WifiMonitor( 1201): Event [CTRL-EVENT-STATE-CHANGE id=0 state=7]

    WifiMonitor接收wpa_supplicant事件

    V/WifiMonitor( 1201): Event [CTRL-EVENT-CONNECTED - Connection to 1c:bd:b9:f6:a7:9f completed (auth) [id=0 id_str=]]

    WifiMonitor接收wpa_supplicant事件

    V/WifiStateTracker( 1201): Changing supplicant state: GROUP_HANDSHAKE ==> COMPLETED

    WIFI状态跟踪:更改请求状态:确认标志位->完成

    V/WifiStateTracker( 1201): New network state is CONNECTED

    WIFI状态跟踪:新网络状态为已连接

    D/NetworkStateTracker( 1201): setDetailed state, ld =AUTHENTICATING and new state=OBTAINING_IPADDR

    网络状态跟踪:更新显示为获取IP地址

    D/ConnectivityService( 1201): Dropping ConnectivityChange for WIFI: CONNECTING/OBTAINING_IPADDR

    连接管理服务:抛出WIFI

    展开全文
  • WiFi模块

    2017-03-13 12:35:05
    #include <reg52.h>#define KEYPORT P2sbit beep = P1^7;#define SEGPORT P0unsigned char High; unsigned char Low; unsigned char code freq[][2]={ 0xD8,0xF7,//00440HZ 1 0xBD,0xF8,//00494HZ 2 ...
    #include <reg52.h>
    
    #define KEYPORT P2
    
    sbit beep = P1^7;
    
    #define SEGPORT P0
    
    unsigned char High;
    unsigned char Low;
    unsigned char code freq[][2]={ 
      0xD8,0xF7,//00440HZ 1   
      0xBD,0xF8,//00494HZ 2  
      0x87,0xF9,//00554HZ 3  
      0xE4,0xF9,//00587HZ 4    
      0x90,0xFA,//00659HZ 5     
      0x29,0xFB,//00740HZ 6     
      0xB1,0xFB,//00831HZ 7      
      0xEF,0xFB,//00880HZ `1
    };
    
    unsigned char code SONG_TONE[]={212,212,190,212,159,169,212,212,190,212,142,159,
                                                    212,212,106,126,159,169,190,119,119,126,159,142,159,0};
    unsigned char code SONG_LONG[]={9,3,12,12,12,24,9,3,12,12,12,24,
                                                    9,3,12,12,12,12,12,9,3,12,12,12,24,0};
    void DelayMS(unsigned int x)
    {
            unsigned char t;
            while(x--)for(t=0;t<120;t++);
    }
    void PlayMusic()
    {
            unsigned int i=0,j,k;
            while(SONG_LONG[i]!=0||SONG_TONE[i]!=0)
            {
                    for(j=0;j<SONG_LONG[i]*20;j++)
                    {
                            beep = ~ beep;
                            for(k=0;k<SONG_TONE[i]/3;k++);
                    }
                    DelayMS(10);
                    i++;
            }
    }
    
    
    void timer0_init(void)
    {
        EA = 1;
        TMOD |= 0x01;
        TH0 = 0;
        TL0 = 0;
        ET0 = 1;
        TR0 = 0;
    }
    
    
    
    
    void timer0_isr(void) interrupt 1
    {
        TH0 = High;
        TL0 = Low;
        beep = ~beep;
    }
    
    
    
    void main()
    {
        unsigned char num = 0;
    
        timer0_init();
    
        while(1)
        {
            switch(KEYPORT)
            {
                case 0xfe : num = 1; break;
                case 0xfd : num = 2; break;
                case 0xfb : num = 3; break;
                case 0xf7 : num = 4; break;
                case 0xef : num = 5; break;
                case 0xdf : num = 6; break;
                case 0xbf : num = 7; break;
                case 0x7f : num = 8; break;
                default : num = 0; break;
            }
            if(num)
            {
                High = freq[num - 1][1];
                Low = freq[num - 1][0];
                TR0 = 1;
            }
            else
            {
                TR0 = 0;
            }
            PlayMusic();
        }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80#include <reg52.h>
    
    #define KEYPORT P2
    
    sbit beep = P1^7;
    
    #define SEGPORT P0
    
    unsigned char High;
    unsigned char Low;
    unsigned char code freq[][2]={ 
      0xD8,0xF7,//00440HZ 1   
      0xBD,0xF8,//00494HZ 2  
      0x87,0xF9,//00554HZ 3  
      0xE4,0xF9,//00587HZ 4    
      0x90,0xFA,//00659HZ 5     
      0x29,0xFB,//00740HZ 6     
      0xB1,0xFB,//00831HZ 7      
      0xEF,0xFB,//00880HZ `1
    };
    
    unsigned char code SONG_TONE[]={212,212,190,212,159,169,212,212,190,212,142,159,
                                                    212,212,106,126,159,169,190,119,119,126,159,142,159,0};
    unsigned char code SONG_LONG[]={9,3,12,12,12,24,9,3,12,12,12,24,
                                                    9,3,12,12,12,12,12,9,3,12,12,12,24,0};
    void DelayMS(unsigned int x)
    {
            unsigned char t;
            while(x--)for(t=0;t<120;t++);
    }
    void PlayMusic()
    {
            unsigned int i=0,j,k;
            while(SONG_LONG[i]!=0||SONG_TONE[i]!=0)
            {
                    for(j=0;j<SONG_LONG[i]*20;j++)
                    {
                            beep = ~ beep;
                            for(k=0;k<SONG_TONE[i]/3;k++);
                    }
                    DelayMS(10);
                    i++;
            }
    }
    
    
    void timer0_init(void)
    {
        EA = 1;
        TMOD |= 0x01;
        TH0 = 0;
        TL0 = 0;
        ET0 = 1;
        TR0 = 0;
    }
    
    
    
    
    void timer0_isr(void) interrupt 1
    {
        TH0 = High;
        TL0 = Low;
        beep = ~beep;
    }
    
    
    
    void main()
    {
        unsigned char num = 0;
    
        timer0_init();
    
        while(1)
        {
            switch(KEYPORT)
            {
                case 0xfe : num = 1; break;
                case 0xfd : num = 2; break;
                case 0xfb : num = 3; break;
                case 0xf7 : num = 4; break;
                case 0xef : num = 5; break;
                case 0xdf : num = 6; break;
                case 0xbf : num = 7; break;
                case 0x7f : num = 8; break;
                default : num = 0; break;
            }
            if(num)
            {
                High = freq[num - 1][1];
                Low = freq[num - 1][0];
                TR0 = 1;
            }
            else
            {
                TR0 = 0;
            }
            PlayMusic();
        }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    8`Uart转TCP\IP模块、UART转WLAN模块、RS232转WIFI
    
    使用场合:手持机,智能家居,遥控玩具、汽车定位、安防器材,监控检测,数据采集,医疗器械,电子称,iphone、Android手机应用,教学教育产品,公交车,
    
    型号:HLK-WIFI-M03 
    
    1. MAC+RF架构,无需额外的MCU.
    
    2.硬件802.11协议,内嵌TCP/IP协议
    
    3.支持多种网络协议:TCP/UDP/ICMP/DHCP/DNS/HTTP
    
    4.支持自动和工作命令模式,支持DHCP
    
    5.支持串口透明传输,内置WEB服务器
    
    6.支持WEP64/WEP128/WPA-PSK/WPA2-PSK
    
    7.提供配置软件配置和IE浏览器配置参数
    
    8.支持基础网(Infra)和自组网(Adhoc)
    
    9.支持AT+编辑命令控制,并提供MCU控制的C源代码
    
    详细资料请联系卖家!
    
    产品特性
    
    接口
        双排(2 x 4)插针式接口
        支持波特率范围:1200~115200bps
        支持硬件 RTS/CTS 流控
        单 3.3V 供电
    无线
        支持 IEEE802.11b/g 无线标准
        支持频率范围:2.412~2.484 GHz
        支持两种无线网络类型:
        基础网(Infra)和自组网(Adhoc)
        支持多种安全认证机制:
        WEP64/WEP128/ TKIP/CCMP(AES)
        WEP/WPA-PSK/WPA2-PSK
        支持快速联网
        支持无线漫游
    其它
        支持多种网络协议:
    TCP/UDP/ICMP/DHCP/DNS/HTTP 
        支持自动和命令两种工作模式
        支持串口透明传输模式
        支持 AT+控制指令集
        支持多种参数配置方式:
        串口/WEB 服务器/无线连接
    
    
    应用领域
    
     智能公交网络,如无线刷卡机
     小额金融支付网络,如无线 POS 机
     业设备联网,如无线传感器
     物联网
    
    全面支持串口透明数据传输模式,真
    正实现串口的即插即用
    全新的 AT+指令集,全部基于 ASCII
    格式,简单易懂,使用更容易
    更加完备的 TCP/IP 协议栈,支持
    DHCP 协议动态 IP 地址分配和 DNS
    域名解析功能
    内置 WEB 服务器,实现使用 IE 浏览
    器通过无线网络远程配置模块参数
    更高的传输性能,最高可达11KB/S
    的发送速率
    
    · 嵌入式802.11b/g无线网络
    
    · 支持点对点访问,支持客户端、串口到WLAN的模式
    
    · 支持串口到无线信号的桥接
    
    · 支持64/128位WEP、WPA和WPA2(AES),具有很高的保密性
    
    · UART、GPIO、U.FL(WLAN)接口   
    
    1.功能概述
    
    硬件部分
    
    
    
    项目
    
    指标
    
    模块
    
    
    
    串口电平
    
    TTL/UART 3.3v
    
    连接器
    
    插针(8PIN,兼容以前的4PIN连接),具有防插反设计
    
    5 - VCC(IN),8 - GND(OUT)
    
    7 - TX(IN),6 - RX(OUT)
    
    4 - RST(IN),3 – READY(OUT)
    
    2 - LED1(OUT),1 - LED2(OUT)
    
    指示灯
    
    连接状态,板上显示,同时输出信号到外部接口READY
    
    无线传输状态,板上显示,同时输出信号到外部接口LED3
    
    状态1,板上显示,同时输出信号到外部接口LED2
    
    状态2,板上显示
    
    状态3,板上显示
    
    天线
    
    外接天线
    
    体积
    
    55.2x25.4x11.5mm(较小)
    
    功耗
    
    
    
    温度范围
    
    0~70 degC
    
    供电
    
    +3.3V +/-10%
    
    转接板
    
    
    
    串口电平
    
    RS232
    
    串口接口类型
    
    DB9(Female)
    
    连接器
    
    8针插座
    
    引脚定义同模块连接器
    
    指示灯
    
    1- 电源
    
    2- 串口TX
    
    3- 串口RX
    
    4- 模块连接状态,连接到READY引脚
    
    5- 无线传输状态,连接到LED1引脚
    
    6- 状态1,连接到LED2引脚
    
    复位按键
    
    连接到RST引脚
    
    电源接口
    
    2.0
    
    其它
    
    与模块实现方便、可靠连接,
    
    
    
    软件部分
    
    
    
    项目
    
    指标
    
    无线功能
    
    
    
    协议标准
    
    802.11b/bg
    
    网络类型
    
    Infra/adhoc/private
    
    安全认证
    
    OPEN/WEP64/WEP128/CCMP/TKIP/WPI
    
    OPEN/SHAREKEY/WPA/WPA2/WAI
    
    节能
    
    打开/关闭RF
    
    
    
    
    
    网络功能
    
    
    
    传输层协议
    
    TCP/IP/UDP/ARP/ICMP/DHCP
    
    应用层协议
    
    HTTP(Server,仅用于远程WEB配置功能)
    
    DNS(Client)
    
    用户接口功能
    
    
    
    串口波特率
    
    1200bps~115200bps
    
    串口传输模式
    
    命令模式:
    
    透明模式:时间/长度(可设置)触发,特殊字符(可设置)退出
    
    控制协议
    
    AT+命令集
    
    配置参数
    
    掉电保存(flash),支持自动备份与恢复
    
    配置管理方式
    
    配置管理程序(串口、远程)方式
    
    远程WEB登陆方式
    
    测试、维护
    
    
    
    生产测试
    
    支持Litepoint测试命令集/设置MAC地址/设置发送功率
    
    支持UART接口
    
    固件升级
    
    通过配置管理程序
    
    上位机软件
    
    
    
    配置管理程序
    
    用于模块的参数配置、联网、传输功能演示等
    
    生产测试程序
    
    用于生产阶段的出厂检测,包括物理层测试、功率校正、MAC地址烧写
    
    虚拟串口客户端程序
    
    与固件中的虚拟串口服务器程序一起用于实现无线串口功能
    
    其它
    
    
    
    
    
    用户手册
    
    
    
    全部AT+指令集的使用方法Demo源码
    
     用IPHONE通过WIFI控制设备
    
    
    
    随着Apple公司的iphone和ipad大热卖,Apple粉丝的增多,iphone周边产品的研发也有越来越多的公司进入。目前以开发iphone手机软件的公司每天都在增加中。
    
    我司的串口wifi模块,型号HLK-WIFI-M03,支持AD-HOC模式,透明传输,上电后我们的M03模块可以发起一个AD-HOC的网络,iphone加入这个网络,之后就可以用iphone控制您的设备了。具体连接方式见下图
    
    
    
    数据从iphone经过我们的串口wifi模块透明传输到客户设备的MCU中,然后MCU做相关的动作控制灯光或者汽车或者其他设备。
    
    M03的参考设置
    
    ²        无线设置
    
    Ø         网络类型:自组网络,使能“网络不存在时自动创建”
    
    Ø         网络名称:my_adhoc(可修改成任何名称)
    
    Ø         加密类型:开放
    
    Ø         其余默认
    
    ²        网络设置
    
    Ø         IP地址:192.168.0.1
    
    Ø         子网掩码:255.255.255.0
    
    Ø         网关地址:192.168.0.1
    
    Ø         DNS地址:192.168.0.1
    
    ²        工作模式设置
    
    Ø         启用自动工作模式
    
    Ø         协议类型:TCP
    
    Ø         C/S模式:服务器
    
    Ø         端口号:6000
    
    Ø         连接超时:0
    
    
    
    Iphone的网络配置
    
    ²        网络设置
    
    Ø         IP地址:192.168.0.2
    
    Ø         子网掩码:255.255.255.0
    
    Ø         网关地址:192.168.0.1
    
    Ø         DNS地址:192.168.0.1
    
    
    
    然后Iphone上运行一个TCP协议的控制程序即可完成data的透明传输。`3
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    
    展开全文
  • wifi模块

    千次阅读 2013-09-27 16:46:25
    Wifi模块  最近研究Wifi模块,查了不少的相关资料,但发现基本上是基于android2.0版本的的分析,而现在研发的android移动平台基本上都是2.3的版本,跟2.0版本的差别,在Wifi模块上也是显而易见的。2.3版本Wifi模块...

    Wifi模块

           最近研究Wifi模块,查了不少的相关资料,但发现基本上是基于android2.0版本的的分析,而现在研发的android移动平台基本上都是2.3的版本,跟2.0版本的差别,在Wifi模块上也是显而易见的。2.3版本Wifi模块没有了WifiLayer,之前的WifiLayer主要负责一些复杂的Wifi功能,如AP选择等以提供给用户自定义,而新的版本里面的这块内容基本上被WifiSettings所代替。

           本文就是基于android2.3版本的Wifi分析,主要分为两部分来分别说明:

    (1)    Wifi模块相关文件的解析

    (2)    Wpa_supplicant解析

    (3)    Wifi的启动流程(有代码供参考分析)

    一,Wifi模块相关文件解析

    1)     wifisettings.java

    packages/apps/Settings/src/com/android/settings/wifiwifisettings.java

           该类数据部分主要定义了下面几个类的变量:

    {

    private final IntentFilter mFilter;

     

    //广播接收器,用来接收消息并做响应的处理工作

    privatefinal BroadcastReceiver mReceiver;

     

    //这是一个扫描类,会在用户手动扫描   AP时被调用

    privatefinal Scanner mScanner;               

    private WifiInfo mLastInfo;

     

           //服务代理端,作为WifiService对外的接口类呈现

    privateWifiManager mWifiManager;

     

    //这个类主要实现Wifi的开闭工作

    privateWifiEnabler mWifiEnabler;

     

    //AP

    private AccessPoint mSelected;

    private WifiDialog mDialog;

    ……

    }

           wifiSettings类的构造函数的主要工作:定义了一个IntentFilter(Intent过滤器)变量,并添加了六个动作,(了解Android的intent机制的同学都知道什么意思,不明白的同学参考Intent机制的资料)接着定义一个广播接收器,并有相应的消息处理函数,下面是该构造函数的定义:

     

    public WifiSettings() {

    mFilter = new IntentFilter();

    //intent机制中的intent消息过滤器,下面添加可以处理的动作

        mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);

          mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);

        mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);

       mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);

           mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);

        mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);

     

    //注册了广播接收器,用来处理接收到的消息事件

           mReceiver = new BroadcastReceiver() {

               @Override

                public void onReceive(Context context,Intent intent) {

                   handleEvent(intent); //事件处理函数

                }

            };

     

            mScanner= new Scanner();     //手动扫描类

    }

           在广播接收器中的相应函数onReceive函数中有个handleEvent函数,它就是用来处理广播接收器接受到的intent消息的,它的功能是根据intent消息中的动作类型,来执行相应的操作,每一种动作对应了activity的一项消息处理能力。

           在oncreate函数中实例化了mWifiManager和mWifiEnabler两个类,这两个类对wifiSettings来说至关重要,它后面的定义的一系列函数都是通过调用这两个类的相应接口来实现的。

    ……

    mWifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);

    mWifiEnabler = new WifiEnabler(this,

                       (CheckBoxPreference) findPreference("enable_wifi"));

    ……

           WifiSettings中还定义了显示菜单和响应菜单键的函数,即onCreateOptionsMenu()和onOptionsItemSelected();还有响应配置对话框中按键的onClick()函数;最后定义了Scanner类,它是一个handler的继承类,实现了消息处理函数,用于处理手动扫描的动作。

     

    2)     WifiEnabler.java:

    packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java

     

    private final Context mContext;

    private final CheckBoxPreference mCheckBox;

     

    //两个重要成员

    private final WifiManager mWifiManager;

    private final IntentFilter mIntentFilter;

     

           wifienabler类中定义了四个成员变量很重要,mContext,mCheckBox,mWifiManager和mReceiver,其中mContext用于获取mwifiManager实例,mReceiver用来接收底层发来的消息,mCheckBox用来改变UI的状态。

    该类中定义了几个重要的函数onPreferenceChange,handleWifiStateChanged和handleStateChanged,onPreferenceChange用来处理按下的Enbler键,它会调用mWifiManager.setWifiEnabled(enable),另外两个用来处理接受的消息事件。

     

           在类的构造函数中,主要做了一下工作:初始化了mContext,mCheckBox,mWifimanager,并且初始化了一个mIntentFilter变量,添加了三个动作,在构造函数的上面定义了一个广播接收器,用来接收下层传来的消息,并根据intent动作的类型调用相应的处理函数,这个广播接收器在onResum函数中被注册。

    public WifiEnabler(Context context, CheckBoxPreferencecheckBox) {

            mContext= context;

           mCheckBox = checkBox;

           mOriginalSummary = checkBox.getSummary();

           checkBox.setPersistent(false);

     

           mWifiManager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);

            mIntentFilter= new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);

            // Theorder matters! We really should not depend on this. :(

           mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);

           mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);

    }

    这里可以总结为:如果上层需要监听或收到下层的消息,那么就要通过定义一个BroadcastReciever,并将它注册,当然在接受到消息后应该有处理消息的函数,然后在onReciever函数中根据消息调用相应的处理函数,这里的消息通知机制是Intent,在BroadcastReciever类的onReciever函数的参数中可以看出。

    该类成员函数的也是通过调用mWifimanager的接口来实现的。

     

     3) WifiManager:

           frameworks/base/wifi/java/android/net/wifi/WifiManager.java

           两个重要的数据成员:

    //WifiService

    IWifiManager mService;

        HandlermHandler;

    IWifiManager mService和HandlermHandler,这个类拥有了一个WifiService实例,就可以通过它进行一系列的调用;WifiManager中定义了的wifi和ap的状态,这些状态会在其他很多类中有使用;然后定义了大量的函数,这些函数几乎都是对WifiService接口函数的封装,直接调用WifiService的函数。

    该类的构造函数很简单:

    public WifiManager(IWifiManager service,Handler handler) {

           mService = service;

           mHandler = handler;

        }

    该类中还定义了一个WifiLock类,这个类用来保证在有应用程序使用Wifi无线电传输数据时,wifiradio可用,即当一个应用程序使用wifi的radio进行无线电数据传输时,就要先获得这个锁,如果该锁已被其他程序占有,就要等到该锁被释放后才能获得,只用当所有持有该锁的程序都释放该锁后,才能关闭radio功能。

     

     4)WifiService:

    frameworks/base/services/java/com/android/server/WifiService.java

    private final WifiStateTrackermWifiStateTracker;

    private Context mContext;

    private WifiWatchdogServicemWifiWatchdogService = null;

    private final  WifiHandler mWifiHandler;

    这是WifiService中的几个重要的数据成员。

    在接下来的构造函数中初始化了mWifiStateTracker,mContext,然后动态生成mWifiThread子线程并启动,在主线程里用mWifiThread调用getLooper()函数获得线程的looper,来初始化创建一个mWifiHandler对象,这个WifiHandler在WifiService类的后面有定义,并重载了Handler类的handlermessage()函数,这样消息就可以在主线程里被处理了,这是android的handlerthread消息处理机制,可参考相关资料,这里不予详述。在构造函数的最后,注册了两个广播接收器,分别用来ACTION_AIRPLANE_MODE_CHANGED和ACTION_TETHER_STATE_CHANGED这两个动作,这里是android的intent消息通知机制,请参考相关资料,代码如下:

     

    mContext = context;

    mWifiStateTracker = tracker;

    mWifiStateTracker.enableRssiPolling(true);

    ……

    HandlerThread wifiThread = newHandlerThread("WifiService");

    wifiThread.start();

    mWifiHandler = newWifiHandler(wifiThread.getLooper());

    ……

    随后定义了一系列的函数,其中有服务器要发送的命令的系列函数,它通过mWifiStateTracker成员类调用自己的的发送命令的接口(其实就是对本地接口的一个封装),最后通过适配层发送命令给wpa_supplicant,而事件处理只到WifiStateTracker层被处理。

    要注意的是,在WifiService中,定义了一些函数来创建消息,并通过mWifiHandler将消息发送到消息队列上,然后在mHandlerThread线程体run()分发\处理消息,在主线程中被mWifiHandler的handlerMessage()函数处理,最后调用mWifiStateTracker的对应函数来实现的。这里我也不明白为什么WifiService不直接调用mWifiStateTracker对应的函数,还要通过消息处理机制,绕了一圈在调用,当然Google这么做肯定是有它道理的,忘高手指点。

     

     5) WifiStateTracker类

    frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java

    NetworkStateTracker继承了handler类,而WifiStateTracker继承了NetworkStateTracker类,就是说WifiStateTracker间接继承了handler类,属于一个事件处理类。

    WifiStateTracker类首先定义了事件日志和事件码(这里包含了所有可能的事件类型),还定义了如下的重要成员数据:

              //几个重要的数据成员

    private WifiMonitor mWifiMonitor;    //被启动用来监听supplicant传来的消息

    private WifiInfo mWifiInfo;        

    private WifiManager mWM;             //服务代理

    private DhcpHandler mDhcpTarget;  //IP地址获取线程

    private DhcpInfo mDhcpInfo;            //Dhcp的相关信息都在这里

                   

    类的构造函数中,初始化了系列成员变量,包括生成了WifiMonitor的实例,在构造函数中,因为WifiStateTracker是一个handler间接子类,所以他会自动调用handler的无参构造函数,获得looper和Queue消息队列。

    然后定义了一些设置supplicant和更新网络信息的辅助函数。

           startEventLoop()函数很重要,用来启动WifiMonitor线程,进入消息循环检测。接着定义了系列通知函数,被WifiMonitor调用来向WifiStateTracker传递从wpa_supplicant接收到的消息,他会调用消息发送函数到消息队列,并被WifiStateTracker的handlermessage()函数处理。这个handlermessage()函数就是在随后被定义的,它主要是调用相应的辅助函数完成动作,并可能会将消息封装后,通过intent机制发送出去,被上层的UI活动接收处理。

    这里也定义了很多的WfiNative接口函数,这是JNI的本地接口;类DhcpHandler extends Handler{}也是在该类中定义的,它也是一个handler的子类,用来处理DHCP相关的消息EVENT_DHCP_START,可以想到它和WifiStateTracker不是共用一个looper。

           注意:handleMessage是在该文件中定义的,用来处理经WifiMonitor转换过的消息。

     

     6) WifiMonitor

           frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java

    声明了一个重要的成员变量:mWifiStateTracker,并在构造函数中由参数提供初始化,还定义了一系列的可能从wpa_supplicant层接收的事件类型及其名字,这些是消息处理机制的基础。

    startMonitoring()函数,这是一个线程启动的封装函数,WifiStateTracker就是通过这个函数启动的WifiThread。

    这个重要的类classMonitorThreadextends Thread{};它是一个监控进程类,里面有一系列的事件处理函数和一个重要的Run()函数,run函数主要流程:connectToSupplicant()连接精灵进程wpa_supplicant,这里有一个mWifiStateTracker.notifySupplicantXXX()的调用,通知上层是否连接成功,然后就是一个轮询过程,其中调用了WifiNative.waitForEvent()本地轮询函数接口,并从返回的事件字符串类型中提取事件的名称,最后通过事件的名称调用相应的事件处理函数,并将事件转换成mWifiStateTracker能识别的类型上报。

           注意:这里的每个事件的捕获(由wifimonitor完成),最终都会调用相应的mWifiStateTracker的消息通知函数上报消息;轮询和事件处理都是在Monitor线程类中实现的。

     

     7)WifiNative

           frameworks/base/wifi/java/android/net/wifi/WifiNative.java

    里面定义了一个类WifiNative:其中声明了许多本地接口,可由native的标志看出,这是Java代码和本地库之间的联系接口;

     

     8) android_net_wifi_Wifi.java

    frameworks/base/core/jni/

           里面定义了许多的JNI的本地代码实现,每个实现中都会调用wpa_supplicant适配层的接口,通过包含适配层的头文件wifi.h获取适配层定义的接口;后面是一个JNINativeMethod数组,定义了每个本地接口和本地实现的对应关系;最后有一个注册函数regester_XXX_XX(),用以把上面的方法类数组注册到系统中。

           该类实现了本地接口的相关功能,并通过调用wpa的适配层的相关函数和wpa_supplicant通信,所以JNI是连接Java框架层和wpa适配层的桥梁.

     

     9)wpa_supplicant适配层,wifi.c:目录libhardware/wifi/

           里面定义很多字符串变量和适配层的接口实现,是对wpa_supplicant程序通信的接口封装,用来完成上层和wpa_supplicant的通信,头文件在libhardware/include/hardware下,这里的函数用来向JNI的本地实现提供调用接口。

           这里的函数,我把它们分为三类函数:

    一类是命令相关的(控制)函数,就是在JNI层android_XXX_Command()函数所调用的::Wifi_Command()函数,调用流程:android_XXX_command()=>docommand()=>wifi_command()=>wifi_send_command()=>wpa_ctrl_require()。

    二类是监听函数,即Wifi_wait_for_event()函数,调用流程:android_net_wifi_Waitforevent()=>wifi_wait_for_event()=>wpa_ctrl_recv()。

    三类是剩下的函数。

     

    10)wpa_supplicant与上层的接口,wpa_ctrl.c:external/wpa_supplicant

    定义了三类套接字,并分别实现了和wpa_supplicant的通信,因此wpa_supplicant适配层和wpa_supplicant层是通过socket通讯的。

    要是从wifi.c中真的很难看出它和wpa_supplicant有什么关系,和它联系密切的是wpa_ctrl.h文件,这里面定义了一个类wpa_ctrl,这个类中声明了两个Socket套接口,一个是本地一个是要连接的套接口,wpa_ctrl与wpa_supplicant的通信就需要socket来帮忙了,而wpa_supplicant就是通过调用wpa_ctrl.h中定义的函数和wpa_supplicant进行通讯的,wpa_ctrl类(其实是其中的两个socket)就是他们之间的桥梁。

     

     11)wpa_supplicant和driver_wext驱动接口的联系:

           driver.h:该文件定义了系列结构,首先是一个wpa_scan_result结构,这是一个扫描结果的通用格式,里面包含了扫描的所有信息(如BSSID,SSID,信号质量,噪音水平,支持的最大波特率等等信息),每个驱动接口实现负责将从驱动中上传的扫描信息的格式转换到该通用的扫描信息格式;然后是一些宏定义和枚举定义,最后也是最重要的是wpa_driver_ops结构,该结构是wpa driver的操作函数集合,里面有驱动接口的名称和很多的函数指针。

           drviers.c:该文件很简单,首先是一些外部变量的引用声明,都是不同驱动操作接口的集合wpa_driver_XXX_ops变量;然后就是定义一个驱动操作接口集合的数组,根据宏定义添加对应的驱动操作接口集合的变量。

           drvier_XXX.h:这是不同驱动接口头文件,主要声明了操作接口

           drvier_XXX.c:实现操作接口,定义一个操作集合变量,并用上面定义的函数初始化其中的函数指针

           注意:下面要搞清楚wpa_supplicant守护进程是如何操作,最后调用驱动接口集合中的函数的;要知道wpa_supplicant是为不同驱动和操作系统具有更好移植性而被设计的,以便在wpa_supplicant层不用实现驱动的具体接口就可以添加新的驱动程序;在wpa_supplicant结构中有一个wpa_drv_ops类型的drvier成员,在wpa_supplicant进程中,经常通过Wpa_supplicant_XXX函数传递wpa_supplicant实例指针wpa_s参数给wpa_drv_XXX函数来调用它,在wpa_drv_XX中会通过wpa_s->driver->XXX()的流程来调用通用驱动接口,简单才是生活的真相,可android始终让我感到真相还是遥不可及。

     

     12)WifiWatchdogService:

    首先声明了两个主要变量mWifiStateTracker,mWifiManager,需要这两个类对象来完成具体的控制工作,在WifiWatchdogService的构造函数中,创建了这两个类,并通过regesterForWifiBroadcasts()注册了BroadcastReceiver,BroadcastReceiver是用来获取网络变化的,然后启动了一个WatchdogTread线程,用来处理从WifiStateTracker接收到的消息。

     

    frameworks/base/services/java/com/android/server/WifiWatchdogService.java

    WifiWatchdogService(Context context,WifiStateTracker wifiStateTracker) {

           mContext = context;

           mContentResolver = context.getContentResolver();

           mWifiStateTracker =wifiStateTracker;

            mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);

           

           createThread();

           

           // The content observer to listen needs a handler, which createThreadcreates

           registerForSettingsChanges();

           if (isWatchdogEnabled()) {

               registerForWifiBroadcasts();

           }

           

           if (V) {

               myLogV("WifiWatchdogService: Created");

           }

        }

           WifiWatchdogHandler继承了handler类,成为一个消息处理类,定义handlemessage()函数,处理消息队列上的消息。

     

    二,wpa_supplicant再解析

    1)wpa_ctrl.h:

           该文件中并没有定义structwpa_ctrl结构,因为在其他包含该文件的.c文件中不能直接使用该结构的成员,这里主要声明了几个用于使用socket通信的函数接口,包括:wpa_ctrl_open,wpa_ctrl_close,wpa_ctrl_attach,wpa_ctrl_detach,wpa_ctrl_cleanup,wpa_ctrl_recv,wpa_ctrl_request, wpa_ctrl_pending, wpa_ctrl_get_fd 等函数。

           这些函数的功能从名字上能看出,open函数就是创建一个socket接口,并绑定连接wpa_supplicant,attach函数用于定义一个控制接口为监听接口,pending函数用于查询有无消息可读处理,request和recv分别用来发送和读取消息。

           其实,这里就是一个使用socket通信的封装,具体内容可以参考socket编程。

     

    2)wpa_ctrl.c:

           这里首先定义了一个wpa_ctrl结构,里面根据UDP,UNIX和命名管道三种domain类型来定义通信实体:

    struct wpa_ctrl {

    #ifdefCONFIG_CTRL_IFACE_UDP

           int s;

           struct sockaddr_in local;

           struct sockaddr_in dest;

           char *cookie;

    #endif /*CONFIG_CTRL_IFACE_UDP */

    #ifdefCONFIG_CTRL_IFACE_UNIX

           int s;

           struct sockaddr_un local;

           struct sockaddr_un dest;

    #endif /*CONFIG_CTRL_IFACE_UNIX */

    #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE

           HANDLE pipe;

    #endif /*CONFIG_CTRL_IFACE_NAMED_PIPE */

    };

           然后是根据上面三个类型分别实现了wpa_ctrl.h中声明的接口函数,这里就不做介绍了。

     

    3)wpa_supplicant.h:

           首先,定义了一个枚举wpa_event_type,罗列了系列wpa的事件类型,然后就是wpa_event_data类型,随后是两个函数:wpa_supplicant_event和wpa_supplicant_rx_eapol。

    wpa_supplicant.c:首先定义一个驱动操作数组externstruct wpa_driver_ops *wpa_supplicant_drivers[],然后是系列wpa_supplicant_XXX()函数,很多函数里面调用wpa_drv_XXX()函数,这些函数是wpa_supplicant_i.h中实现的函数。几乎每个函数都需要一个wpa_supplicant结构,对其进行所有的控制和通信操作。

     

    4)wpa_supplicant_i.h:

           开头定义了几个结构,如:wpa_blacklist,wpa_interface,wpa_params,wpa_global,wpa_client_mlme和wpa_supplicant等结构,其中wpa_supplicant最为重要,wpa_supplicant结构里有一个重要的driver成员,它是wpa_driver_ops类型,可以被用来调用抽象层的接口。

           接下来是系列函数声明,这些函数声明在wpa_supplicant.c中实现,然后就是wpa_drv_XXX函数,这些函数就是在wpa_supplicant.c中被wpa_supplicant_xxx函数调用的,而这些wpa_drv_xxx函数也都有一个wpa_supplicant结构的变量指针,用来调用封装的抽象接口。

           这里要注意的是:在wpa_suppliant.c文件中定义的很多函数是在该头文件中声明的,而不是在wpa_supplicant.h中声明的。

     

    5)driver.h:

           该文件中定义了一个重要的数据结构:wpa_scan_result,这是一个从驱动发来的数据被封装成的通用的扫描结果数据结构,每个驱动结构的实现都要遵循的扫描结果格式,比如driver_wext.c中的实现。后面还有定义了很多的数据结构,这里不具表。

           文件中最重要的一个数据结构是:wpa_driver_ops,这是所有驱动接口层必须为之实现的API,是所有驱动类型的一个接口封装包,wpa_supplicant就是通过该接口来和驱动交互的。

     

    6)driver_wext.h:

           该文件很简单,就是声明了该驱动的一些对应驱动API接口的函数。

    driver_wext.c:

           此文件就是实现了上面的一些函数,最后初始化了一个wpa_drv_ops变量。

     

    三,Wifi模块解析

    1)框架分析

     

     

    图示1:Wifi框架

           首先,用户程序使用WifiManager类来管理Wifi模块,它能够获得Wifi模块的状态,配置和控制Wifi模块,而所有这些操作都要依赖Wifiservice类来实现。

           WifiService和WifiMonitor类是Wifi框架的核心,如图所示。下面先来看看WifiService是什么时候,怎么被创建和初始化的。

           在systemServer启动之后,它会创建一个ConnectivityServer对象,这个对象的构造函数会创建一个WifiService的实例,代码如下所示:

     

    framework/base/services/java/com/android/server/ConnectivityService.java

    {

    ……

    case ConnectivityManager.TYPE_WIFI:

                   if (DBG) Slog.v(TAG, "Starting Wifi Service.");

                   WifiStateTracker wst = new WifiStateTracker(context, mHandler);                             //创建WifiStateTracker实例

                    WifiService wifiService = newWifiService(context, wst);//创建WifiService实例

                   ServiceManager.addService(Context.WIFI_SERVICE, wifiService);           //向服务管理系统添加Wifi服务

                   wifiService.startWifi();     //启动Wifi

                   mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;

                    wst.startMonitoring(); //启动WifiMonitor中的WifiThread线程

    ……

    }

           WifiService的主要工作:WifiMonitor和Wpa_supplicant的启动和关闭,向Wpa_supplicant发送命令。

           WifiMonitor的主要工作:阻塞监听并接收来自Wpa_supplicant的消息,然后发送给WifiStateTracker。

           上面两个线程通过AF_UNIX套接字和Wpa_supplicant通信,在通信过程中有两种连接方式:控制连接和监听连接。它们创建代码如下:

    ctrl_conn =wpa_ctrl_open(ifname);

    .. .. ..

     monitor_conn = wpa_ctrl_open(ifname);

     

     

    2)Wifi启动流程

           (1)使能Wifi

           要想使用Wifi模块,必须首先使能Wifi,当你第一次按下Wifi使能按钮时,WirelessSettings会实例化一个WifiEnabler对象,实例化代码如下:

     

    packages/apps/settings/src/com/android/settings/WirelessSettings.java

    protected void onCreate(Bundle savedInstanceState) {

           super.onCreate(savedInstanceState);

    ……

                  CheckBoxPreferencewifi = (CheckBoxPreference) findPreference(KEY_TOGGLE_WIFI);

                  mWifiEnabler= new WifiEnabler(this, wifi);

    ……

    }

           WifiEnabler类的定义大致如下,它实现了一个监听接口,当WifiEnabler对象被初始化后,它监听到你按键的动作,会调用响应函数onPreferenceChange(),这个函数会调用WifiManager的setWifiEnabled()函数。

    public class WifiEnabler implementsPreference.OnPreferenceChangeListener {

    ……

    public boolean onPreferenceChange(Preference preference,Object value) {

            booleanenable = (Boolean) value;

    ……

    if (mWifiManager.setWifiEnabled(enable)) {

                    mCheckBox.setEnabled(false);

    ……

    }

    ……

    }

           我们都知道Wifimanager只是个服务代理,所以它会调用WifiService的setWifiEnabled()函数,而这个函数会调用sendEnableMessage()函数,了解android消息处理机制的都知道,这个函数最终会给自己发送一个MESSAGE_ENABLE_WIFI的消息,被WifiService里面定义的handlermessage()函数处理,会调用setWifiEnabledBlocking()函数。下面是调用流程:

     

    mWifiEnabler.onpreferencechange()=>mWifiManage.setWifienabled()=>mWifiService.setWifiEnabled()=>mWifiService.sendEnableMessage()=>mWifiService.handleMessage()=>mWifiService.setWifiEnabledBlocking().

     

    在setWifiEnabledBlocking()函数中主要做如下工作:加载Wifi驱动,启动wpa_supplicant,注册广播接收器,启动WifiThread监听线程。代码如下:

    ……

    if (enable) {

               if (!mWifiStateTracker.loadDriver()) {

                   Slog.e(TAG, "Failed toload Wi-Fi driver.");

                   setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);

                    return false;

               }

               if (!mWifiStateTracker.startSupplicant()) {

                    mWifiStateTracker.unloadDriver();

                    Slog.e(TAG, "Failed tostart supplicant daemon.");

                   setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);

                    return false;

               }

     

               registerForBroadcasts();

               mWifiStateTracker.startEventLoop();

    ……

           至此,Wifi使能结束,自动进入扫描阶段。

     

    (2) 扫描AP

           当驱动加载成功后,如果配置文件的AP_SCAN = 1,扫描会自动开始,WifiMonitor将会从supplicant收到一个消息EVENT_DRIVER_STATE_CHANGED,调用handleDriverEvent(),然后调用mWifiStateTracker.notifyDriverStarted(),该函数向消息队列添加EVENT_DRIVER_STATE_CHANGED,handlermessage()函数处理消息时调用scan()函数,并通过WifiNative将扫描命令发送到wpa_supplicant。

     

    Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java

    private void handleDriverEvent(Stringstate) {

               if (state == null) {

                    return;

               }

               if (state.equals("STOPPED")) {

                   mWifiStateTracker.notifyDriverStopped();

               } else if (state.equals("STARTED")) {

                    mWifiStateTracker.notifyDriverStarted();

               } else if (state.equals("HANGED")) {

                    mWifiStateTracker.notifyDriverHung();

               }

           }

     

    Frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java

    case EVENT_DRIVER_STATE_CHANGED:

            

                    switch(msg.arg1) {

                    case DRIVER_STARTED:

                        /**

                         *Set the number of allowed radio channels according

                         *to the system setting, since it gets reset by the

                         *driver upon changing to the STARTED state.

                         */

                        setNumAllowedChannels();

                       synchronized (this) {

                           if (mRunState == RUN_STATE_STARTING) {

                               mRunState = RUN_STATE_RUNNING;

                               if (!mIsScanOnly) {

                                    reconnectCommand();

                               } else {

                                    // In somesituations, supplicant needs to be kickstarted to

                                    // start thebackground scanning

                                    scan(true);

                               }

                           }

                        }

                       break;             

     

    上面是启动Wifi时,自动进行的AP的扫描,用户当然也可以手动扫描AP,这部分实现在WifiService里面,WifiService通过startScan()接口函数发送扫描命令到supplicant。

     

    Frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java

    public boolean startScan(booleanforceActive) {

           enforceChangePermission();

     

           switch (mWifiStateTracker.getSupplicantState()) {

               case DISCONNECTED:

               case INACTIVE:

               case SCANNING:

               case DORMANT:

                    break;

               default:

                   mWifiStateTracker.setScanResultHandling(

                           WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY);

                    break;

           }

           return mWifiStateTracker.scan(forceActive);

        }

           然后下面的流程同上面的自动扫描,我们来分析一下手动扫描从哪里开始的。我们应该知道手动扫描是通过菜单键的扫描键来响应的,而响应该动作的应该是WifiSettings类中Scanner类的handlerMessage()函数,它调用WifiManager的startScanActive(),这才调用WifiService的startScan()。

     

    packages/apps/Settings/src/com/android/settings/wifiwifisettings.java

    public boolean onCreateOptionsMenu(Menu menu) {

           menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan)

                   .setIcon(R.drawable.ic_menu_scan_network);

           menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)

                   .setIcon(android.R.drawable.ic_menu_manage);

           return super.onCreateOptionsMenu(menu);

        }

     

           当按下菜单键时,WifiSettings就会调用这个函数绘制菜单。如果选择扫描按钮,WifiSettings会调用onOptionsItemSelected()。

     

    packages/apps/Settings/src/com/android/settings/wifiwifisettings.java

    public booleanonOptionsItemSelected(MenuItem item) {

           switch (item.getItemId()) {

               case MENU_ID_SCAN:

                    if(mWifiManager.isWifiEnabled()) {

                        mScanner.resume();

                    }

                    return true;

               case MENU_ID_ADVANCED:

                    startActivity(new Intent(this,AdvancedSettings.class));

                    return true;

           }

           return super.onOptionsItemSelected(item);

    }

     

    private class Scanner extends Handler {

           private int mRetry = 0;

     

           void resume() {

               if (!hasMessages(0)) {

                    sendEmptyMessage(0);

               }

           }

     

           void pause() {

               mRetry = 0;

                mAccessPoints.setProgress(false);

               removeMessages(0);

           }

     

           @Override

           public void handleMessage(Message message) {

               if (mWifiManager.startScanActive()){

                    mRetry = 0;

               } else if (++mRetry >= 3) {

                    mRetry = 0;

                   Toast.makeText(WifiSettings.this, R.string.wifi_fail_to_scan,

                           Toast.LENGTH_LONG).show();

                    return;

               }

               mAccessPoints.setProgress(mRetry != 0);

               sendEmptyMessageDelayed(0, 6000);

           }

        }

          

    这里的mWifiManager.startScanActive()就会调用WifiService里的startScan()函数,下面的流程和上面的一样,这里不赘述。

    当supplicant完成了这个扫描命令后,它会发送一个消息给上层,提醒他们扫描已经完成,WifiMonitor会接收到这消息,然后再发送给WifiStateTracker。

     

    Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java

    void handleEvent(int event, String remainder) {

                switch (event) {

                    caseDISCONNECTED:

                       handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED,remainder);

                        break;

     

                    case CONNECTED:

                       handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED,remainder);

                        break;

     

                    case SCAN_RESULTS:

                        mWifiStateTracker.notifyScanResultsAvailable();

                        break;

     

                    case UNKNOWN:

                        break;

                }

            }

    WifiStateTracker将会广播SCAN_RESULTS_AVAILABLE_ACTION消息:

     

    Frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java

    public voidhandleMessage(Message msg) {

            Intent intent;

    ……

    case EVENT_SCAN_RESULTS_AVAILABLE:

                    if(ActivityManagerNative.isSystemReady()) {

                        mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));

                    }

                    sendScanResultsAvailable();

                    /**

                     * On receiving the first scanresults after connecting to

                     * the supplicant, switch scanmode over to passive.

                     */

                    setScanMode(false);

                    break;

    ……

           由于WifiSettings类注册了intent,能够处理SCAN_RESULTS_AVAILABLE_ACTION消息,它会调用handleEvent(),调用流程如下所示。

     

    WifiSettings.handleEvent() =>WifiSettings.updateAccessPoints() => mWifiManager.getScanResults() => mService.getScanResults()=> mWifiStateTracker.scanResults() => WifiNative.scanResultsCommand()……

     

    将获取AP列表的命令发送到supplicant,然后supplicant通过Socket发送扫描结果,由上层接收并显示。这和前面的消息获取流程基本相同。

     

    (3)配置,连接AP

    当用户选择一个活跃的AP时,WifiSettings响应打开一个对话框来配置AP,比如加密方法和连接AP的验证模式。配置好AP后,WifiService添加或更新网络连接到特定的AP。

     

    packages/apps/settings/src/com/android/settings/wifi/WifiSetttings.java

    public booleanonPreferenceTreeClick(PreferenceScreen screen, Preference preference) {

           if (preference instanceof AccessPoint) {

               mSelected = (AccessPoint) preference;

               showDialog(mSelected, false);

           } else if (preference == mAddNetwork) {

               mSelected = null;

               showDialog(null, true);

           } else if (preference == mNotifyOpenNetworks) {

               Secure.putInt(getContentResolver(),

                       Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,

                       mNotifyOpenNetworks.isChecked() ? 1 : 0);

           } else {

               return super.onPreferenceTreeClick(screen, preference);

           }

           return true;

        }

     

           配置好以后,当按下“Connect Press”时,WifiSettings通过发送LIST_NETWORK命令到supplicant来检查该网络是否配置。如果没有该网络或没有配置它,WifiService调用addorUpdateNetwork()函数来添加或更新网络,然后发送命令给supplicant,连接到这个网络。下面是从响应连接按钮到WifiService发送连接命令的代码:

     

    packages/apps/settings/src/com/android/settings/wifi/WifiSetttings.java

    public void onClick(DialogInterfacedialogInterface, int button) {

           if (button == WifiDialog.BUTTON_FORGET && mSelected != null) {

               forget(mSelected.networkId);

           } else if (button == WifiDialog.BUTTON_SUBMIT && mDialog !=null) {

               WifiConfiguration config = mDialog.getConfig();

     

               if (config == null) {

                    if (mSelected != null&& !requireKeyStore(mSelected.getConfig())) {

                        connect(mSelected.networkId);

                    }

               } else if (config.networkId != -1) {

                    if (mSelected != null) {

                        mWifiManager.updateNetwork(config);

                        saveNetworks();

                    }

               } else {

                    int networkId =mWifiManager.addNetwork(config);

                    if (networkId != -1) {

                       mWifiManager.enableNetwork(networkId, false);

                        config.networkId =networkId;

                        if (mDialog.edit || requireKeyStore(config)){

                            saveNetworks();

                        } else {

                            connect(networkId);

                        }

                    }

               }

           }

        }

          

          

    Frameworks\base\wifi\java\android\net\wifi\WifiManager.java

           public intupdateNetwork(WifiConfiguration config) {

            if(config == null || config.networkId < 0) {

               return -1;

            }

            return addOrUpdateNetwork(config);

    }

    private intaddOrUpdateNetwork(WifiConfiguration config) {

           try {

               return mService.addOrUpdateNetwork(config);

           } catch (RemoteException e) {

               return -1;

           }

        }

     

    WifiService.addOrUpdateNetwork()通过调用mWifiStateTracker.setNetworkVariable()将连接命令发送到Wpa_supplicant。

     

    (4)获取IP地址

    当连接到supplicant后,WifiMonitor就会通知WifiStateTracker。

     

    Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java

    Public void Run(){

    if (connectToSupplicant()) {

                    // Send a message indicatingthat it is now possible to send commands

                    // to the supplicant

                    mWifiStateTracker.notifySupplicantConnection();

               } else {

                   mWifiStateTracker.notifySupplicantLost();

                    return;

               }

    ……

    }

     

    WifiStateTracker发送EVENT_SUPPLICANT_CONNECTION消息到消息队列,这个消息有自己的handlermessage()函数处理,它会启动一个DHCP线程,而这个线程会一直等待一个消息事件,来启动DHCP协议分配IP地址。

     

    frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java

    void notifySupplicantConnection() {

           sendEmptyMessage(EVENT_SUPPLICANT_CONNECTION);

    }

     

    public void handleMessage(Message msg) {

           Intent intent;

     

           switch (msg.what) {

               case EVENT_SUPPLICANT_CONNECTION:

                 ……

                 HandlerThread dhcpThread = newHandlerThread("DHCP Handler Thread");

                    dhcpThread.start();

                    mDhcpTarget = newDhcpHandler(dhcpThread.getLooper(), this);

    ……

    ……

    }

    当Wpa_supplicant连接到AP后,它会发送一个消息给上层来通知连接成功,WifiMonitor会接受到这个消息并上报给WifiStateTracker。

     

    Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java

    void handleEvent(int event, String remainder) {

               switch (event) {

                   case DISCONNECTED:

                       handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED,remainder);

                       break;

     

                   case CONNECTED:

                       handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED,remainder);

                       break;

                    ……

    }

     

    private voidhandleNetworkStateChange(NetworkInfo.DetailedState newState, String data) {

            StringBSSID = null;

            intnetworkId = -1;

            if(newState == NetworkInfo.DetailedState.CONNECTED) {

               Matcher match = mConnectedEventPattern.matcher(data);

                if(!match.find()) {

                   if (Config.LOGD) Log.d(TAG, "Could not find BSSID in CONNECTEDevent string");

                }else {

                   BSSID = match.group(1);

                   try {

                       networkId = Integer.parseInt(match.group(2));

                   } catch (NumberFormatException e) {

                       networkId = -1;

                    }

                }

            }

            mWifiStateTracker.notifyStateChange(newState,BSSID, networkId);

    }

          

    void notifyStateChange(DetailedState newState, StringBSSID, int networkId) {

            Messagemsg = Message.obtain(

               this, EVENT_NETWORK_STATE_CHANGED,

                newNetworkStateChangeResult(newState, BSSID, networkId));

           msg.sendToTarget();

        }

     

    caseEVENT_NETWORK_STATE_CHANGED:

    ……

    configureInterface();

    ……

     

    private void configureInterface() {

           checkPollTimer();

            mLastSignalLevel = -1;

            if(!mUseStaticIp) {          //使用DHCP线程动态IP

                if(!mHaveIpAddress && !mObtainingIpAddress) {

                   mObtainingIpAddress = true;

     

                                       //发送启动DHCP线程获取IP

                    mDhcpTarget.sendEmptyMessage(EVENT_DHCP_START);

                }

            } else {        //使用静态IP,IP信息从mDhcpInfo中获取

                intevent;

                if(NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {

                   mHaveIpAddress = true;

                   event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;

                   if (LOCAL_LOGD) Log.v(TAG, "Static IP configurationsucceeded");

                }else {

                   mHaveIpAddress = false;

                   event = EVENT_INTERFACE_CONFIGURATION_FAILED;

                   if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration failed");

                }

               sendEmptyMessage(event);           //发送IP获得成功消息事件

            }

        }

                  DhcpThread获取EVENT_DHCP_START消息事件后,调用handleMessage()函数,启动DHCP获取IP地址的服务。

     

    public void handleMessage(Message msg) {

                intevent;

    switch (msg.what) {

                   case EVENT_DHCP_START:

     

    ……

    Log.d(TAG, "DhcpHandler: DHCP requeststarted");

     

    //启动一个DHCPclient的精灵进程,为mInterfaceName请求分配一个IP地//址

        if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {

         event= EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;

             if(LOCAL_LOGD) Log.v(TAG, "DhcpHandler: DHCP request succeeded");

        } else {

               event= EVENT_INTERFACE_CONFIGURATION_FAILED;

               Log.i(TAG,"DhcpHandler: DHCP request failed: " +

                                NetworkUtils.getDhcpError());

            }

    ……

    }

    这里调用了一个NetworkUtils.runDhcp()函数,NetworkUtils类是一个网络服务的辅助类,它主要定义了一些本地接口,这些接口会通过他们的JNI层android_net_NetUtils.cpp文件和DHCP client通信,并获取IP地址。

    至此,IP地址获取完毕,Wifi启动流程结束。


    展开全文
  • ESP8266 wifi模块开发汇总

    千次阅读 2018-10-23 08:35:19
    ESP8266 wifi模块开发汇总   ESP8266 wifi模块开发汇总本文档主要介绍开发者在ESP8266开发中常见的一些问题。 这些问题主要包括以下几大类: 基本概念相关 ESP8266 相关 AiCloud 相关 固件编译调试相关 文档...
  • ESP8266wifi模块与51单片机通信教程

    千次阅读 2019-11-10 17:45:59
    ESP8266wifi模块与51单片机通信教程 准备两个200欧左右的电阻 准备6根杜邦线 然后将ES8266通过如图这种方式连接起来 注意:在连接单片机的RX和TX引脚时必须和ESP8266模块的RX和TX反接才能保证正常通信 将程序下载...
  • ESP8266WiFi模块入门使用手册

    千次阅读 2018-06-04 11:10:00
    ESP8266WiFi模块系列模块使用指南,模组全家福,技术资料请 点击打开链接
  • 嵌入式串口WIFI模块

    2011-12-05 13:29:47
    内置Wifi模块 智能家居 物联网 三屏互联 产品简介无线WIFI模块 、无线WIFI模组 本文关键字:www.xlwtech.com 新力维WIFI模块,SEANYWELL WIFI模块,新力维串口转WIFI, 无线WIFI模块 WIFI无线模组,新力维串口转...
  • 串口WIFI模块简介彩页

    2011-12-05 13:40:02
    内置Wifi模块 智能家居 物联网 三屏互联 产品简介无线WIFI模块 、无线WIFI模组 本文关键字:www.xlwtech.com 新力维WIFI模块,SEANYWELL WIFI模块,新力维串口转WIFI, 无线WIFI模块 WIFI无线模组,新力维串口转...
  • ESP8266WiFi模块AT指令集

    2018-06-04 11:17:59
    无线wifi模块广泛地运用在无人通信控制、智能医疗器械、智能监控安防控制系统、智能背景音乐、智能影院控制、电动窗帘控制系统机器人控制、WiFi模块、 智能插座 、物联网、智能家居、无线模组研发与生产 , 物联网...
  • ESP8266WIFI模块了解一下(新手入门级别)

    万次阅读 多人点赞 2018-05-01 10:02:51
    安信可ESP8266WIFI模块了解一下 ESP8266是有乐鑫公司开发的一套高度集成的WIFI芯片,可以方便进行而二次开发。乐鑫是ESP8266的芯片厂商,安信可是依据ESP8266生产模组,并提供一系列开发方案的厂家。 接...
  • WG229是一款基于ESP8266芯片方案的小尺寸低成本串口WiFi模块,符合802.11b / g / n 无线模块标准,支持UART-WiFi -以太网数据传输。专为移动设备和物联网应用设计,可将用户的物理设备连接到WiFi无线网络上,进行...
  • SKW93A是一款集成MT7628A,MT7610E双芯片的ac AP/Router双频WiFi模块,具备高性能,高集成的无线AP解决方案模块!SKW93A内置580M双内核,对于消费电子设备,该模块仅需要外部3.3V电源。 SKW93A符合802.11a/b/g/n/ac...
  • 低功耗,小体积 串口wifi模块。 WF-30-11N模块完全兼容IEEE802.11b\g\n协议,为任何具有串口的设备提供无线接入以太网的功能。此模块集成了MAC、基带处理器、具有运放的射频传输和天线接口。WLAN协议、网络协议栈...
  • 低功耗,小体积 串口wifi模块。 电话:13439720658 QQ:554211685 WF-30-11N模块完全兼容IEEE802.11b\g\n协议,为任何具有串口的设备提供无线接入以太网的功能。此模块集成了MAC、基带处理器、具有运放的射频传输和...
  • SKW100是一款集成QCA9531、QCA9887双芯片的ac AP/Router双频WiFi模块,具备高性能,高集成的无线AP解决方案模块!SKW100内置650MHz双内核,对于消费电子设备,该模块仅需要外部3.3V电源。 SKW100符合802.11a/b/g/n/...
  • 低功耗,小体积 串口wifi模块。 WF-30-11N模块完全兼容IEEE802.11b\g\n协议,为任何具有串口的设备提供无线接入以太网的功能。此模块集成了MAC、基带处理器、具有运放的射频传输和天线接口。WLAN协议、网络协议栈...
  • 低功耗,小体积 串口wifi模块。 WF-30-11N模块完全兼容IEEE802.11b\g\n协议,为任何具有串口的设备提供无线接入以太网的功能。此模块集成了MAC、基带处理器、具有运放的射频传输和天线接口。WLAN协议、网络协议栈...
  • 基于STM32串口通信的ESP8266WiFi模块使用

    万次阅读 多人点赞 2019-04-01 15:28:32
    因此,掌握RS232通信协议、SPI通信协议以及esp8266的配置就可以基本搞定WiFi模块的使用。 参考文章: [1] ESP8266WiFi模块AT指令入门指南 [2] ESP8266 WIFI串口通信模块使用详解(实例:附STM32详细代码) [3] STM...
  • 之前写的:ESP8266 WIFI模块学习之路(3)是WIFI模块向单片机发送信息,我们绝不能满足于仅仅是给单片机下发数据,我们还想自己接受数据,只有这样互相通信才是真正的物联网技术。因此,我就在研究如何实现WIFI模块...
  • 嵌入式WIFI模块 串口转WIFI模块 操作演示教程 嵌入式WIFI模块 串口转WIFI模块 操作演示教程
  • QCA9331WiFi模块 QCA9331WiFi模块开发pdf,QCA9331WiFi模块 QCA9331WiFi模块开发
  • AMPAK正基WIFI模块定频软件-适用AP系列所有WIFI模块,AP6181,AP6212,AP6236,AP6255,AP6256等WIFI模组都可适用,世联芯科技长期供应AMPAK正基AP全系列模组.提供技术支持!
  • WiFi模块设计方案.pdf

    2019-07-23 14:11:25
    完整的WIFI模块解决方案,模块非常适合便携式系统启用WiFi /蓝牙连接,  - 设计效率:轻松应用WiFi模块跨越多个平台和 重新设计和开发时间最短的产品  - 适合您的ID:最小的占地面积,...

空空如也

1 2 3 4 5 ... 20
收藏数 5,927
精华内容 2,370
关键字:

wifi模块