最近公司內部有個需求,需要開發一個 Android app 裝在我們的機器上,讓它可以透過 UART port 跟機器溝通。過程基本上不難,但還挺繁雜的,乾脆做個紀錄以便日後查詢。
我的開發環境是 Windows 10 搭配 Android Studio 4.1.2
照著做
去 Settings
裡頭,確認 NDK
跟 CMake
都有安裝了。
建立一個新專案,然後選擇 New -> Folder -> JNI Folder
,我們要透過 JNI 的方式調用 C 程式碼跟 UART 溝通。
下載這個壓縮檔,解開之後有五個檔案,把它們放到剛剛建立的 JNI 目錄裡頭。
在 java
目錄按右鍵,選擇 New -> Package
,建立一個名叫 android_serialport_api
的 package。注意:一定要叫這個名字。
在剛剛建立的 package 裡頭建立一個 Java Class
,名字要叫做 SerialPort
。
這時候應該產生一個 SerialPort.java
了,用這個檔案替換它的內容。
在 app
目錄按右鍵,選擇 Link C++ Project with Gradle
。
此時會跳出一個對話框,Build System
選擇 ndk-build
,Project Path
選擇剛剛放到 JNI 目錄的 Android.mk
。
選好之後就可以 Build -> Make Project
,一切順利的話會產出 libserial_port.so
。
做為測試用的 app,我們的 MainActivity.java
可以這樣寫,記得把 UART port 的路徑以及 baud rate 改成你的環境:
import android_serialport_api.SerialPort;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
protected SerialPort mSerialPort;
protected InputStream mInputStream;
protected OutputStream mOutputStream;
private ReadThread mReadThread;
private class ReadThread extends Thread {
@Override
public void run() {
super.run();
Log.e(TAG, "Read thread is open.");
while (!isInterrupted()) {
if (mInputStream == null)
return;
try {
byte[] buffer = new byte[64];
int size = mInputStream.read(buffer);
if (size > 0) {
onDataReceived(buffer, size);
}
} catch (IOException e) {
e.printStackTrace();
return;
}
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
Log.e(TAG, "Try to open serial port");
mSerialPort = new SerialPort(new File("/dev/ttyXXX"), 115200, 0);
mInputStream = mSerialPort.getInputStream();
mOutputStream = mSerialPort.getOutputStream();
Log.e(TAG, "Serial port opened!");
mReadThread = new ReadThread();
mReadThread.start();
} catch (SecurityException e) {
Log.e(TAG, "Security Exception");
e.printStackTrace();
} catch (IOException e) {
Log.e(TAG, "Fail to open serial port");
e.printStackTrace();
}
try {
mOutputStream.write("help".getBytes());
mOutputStream.write('\n');
} catch (IOException e) {
Log.e(TAG, "Fail to send out message");
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
Log.d(TAG, "in onDestroy()");
if (mReadThread != null) {
mReadThread.interrupt();
}
if (mSerialPort != null) {
mSerialPort.close();
mSerialPort = null;
try {
mOutputStream.close();
mOutputStream = null;
mInputStream.close();
mInputStream = null;
} catch (IOException e) {
e.printStackTrace();
}
}
super.onDestroy();
}
protected void onDataReceived(final byte[] buffer, final int size) {
runOnUiThread(() -> {
String receivedMessage = new String(buffer, 0, size);
Log.d(TAG, "Received message: " + receivedMessage);
});
}
}
我們可以試試看 Build -> Clean Project
然後 Build -> Rebuild Project
,確認一切都正常,沒事的話就可以跑在機器上了。
問題排解
Q:Compile 失敗了
A:確認剛剛建立的 packange 名稱是 android_serialport_api
,以及建立的 class 名稱是 SerialPort.java
。
Q:照做了還是無法通訊?
A:確認 Android 跟 port 是可以通的,有可能根本不能存取那個 port。
Q:確認過了還是無法通訊?
A:確認路徑跟 baud rate 是對的。
Q:確認過路徑跟 baud rate 了,還是無法通訊?
A:可能是權限問題,執行以下命令再試試看。
adb shell
su
chmod 777 /dev/ttyXXX
setenforce 0
Q:如果還是不行呢?
A:換一台硬體試試看。