在工科類項目中,嵌入式系統與軟件系統或后台數據庫之間的信息傳輸是實現“物聯網”的一種必要的途徑,對已簡單概念的物聯網,通常形式都是一個單片機/嵌入式系統實現數據的采集及其處理,通過藍牙,wifi或者是ZigBee等無線模塊進行傳輸,再由一些軟件端來顯示數據實現人機交互。
例如在進行的一個項目中,需要在stm32上獲取位置的信息,再傳輸到移動設備或者電腦端來顯示數據,選用wifi來作為傳輸媒介,那么就要考慮wifi間數據傳輸的形式——TCP或者UDP傳輸。
簡單記錄一下在實際開發中,利用Android平台下TCP/IP協議來實現與搭載WIFI模塊的硬件系統進行通信的程序設計與實現。
設計一個界面,三個EditText,兩個Button,還有一個用於顯示的TextView;整體使用LinearLayout的布局(個人喜好,結構清晰)
程序如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:gravity="center" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <EditText android:hint="IP地址" android:id="@+id/IPAddress" android:layout_weight="1" android:layout_width="match_parent" android:layout_height="wrap_content" /> <EditText android:hint="端口號" android:id="@+id/port" android:layout_weight="2" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/connect" android:text="開始連接" /> <EditText android:hint="輸入需要發送的信息" android:id="@+id/sendData" android:layout_width="match_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/send" android:text="發送數據" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:background="#eeeeee" android:id="@+id/information" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
實現一個大致的界面,如下所示:
接下來是功能實現部分,在TCP/IP協議的網絡連接,需要兩個主要信息:IP地址,以及端口號。
在JAVA環境下,TCP連接是相對簡潔的,這也是JAVA的一個優點:
Socket socket=new Socket(IP_Address,Port);
一句簡單的話就可以實現網絡連接。
還需要一個BufferedReader和一個PrintWriter來實現數據的傳輸,當Socket操作完成之后,對其進行設定:
mBufferedReaderClient = new BufferedReader(new InputStreamReader(mSocket.getInputStream())); mPrintWriterClient = new PrintWriter(mSocket.getOutputStream(),true);
然后開辟其它線程來實現數據的傳輸以及一些視圖的更新。
java部分代碼如下:
package com.example.hp.acceleration; import android.os.Handler; import android.os.Message; import android.os.StrictMode; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.text.method.ScrollingMovementMethod; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; public class MainActivity extends AppCompatActivity { private Button mButtonConnect; private Button mButtonSend; private TextView mTextViewMessage; private boolean isConnect; private String Information; private EditText mEditTextIP; private EditText mEditTextPort; private EditText mEditTextSendData; private Socket mSocket=null; private BufferedReader mBufferedReaderClient=null; private PrintWriter mPrintWriterClient=null; private String IP=""; private int port; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads()//磁盤讀取操作 .detectDiskWrites()//磁盤寫入操作 .detectNetwork()//網絡操作 .penaltyLog()//在Logcat中打印違規異常信息 .build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects()//泄露的SqLite對象 .penaltyLog() .penaltyDeath() .build()); mButtonConnect=(Button)findViewById(R.id.connect); mButtonSend=(Button)findViewById(R.id.send); mTextViewMessage=(TextView)findViewById(R.id.information); mEditTextIP=(EditText)findViewById(R.id.IPAddress); mEditTextPort=(EditText)findViewById(R.id.port); mEditTextSendData=(EditText)findViewById(R.id.sendData); mTextViewMessage.setMovementMethod(ScrollingMovementMethod.getInstance()); mTextViewMessage.setTextIsSelectable(true); isConnect=false; mButtonSend.setEnabled(false); mButtonConnect.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (isConnect) { isConnect=false; mButtonConnect.setText("開始連接"); mButtonSend.setEnabled(false); mTextViewMessage.setText(""); if(mSocket!=null) { try { mSocket.close(); } catch (IOException e) { } } }else{ isConnect=true; mButtonConnect.setText("斷開連接"); mButtonSend.setEnabled(true); IP=mEditTextIP.getText().toString(); String portString=mEditTextPort.getText().toString(); if (portString.length()>1){ port=Integer.valueOf(portString); } Thread thread=new Thread(new Runnable() { @Override public void run() { try { mSocket=new Socket(IP,port); mBufferedReaderClient = new BufferedReader(new InputStreamReader (mSocket.getInputStream())); mPrintWriterClient = new PrintWriter(mSocket.getOutputStream(), true); Message msg=new Message(); msg.what=0; mHandler.sendMessage(msg); }catch (IOException e) { mTextViewMessage.setText("error"); } char[] buffer=new char[256]; int num=0; while (isConnect) { try{ if ((num=mBufferedReaderClient.read(buffer))>0) { Information=getInfoBuff(buffer,num); Message msg=new Message(); msg.what=1; mHandler.sendMessage(msg); } }catch (Exception e) { } } } }); if (IP.length()>1&&portString.length()>=1) { thread.start(); }else { mTextViewMessage.setText("IP地址錯誤或端口號錯誤"); } } } }); mButtonSend.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Thread thread=new Thread(new Runnable() { @Override public void run() { try { String string=mEditTextSendData.getText().toString(); mPrintWriterClient.print(string); mPrintWriterClient.flush(); }catch (Exception e){ } } }); thread.start(); } }); } private String getInfoBuff(char[] buff,int count){ char[] temp=new char[count]; for(int i=0;i<count;i++){ temp[i]=buff[i]; } return new String(temp); } Handler mHandler=new Handler(){ public void handleMessage(Message msg){ super.handleMessage(msg); if (msg.what==0){ Toast.makeText(MainActivity.this,"連接成功!",Toast.LENGTH_SHORT).show(); } else if (msg.what==1){ mTextViewMessage.append(Information+"\r\n"); } } }; }
最后一點,在Android環境下,需要在AndroidManifest.xml文件下賦予權限
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
以上幾個權限是Android使用wifi進行TCP協議下通訊必須的幾個。