티스토리 뷰

아두이노와 안드로이드간의 통신을 하기위해서 OTG케이블을 이용하여 USB-시리얼 통신을 하려고 한다.


아두이노-외부기기 연결방법 참조: http://www.hardcopyworld.com/ngine/aduino/index.php/archives/817


OTG케이블을 이용하여 스마트폰을 Host로 만들고 아두이노를 Client로 만들어서, PC-아두이노와 같은 원리로 스마트폰 과 아두이노의 usb통신이 가능하게 한다. Serial 통신을 할때 D0, D1 핀으로 하는데, 이 Serial 통신을 USB통신으로 변환하기 위해서 FTDI칩이 필요하다.(UNO, Nano 등의 보드는 FTDI칩을 탑재하고 있지만, Pro Mini 보드는 이 칩을 탑재하지 않기에 별도의 FTDI 모듈을 달아줘야한다.)


USB로 상호간에 연결이 되면 아두이노에선 Serial 클래스에 정의된 함스들로 데이터를 전송 및 수신이 가능하다.

[흔히 Serial.available(), Serial.print(), Serial.println(), Serial.read(), Serial.write() 함수 사용]


Android USB Host 참조: http://www.hardroid.net/archives/194


Android USB Host 모드는 안드로이드 3.1이상에서 지원하며, Host 안드로이드 장비는 USB host로서의 역할을 수행하고 전력을 공급하며 연결된 USB 장비를 열거한다.


참조한 프로젝트: https://github.com/mik3y/usb-serial-for-android


API preview:

android.hardware.usb package에 있는 주요 클래스들을 살펴보자.



  • UsbManager: 유저에게 연결된 usb devices를 열거하고 통신하는 것을 허락하는 클래스
  • UsbDevice: 연결된 usb devices를 나타내고 그 디바이스들의 identifying 정보, 인터페이스, endpoints에 접근하는 메소드를 포함한다.
  • UsbInterface: a usb device의 인터페이스를 나타내는데, 그 인터페이스는 그 디바이스를 위한 펑셔날리티의 집합을 정의한다. 디바이스는 통신을 하기위해 하나 혹은 그이상의 인터페이스들을 가질 수 있다.
  • UsbEndpoint: 인터페이스를 위한 통신 채널인 interface endpoint를 나타낸다. interface는 하나 혹은 그이상의 endpoint를 가질 수 있고, 디바이스와의 양방향 통신을 위해 보통 input, output endpoints를 가진다.
  • UsbDeviceConnection: endpoints에서 데이터를 전송하는 디바이스로의 연결을 나타낸다. 이 클래스는 유저에게 동기식, 비동기식으로 데이터의 송수신을 허락한다.
  • UsbRequest: UsbDeviceConnection을 통한 디바이스와의 통신하기 위한 비동기 리퀘스트를 나타낸다.
  • UsbConstants: 리눅스 커널의 linux/usb/ch9.h에서 definition에 응답하는 USB constants를 정의한다.
대부분의 상황에서, a USB device와 통신할 때 당신은 이 모든 클래스를 쓸 필요가 있다.(UsbRequest는 너가 비동기 통신을 할때만 필요하다.) 일반적으로 당신은 원하는 UsbDevice를 되찾기 위해 UsbManager를 얻어야 한다. 당신이 device를 가지고 잇을때, 당신은 적절한 UsbInterface와 통신하는 저 interface의 UsbEndpoint를 찾을 필요가 있다. 일단 당신이 올바른 endpoint를 얻게 되면, the USB device와 통신하기 위한 UsbDeviceConnection이 열린다.



메니페스트 설정:








XML 자원파일(해당 프로젝트에선 device_filter.xml)에서 <usb-device> element의 attribute로 usb연결을 수신하기 위한 device의 정보를 입력한다. 일반적으로, 특정 디바이스를 필터링하고 싶을 때 vvendor와 product ID를 사용한다. 그리고 만약 대형 저장 장치나 디지털 카메라와 같은 usb 장치들을 필터링하고 싶으면 class, subclass, 그리고 protocol을 사용한다. 당신은 아무것도 specify하지않을 수도 있고, 모든 attribute를 specify할 수도 있다.

아무 애트리뷰트도 specifying하지 않는 것은 모든 usb 디바이스를 match시킨다. 그렇기 때문에 오직 애플리케이션이 원할때만 이렇게 해야한다.


  • vendor-id
  • product-id
  • class
  • subclass
  • protocol (device or interface)

해당 자원 파일은 res/xml/ 디렉토리에 저장하라. 이 자원 파일의 이름은 Manifest.xml의 <meta-data> element에 기술한 것과 같은 이름이여야한다.

- example

<?xml version="1.0" encoding="utf-8"?>

<resources>
   
<usb-device vendor-id="1234" product-id="5678" class="255" subclass="66" protocol="1" />
</resources>


장비와의 작업:
usb 디바이스를 안드로이드 디바이스에 연결할때, 안드로이드 시스템은 애플리케이션이 해당 usb 디바이스에 관심있는지 구분한다.  만약 연결을 희망한다면 당신을 통신을 할 수 있다. 통신을 위하여 애플리케이션은 몇가지 작업을 해야만 한다.

  1. 사용자가 usb 디바이스를 연결할때 알림을 받기위한 intent-filter를 이용함으로써, 혹은 이미 연결된 usb 디바이스들을 열거함으로써, 연결된 usb 디바이스를 찾아야만 한다.
  2. 만일 usb 디바이스에 연결하기 위한 permission을 획득하지 않았다면 user에게 물어야한다.
  3. 적절한 interface endpoint에서 데이터를 읽고,씀으로써 usb 디바이스와 통신해야만 한다.

장비 찾기:

당신의 애플리케이션은 유저가 디바이스를 연결할때 알림을 받기위해 intent filter를 통하여 USB device를 찾을 수 있거나, 이미 연결된 USB device들을 열거함으로써 USB device를 찾을 수 있다. intent filter를 사용하는 것은 만약 당신이 당신의 애플리케이션이 자동으로 원하는 디바이스를 찾을 수 있길 원한다면 도움이 된다. 만약 당신이 연결된 장치들을 모두 얻길 원하거나, 당신의 애플리케이션이 intent를 필터링하지 않을때, 연결된 USB devices를 열거하는 것이 도움이 된다.


-intent filter 사용하기:


당신의 애플리케이션이 특정한 USB device를 찾도록 하기 위해서, 당신은 android.hardware.usb.action.USB_DEVICE_ATTACHED intent를 필터링하도록 intent filter를 명시해야한다. 이 intent filter와 함께, 당신은 product id나 vendor id와 같은 USB device의 리소스 파일을 명시할 필요가 있다. 유저가 당신의 device filter와 부합하는 장치를 연결하려 할 때, 시스템은 그들이 당신의 애플리케이션을 시작하길 원하는지에 대한 문구와 함께 그들을 나타낸다. 유저가 받아들인다면, 그 디바이스의 연결이 해제될때까지 당신의 애플리케이션을 자동적으로 그 디바이스에 접근할 권한을 얻는다.


intent filter 예제 코드

<activity ...>
...
   
<intent-filter>
       
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
   
</intent-filter>

   
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
       
android:resource="@xml/device_filter" />
</activity>


다음은 당신이 연결하길 원하는 USB devices를 명시하는 리소스 파일을 선언하는 방법이다.

<?xml version="1.0" encoding="utf-8"?>

<resources>
   
<usb-device vendor-id="1234" product-id="5678" />
</resources>


당신의 Activity에서,  당신은 intent로부터 이와 같이 연결된 디바이스를 나타내는 UsbDevice를 얻을 수 있다.

UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);


-장비 열거하기:


만약 당신의 애플리케이션이 실행되는 동안 현재 연결된 모든 USB devices를 조사하길 원한다면, 애플리케이션을 bus위에서 디바이스를 열거할 수 있다. 연결된 모든 USB devices의 해시맵을 얻기 위해 getDeviceList()메서드를 사용하라. 


만약 그 해시맵으로 부터 당신이 디바이스를 얻길 원한다면, USB device의 이름에 의해 해시맵에 정보가 입력된다.

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
...  
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
UsbDevice device = deviceList.get("deviceName");


만약 원한다면, 당신은 또한 해시맵으로부터 iterator를 얻을 수 있고 하나씩 해당 디바이스를 다룰 수 있다.

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
...
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
while(deviceIterator.hasNext()){
   
UsbDevice device = deviceIterator.next();
   
//your code
}


디바이스와 통신하기위한 퍼미션 획득하기:

USB 디바이스와 통신을 하기전에, 당신의 애플리케이션은 사용자로부터 퍼미션을 받아야만 한다.


Note: 만약 당신의 애플리케이션이 USB device와 연결됨으로써 그 디바이스를 찾기위해 intent filter를 사용한다면,  유저가 당신의 애플리케이션이 그 intent를 조작하도록 허락할때, 애플리케이션은 자동적으로 퍼미션을 받는다.

만약 허락하지 않는다면,  당신은 디바이스르 연결하기전에 명확하게 퍼미션을 요청해야만 한다.


명확하게 허락을 구하는 것은 몇가지 상황에선 필수적일 수 있다. 그 상황은 예를 들어 당신의 애플리케이션이 이미 연결된 usb device를 열거하거나, 연결된 디바이스와 통신을 원할때이다. 당신은 디바이스와 통신을 시도하기 전에 해당 디바이스를 접근하기위한 퍼미션을 얻어야만 한다. 만약 그러지 않는다면, 유저가 디바이스에 접근하기 위한 퍼미션을 거절할 때 당신은 런타임 에러를 얻을 것이다.


명확하게 퍼미션을 얻기위해서, 우선 broadcast receiver를 생성해야한다. 이 receiver는 당신이 requestPermission()를 호출할 때 broadcast를 얻는 intent에 귀를 기울이고 있는다. requestPermission()의 호출은 유저에게 디바이스에 연결을 위한 퍼미션을 요청하는 문구를 보여준다. 다음의 샘플 코드는 broadcast receiver를 생성하는 방법을 보여준다.


private static final String ACTION_USB_PERMISSION =
   
"com.android.example.USB_PERMISSION";
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {

   
public void onReceive(Context context, Intent intent) {
       
String action = intent.getAction();
       
if (ACTION_USB_PERMISSION.equals(action)) {
           
synchronized (this) {
               
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

               
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                   
if(device != null){
                     
//call method to set up device communication
                   
}
               
}
               
else {
                   
Log.d(TAG, "permission denied for device " + device);
               
}
           
}
       
}
   
}
};


broadcast receiver를 등록하기 위해, 당신의 activity의 onCreate()에 다음의 코드를 추가하라.

UsbManager mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
private static final String ACTION_USB_PERMISSION =
   
"com.android.example.USB_PERMISSION";
...
mPermissionIntent
= PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver
(mUsbReceiver, filter);


디바이스에 연결하기 위한 퍼미션을 유저에게 묻는 문구를 보여주기 위해, requestPermission()메서드를 호출하라.

UsbDevice device;
...
mUsbManager
.requestPermission(device, mPermissionIntent);

유저가 문구에 응답할 때, 당신의 broadcast receiver는 EXTRA_PERMISSION_GRANTED extra를 포함하는 intent를 받는다. EXTRA_PERMISSION_GRANTED는 boolean의 표헌하는 대답이다. 디바이스를 연결하기전에, 이 extra가 true 값인지 확인하라.



디바이스와 통신하기:

USB device와의 통신은 동기적, 혹은 비동기적일 수 있다. 이 두가지케이스 모두에서, 당신은 새로운 스레드를 생성해야한다. 이 스레드는 모든 데이터 전송을 맡고, 그로인해 당신이 UI 스레드를 막지 않도록 한다. 디바이스와의 통신을 적절히 구축하기 위해서, 당신은 당신이 통신하고 싶어하고, UsbDeviceConnection와 함께 이 endpoint에서 요청을 보내고 싶어하는 "디바이스"와의 적절한 UsbInterface와  UsbEndpoint를 얻을 필요가 있다. 


일반적으로, 당신의 코드는:

  •  당신이 이 디바이스와 통신하고 싶어하는지 아닌지 파악하기위해 UsbDevice 오브젝트의 애트리뷰트들; product ID, vendor Id, 혹은 device class와 같은 애트리뷰트를 확인 해야한다. 
  • 당신이 이 디바이스와 통신을 하고 싶어하는 것이 확실하다면, 적절한 UsbInterface을 찾아야만한다. 이 UsbInterface는 당신이 이 interface의 적절한 UsbEndpoint와 함께 통신하기 위해 사용을 원하는 UsbInterface이다. Interfaces는 하나, 혹은 그 이상의 endpoint를 가질 수 있으며, 흔히 양방향 통신을 위해 입력, 출력 endpoint를 가진다.
  • 당신이 적절한 endpoint를 찾았을 때, 저 endpoint에서 UsbDeviceConnection 열어야만 한다.
  • bulkTransfer() 나 controlTransfer() 메서드와 함께 endpoint에서 당신이 전송하길 원하는 데이터를 제공해야만 한다. 당신은 메인 UI 스레드의 blocking을 막기 위해 다른 스레드에서 이 작업을 수행해야만 한다. 스레드를 사용하는 것에 대한 더 많은 정보를 얻고 싶다면, Processes and Threads를 참조하라.

다음의 코드는 동기식 데이터 전송을 하기 위한 간단한 방법이다. 당신의 코드는 통신하기 위한 올바른 interface와 endpoint를 올바르게 찾기위해 더 많은 로직을 가져야 한다. 또한 당신의 코드는 main UI 스레드말고, 다른 스레드하에서 어느 데이터 전송도 해야한다.
private Byte[] bytes;
private static int TIMEOUT = 0;
private boolean forceClaim = true;

...

UsbInterface intf = device.getInterface(0);
UsbEndpoint endpoint = intf.getEndpoint(0);
UsbDeviceConnection connection = mUsbManager.openDevice(device);
connection
.claimInterface(intf, forceClaim);
connection
.bulkTransfer(endpoint, bytes, bytes.length, TIMEOUT); //do in another thread
비동기식으로 데이터를 보내기 위해선,initialize (초기화하기 위해), 그리고 비동기 요청을 queue (정렬시키기 위해) UsbRequest클래스를 사용해라. 그리고, requestWait()와 함께 결과를 기다려라.


디바이스와의 통신 종료하기:


디바이스와의 통신이 끝났거나, 디바이스의 연결이 끊겼을 때, releaseInterface()와  close()를 호출함으로써 UsbInterface 와 UsbDeviceConnection를 닫아라. 연결이 끊긴 사건을 파악하기 위해서, 아래와 같은 broadcast receiver를 생성하라.


BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
   
public void onReceive(Context context, Intent intent) {
       
String action = intent.getAction();

     
if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
           
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
           
if (device != null) {
               
// call your method that cleans up and closes communication with the device
           
}
       
}
   
}
};

애플리케이션에서 broadcast receiver의 생성(manifest가 아닌)은 당신의 애플리케이션이 오직 실행될 때 디바이스의 연결이 해제되는 사건을 조작하도록 허락한다. 연결이 해제되는 사건은 오직 현재 실행중인 애플리케이션에만 전달된다.

















댓글
  • 프로필사진 루렘 안드로이드와 usb카메라를 연동하려고 하는데요 usb카메라 snapshot 버튼을 눌러 저장하려고 하는데 usb 입력신호를 받아 와서 처리하려면 usb카메라 mcu 정보나 기타 다른 하드웨어 구성도같은게 필요 할까요 ?? 2016.04.22 17:19 신고
  • 프로필사진 DevES 음, 저도 안해봐서 잘은 모르겠지만, usb 카메라 모듈의 제조사에서 제공하는 통신 라이브러리같은 것은 없을까요?
    저 같은 경우는 아두이노와 안드로이드간의 통신을 통해서 다른 모듈을 제어하였는데, 안드로이드와 모듈을 직접적으로 통신하는 것은 잘 모르겠네요..
    2016.05.03 21:43 신고
  • 프로필사진 익명 endpoint를 이용하여 값 주고 받는 부분 좀더 자세하게 포스팅 요청 가능한가요 ㅠㅠ 거기만 못하고 있어서.. 2017.03.22 13:58 신고
댓글쓰기 폼
공지사항
Total
72,445
Today
86
Yesterday
119
TAG more
«   2018/08   »
      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  
글 보관함