softAP配网,即利用设备的无线芯片,将设备进入到softAP模式,开启一个无线局域网,手机(或其它移动设备)通过连入设备开启的无线局域网后,向设备发送路由器的ssid及password等信息,让设备在无屏幕的情况下,获取到路由器的ssid信息,达到联网的目的。

配网的流程其实还是比较繁杂的,手机要先和原来的路由器断开连接,然后设备开softap,然后手机连入softap,然后发送ssid和密码,然后关闭softap模式,退回到正常的station模式,然后设备连接路由器,手机也视情况重新连接路由器。但是这个过程是可以在软件端整合起来的,以达到一键联网的结果,断开和连接的操作都整合起来。在这方面小米的体验就做得很好。我本来是比较推崇无线数据帧配网的方式的,因为在软件端只要不断地往空气中发无线数据包就可以了,设备端也只需要捕捉到这些包就可以了。但是无线数据帧配网的方式极不稳定,配网成功率低,受环境、手机型号、路由器型号、设备laytout和无线芯片的选型影响较大。而softAp配网,本身稳定性较高,通过开发者的整合,可以达到和无线数据帧配网一样的用户体验。

softAp配网流程图如下:

我这里实现配网的两端设备,一边是一个跑linux系统的开发板,另一边是我们日常用的普通Android手机。如图:

设备端的linux系统,需要移植好wifi芯片对应的驱动,并且可以配置softAp模式(p2p模式),这个一般是有wifi芯片厂商在fw里实现好的,我们只要切换节点,加载驱动,push fw就可以了。不同的厂商实现的方法可能有异,我在此就不介绍了。

基于已经移植好wifi驱动的设备,我这里介绍如何在应用层实现softAP配网的demo。

设备端实现

设备端需要实现的流程有如下:

(1)将设备进入到softAP模式,需要所使用的wifi芯片支持spftAP模式;

(2)创建一个socket用于接收手机端发来的ssid信息,demo中使用广播的方式收发信息;

(3)接收手机发来的ssid信息;

(4)收到手机发来的ssid信息后返回确认信息,让手机暂时停止发送ssid信息并断开和设备端的连接(即使不主动断开在设备端退出softAP模式之后,手机也会因为找不到softAP而被动断开连接);

(5)退出sopAP模式;

(6)解析从手机端得到的ssid、password等信息;

(7)连接路由器;

(8)结束。如因ssid信息错误等原因,未能成功连上路由器,可根据用户体验需求重新开始配网流程,该循环的触发流程,请根据实际功能定位自行设计。

#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>#include <net/if.h>
#include <errno.h>
#include <sys/ioctl.h>#include "include/smartlink.h"#define IP_SIZE 16#define MSG_LEN_MX 256
#define CMD_LEN_MX 128#define PORT_DEFAULT 8066//server
int main(int argc, char **argv){int port = PORT_DEFAULT;if(argc >=2){port = atoi(argv[1]);}printf("*****softAP demo*****\n");//先把设备设置为softAP模式if(set_softAP_up()==0){printf("start softAP success.\n");}/*创建socket接收手机发来的ssid信息,并返回确认信息*///创建socketstruct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(port);printf("port:%d \n", port);addr.sin_addr.s_addr = htonl(INADDR_ANY);int sock;if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0){printf("socket error!\n");printf("%s \n", strerror(errno));exit(1);}else{printf("socket success.\n");}//printf("addr:%s \n",addr.sin_addr.s_addr);if(bind(sock, (struct sockaddr *)&addr, sizeof(addr))< 0){printf("bind error!\n");printf("%s \n", strerror(errno));exit(1);}else{printf("bind success.\n");}//接收手机发来的ssid信息char buff[256];struct sockaddr_in clientAddr;int len = sizeof(clientAddr);int n;while(1){printf("recvfrom...\n");n = recvfrom(sock, buff, 511, 0, (struct sockaddr*)&clientAddr, &len);printf("addr: %s -port: %u says: %s \n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port), buff);if(n>0){printf("recvfrom success.\n");break;}}//往手机返回确认信息char buff_confirm[3];strcpy(buff_confirm,"OK");while(1){n = sendto(sock, buff_confirm, n, 0, (struct sockaddr *)&clientAddr, sizeof(clientAddr));printf("addr: %s -port: %u says: %s \n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port), buff_confirm);if(n>0){printf("sendto success.\n");//usleep(10*1000000);break;}}usleep(5*1000000);close(sock);//设备退出softAP模式,退出后将自动恢复到station模式if(set_softAP_down()==0){printf("down softAP success.\n");}usleep(5*1000000);//解析设备端发过来的ssid信息char* smartlink_softAP_ssid;// = "12345678";char* smartlink_softAP_password;// = "12345678";char *p =NULL;int ssid_len = strstr(buff, "::Password::")-buff-8;printf("ssid_len  %d\n",ssid_len);smartlink_softAP_ssid = (char *)malloc(ssid_len+1);if((p = strstr(buff, "::SSID::")) != NULL){p += strlen("::SSID::");//跳过前缀if(*p){if(strstr(p, "::Password::") != NULL){memset((void*)smartlink_softAP_ssid,'\0',ssid_len+1);strncpy(smartlink_softAP_ssid, p, ssid_len);}}}printf("%s\n",smartlink_softAP_ssid);int password_len = strstr(buff, "::End::") - strstr(buff, "::Password::") - 12;printf("password_len  %d\n",password_len);smartlink_softAP_password = (char *)malloc(password_len+1);if((p = strstr(buff, "::Password::")) != NULL){p += strlen("::Password::");//跳过前缀if(*p){if(strstr(p, "::End::") != NULL){memset((void*)smartlink_softAP_password,'\0',password_len+1);strncpy(smartlink_softAP_password, p, password_len);}}}printf("%s\n",smartlink_softAP_password);//使用解析到的ssid信息连接wifismartlink_softAP_connect_ap(smartlink_softAP_ssid, smartlink_softAP_password);free(buff);free(smartlink_softAP_ssid);free(smartlink_softAP_password);return 0;
}

手机端实现

手机端需要实现的流程如下:

(1)检查手机当前wifi状态,如未开启需先开启wifi,如已连接某路由器需与该路由器断开连接;

(2)搜索设备开启的wifi,直到找到或超时为止;

(3)连入设备开启wifi;

(4)创建socket,与设备进行通信,demo中选用广播的方式进行通信;

(5)开启接收线程,等待接收设备端发来的确认信息。接收线程建议在发送线程开启前先开启,以免收不到确认信息;

(6)开启发送线程,向设备发送ssid信息;

(7)在收到设备发送的确认信息后,断开与设备的连接(即使不主动断开,也会在设备退出softAP模式后,手机也会因为找不到AP而被动断开连接);

(8)重新连入路由器。可在重新连入路由器后询问设备是否也成功连入路由器,如没有可重新进行配网,设备端也要做相应的操作,具体可根据场景需求自行设计。

Android手机需要实现的权限:

<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" /><uses-permission android:name="android.permission.INTERNET"/><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

代码示例

package com.example.chenkunyao.ckysoftapdemo;import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;import com.example.chenkunyao.ckysoftapdemo.tools.SmartlinkInfo;
import com.example.chenkunyao.ckysoftapdemo.tools.WifiAdmin;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;public class MainActivity extends AppCompatActivity {private Button btnSend;private Button btnStop;private int sendNum=0;int recvNum=0;private EditText etTargetIP;private EditText etTargetPort;private EditText etLocalIP;private EditText etLocalPort;private EditText etSSID;private EditText etPassword;private Spinner spEncrypt;private String strTargetIP;private String strTargetPort;private String strLocalIP;private String strLocalPort;private String strSSID;private String strPassword;private String strEncrypt;private Button btnConnect;DatagramSocket socket;InetAddress addr;public int STOPNUM = 1024;public String SOTFAPSSID = "Smart-AW-HOSTAPD";private Context context;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);context=this.getBaseContext();init();}private void init(){btnSend = (Button) this.findViewById(R.id.btnSend);btnSend.setOnClickListener(click);btnStop = (Button) this.findViewById(R.id.btnStop);btnStop.setOnClickListener(click);etTargetIP= (EditText) findViewById(R.id.etTargetIP);etTargetPort= (EditText) findViewById(R.id.etTargetPort);etLocalIP= (EditText) findViewById(R.id.etTargetPort);etLocalPort= (EditText) findViewById(R.id.etLocalPort);etSSID= (EditText) findViewById(R.id.etSSID);etPassword= (EditText) findViewById(R.id.etPassword);spEncrypt= (Spinner) findViewById(R.id.spEncrypt);spEncrypt.setOnItemSelectedListener(select);spEncrypt.setSelection(1,true);strEncrypt= String.valueOf(spEncrypt.getSelectedItemId());btnConnect= (Button) findViewById(R.id.btnConnect);btnConnect.setOnClickListener(click);}private Spinner.OnItemSelectedListener select= new Spinner.OnItemSelectedListener() {@Overridepublic void onItemSelected(AdapterView<?> parent, View view, int position, long id) {String[] languages = getResources().getStringArray(R.array.encrypt);Log.e("Encrypt:",languages[position]);}@Overridepublic void onNothingSelected(AdapterView<?> parent) {}};private Button.OnClickListener click =new Button.OnClickListener(){public void onClick(View v){switch (v.getId()){case R.id.btnStop:{sendNum = STOPNUM;recvNum = STOPNUM;break;}case R.id.btnConnect:{SmartlinkInfo info = new SmartlinkInfo();info.ssid=SOTFAPSSID;info.password="wifi1111";info.wlansecurity=3;//获取屏幕上输入的IP\port\ssid等信息strTargetIP= String.valueOf(etTargetIP.getText());strTargetPort= String.valueOf(etTargetPort.getText());strLocalIP= String.valueOf(etLocalIP.getText());strLocalPort= String.valueOf(etLocalPort.getText());strSSID= String.valueOf(etSSID.getText());strPassword = String.valueOf(etPassword.getText());Log.e("Info",strTargetIP+" "+strTargetPort);strSSID= String.valueOf(etSSID.getText());strPassword = String.valueOf(etPassword.getText());try {socket = new DatagramSocket(Integer.parseInt(strTargetPort));socket.setBroadcast(true);addr = InetAddress.getByName(strTargetIP);} catch (UnknownHostException e) {e.printStackTrace();} catch (SocketException e) {e.printStackTrace();}connectWifi(info);break;}default:break;}}};private int connectWifi(SmartlinkInfo info) {WifiAdmin mWifiAdmin = new WifiAdmin(this);mWifiAdmin.openWifi();boolean FOUNDSOFTAP=false;for(int j=0;j<20;j++) {mWifiAdmin.startScan();for (int i = 0; i < mWifiAdmin.getWifiList().size(); i++) {//Log.e("WIFIINFO", i+": " + mWifiAdmin.getWifiList().get(i).SSID);if (mWifiAdmin.getWifiList().get(i).SSID.equals(SOTFAPSSID)) {FOUNDSOFTAP=true;Log.e("WIFIINFO", i+": " + mWifiAdmin.getWifiList().get(i).SSID);Log.e("WIFIINFO", "FOUND SOFTAP.");break;}}if(FOUNDSOFTAP){mWifiAdmin.creatWifiLock();WifiConfiguration config = mWifiAdmin.CreateWifiInfo(info.ssid,info.password,info.wlansecurity);mWifiAdmin.addNetwork(config);mWifiAdmin.releaseWifiLock();break;}}if (context != null) {Log.e("SLEEP","if (context != null)");ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);Log.e("SLEEP"," ConnectivityManager mConnectivityManager = (ConnectivityManager) context");//NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();//Log.e("SLEEP"," NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();");while (mConnectivityManager.getActiveNetworkInfo()==null) {//WIFI_STATE_ENABLING = 2;Log.e("SLEEP", "mNetworkInfo==null sleep 2 s~");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}while (!mConnectivityManager.getActiveNetworkInfo().isConnected()) {//WIFI_STATE_ENABLING = 2;Log.e("SLEEP", "!mNetworkInfo.isConnected() sleep 2 s~");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}Log.e("mNetworkInfo","mNetworkInfo == null");}//打开接收线程Thread recvThread = new Thread(recvRunnable);recvThread.start();//打开发送线程Thread sendThread = new Thread(sendRunnable);sendThread.start();/*mWifiAdmin.creatWifiLock();WifiConfiguration config = mWifiAdmin.CreateWifiInfo(info.ssid,info.password,info.wlansecurity);mWifiAdmin.addNetwork(config);mWifiAdmin.releaseWifiLock();*/return 0;}private Runnable sendRunnable = new Runnable() {@Overridepublic void run() {String strInfo="::SSID::"+strSSID+"::Password::"+ strPassword+"::End::";try {byte[] buffer = strInfo.getBytes();DatagramPacket packet = new DatagramPacket(buffer,buffer.length);packet.setAddress(addr);packet.setPort(Integer.parseInt(strTargetPort));for(sendNum=0;sendNum<60;sendNum++) {socket.send(packet);Thread.sleep(1000);Log.e("Send","Num:"+sendNum+"  "+strInfo);}} catch (Exception e) {e.printStackTrace();Log.e("发送线程",e+"");}/*SmartlinkInfo info = new SmartlinkInfo();info.ssid=strSSID;info.password=strPassword;info.wlansecurity=3;connectWifi(info);*/}};private Runnable recvRunnable = new Runnable() {@Overridepublic void run() {try {byte[] buffer ="XX".getBytes();DatagramPacket packet = new DatagramPacket(buffer,buffer.length);packet.setAddress(addr);packet.setPort(Integer.parseInt(strTargetPort));for(recvNum=0;recvNum<65;recvNum++) {Log.e("recv", "正在接收..."+recvNum );socket.receive(packet);if(new String(packet.getData()).equals("OK")){sendNum = STOPNUM;Log.e("recv", "收到消息:" + new String(packet.getData()));break;}Thread.sleep(1000);}/*MainActivity.this.runOnUiThread(new Runnable() {@Overridepublic void run() {//mTextView.setText("收到消息" + new String(packet.getData()) + ":" + String.valueOf(++index));try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});*///socket.close();} catch (Exception e) {e.printStackTrace();Log.e("接收线程",e+"");}}};
}

手机端界面:

设备端收到手机端发来的广播,解析出wifi密码并返回wifi信息:

softAP配网:用Android手机为linux无屏设备输入wifi密码相关推荐

  1. Unity实现在Android端获取Android手机的唯一ID(设备号)(亲测Android11可用)

    Unity实现在Android端获取Android手机的唯一ID(设备号)(亲测Android11可用) 备注:测试版本Unity2020,理论上Unity2018以上都可用,未做测试 - 文章初衷 ...

  2. linux进程泄露命令明文参数,sshpass 使Linux可以明文参数输入SSH密码(示例代码)

    sshpass 使Linux可以明文参数输入SSH密码 这几天配置一台服务器,在某云平台创建云服务器后,生成了巨长.巨复杂的一串密码,在输入几十次密码后,依然是密码错误.这时候就想如果密码是非交互式输 ...

  3. esp8266 wifi模组手机一键配网,配置一次,下次重启设备后不需再进行配网

    编写时间:2018年9月2日 #define  ATCMDLEN   20 extern uint8_t usart3RecvEndFlag; extern UART_HandleTypeDef hu ...

  4. 你想在旧Android手机上装Linux系统吗?看这里

    如果您对此文感兴趣,说明您应该使用过Linux,并且知道Linux几乎可以在任何硬件上运行.但有时我们也会想,Android不是已经是Linux了吗?为啥还要再安装Linux呢? Linux操作系统实 ...

  5. android手机连接无线路由器上网设置,能连接WIFI但无法上网?教你如何为手机分配固定IP图文教程...

    前天遇到了一个奇怪的问题,手机使用家里面的wif无线网络上网,能连接WIFI无线网络,也能够显示连接成功的信息,在手机的通知栏里面同时也显示了wif连接成功的图标,可怎么就上不了网.不论是登腾讯QQ还 ...

  6. ESP32-C3入门教程 WiFi篇⑨——WiFi配网失败常见问题与解决办法(找不到WiFi AP | WiFi密码错误 | 距离AP过远 RSSI判断)

    文章目录 一.前言 二.WiFi配网的常见问题 三.5GHz WiFi 四.找不到WiFi AP 4.1 App限制WiFi名称的输入 4.2 设备回传 配网失败错误码 五.WiFi密码错误 5.1 ...

  7. Android手机:破解锁屏密码

    Android手机破解锁屏密码很简单,只需要升级为Root用户即可简单的破解. 升级root用户,安装一个Kingroot即可. 破解步骤如下: 手机连接电脑,使用adb shell命令进入ADB环境 ...

  8. 美国人用什么android手机,美国人也爱大屏手机,三星们笑了

    大屏手机推出以来,人们渐渐接受手机的越来越巨大化.随着潮流的变化,当时嫌弃大屏手机的消费者(特别是比较娇小的女生),也都乐于接受手里把持着一个智能巨兽.大屏手机在市场上的火热反应也证明了这一点.不过有 ...

  9. android手机蓝屏代码,电脑通过usb连接多个android手机,出现蓝屏现象

    今天上班带着小米,在两个手机之间倒腾通讯录与名片,后来没电了,拿到手之后没充过.但我插上usb连接线,3秒之后,电脑蓝屏了,当时我的诺基亚E66也在usb充电,我就以为是电压的原因.但事实证明远不是那 ...

最新文章

  1. 转:秒杀系统架构分析与实战
  2. Java 集合类(一)
  3. webpack 安装卸载
  4. python去重语句_Python Dataframe 指定多列去重、求差集的方法
  5. java 图片压缩 base64_图片改变像素,宽高,Base64编码处理
  6. 实现mysql百度式查询_mysql查询优化建议(百度)
  7. C#实现反射调用动态加载的DLL文件中的方法
  8. xshell6左侧导航显示_【iOS12人机交互指南】7.1-导航栏
  9. python四分位数_Python解释数学系列——分位数Quantile
  10. phoenixframework自动化测试平台1.4.6版本发布
  11. 部署mysql主从同步
  12. Harmony OS — RoundProgressBar圆形进度条
  13. matlab数据归一化mapminmax函数
  14. 2020vue面试题汇总
  15. 关于小米手机用微信会重启的问题
  16. 106572050018总是发彩信?实用办法关闭彩信提醒
  17. 简单三个步骤网站建设
  18. 并列关系表合集PPT模板
  19. 微医与友邦中国战略合作,智能医务室为职员健康护航
  20. 基于LCD1602的多功能万年历,温湿度计,非RTC时钟芯片单片机技术

热门文章

  1. Nginx 出现504 Gateway Time-out的解决方法
  2. 使用HTML5的自定义数据属性的jQuery选择器
  3. 如何更改远程Git存储库的URI(URL)?
  4. sshpass连接主机以及执行命令
  5. Java入门(一):Hello World !
  6. 从零开始搭建基于CEFGlue的CB/S的winform项目
  7. 安装sql server 2016 always on配置dtc支持时遇到的问题
  8. ubuntu终端快捷键
  9. Phpcms V9当前栏目及所有二级栏目下内容调用标签
  10. Codeforces Round #278 (Div. 2)