前段時間,公司利用 ESP8266 這個WiFi模組,做了好多小產品。從手機 APP 直連這個 ESP8266 進行通訊,再到實現遠端控制。中間實現過程磕磕碰碰,雖然這方面已經做得非常成熟,但是網上的資料少之又少。現在把實現方式展示出來,同時也算是做一個筆記。
首先這裡要實現的是Android端的APP直連ESP8266進行雙向通訊。
如果想了解Android端的APP遠端連線與ESP8266進行雙向通訊的,實現真正的智慧家居,可以參與這場Chat: 智慧家居遠端控制,實現APP與ESP8266遠端通訊
首先我們來說一下這個ESP8266,這個在淘寶上非常便宜,10塊左右,安信可的產品。這個WiFi模組已經做得非常的成熟,下面介紹一下它的基本使用,首先這個模組有三種模式:
1:STA 模式: ESP8266模組通過路由器連線網際網路,手機或電腦通過網際網路實現對裝置的遠端控制。
2:AP 模式: ESP8266模組作為熱點,實現手機或電腦直接與模組通訊,實現區域網無線控制。
3:STA+AP 模式: 兩種模式的共存模式,即可以通過網際網路控制可實現無縫切換,方便操作。
今天的實現用AP模式就夠了,指令有下面這幾個就夠了:
- 設定wifi模式:AT+CWMODE=2
- 重啟生效:AT+RST
- 啟動多連線:AT+CIPMUX=1
- 建立server:AT+CIPSERVER=1
另外還有非常多的指令可以修改這個模組的引數,甚至還可以修改裡面的程式重新燒錄,更多的詳情就參考安信可的官網。這個就需要電子比較厲害的人才會適合了,我是Android開發的,所以這方面不太瞭解,還望海涵。
這是裝置:
接下來通過串列埠傳送指令開啟ESP8266的WiFi:
傳送完這四個指令之後,開啟手機就可以看到相應的WiFi開啟了(這個WiFi名給我改過):
好了,硬體準備完畢,接下來我們準備APP軟體,針對Android端的。新建一個Android專案,專案結構:
新增一個非同步處理類:
/**
* Created by Layne_Yao on 2017/5/12.
* CSDN:http://blog.csdn.net/Jsagacity
*/
public class SendAsyncTask extends AsyncTask<String, Void, Void> {
//這裡是連線ESP8266的IP和埠號,IP是通過指令在微控制器開發板查詢到,而埠號可以自行設定,也可以使用預設的,333就是預設的
private static final String IP = "192.168.4.1";
private static final int PORT = 333;
private Socket client = null;
private PrintStream out = null;
@Override
protected Void doInBackground(String... params) {
String str = params[0];
try {
client = new Socket(IP, PORT);
client.setSoTimeout(5000);
// 獲取Socket的輸出流,用來傳送資料到服務端
out = new PrintStream(client.getOutputStream());
out.print(str);
out.flush();
if (client == null) {
return null;
} else {
out.close();
client.close();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
複製程式碼
在手機端建立一個作為接受ESP8266傳送的訊息的伺服器:
public class MobileServer implements Runnable {
private ServerSocket server;
private DataInputStream in;
private byte[] receice;
private Handler handler = new Handler();
public MobileServer() {
}
public void setHandler(Handler handler) {
this.handler = handler;
}
@Override
public void run() {
try {
//5000是手機端開啟的伺服器的埠號,ESP8266進行TCP連線時使用的埠,而IP也是通過指令查詢的聯入裝置的IP
server = new ServerSocket(5000);
while (true) {
Socket client = server.accept();
in = new DataInputStream(client.getInputStream());
receice = new byte[50];
in.read(receice);
in.close();
Message message = new Message();
message.what = 1;
message.obj = new String(receice);
handler.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
複製程式碼
佈局檔案:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.itman.connectesp8266.MainActivity" >
<TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="25dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:background="#fe9920"
android:gravity="center"
android:text="接收的內容" />
<Button
android:id="@+id/bt_send"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tv_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="40dp"
android:text="傳送" />
<TextView
android:id="@+id/tv_send_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/bt_send"
android:layout_centerHorizontal="true"
android:layout_marginTop="33dp"
android:text="傳送的內容" />
</RelativeLayout>
複製程式碼
最後是MainActivity:
public class MainActivity extends ActionBarActivity implements OnClickListener {
private TextView tv_content, tv_send_text;
private Button bt_send;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
InitView();
//開啟伺服器
MobileServer mobileServer = new MobileServer();
mobileServer.setHandler(handler);
new Thread(mobileServer).start();
}
private void InitView() {
tv_content = (TextView) findViewById(R.id.tv_content);
tv_send_text = (TextView) findViewById(R.id.tv_send_text);
bt_send = (Button) findViewById(R.id.bt_send);
bt_send.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_send:
String str = "Sent to the ESP8266";
new SendAsyncTask().execute(str);
tv_send_text.setText(str);
break;
}
}
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
tv_content.setText("WiFi模組傳送的:" + msg.obj);
Toast.makeText(MainActivity.this, "接收到資訊", Toast.LENGTH_LONG)
.show();
}
}
};
}
複製程式碼
最後不要忘了新增網路許可權:
<uses-permission android:name="android.permission.INTERNET"/>
複製程式碼
執行到真機,確保手機連線上ESP8266的WiFi,就可以進行手機傳送資訊到ESP8266了。手機APP傳送過去的:
ESP8266接收到的:接下來是ESP8266傳送資料到APP。首先ESP要使用到的指令有:
- 建立TCP連線:AT+CIPSTART=0,"TCP","192.168.4.2",5000
- 確定傳送資料的長度:AT+CIPSEND=0,19
- 傳送資訊:Sent to the Android
操作指令:
APP端接受到的資訊:
以上是簡單的實現APP和ESP8266直連通訊的實現。如果想要實現遠端控制,過程是比較繁雜的,但是並不複雜。
這裡只簡單的說明一下大致的實現方式:
1、要實現遠端控制就必須得租用一個伺服器,當然自己電腦也可以作為伺服器,就是需要配置。最簡單的方式是租用雲伺服器,比如阿里雲的ECS,如果是學生,還有學生價。
2、接下來是最麻煩的步驟:
1)手機發資料到雲伺服器,這個不用多說了,使用json資料的網路通訊;
2)接著就是雲伺服器繼續把手機傳送過來的轉發的ESP8266,而云伺服器和ESP8266之間的通訊是需要使用TCP長連線的。因為ESP8266這邊的IP是會變化的所以只能使用長連線;
3)ESP8266發資料到雲伺服器就不用再多說了,就第2點中的長連線。但是雲伺服器怎麼推送資料到APP呢?答案也是長連線的,這裡可以使用別人整合好的框架mina。
以上就是遠端控制的大致過程要點,想要實現就各自去完成了。當初我還是在別的平臺問人問到的實現方案,網上根本沒有相應的資料,或者是方案。以上的實現方案雖然有點繁雜,但是並不複雜,慢慢實現是沒有很大難度的。