아두이노와 안드로이드의 유선 연결은 크게 두 가지 방법이 있습니다.
- 안드로이드의 USB Host 기능을 이용한 시리얼 통신 방법
- ADK(Accessory Development Kit)을 이용한 연결 방법
그런데 2번 ADK 의 경우는 아두이노, 안드로이드 설정/조건이 까다롭고, 비싸고, 복잡한 관계로 다음에 분석하기로 하고(잘 모르기도 하고…) 여기서는 가장 간단한 방법인 1번 USB Host 기능을 이용한 시리얼 통신 방법을 설명하겠습니다. 1번 방법은 안드로이드가 PC 역할을 하는 방법인데 OTG 케이블과 USB 케이블만 있으면 쉽게 아두이노와 통신이 가능합니다.
위 사진처럼 1번 방법은 (안드로이드 – OTG 케이블 – USB 케이블 – 아두이노) 순서로 연결됩니다. 안드로이드의 USB Host 기능을 이용하는 방법인데, 안드로이드 기기가 PC처럼 USB Host 기능을 해주는 것입니다. 이 기능이 아마 허니컴 버전 이상에서만 지원 될 겁니다.(제조사별로 차이가 있을듯 합니다.) 휴대폰과 연동하신다면 4.0 버전 ICS 이상에서 해보시길 권장합니다.
1. 아두이노
아두이노를 PC와 연결해서 시리얼 통신하는 것과 마찬가지로 USB 케이블과 OTG를 통해 안드로이드에 연결됩니다. 따라서 아두이노에서는 스케치를 작성할 때 시스템에서 기본으로 제공하는 Serial 통신 함수인 read(), write(), print(), println() 을 그대로 사용하면 됩니다.
다만 이때 사용되는 버퍼가 1byte 배열임을 고려해야 합니다. 아두이노의 int 사이즈가 2byte 이므로 255 이상의 값을 송수신 한다면 어떻게 보내고 어떻게 받을지에 대한 적절한 고려가 필요합니다. (특히 음수나 실수의 경우)
2. 안드로이드
편의를 위해 안드로이드 앱을 미리 작성해 두었습니다. 안드로이드 앱은 다은과 같은 기능을 지원합니다.
- a0.7823z 처럼 a~z 로 감싸여진 문자가 들어오면 UI 상단에 큰 폰트로 표시해 줍니다.
예) a0.7823z 수신 ==> 0.7823 을 표시 - 그 외의 데이터는 표시되지 않습니다. 더하고 싶은 기능이 있는 경우 소스코드를 직접 수정하셔야 합니다.
- 하단의 4개의 버튼을 누를 경우 b1, b2, b3, b4 문자가 연결된 장치로 전송됩니다.
앱을 사용하는 방법은 아래와 같습니다. GitHub에서 예제로 만든 프로젝트를 다운로드 합니다.
https://github.com/godstale/Arduino-Serial-Controller
아래 링크에서 안드로이드 Host 연결을 지원하는 드라이버 소스코드를 다운로드 합니다. 둘 중 하나를 받으세요.
- https://github.com/mik3y/usb-serial-for-android (official)
- https://github.com/andreasb242/usb-serial-for-android (CH34x 칩 추가 지원)
소스코드를 모두 받으면 아래 순서로 빌드해 볼 수 있습니다.
- usb-serial-for-android 드라이버 소스코드를 적당한 곳에 압축을 풉니다.
- Eclipse 에서 Arduino Serial Controller 안드로이드 프로젝트를 불러옵니다.
- usb-serial-for-android 드라이버를 프로젝트에 넣어줘야 합니다. 프로젝트 이름에서 마우스 우클릭 후 [Build Path – Link source]를 선택합니다. 그리고 드라이버 소스코드가 있는 경로를 지정해 줍니다. 이때 드라이버 소스코드 폴더 안에 있는 아래 폴더를 선택해 줍니다.
[ Android\usbSerialForAndroid\src\main\java ] - 빌드를 해보고 에러가 없는지 확인합니다.
- 앱을 실행해서 안드로이드 폰에서 확인합니다.
소스 전체를 보기는 힘드니 중요한 부분만 보도록 하겠습니다.
USB 장치가 OTG 케이블을 통해 연결되면 vendor-id와 product-id에 따라서 이 장치와 연결될 앱을 설정하는 부분이 있습니다. [res / xml / device_filter.xml] 파일에 기록된 아래와 같은 내용이 이런 역할을 합니다.
- …
- <usb-device vendor-id=”9025″/>
- …
하지만 device filter 는 우리가 처리할 usb device 목록일 뿐이고 앱이 이걸 처리할 수 있도록 연결해 주는 건 AndroidManifest.xml 파일입니다. 이 파일안에 아래 intent-filter 설정과 uses-feature 설정이 들어가야 합니다.
<!— 이 부분이 추가되는 부분 –>
<uses-feature android:name=”android.hardware.usb.host” />
<!— 요기까지 –>
<activity
android:name=”com.mlab.aduinocontroller.AduinoControllerActivity”
android:label=”@string/app_name” >
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
<!— 이 부분이 추가되는 부분 –>
<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>
이 단계에서 문제가 있다면 앱을 실행하고 아두이노를 연결한 다음 디바이스 목록을 얻어오고 아두이노 디바이스를 open 하는 단계에서 퍼미션 에러가 나거나 exception 이 발생합니다.
여기까지 확인 되었으면 이제 실제 USB 장치와 통신할 수 있도록 port를 생성하고 컨트롤하는 부분을 보겠습니다. USB 장치와 통신하기 위한 준비과정과 통신을 도와주는 함수는 모두 SerialConnector.java 클래스에 들어 있습니다.
SerialConnector 클래스에 initialize() 함수가 있습니다. 여기서 USB 장치가 연결되면 FTDI 모듈에 맞는 드라이버를 통해 통신할 준비를 합니다. 불필요한 부분들을 지우고 보면 아래와 같은 순서로 진행됩니다.
UsbManager manager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE); List<UsbSerialDriver> availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager); mDriver = availableDrivers.get(0); UsbDeviceConnection connection = manager.openDevice(device); mPort = mDriver.getPorts().get(0); try { mPort.open(connection); mPort.setParameters(9600, 0, 0, 0); // baudrate, dataBits, stopBits, parity } catch (IOException e) { } // Everything is fine. Start serial monitoring thread. startThread();
UsbSerialPort 인스턴스인 mPort 를 눈여겨 보세요. 연결이 제대로 설정되면 이후부터 데이터를 주고 받는 작업은 모두 mPort를 이용해서 이루어집니다. 마지막에 startThread() 함수가 호출되면서 SerialMonitorThread 가 실행됩니다. 이 thread는 앱이 켜져있는 동안 데이터가 들어온게 있는지 확인하는 작업을 무한 반복 합니다. SerialMonitorThread 의 run() 을 보시면 데이터가 들어온 경우 UI에 들어온 데이터를 보내주도록 작성되어 있습니다.
반대로 데이터를 연결된 장치로 보낼 때는 필요할 때 sendCommand() 만 호출해주면 됩니다.
mPort.write(cmd.getBytes(), cmd.length()); // Send to remote device
안드로이드에 아두이노가 연결되어 있다면 아두이노에서 데이터가 오는지 계속 체크하고 있겠죠.
주의!! usb-serial-for-android 는 FT232R, ATmega8u2, CH34x 등의 FTDI 모듈을 지원하는 것으로 되어있지만 실제 연결해보면 에러가 발생하는 경우도 많습니다. 각자가 사용하는 FTDI 모듈에 따라 연결되지 않을 수도 있으며 이 경우 직접 디버깅을 하셔야 합니다.
[…] 예제는 http://www.hardcopyworld.com/ngine/aduino/index.php/archives/241 […]
[…] 개인적인 생각입니다만 근래에는 블루투스를 이용한 무선 연결을 통해 안드로이드나 iOS와의 연동을 많이 사용하고 그 편이 HW/SW 적으로 간단하므로 굳이 ADK 를 이용할 필요는 없어 보입니다. ADK 관련 자료도 찾기가 좀 어렵고 사용법도 복잡하더군요. ADK에 대해 미리 사전조사를 하고 블루투스나 WiFi 와의 특징 비교를 해보신 후 Mega ADK 구입을 고려하세요. 우리나라에서는 잘 안팔리는 보드들의 가격이 상당히 높게 책정되는 경향이 있어 구입할 때 가격면에서도 불리합니다. 만약 안드로이드 폰이 4.0 이상의 버전이라면 일반적인 UNO, NANO 보드에서도 OTG 케이블을 이용해 USB 연결 및 통신이 가능합니다. http://www.hardcopyworld.com/ngine/aduino/index.php/archives/241 […]
좋은 설명 고맙습니다. 소스 다운받아 테스트 중입니다.
한가지 궁금한 점은 아래 소스에서 ‘…중지되었습니다’라고 죽는 현상이 있습니다.
HashMap deviceList = manager.getDeviceList();
원인을 모르겠어요–;
저 라인에서 죽었다면 manager 가 null 이라서 null pointer exception 이 난 것 같습니다. USB 연결에 문제가 있거나 단말에서 지원하지 않아서 manager가 null이 되는게 아닐까 싶네요. 정확한건 에러로그로 구글 검색을 해봐야 할 것 같습니다.
참고로,
제가 다른 것으로 테스트를 과거에 해봤는데요.
중간에 usb hub를 둔 경우에 부팅시에 usb device를 인식하지 못하더군요.
환경이 다양하겠지만 암튼 경험상 아직 해법을 못 찾았습니다.
SerialConnector의 mDriver = UsbSerialProber.acquire(manager, mDevice);
부분에서 Permission Exception이 발생합니다. device_filter.xml과 메니페스트에 이상 없이 했는데도 말이죠…
문제의 원인이 뭔지 찾기가 좀 어렵네요. 일단 아래 링크에 있는 매뉴얼대로 다시 코드를 확인해 보시길 바랍니다.
https://github.com/mik3y/usb-serial-for-android
이것저것 자료를 찾다가 우연히 들리게 됐네요.
저는 아두이노가 아닌 모 회사에서 제공받은 다른 디바이스를 붙이고 있는 중입니다.
첫번째로 의심할 수 있는 부분은 device_filter.xml 파일이라고 합니다.
여기에 예제로 올라온 xml 파일내부의 와
본인이 소지한 device의 vendor-id가 다르다면, 그러한 문제가 생길 수 있습니다.
안드로이드 기기(폰)에 usb device를 연결하면
USB 접근을 허용할는지 확인하는 dialog가 나타나나요?
이 창에서 “Yes(예)”를 선택하면 permission이 자동으로 주어집니다.
만약 안나타난다면 vendor-id가 잘못된 확률이 높습니다.
그렇다고 해서 완전히 못쓰는건 아닙니다.
UsbManager의 hasPermission 함수로 permission이 있는지 체크할 수 있으며,
requestPermission 함수로 permission 설정하는 dialog를 띄울 수 있습니다.
이와 관련된 예제는 인터넷에 많기 때문에 찾아보시면 됩니다.
만약 dialog가 나타나는데도 안된다면 저도 모르겠네요.
저도 며칠전부터 처음 만들기 시작하는데, device를 2개 받아서 테스트를 하다보니
서로 vendor가 틀린 경우 비슷한 상황이 발생하여 경험상 적어봤습니다.
수고하세요.
좋은 정보 감사 드립니다.
좋은 글 감사드립니다. 많은 도움이 되었습니다.
opencv와 아두이노를 연결해 보는 프로젝트를 진행중인 학생입니다.
Subthread를 통해 아두이노로부터 serial을 받아오는것에는 성공을 했습니다.
혹시 안드로이드->아두이노
로 send 하는 부분은 어떻게 하는것입니까?
usbdriver.read
에 반대되는 함수를 찾고 있습니다.
혹시 도와주실수 있습니까?
말씀 하신대로 데이터 수신을 위해서 thread 를 통해 계속 입력이 오는지 모니터링 합니다.
데이터 송신은 계속 모니터링 할 필요가 없으므로 thread 의 run() 코드 안에는 없습니다.
SerialConnector.java 파일의 sendCommand(SerialCommand cmd) 함수가 데이터 전송하는 역할을 합니다.
개발시, ADK로 usb로 연결되면 pc(이클립스)에서의 앱 다운로딩 및 logcat사용은 어떻게 되는 건가요?
PC와는 연결이 안됩니다. 따라서 WiFi 를 이용한 logcat 등 우회적인 방법을 사용해야 합니다.
그 프로젝트 어떻게열어요 ㅠㅠ 완전 초보라서 그런데 ㅠㅠ
구글에서 안드로이드 프로젝트 열기로 검색해보세요. eclipse에서 프로젝트 불러오기 등으로 검색하셔도 됩니다. 먼저 eclipse 개발환경이 설치되어 있어야 합니다. 이것도 여기서 다루기는 내용이 많으니 구글 검색을 권장합니다. 자료가 많아 금방 찾으실 수 있습니다.
제가 안드로이드 스튜디오를 사용중인데요 그럴경우에는 usb serial for android 드라이버를 어떻게 link 시키나요 ㅠㅠ
file->Project Structure..->좌측상단 + 기호->Import.JAR/.AAR Package
하니까 되네요
저도 맨 윗분처럼 HashMap deviceList = manager.getDeviceList(); 에서 null이 반환되고 경고문구와 함께 종료됩니다..
중간에 vendor id를 확인해 보라는 답변이 있었는데 이건 어떻게 확인할 수 있는건가요?
현재 LG V400이라는 태블릿으로 작업중입니다
SerialConnector 클래스에 initialize() 함수가 있습니다. 여기서 USB 장치가 연결되면 FTDI 모듈에 맞는 드라이버를 통해 통신할 준비를 합니다. 불필요한 부분들을 지우고 보면 아래와 같은 순서로 진행됩니다
이 부분을 initialize 함수에 적용했었는데 다운받은 그대로 실행하니까 에러가 사라졌습니다
그런데 availableDrivers의 size가 0이라서 Error: There is no available device가 뜹니다…
제가 도배하고있네요;;;;;
사용하는 시리얼 케이블의 칩셋이 FT232R인 경우(보통 PC에서 사용하는 USB to Serial) UsbDevice로 잡혀서 위 소스를 사용할 수 있지만
FT312D인 경우 UsbAccessory로 잡혀서 많은 부분을 수정해야 합니다.
Initialize에
UsbManager manager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
UsbAccessory[] accessoryList = manager.getAccessoryList();
를 추가해서 Accessory 값이 넘어오는지 확인해보면 알 수 있는데, 어떻게 접속해야 하는지는 아직 공부중이라 모르겠네요;;
찾으면 또 오겠습니다 하하;;
아두이노에서 많이 사용하는 FTDI 모듈에 사용되는 칩은 보통 4가지 정도가 있는데… CP2102, FT232RL, CH340G 등등입니다. 이 외에도 여러 변종 칩들이 있는걸로 아는데 이 모두를 지원하기는 현실적으로 힘들고 문제도 많은것 같습니다. 제가 만든 앱에서는 가장 많이 사용되는 2~3 종류의 칩을 지원하는 라이브러리를 찾아 앱에 맞게 약간 수정해서 집어 넣었습니다. FT312D의 경우에는 어떻게 수정해줘야 할지 저도 잘 모르겠네요;;; 관련 안드로이드용 라이브러리가 있으면 그나마 해볼만 할텐데 아쉽습니다.
개발이 끝난뒤에야 찾아왔습니다 ^^;
https://github.com/bergermeister/Android-UsbAccessoryTester?files=1
HashMap deviceList = manager.getDeviceList(); 에서 Device 검색이 안돼시는 분들은
Device가 아닌 Accessory 일 수 있으니 위 소스를 참고해 보시기 바랍니다
저는 덕분에 개발이 잘 끝났네요
개발 잘 끝내셨다니 다행입니다. 좋은 정보 감사합니다.
핸드폰으로 아두이노에 연결된 서보 모터를 컨트롤 하고 싶습니다 가능한지 알고 싶네요
가능합니다. 챗 앱으로 특정 문자를 보내면 아두이노에서 원하는 만큼 서보모터를 제어하도록 하면 됩니다.
덕분에 stm32f4 board를 android에 연결하는데 성공했습니다. 처음에 안되어서 CdcAcmSerialDriver.java에 vid와 pid를 추가하니까 되는군요. 한가지 이해가 안가는 점은 android와 stm32f4 board는 USB protocol에 따라서 신호를 주고 받는 것인데 왜 chip마다(CdcAcmSerialDriver, Ch34SerialDriver, …) 드라이버가 있는걸까요? 안드로이드 입장에서는 usb 건너편에 무었이 있던지 상관없이 protocol에 맞추어 동작해야 되는것 아닌가요? 지식이 짧아서 코드를 분석할 수도 없고… 혹시 아시면 좀 알려주시면 감사하겠습니다.
저는프로젝트를 다운받아서 안드로이드 스튜디오에서 열고 싶은데.. 연동이 안되고 있습니다.. 혹시 어떻게 import 를 시켜야하나요…?
감사합니다 ㅠㅠ 시리얼 통신용으로 잘 쓰고 있습니다
안녕하세요!!!!! 정말 큰 도움이 됐습니다.
궁금한 게 있는데요.. USB 포트 2개를 연결해서 2개 포트 데이터를 동시에 받을 수 있나요?
태플릿은 갤럭시 탭 사용중입니다.