안드로이드 IOIO - 개발자의 고장난 RC 자동차 장난감 살리기 #1
안드로이드 IOIO - 개발자의 고장난 RC 자동차 장난감 살리기 #2
이전 포스팅에서 IOIO와 고장난 RC 장난감 자동차를 연결하는 작업을 진행 했었습니다. 이번 포스팅에선 본격적으로 간단한 코딩과 동작완료!
우선 IOIO 관련 Android Java Library가 있습니다. 정확한 레퍼런스는 아래 링크에서 확인 할 수 있습니다.
https://github.com/ytai/ioio/wiki/Building-IOIO-Applications-With-Gradle
개발에 앞서 개발환경은 아래와 같습니다.
조정 단말기 |
Nexus5 N (Nougat 7.1) |
compile SDK version | 25 (2017, 01-22 기준 최신) |
min SDK | L (Lollipop 5.0) |
Android Studio Gradle | 2.2.3 (현재 최신) |
단말기와 IOIO간 Interface | Bluetooth 3.0 |
먼저 안드로이드 프로젝트를 생성합니다. 생성은 안드로이드 개발자라면 충분히 알 듯하기에 생략....
https://github.com/ytai/ioio/wiki/Building-IOIO-Applications-With-Gradle 위 링크에 가이드와 조금 다르게 라이브러리가 구성 되어있습니다. 개별적으로 재구성하여 필요한 라이브러리를 통합하였기 때문입니다. ( 사실 라이브러리 전부를 한 곳에 넣어두었다고 생각하면됩니다. -_-;;;)
그리고 필요한 관련 Permission들은 전부 라이브러리에 추가되어 있습니다. IOIO Android Library 정식 버전을 확인하고 싶다면 반드시 위 링크를 확인하시길 바랍니다.
프로젝트 최상위 build.gradle 에 아래 코드를 추가 합니다.
1 2 3 4 5 6 | allprojects { repositories { jcenter() maven { url "https://jitpack.io" } } } | cs |
그리고 프로젝트 application 쪽 build.gradle에 아래 dependency를 추가 합니다.
1 2 3 4 5 6 7 8 9 | dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:25.1.0' compile 'com.github.fivetrue:ioio-android:1.0.0' testCompile 'junit:junit:4.12' } | cs |
추가 되었다면 개발 준비가 완료되었습니다.
샘플 코드는 아래 Git 에서 확인 할 수 있습니다.
https://github.com/fivetrue/ioio-broken-car
https://github.com/fivetrue/ioio-broken-car.git
* 위 샘플 코드에 대한 설명은 모델, 컨트롤러 순으로 진행됩니다.
1. 모델 (Output)
IOIO에 사용하는 Pin은 43~46 번 Pin 이었습니다. 해당 Pin 모두 Motor를 제어하기 위한 Pin이기 때문에 1 or 0 인 Digital 신호를 보냅니다. 이 부분을 가독성있게 정의합니다.
각 Pin은 Unique하기 때문에 Singletone으로 정의합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public enum DefinedPIN implements Runnable{ FRONT_MOTOR_LEFT(43), FRONT_MOTOR_RIGHT(44), REAR_MOTOR_BACKWARD(45), REAR_MOTOR_FORWARD(46); public final int pin; DefinedPIN(int pin){ this.pin = pin; } @Override public void run() { } } | cs |
고장난 RC 장난감 자동차 프로젝트를 추가적으로 더 진행 할 진 모르겠지만, 확정성을 위해 Output interface를 정의합니다.
1 2 3 4 5 6 7 8 | public interface IOIOOutput { void openOutput(IOIO ioio) throws ConnectionLostException; void closeOutput() throws ConnectionLostException; void run() throws ConnectionLostException; } | cs |
그리고 Digital 신호를 내보내는 Class를 정의합니다. 물론 해당 Class는 IOIOOutput interface를 구현합니다.
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 32 33 | public class IOIODigitalOutput implements IOIOOutput { private static final String TAG = "IOIODigitalOutput"; public final DefinedPIN definedPIN; public DigitalOutput digitalOutput; public boolean enable; public IOIODigitalOutput(DefinedPIN definedPIN){ this.definedPIN = definedPIN; } @Override public void openOutput(IOIO ioio) throws ConnectionLostException { if(LL.D) Log.d(TAG, "openOutput: " + this); digitalOutput = ioio.openDigitalOutput(definedPIN.pin); } @Override public void closeOutput() throws ConnectionLostException { if(digitalOutput != null){ digitalOutput.write(false); } } @Override public void run() throws ConnectionLostException { if(digitalOutput != null){ digitalOutput.write(enable); } } } | cs |
이렇게 모델 구현은 완료 되었습니다.
2. 컨트롤러 (IOIO Looper)
이제 컨트롤러를 구현 할 차례입니다. 원래 IOIO 기본 코드에서는 Activity 가 Control Looper를 가지고 있습니다. 위 샘플 코드에서는 분리되어있습니다.
IOIO는 하나의 Looper (무한 루프 Thread) 를 이용하여 신호를 반복하여 전달 받습니다. IOIOOutput의 run() method는 IOIO 에서 호출하는 반복 루프에서 Output 동작을 수행합니다.
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | public class ControlLooper extends BaseIOIOLooper { private static final String TAG = "ControlLooper"; public interface DeviceControlImpl{ void showVersionInfo(IOIO ioio, String msg); void disconnected(); } private List<IOIOOutput> mIOIOOutputs; private IOIO mIOIO = null; private DeviceControlImpl mControlImpl = null; public ControlLooper(DeviceControlImpl impl, List<IOIOOutput> outputs){ mControlImpl = impl; mIOIOOutputs = outputs; } /** * Called every time a connection with IOIO has been established. * Typically used to open pins. * * @throws ConnectionLostException * When IOIO connection is lost. * */ @Override protected void setup() throws ConnectionLostException { mIOIO = ioio_; mControlImpl.showVersionInfo(mIOIO, "IOIO connected!"); for(IOIOOutput output : mIOIOOutputs){ output.openOutput(mIOIO); } } /** * Called repetitively while the IOIO is connected. * * @throws ConnectionLostException * When IOIO connection is lost. * @throws InterruptedException * When the IOIO thread has been interrupted. * * @see IOIOLooper#loop() */ @Override public void loop() throws ConnectionLostException, InterruptedException { for(IOIOOutput output : mIOIOOutputs){ output.run(); } Thread.sleep(10L); } /** * Called when the IOIO is disconnected. * * @see IOIOLooper#disconnected() */ @Override public void disconnected() { mControlImpl.disconnected(); for(IOIOOutput output : mIOIOOutputs){ try { output.closeOutput(); } catch (ConnectionLostException e) { Log.e(TAG, "disconnected: ", e); } } } /** * Called when the IOIO is connected, but has an incompatible firmware version. * * @see IOIOLooper#incompatible(IOIO) */ @Override public void incompatible() { mControlImpl.showVersionInfo(mIOIO, "Incompatible firmware version!"); } } | cs |
위 코드를 보면 0.01초 단위로 Loop를 돌며 run을 수행합니다. (정확히 표현하자면 loop를 0.01 초 멈추고 다시 진행합니다.)
3. View (Activity)
IOIO를 동작하기 위해서는 IOIOActivity를 상속 받아 구현하여야합니다. 그리고 IOIOActivity에서는 IOIOLooper 객체를 갖고 있어야합니다.
(그래야 IOIO와 연결 시 Loop를 진행 시켜 IOIOLooper에게 동작을 받아오겠죠?)
간단한 전진, 후진, 앞 바퀴 좌,우 동작을 하는 Activity 의 View입니다. 방향 선택 시 IOIO로 신호를 전달하는 핵심적인 코드는 onTouchListener에 있습니다.
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | public class MainActivity extends IOIOActivity implements ControlLooper.DeviceControlImpl, View.OnTouchListener, View.OnClickListener{ private ControlLooper mControlLooper; private final IOIOOutput mFrontMotorLeft = new IOIODigitalOutput(DefinedPIN.FRONT_MOTOR_LEFT); private final IOIOOutput mFrontMotorRight = new IOIODigitalOutput(DefinedPIN.FRONT_MOTOR_RIGHT); private final IOIOOutput mRearMotorBackward = new IOIODigitalOutput(DefinedPIN.REAR_MOTOR_BACKWARD); private final IOIOOutput mRearMotorForward = new IOIODigitalOutput(DefinedPIN.REAR_MOTOR_FORWARD); private ImageView mFrontRight; private ImageView mFrontLeft; private ImageView mBackward; private ImageView mForward; ... ... private void initData(){ ArrayList<IOIOOutput> outputs = new ArrayList<>(); outputs.add(mFrontMotorLeft); outputs.add(mFrontMotorRight); outputs.add(mRearMotorBackward); outputs.add(mRearMotorForward); mControlLooper = new ControlLooper(this, outputs); } private void initView(){ ... mFrontRight.setTag(mFrontMotorRight); mFrontLeft.setTag(mFrontMotorLeft); mForward.setTag(mRearMotorForward); mBackward.setTag(mRearMotorBackward); ... } ... @Override public boolean onTouch(View view, MotionEvent motionEvent) { if(view.getTag() != null && view.getTag() instanceof IOIODigitalOutput){ switch (motionEvent.getAction()){ case MotionEvent.ACTION_DOWN : ((IOIODigitalOutput) view.getTag()).enable = true; return false; case MotionEvent.ACTION_UP : ((IOIODigitalOutput) view.getTag()).enable = false; return false; } } return false; } } | cs |
각 모터를 제어하는 View에 tag로 제어 할 모터 DigitalOuput 객체를 설정합니다. 그 후 onTouch() 에서 버튼이 눌렸을 때 Digital신호를 true로 눌리지 않았을 때 false로 변경하는 로직을 넣습니다.
(onClickListener도 구현되어있는데 이 부분은 View Selector동작을 위해 구현만 되어있는 것입니다.)
이렇게 하면 구현이 완료되었습니다 !
우리 아이의 반응은 정말 좋았지만, 아직 가지고 놀기엔 너무 이른 나이인 것 같습니다 ㅠㅠ.. 언제한번 날씨가 좋아지면 모터에 7V이상의 전압을 달아 야외에서 아들과 함께 시연해야 할 것 같습니다. 그럼 고장난 RC 장난감 살리기 프로젝트는 간단히 마무리!
날씨가 점점 추워지는데 감기 조심하세요!
'개발 > 안드로이드' 카테고리의 다른 글
안드로이드 프로젝트 Jenkins 설정 에러 #2 (0) | 2017.03.25 |
---|---|
안드로이드 프로젝트 Jenkins 설정 에러 #1 (0) | 2017.03.25 |
안드로이드 IOIO - 개발자의 고장난 RC 자동차 장난감 살리기 #2 (0) | 2017.01.20 |
안드로이드 IOIO - 개발자의 고장난 RC 자동차 장난감 살리기 #1 (0) | 2017.01.17 |
FirebaseMessaging 적용, App Background 상태 이슈 (0) | 2017.01.09 |