From 5f51c1caa17c89b5005f20c595f2e17f29725d00 Mon Sep 17 00:00:00 2001 From: Ricky Barrette Date: Mon, 12 Mar 2012 00:53:13 -0400 Subject: [PATCH] Started to update the library to API 3.22 RC Change-Id: I8eaf3151b1d405799eb582564f0e1e8fbcb7744c Signed-off-by: Ricky Barrette --- .../android/ioio/IOIOManager.class | Bin 4644 -> 3469 bytes .../TwentyCodes/android/ioio/IOIOThread.class | Bin 3577 -> 1899 bytes .../classes/ioio/lib/api/AnalogInput.class | Bin 336 -> 556 bytes IOIOLib/bin/classes/ioio/lib/api/IOIO.class | Bin 3228 -> 3382 bytes .../classes/ioio/lib/api/IOIOFactory.class | Bin 2709 -> 1503 bytes .../ioio/lib/api/SpiMaster$Config.class | Bin 757 -> 757 bytes .../classes/ioio/lib/api/SpiMaster$Rate.class | Bin 2298 -> 2298 bytes .../classes/ioio/lib/api/Uart$Parity.class | Bin 1107 -> 1107 bytes .../classes/ioio/lib/api/Uart$StopBits.class | Bin 1065 -> 1065 bytes .../bluetooth/BluetoothIOIOConnection.class | Bin 3632 -> 3237 bytes .../BluetoothIOIOConnectionDiscovery.class | Bin 2390 -> 0 bytes .../classes/ioio/lib/impl/AbstractPin.class | Bin 717 -> 717 bytes .../ioio/lib/impl/AbstractResource.class | Bin 1509 -> 1509 bytes .../ioio/lib/impl/AnalogInputImpl.class | Bin 2221 -> 3747 bytes .../ioio/lib/impl/DigitalInputImpl.class | Bin 2215 -> 2215 bytes .../ioio/lib/impl/DigitalOutputImpl.class | Bin 1023 -> 1135 bytes ...owControlledOutputStream$FlushThread.class | Bin 1943 -> 1943 bytes .../lib/impl/FlowControlledOutputStream.class | Bin 3152 -> 3102 bytes .../lib/impl/FlowControlledPacketSender.class | Bin 3023 -> 3023 bytes .../ioio/lib/impl/IOIOImpl$State.class | Bin 1180 -> 0 bytes .../bin/classes/ioio/lib/impl/IOIOImpl.class | Bin 20016 -> 20857 bytes .../impl/IOIOProtocol$IncomingThread.class | Bin 7199 -> 7287 bytes .../ioio/lib/impl/IOIOProtocol$PwmScale.class | Bin 1490 -> 1490 bytes .../classes/ioio/lib/impl/IOIOProtocol.class | Bin 12885 -> 13575 bytes .../ioio/lib/impl/IcspMasterImpl.class | Bin 3422 -> 3422 bytes .../bin/classes/ioio/lib/impl/IncapImpl.class | Bin 3619 -> 3619 bytes .../classes/ioio/lib/impl/IncomingState.class | Bin 10140 -> 10140 bytes .../ioio/lib/impl/ModuleAllocator.class | Bin 3038 -> 3038 bytes .../ioio/lib/impl/PinFunctionMap.class | Bin 1828 -> 1828 bytes .../bin/classes/ioio/lib/impl/PwmImpl.class | Bin 2199 -> 2199 bytes .../ioio/lib/impl/QueueInputStream.class | Bin 1786 -> 2563 bytes .../ioio/lib/impl/SocketIOIOConnection.class | Bin 2905 -> 2864 bytes .../classes/ioio/lib/impl/SpiMasterImpl.class | Bin 5791 -> 5791 bytes .../classes/ioio/lib/impl/TwiMasterImpl.class | Bin 4684 -> 4684 bytes .../bin/classes/ioio/lib/impl/UartImpl.class | Bin 2662 -> 2673 bytes .../AbstractIOIOActivity$IOIOThread.class | Bin 3701 -> 3776 bytes .../ioio/lib/util/AbstractIOIOActivity.class | Bin 4822 -> 4868 bytes ...nnectionDiscovery$IOIOConnectionSpec.class | Bin 646 -> 0 bytes .../lib/util/IOIOConnectionDiscovery.class | Bin 410 -> 0 bytes .../util/SocketIOIOConnectionDiscovery.class | Bin 1186 -> 0 bytes IOIOLib/bin/ioiolib.jar | Bin 97909 -> 0 bytes IOIOLib/src/ioio/lib/api/AnalogInput.java | 131 +- IOIOLib/src/ioio/lib/api/DigitalInput.java | 280 +-- IOIOLib/src/ioio/lib/api/DigitalOutput.java | 240 +-- IOIOLib/src/ioio/lib/api/IOIO.java | 1334 ++++++------- IOIOLib/src/ioio/lib/api/IOIOConnection.java | 0 IOIOLib/src/ioio/lib/api/IOIOFactory.java | 64 +- IOIOLib/src/ioio/lib/api/IcspMaster.java | 0 IOIOLib/src/ioio/lib/api/PulseInput.java | 2 +- IOIOLib/src/ioio/lib/api/SpiMaster.java | 13 +- IOIOLib/src/ioio/lib/api/TwiMaster.java | 4 +- IOIOLib/src/ioio/lib/api/Uart.java | 215 ++- .../exception/IncompatibilityException.java | 0 .../bluetooth/BluetoothIOIOConnection.java | 63 +- .../BluetoothIOIOConnectionBootstrap.java | 93 + .../BluetoothIOIOConnectionDiscovery.java | 37 - IOIOLib/src/ioio/lib/impl/AbstractPin.java | 3 +- .../src/ioio/lib/impl/AbstractResource.java | 3 +- .../src/ioio/lib/impl/AnalogInputImpl.java | 109 +- IOIOLib/src/ioio/lib/impl/Constants.java | 90 +- .../src/ioio/lib/impl/DigitalInputImpl.java | 3 +- .../src/ioio/lib/impl/DigitalOutputImpl.java | 18 +- .../lib/impl/FlowControlledOutputStream.java | 9 +- .../lib/impl/FlowControlledPacketSender.java | 2 +- IOIOLib/src/ioio/lib/impl/IOIOImpl.java | 1354 +++++++------- IOIOLib/src/ioio/lib/impl/IOIOProtocol.java | 1655 +++++++++-------- IOIOLib/src/ioio/lib/impl/IcspMasterImpl.java | 2 +- IOIOLib/src/ioio/lib/impl/IncapImpl.java | 2 +- IOIOLib/src/ioio/lib/impl/IncomingState.java | 2 +- .../src/ioio/lib/impl/ModuleAllocator.java | 2 +- IOIOLib/src/ioio/lib/impl/PinFunctionMap.java | 2 +- IOIOLib/src/ioio/lib/impl/PwmImpl.java | 2 +- .../src/ioio/lib/impl/QueueInputStream.java | 56 +- .../ioio/lib/impl/SocketIOIOConnection.java | 19 +- .../impl/SocketIOIOConnectionBootstrap.java | 63 + IOIOLib/src/ioio/lib/impl/SpiMasterImpl.java | 2 +- IOIOLib/src/ioio/lib/impl/TwiMasterImpl.java | 2 +- IOIOLib/src/ioio/lib/impl/UartImpl.java | 5 +- .../ioio/lib/spi/IOIOConnectionBootstrap.java | 41 + .../ioio/lib/spi/IOIOConnectionFactory.java | 21 + .../lib/spi/NoRuntimeSupportException.java | 9 + .../ioio/lib/util/AbstractIOIOActivity.java | 664 ++++--- IOIOLib/src/ioio/lib/util/BaseIOIOLooper.java | 48 + .../ioio/lib/util/IOIOApplicationHelper.java | 166 ++ .../lib/util/IOIOConnectionDiscovery.java | 17 - .../ioio/lib/util/IOIOConnectionRegistry.java | 133 ++ IOIOLib/src/ioio/lib/util/IOIOLooper.java | 56 + .../src/ioio/lib/util/IOIOLooperProvider.java | 36 + .../util/SocketIOIOConnectionDiscovery.java | 15 - .../util/android/ContextWrapperDependent.java | 41 + .../ioio/lib/util/android/IOIOActivity.java | 148 ++ .../android/IOIOAndroidApplicationHelper.java | 69 + .../ioio/lib/util/android/IOIOService.java | 149 ++ 93 files changed, 4500 insertions(+), 2994 deletions(-) delete mode 100644 IOIOLib/bin/classes/ioio/lib/bluetooth/BluetoothIOIOConnectionDiscovery.class delete mode 100644 IOIOLib/bin/classes/ioio/lib/impl/IOIOImpl$State.class delete mode 100644 IOIOLib/bin/classes/ioio/lib/util/IOIOConnectionDiscovery$IOIOConnectionSpec.class delete mode 100644 IOIOLib/bin/classes/ioio/lib/util/IOIOConnectionDiscovery.class delete mode 100644 IOIOLib/bin/classes/ioio/lib/util/SocketIOIOConnectionDiscovery.class delete mode 100644 IOIOLib/bin/ioiolib.jar mode change 100755 => 100644 IOIOLib/src/ioio/lib/api/IOIOConnection.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/api/IcspMaster.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/api/PulseInput.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/api/SpiMaster.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/api/TwiMaster.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/api/exception/IncompatibilityException.java create mode 100644 IOIOLib/src/ioio/lib/bluetooth/BluetoothIOIOConnectionBootstrap.java delete mode 100644 IOIOLib/src/ioio/lib/bluetooth/BluetoothIOIOConnectionDiscovery.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/AbstractPin.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/AbstractResource.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/AnalogInputImpl.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/Constants.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/DigitalInputImpl.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/DigitalOutputImpl.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/FlowControlledOutputStream.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/FlowControlledPacketSender.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/IOIOImpl.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/IOIOProtocol.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/IcspMasterImpl.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/IncapImpl.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/IncomingState.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/ModuleAllocator.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/PinFunctionMap.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/PwmImpl.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/QueueInputStream.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/SocketIOIOConnection.java create mode 100644 IOIOLib/src/ioio/lib/impl/SocketIOIOConnectionBootstrap.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/SpiMasterImpl.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/TwiMasterImpl.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/impl/UartImpl.java create mode 100644 IOIOLib/src/ioio/lib/spi/IOIOConnectionBootstrap.java create mode 100644 IOIOLib/src/ioio/lib/spi/IOIOConnectionFactory.java create mode 100644 IOIOLib/src/ioio/lib/spi/NoRuntimeSupportException.java mode change 100755 => 100644 IOIOLib/src/ioio/lib/util/AbstractIOIOActivity.java create mode 100644 IOIOLib/src/ioio/lib/util/BaseIOIOLooper.java create mode 100644 IOIOLib/src/ioio/lib/util/IOIOApplicationHelper.java delete mode 100644 IOIOLib/src/ioio/lib/util/IOIOConnectionDiscovery.java create mode 100644 IOIOLib/src/ioio/lib/util/IOIOConnectionRegistry.java create mode 100644 IOIOLib/src/ioio/lib/util/IOIOLooper.java create mode 100644 IOIOLib/src/ioio/lib/util/IOIOLooperProvider.java delete mode 100644 IOIOLib/src/ioio/lib/util/SocketIOIOConnectionDiscovery.java create mode 100644 IOIOLib/src/ioio/lib/util/android/ContextWrapperDependent.java create mode 100644 IOIOLib/src/ioio/lib/util/android/IOIOActivity.java create mode 100644 IOIOLib/src/ioio/lib/util/android/IOIOAndroidApplicationHelper.java create mode 100644 IOIOLib/src/ioio/lib/util/android/IOIOService.java diff --git a/IOIOLib/bin/classes/com/TwentyCodes/android/ioio/IOIOManager.class b/IOIOLib/bin/classes/com/TwentyCodes/android/ioio/IOIOManager.class index 9b214e88a8ed1c95626f1160079a53a84e3410ce..cb7b843c2af04cc3b7c50083818f5c289b6df710 100644 GIT binary patch literal 3469 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 diff --git a/IOIOLib/bin/classes/com/TwentyCodes/android/ioio/IOIOThread.class b/IOIOLib/bin/classes/com/TwentyCodes/android/ioio/IOIOThread.class index b74834e707536706031f517669f27f483fc0f033..55f76308732d66adee968717b1da47b4748b6cc0 100644 GIT binary patch delta 360 zcmYL_K}!Nr6ot>IWDS}mREsvRRWlI?(W+W#rZr{_Q3@3U(fs;-`v94_lpe5yRbNhn3vXTdHp7cK{k1WXNtCM2VZS&r+tRlzyyO<0?F zoG1;camdKTtJ9tma!kC4aG?l24@2MUQ5{I`gq*Te_U{A6RYa@{B|YBXB5rr618+LE zX`+l;4y&jeShkw~y=Um2-A>%>3dva4cH6*G#8fN1)x{hhGZ|CgK*nm=?U#q_D7B9r zq+#f1nC~cG6%Sr<_^DM28+y)7oe?a(=VlaaY-+WRBOE84ypwj&NS^YQ!U+nRFwmS{ GpZ)=%6JYlM literal 3577 zcmb7GU2q%K75=Uzdu@3glRra~5HP_+mg6W%X$vuNaqKwQtnGjuQ$kC+mbS_!(yrKD z*`y7H(jTBH|FjS$zyJ@Pfxe{0H12c?l$ms=e-B_Ep3~5vurueDvQu-G6PzA&^Om1AoxJDT`K;~QZgym1 zWMb-sXPJ2oNrmf9nzLrMXgY;#BvnXE4IEb3VAM2|foD5~K83bH*YN|>38u|r#cD>2 zLaGL+&{&d{!bT(XRkUZaX4wvXa(9dE56rk;VC5CmF@^Th(Q@7ltPv-$yjinI=_n(z z{mH-#3@d-gF=vWm+)^@ZKd>CjQ|K|SdAzDIU8YKdMbr1k&5|Y24R{5g(LJ={Y!fpl ztz1CUR@>+494^FV(_OY>2mK1W)BlIV?rDXjke)&UTXkHIZR9iEJ>3F@?K*U{H8NB3 zmlDw}I@Y0GX1D6N4s9~KO~-m{AUlTbSmTw_jO9%U50Vl$XBMYT&z5y8Oa>=xo~irV zQAYXYNt=tyY)+uwzz(Im&&qhk{`Gl#iaB9dm9helJXL5#kl zBZbyhd<}PMxIK>lnfzW<1AB5s>E1>Fzq^D?o?E^d5!%UyQZ-FrzRu+^cYN?S4fy z>ITDZ#mOIfG-s6q+jVq|$&uU3ynI1;)G5!*S;EqUjw7O5H)q=1<;8_7s{3_JVu}>H zWy=||3wB@@Cn`a?63}IOg6n0Mw z{x@}eOJYwJUANqd<1jURTVczp^sEX?#|(13zH^=}`AMdabx)_5XG_O-P@uvuT2@&U zqmc_+$H|Z$mkd9(dzY1uGb{ zeBUfErcJDw+OGH_rBK0~hFSKsY74Feb}?(X1s#v#H2WR5?yOTxS7UXLNPI>LXC%nS z6ryfk?XXt0XtE;?GgmePd&Vx>!Rh6wWhV>o33jwqOxJeC>0{*?t{*IKWS3eIPW@74 zQ_=jWX)A17o%&U&q~V^`>TCMvmYY`uP%k4i*}E3%T72|rc$!Z^Eop3z?p)Mp*ZY=S zxK(eE$g0oPZ2Vj`l%x(O-HMm9hHYul?X}l%w?Lzlb(&ziZ^0HcqKPYws|Gal!7X?#|`}c5?48TY2dgu^BZi*Bo=UEW)p5& zK&Q-iWO^3Rbs^lmliyoHz&nscmVxcz%d?lzcT&C|Y3e&7D0Cyk6+zL%P*|>XxGQ$} z3>_w^>#Fatmkz~iZ)Vp5vNhMYFmB+$6|Vc~`s=s}cjH#JSqUK%fiH*9#Sn!(I^++X z&0&CDVOR-r{AFy5!#YZhSaoEQ!odf){StH;7V%XDi6a4_U_*$1FLPq>RURYaMCezK zcE^7ELVLBDE;?{m>>|riTr?8)z%Myz2oa;`rdOae#weX6xZdU+S8jf|&Spho;=?6M z5$Ebi=0W2R^nT+xH1~WuuMTqFdkHBH**~H!a}jqh;P!bIf+$3K5yPRRd2H8*7~vi? z^?dq~aCCrbk~6L#lSFb6_a$I(-W4g1Hi)8Y5fAaV7iT-pK}*adNyCRjRp&+I=a>Ew zZdt?$;;`ak)KeiUH&?~;ONYnjmj;au%{?E&xrpEaj^B+uF+Y)~s&OQ~8Uy!_&tp8o z_+E_h{k4oQ|80E!@?Xc+QX@IO>!S<&6_)1Xupgr=i#9&;V9Ex zh~G&o=IUdPpW(kOhH$NtIHNYIjOq>7@2I`R`52$csFKvkxKE8JgZiipFR`ovn_f?{ z8Jwcm61Kx(G>-V_(nnIE^gZ^G_d^n**4eb&x0;yJHez%#S%qb+1k*2A(x9)z V`W3N$y$04w1$Q2)W2Q(7#J9g7^Eh0J*ej_PAzdNO-oBHVq{>^@YD=rWDrPCE%7f)ElSJD zFL%x_%`0JKVAjy|WMtq>EKAJHNleN~Wn|zfN=;0GXiQCEWDo-C3CqtZNlZ^g2=W32 zgA;QLa#Dj5OMrT~g7Zs@l2hF>K?Vpo<|XFjPy8#v$T?Yz(V&V2D8wMh0JH_@I2Ity r0~8Tr5JnRb!4MH;;DD-OV_;(70_zjQA|?(O1Dg)iBLTFDi9r$o=KwO# delta 39 ucmZ3(a)F8K)W2Q(7#J9g7=$KrJ(wKEsLjYcxrxz$i-D0r04UDHAP4~VXbBkr diff --git a/IOIOLib/bin/classes/ioio/lib/api/IOIO.class b/IOIOLib/bin/classes/ioio/lib/api/IOIO.class index 3fea9fd08dc374878b6040b045cd0079e52993a9..c9a6c473d9e91422ee63ad02129d1fcbc4d0e03d 100644 GIT binary patch delta 802 zcmZ{i%Wl(95QhJW-15|g-;CV(Zx0_7oQ z%Z3*~s*qT)V2yZ)N<0BCfGVcm#TKjopKs2YIr?=zncq^zuifuI0Lt>^kp z-|=0-)XeO?ceF z?@-a;k6M}T@E7g4(vl0jttI)7mZ1B*tIg9RuISVBgj>2r&-sI1q&9!n^O-K;@Yw9u zd#~=i^WC>9Uht5FyhMax_A$5IM&EeFsKG%!GImvmr8v6A^MUL`panA$EwAQ3;cS z@}0)RDYFsJk0hl9EQVTVB1tZd6U1;>+`0BF zHSrgiXiQwVaOKJ$;MRrz!+45u;hoiU-siseX6Af$-#Yg9-(S80m__TOJx`w=(^Q(@ z>ykP_DDXn6kk&XwaM(*ZHbJdzHyavfc`OsqIbO>6>O8@f(?PE7NlW=6!T%R~{chc_ z#+yxz%ls=dqplG=uBki+R#sIB!xDxE2v%h)Zai5HJJMYK+wu67)u+}7qp$T4f3PgI zK^T%bM@U!Vr>*Fb#x4G7mDF9r2v6&g361;GiQ*^@wxW6vZb!lT^Qam=>onUMHA0?W z_T=|#goOV8Y8zp@BkwMU?WprYW3&5LZ+cvQXU~ztKkTE$904RaVl!pxXv z07ncp%dOma8!61m4pW%NF=1VHcpN8j8WzsTR4zMMKxH>ykg1z5$wOC!S8*MS!X@E~ ca24ygA@^R diff --git a/IOIOLib/bin/classes/ioio/lib/api/IOIOFactory.class b/IOIOLib/bin/classes/ioio/lib/api/IOIOFactory.class index 179048c9ef1ea54f5684d197cf744bff64c06cbe..f6d3f37cffe6d3c884b5e24793b4a5962cd60224 100644 GIT binary patch literal 1503 zcmah}T~pIg5Iq+PDQyr$sK|#DL@1zCK@lw=h=7#}Gs56FK80{C7emrXQwM&G{{f!_ zol&3tQR+zwq}Vb(^xoe6IJUxI(E@BY;Vi3N-~Njfp`U? zK;w+El{ZI_)X0`VWYO7?G1TLVju_$sEjeY&wbK5!bT^G{OZssf)3CM-SNZQ#VZ_^2 zMS-r|Ww|q#M$?ssM}&^lNVQ!ih6Fk_To)KT2Pt{V3K(D+HfvRm{aWT#(Q^-V+(4H= zA}_sAStd$JrqanE0U-g#qGMTQ|4bY`xT&F6puLt}M<0>`QRPY3AnOcq0V#VbC(Zx{ zHQXX2AZCCNC^y_ms&jS^}odªh9b>q|=9ei9Z3Oyi zkr^70R)KLH*N_&NNLBM*VOpyzQQW8Bnv*ooJCIbxBxg`re*edlo9ukDxa*Xx9edbI z?iz)Hw8vwZ#6t}an6qK;xQ^OM2aU)%c^y-DBp^5m!>Q__t(J^%FCGgdLz>r|jgq;$ zWXXMLdrJqVEcl}#(DXjolwbf7wcAh5D-^YXIIM|_Q+`-DoAj9q?{KPSLSip%WM+*sHkf!;8kvu{X6pJ}|| zlw4CTD}SL|D?SM0KF8?i%+&LX#Dzu!8hLAc)gekLoe}4$Q`*}lxvH&-8;s_ zo3S7CiZGpSO!M}mbTJOF!jzUN$x`!@o4txb=b;$#Bly)XR> zee092_67Lrr`Fg0m;Qr(wd+h`W+<`k135W6`|Q2;+UxAY?|=RJ3cxrv6m$t3G9A;& zS!OAx)y!OeF~2yg>7L_mDu@aUJki#*oTb_2++yj8p?dKG}MOnw2X!`kLX%I*8q7 zRi)-1}O_th&jKBv4r)rR1yR4dZb=`0`)eYC-u41@s)k$P!t41ZQp{%W9M~Vtg3#dU3 zpQ#1Mn{^uYN;?x3LcUIf0(ACc8~X~LrmxLwwN~rn?I3kY%ee$T!{-7=n%xfiHnVAK zRg==OHU(xnVEuoj1p)moT##M3C@`McE2)rcC-wyn=xtRs%k@>03bD|lkrzXA%M|#5 z4j={MK^%dj{|ZFG6{>2t%B8;ZlzSYPaZNUJnjQO(dW!NITu)#YH>lTD$89S9WCvcu z0`|)!aS`Mb_zJfKdU#uWD~sE8UEaA4V?KceEDH2XS2;u6h8#TXbtX(l^FTP8Yphzl zw7JkB=KdN51$RhPAj%Go2`u5Rz&_j9$opeUr$A5bO4zVG;Lb>mCSXJMHTgd6RkCq%) z!8MlMM&cLWrJtYj0PqF&^E1(OzyOlm^>TzAphX@o`@w+Yse@Y>ioQf-Ig(7i+2J#= zgA>nhy}-%k)TwQZy!Jy3VhCeIB@^`f=w@lT!P$^+CmJTQGeiZP>B^cWXy+kJVv3=Z6xJk|9HeyMM3;?3!W5PhYP>LE6ck|ar*&WQxkG^_| z;fZMEeC!!g>F8i=bTBr)g`0&RVuG7jKfnEBEFIm#+(axL8{NX~g=a`kL|>q|occB$ z-NwDwr{Cb=Pw4YakGG+{KK&aWJ}2B5F0i-~^GSxUv0=a}ttMOegvG7lL#`QA$*oOK zhXmAc1y3=<^(N|AgogqexXX1J8-9%sSRc{1iF?!{E=G?P7_9dK$y%itB8)NA`j%i9 zE6f~aj5M=Kn$NPgYq0ojiipk-N0d0gGhMXFXb!(My=uO{{D(63me9KuJXKIvs1*hL ccM<(>aR`yXDioCdM4y7l0GVhoY8Tf31{FM_umAu6 diff --git a/IOIOLib/bin/classes/ioio/lib/api/SpiMaster$Config.class b/IOIOLib/bin/classes/ioio/lib/api/SpiMaster$Config.class index e8378f5cf0782348e478c558cc3ca4ce547e75ce..cebccbc1e013ab1ce5b54bad3dd7da8b163e5eee 100644 GIT binary patch delta 39 vcmey$`jvHq3zJ|U0}Dex11G}-20n&~48jbPCWkV`vd(5;XP7g2JChv%&>RX5 delta 39 vcmey$`jvHq3zJ|k0}DeR11Cd210TZ#24RMYlS7$eS!Xe@Gt8d6oyiUW&zlMd diff --git a/IOIOLib/bin/classes/ioio/lib/api/SpiMaster$Rate.class b/IOIOLib/bin/classes/ioio/lib/api/SpiMaster$Rate.class index 5ed0359bb62b9a0f916ad3f3a1f7fa17c849d107..78849bbc045df26239bc9e4852de6e4ddc0875d1 100644 GIT binary patch delta 23 ecmew*_)Bnu00(O-V+2FWWKj+i#+1ok92)>xsRrZ# delta 23 ecmew*_)Bnu00(OdV+2F;WKj+i#^lLf92)>xiU#2T diff --git a/IOIOLib/bin/classes/ioio/lib/api/Uart$Parity.class b/IOIOLib/bin/classes/ioio/lib/api/Uart$Parity.class index 2ccb9483b19d0d9eaac83e14237052be16497942..50617a720baed4a24406346c6291d967eeea4d89 100644 GIT binary patch delta 43 zcmcc2ahYR7D6^y=0~doo1201WgE&JVgA_v$g9<}1gF1un`m}U=cV4JK-7Er(& zc)#B-Gk)+>zk*{6BQr81Gk)-w_z#?M81XqtcDofUbf!5cIq&s(pZ7ic$3MUP3g8iZ ztD;Up+Hx!>Q?w>B6UDOd9mk)_^j3D+;p}jqW81>?Eyq^TpkUbv&kVz-j`AEW6vQdZ6nC@GA4t){65E)T(lMh@aE0e_ z+xUcKTmBveo05wK5t&GhDQM_(3PM9YT6L_z8sd>mjWvO&>Cn+EcMs}l!F_V~kd9@z zUqd@Ks93L{eUU^%A38STVWu`I{2@lGU_~-@JJizlW*u?Fr0tZBv{cflv^Pvm3T)A_ z95Ds=Wk7Jht-E@WFi;xcr z4%}107AtDREa~V8SdZRd&^d_Alw-CD(Kkj`(m2;+LH<2 zNkmMQ+iC|SLacTrH0(j2blI<9Wv$DolTu?+VnD}!93aF-sU+-zg3ddM)&v}CM?v*S z69;uXEtwJXo$ye@Acj>8vGprdEc;e5lXE6@JcC1QtW%`c9UR@}rze%wkjG&aql*(e z#97Cf>=rcwCWdFJwU5(cqG)+jqEJDMe377qP$gnupKFPzVRt2#aM+pH2V{1*T3Z#zD+j?CtT~ucux`HMS=$$` zTQ2#cP?aHC?533=Gz;yrHKOke&GLJ5-Z#vXgGMQ6JQenW0GNYwTDD~qN^n)YL{?Tx z<#?H(x*Ny#jpVb#LpnV8#5CXk^s(#^%b;XqVEs0B@vpnPtGiQfI=kg#r+jpE?d;ky z@ObaG?E}3U&fpalFSB)nECU`KKAi2>aTe!@`f=Bp4p~EscVI*`m#TVXTsvOX@fu!N zkP5psYkR^hyJFJj==%wVQIqaK5_9}8m__lTqBhmSh`SbWVg?P%uDzYe4}nbg%-? zU&YaxTR#rY-1^}%QD4r?+hH=+&LFEU<|4D(#%??dfe-OHzQQCkktmH2XJ9v__h2cW zq;@a0`>-DU=-~HlIEWlae4bCi(e86SgCSZP#yQUO3phlJqqKM!U*iD2!+3yfB19@e zc0DU#;RF#qM^0EcNlc6Mn}l`~%T*|YD$J&S71MvC?l0_DA#P$Bp9fS-{w*uoB(Lb2 zikPw@%%YB#fVQ9DEYp5Ob2?tWiqmsAf2p#2;}XxqTth}2CBqEbn;;_$GO)m$1zNdE zM(ClvfCmU-B$wla&QZ`6dvYx-AMJ{uAX9f+q%hYQl zVIb=RlAk0&i`?040*7QTQOc#1hs`)u6HGD!Q34i2ee5W)A{%A<*8mbwlu6YG zvBg9GA708;i4r6x+p!WwOHiV-{Bn+PoUdt!c;vAuDqC1AHYLkp(heuc-8_GKch48; d`7L^Q`<^}5k-Cx$U*Jow@~2yg#QO$K{{kim4~hT) literal 3632 zcmb7GTWlOx8UD_ClUa`^PP&Z~#|iGn4as^PXKfOj!rZLl2&mxgwBXKYWr zo>^vRoe(airQ9jk-a;>2B|jl05^*ewpdyF|ctt!QAtZR=g=eG+wR~r0_ToC-l(l^3 z%sJ=3f8T%Rk8l6#Hvm3_pDTz6q%6mB(giD*&K1h0=Q!R{dbl#nOl77<9NRYYp5@pI zq5``v>#KUYpxcY-soZ65DA+Erzp6zqTItPV0!_1L9}$RWYoarrYuSrK0z0U_mejCf37o2{>yoX2#- zaLrPQ)9t4%+w#r`97%3S2VsiF?hWii`*qOC#bF#dWGu>G|S1@I2oV;F`(_Krxhl^2f$tnpP%~o@J&k8Z- zsEm-Q3Ih~qy`o#*nB#_Z6i`R6=FOrkXh}i0z_F@+Sv#|`nXVN+>y*5zkcuAkDmW_8 z)u0Av4ZY}@u7+bs3*5J8dgsgqy-N#%yZ_d zm1md_-=$^^)dqspn#3Lj4-swe2IX+sDj24#;v@zYoZ5u=0Ja*2aGEJzm5`lUkfGG! zBw)o*cimEmb`=9SBN6ivfwp>)aKC#lhO@Mn7;#PABR>)W-lg1#hI1GtTJ>Vlv<-pY zyD8XWGYp3D4&V_D=aC`yJtr_Iu$wh_clKX>;C!+X0K!T&tY*k`S@#J{-oPrgB`~Bjv ze`ltup(=1H8-ACr+xWCVXRX{Bn*?>sq^VIo<2MeUxdMk8Ri{Ev#vFt-pNjdcOo$MPJJ2ia##MrQkr|~5P&#;mDsq?9D;X>w|hG+3*hQHuCD-}K+ zss~?%)j^k~2UI&!uuR)a$m`Z(8!!^J1G4A5py5ToyBLy%FKf6i>9s9ia7x5WD|>&&E|xu} z)zpdoy_Mc2AynI7dLPGY8h(gZf(v7+?A1xN*Gbjy_+mfN@H&1(&u5&nn>WWSc{d+; zkI#`~GD|qf_SVGzZT#zif*9w`=_)mTY@VT95Z{x%9kFH~Q9b+39znDrLUB}1h{u*`;My15Q=xxky zmAP1x>Akat?Y(!lo|&(mp|$pPd@?W{f09;x*;RV@8Txh%>f1;t z=uvP@fml>fXn9h>=iWl(KNweF{};PBo>x$O3qpc`w{jf_3O){>;);Y%TTlE88~EZn zzH(!tGX2I43W1nY@m!dePJ$i`ULZqMp6AXuTF7P%S*GaGIy|;j@z{p%Fc5y9`+15!N8y$JV`K{cZ{<7)i-8+iU=Dk`}_Nc;h<^0AJW))39!q}&0b z{xHiLl&#CB+BChK;p_q_IEOY|ml6FELr9Uw=#V#?2B!=uJ^kWlS z2#6-%w|HRx)f-ufwH}EzUzcnK6)#%-l;*gSXZ8%=;+{Y{wAjpbSyWNU12Q840-2eap diff --git a/IOIOLib/bin/classes/ioio/lib/bluetooth/BluetoothIOIOConnectionDiscovery.class b/IOIOLib/bin/classes/ioio/lib/bluetooth/BluetoothIOIOConnectionDiscovery.class deleted file mode 100644 index 68d4ebc2d7b9dee052f9952c9450544b8664f3f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2390 zcmbtWTU!%X6kR6-2ErhcA{x*d5tMsc#A*qbLO=}$(wYE*mv)jlguyVwH<|FsPig;v z&%U6vUoX%7QKf4p2}vvk`t@OQX7<@A&e*(W*w^l zm0cC17-JKd@~tD#SwX02)~JFn1a6%XTFLnUA{bY34-*0k16$E{S+jb?a_2VVE1Goc z5x_%CD)_pNBqc2sQA`O0*x@Z2kd4Q?u6Ge10O!~fU>3ce8-IyrrA z69+JlaRo6FrAo6Acm*xYzvK3FU ztRTS*R*P2UhKdy=$zV;_Kgmi3Vbd-9cPgGC#d>UOg%z*Dp0sz3LV?39Y;A?h)gP-$$L15Oi|J%C{bV+Pb!;ej&LUR2 z#H&HV%Bk4I3jx7SxO+2d<|Es z$>DTSv=Uzh`Yu9Baw}8M!}c0vmxVN{dmI(GeHLPg3sk>R8{4NLaKEd5rXK87=IXN= z%hj0v*N_vKr~zd-Qx^zfhEBGn94-51S=%em{6NYo+FAL?aBqeF^RAjPw}=92oY$i5 zF~ps4!GF$SPDA!GpGH32P|(b^g;%c+0X~F48-FiR65%SiHV%D2`+HBNgTGClP(_gM zYDoc~p_5;v2f7Gy)ziJ^f`|+aH1T`)BSOm~hq$>idW72ola22X3pE~LP~Zp6%88=-ezjUQ(8!Uz~dt<`LOPu|5o6qXfs z(;u<6KJxo}&#l+c@DhHwPA_@xUSZ^&lyouTs|e#7Cvu2?I=b-yJscf9Sj2VGJ%lv( zHQYpoS~_#K#dQb0$n%D91D`1cRSult;WfUeRX<+g2cGoN!gHR^(qb4B#E{&}Fvpw3 z@*zhIH&vyR+dNs)H>kfu=`j4qbhSx=Q1Dbiw?e}T#|&nVnRQ=D?ry$x%l3vxd~|M? NYk*+WEU$@{zX9thih=+D diff --git a/IOIOLib/bin/classes/ioio/lib/impl/AbstractPin.class b/IOIOLib/bin/classes/ioio/lib/impl/AbstractPin.class index 4d7f20798e2c0a382b9df36bc06025d13d1fe056..a9e5dce7c8f116e88d924ec0c88a9208e8d44653 100644 GIT binary patch delta 27 jcmX@hdX{y=cSc5q$v+r3aB4HKFz7JwGw4p<$Rq;*fv5+p delta 27 jcmX@hdX{y=cSc6V$v+r3aB4BIFlaOIGw4j-$Rq;*fuIMf diff --git a/IOIOLib/bin/classes/ioio/lib/impl/AbstractResource.class b/IOIOLib/bin/classes/ioio/lib/impl/AbstractResource.class index 8f74d33e709d78d1c51c6d944a1ac4c9aefd22d1..2ad47bf506ec035292a6677506d5eb7645c0e7bd 100644 GIT binary patch delta 70 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 EUkM7xfB*mh diff --git a/IOIOLib/bin/classes/ioio/lib/impl/DigitalInputImpl.class b/IOIOLib/bin/classes/ioio/lib/impl/DigitalInputImpl.class index c0951ec9de30f9251c6c370c9c8f86ae2b7d0beb..ca776c9e83a5675fbd818a266eda167056167f7c 100644 GIT binary patch delta 98 zcmZ23xLk091}me&W=+<+jG{ISEDW{`>jKPCJ voWX1IRQ6I)f1pSJP;DSkZ2*G|LokB^L&#)#4sXs#piC442SfDaVh$4kK5-B9 delta 98 zcmZ23xLk091}mfDW=+<+jH1>IEDSab>8)n&_U_@Bw@S^`ACz7#;+0U>i5_00E&3hvkns{ z^+w0535cq|a9KCCzafxvc7x9%K2_SO<|+c#*6D$FhaZ|rsaUGKze z(9sFWEhsz)N!Nm^Oo@lVsZ0m&wJDH$d#=270F>?n-$_L9mdvvL=b zmzZ!`8{xM|VfMeJ zZ`lcr^X>)GwtJ4)dt{mB&vA!o(Y`>zwMp_3W6!=py;!`&{U2w2=nUqu&gl?umM=#a z0u_j{%;iOPw^xwGDweR;=gkaO{o~E!5zz!SCQ2w1?lDaa$O8lG;a}chDY5w%qU2W$ delta 491 zcmYjOOD_Xa6#nj=wznOliW;(a5d(@^-~`RvI5h zh(TRHE0t@GU9Fr5v^AP}7L*Zox#q1O+fJFmliE!eRWL*}L@_`OrGs+mu;A>wStXS& z1}GWRU?9xkwX1e@F8~w60z>khMU@eZ${^20lNwUc8AQ#lY@HtMsj!e0$PlxP@rYN$ z7{+Ct8-5iN8U!Zg6(3~N@{t>CSHAMXxk-~0I$m0e(-H8K4G{8BRE0)pgDMm^XJrQN z(0)UZql0d>)slb@NrcFc|CFHC>gIQwe Yr3L3OPdfC4mN;G%m~tO|kz9_YZxAv;IsgCw diff --git a/IOIOLib/bin/classes/ioio/lib/impl/FlowControlledOutputStream$FlushThread.class b/IOIOLib/bin/classes/ioio/lib/impl/FlowControlledOutputStream$FlushThread.class index 040180b8f77a61bbd5ee8f13a7b7ad9f974a2f7a..2c326297a14f72af97096b6b3d1a2638a43a3061 100644 GIT binary patch delta 87 zcmbQvKb?QWW>&_u$y-=|yJj-5Fyu0DGvqPwG2}Cd0%<*lLIzWYA_i|D?axrm5W-Lb llqqH?XDDN60g~N7^^+MY8J06-Gpu5$W?0QoJNXOSVgR5S732T_ delta 87 zcmbQvKb?QWW>&`Z$y-=|yJj)4Fyt|CGvqVyF%&R}0%<*lA_h~2Vg_#@?axrc5W-Lj l6fI#W2f`MH3Wjc=`pFDc49gjE7*;XVFsx>%oBV}sF#w|`754xD diff --git a/IOIOLib/bin/classes/ioio/lib/impl/FlowControlledOutputStream.class b/IOIOLib/bin/classes/ioio/lib/impl/FlowControlledOutputStream.class index c1157f0cd8a6e1a41f79efa491bbc9cbc139fc27..c732183f1c289743b5a57b4802342d08c7b72f67 100644 GIT binary patch delta 252 zcmXAiy-osA5QV?lyUX2VApvPt5`#7dZX=qYyn*onY*o+%5*7b{Kz)i^(f9;fY)}dd z8#@yd65q!-Vln5OIVW>wnm(q*&!4wr!uunmEj{wobdG~@F5oRuY#kmP>HI^szG7Sw)#R@sa9W1-Vv?6*>5Hb=49rT{ZREOXEXm<^*I;*D_J#{TzSbis@GZ* TaKo+goT?>#GEhBKzR$(KN*FIG delta 285 zcmZXNy-vbl6o#MgYfEd(fS4GZOc#ePR1?rUFw72a0trnpRMh(ahu2`fk%TK?Aqayp zUVsZM-j4BTN0;-w=S|LYMwMm7|NMUY0CpHm{6ojFd&g-yZ8~4depFOnTGc>NI7s@P zBqKDL+fc>nyF1p;`)HJ0u15BZD4NrbUfG({ok*#v$t+{bJi?*L3N1>ssY_GL57}i; zwvZkh(yU9a3xB2FKbJ2~=-`t4Io5KCmrF_>DJN#($5x+Hql4Hwlf5Q9Co^5)1(#+R eY+F}cn?(?B-iS_f%bl<+DU`?ek{^Um1@8y5gfQ&@ diff --git a/IOIOLib/bin/classes/ioio/lib/impl/FlowControlledPacketSender.class b/IOIOLib/bin/classes/ioio/lib/impl/FlowControlledPacketSender.class index eb76ced0395be95c3fbf3f2e939fdf52c3a60515..e72b01e4b1f8e657e27accc76e2896127bc9f7fa 100644 GIT binary patch delta 14 VcmX>veqMY-KRcts<_YZfEC4Ea1i=6R delta 14 VcmX>veqMY-KRcu1<_YZfEC4Eg1i}CS diff --git a/IOIOLib/bin/classes/ioio/lib/impl/IOIOImpl$State.class b/IOIOLib/bin/classes/ioio/lib/impl/IOIOImpl$State.class deleted file mode 100644 index 5785c2ef23ef456474f0804b6db614b17c79b04d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1180 zcmah}ZBN=z7(Lelg<9X3!*`vV+W?)Qo9+c-bl4@$5SA24$UYU;v6@m$DQ5nw4L@kM z%=zn$8?(+&F!`rLV8KBFw7TkqvjZ5g>tE+6)M^;gQ}Mb<-HF(75!~d zqZYf`4y_JqrTx97>P~TAGZ+>=-qkUhZ#cGV`&$fBGF7EMh4v>aim(EPF%@y#V~FJY zC5dG5TDswTw%g3ZYH%zB$dN#*mQGtXp66b%hJEZ5xbh;Kz7L%zv94bg6EhEJ+8c=@R z@vWAMWvtMdnx1DKH`<5C4C$c1a{a(+_&Ggb`ErRoGsr?k`7^_Mkk&tz)a@tF4P{k4 z!y3azkX~@iPA3<5=2gFI{X8_32E}N1y@vJ9=7%6(7mqrGgP9p~n6 z*S1ByRo!(hPl&NPGF~wx{+~^Tun6{cmhM!d*C4z-#ghQgNFfj--M<*=>VeT|e(_Wp zqFksLBHl2euY~!h@)N8UACXQmdV+BQ5+MO51egj7FfG7rM1VQ@7m=hM#*JF2hY6z= z?qS-fMS7SsPO$Ku`VL@`eia&lJYFD&hhmlQP|Eu%*2n{QA*YvTPx0uQ7@-36NvfF3 zSmJgl&yVq>Pv0c#oSx`mq4r)%pV#tS*`73WhNmmvXy-!4MK^XOAx249?e|Di4S$vr hXX!I!euLf2ljvWE_k2!<__%>h&Llp=OT6Bm{09wK_)Gu* diff --git a/IOIOLib/bin/classes/ioio/lib/impl/IOIOImpl.class b/IOIOLib/bin/classes/ioio/lib/impl/IOIOImpl.class index 153fa9bad8cae1058e3e52c699b3046173877fde..738814284cb8a1db930c980658e187d17008b79b 100644 GIT binary patch literal 20857 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 b@%$Ek_3(C%7x90t`bCac{Sw!AbG-i#$hc81 diff --git a/IOIOLib/bin/classes/ioio/lib/impl/IOIOProtocol$IncomingThread.class b/IOIOLib/bin/classes/ioio/lib/impl/IOIOProtocol$IncomingThread.class index 1fc9970f494913144d64a19e8c818abac812d20c..3233d4fdaf08a91777c49a39b5f93a7691697176 100644 GIT binary patch delta 2302 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 diff --git a/IOIOLib/bin/classes/ioio/lib/impl/IOIOProtocol$PwmScale.class b/IOIOLib/bin/classes/ioio/lib/impl/IOIOProtocol$PwmScale.class index 73a9d447f6c3cc9252b93849104f74e8ffdb2172..5a0471bc869379e3e1f69b3f38102153e060f9f1 100644 GIT binary patch delta 41 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 Qc)@5_2v`2h_ikJ8|4)Hti~s-t literal 12885 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 M6Rw7t&+cyif8Kz|t^fc4 diff --git a/IOIOLib/bin/classes/ioio/lib/impl/IcspMasterImpl.class b/IOIOLib/bin/classes/ioio/lib/impl/IcspMasterImpl.class index d60563fd652727dc1ac6754732245f51b62eb5b4..5434699e3a03edf7d6a44c55732db2cad548bec2 100644 GIT binary patch delta 14 Vcmca7bx&$T4m+d5=3MrVtN<(I1yTS2 delta 14 Vcmca7bx&$T4m+db=3MrVtN<(O1ycY3 diff --git a/IOIOLib/bin/classes/ioio/lib/impl/IncapImpl.class b/IOIOLib/bin/classes/ioio/lib/impl/IncapImpl.class index 6ef1255d042f05bf319dac8ba815b1f025d3b2dd..ad7ed8917ea9f2ab80f04b8afbc6ae74d78e114f 100644 GIT binary patch delta 14 VcmZ21vsh-sVh%=y%}Y4ySpg^r1l|Au delta 14 VcmZ21vsh-sVh%>d%}Y4ySpg^x1m6Gv diff --git a/IOIOLib/bin/classes/ioio/lib/impl/IncomingState.class b/IOIOLib/bin/classes/ioio/lib/impl/IncomingState.class index 98e9961d190fc682d28f36d6c7ea64c63e5e91ce..cf806a190ac8a8561366d9b348c95860b5da2e89 100644 GIT binary patch delta 14 VcmbQ^KgWMVybzI|QW;f=QjEoGMKd_iE0RTqb1_A&8 delta 21 dcmZ3&w}fwlD>I|wW;f=QjEsz%Kd_iE0RTqu1_S^A diff --git a/IOIOLib/bin/classes/ioio/lib/impl/PwmImpl.class b/IOIOLib/bin/classes/ioio/lib/impl/PwmImpl.class index e77877ac538103c198d8bc9e67e19a46bff67c54..604ccd313c7088e99b3f4456404ee3cdbc96c89d 100644 GIT binary patch delta 14 VcmbO(I9+fHf7=FHF$wF9EK)?gh;4uU>if2J80mNVwZ4?9*yGb^%5_WO3fnsO8a;EL{ z+KYNsXIj1C#!fxenYP!x>rd!KFZvfcozmyqq#-g^d(mN-{SNQxdEV#!^7CKc{{WyH ziz<8q+f3WEV|gvCBoHXe6xpqMLFZL!VQj2sGc;7xWm-sxAZqcckqtfyShF zUv$l~e~&<2%AB@zx9AuGJJ;U#mJaf|H64pDxkgVPJpu(+cV)Oe$#<8uD zHZo2m(K%KJfmRJQ2-IN{wy4-FuJ!J&^c#=L9VRzby{>W+<8Zi0v) zs-jb1gU5n-yEsK&578FX;2-=BknF z@kF9CA@N+&a2Y$vdBMEPXd*4(yUybMb}q*)d+N^{7FVWgSW_Lz5hM?o@AA%M;En*K*G%g*OdcU4z0EuqFBe>!Po* zVWLsN3p71LOY~c`Jwy9b-ukdj+AtolhaO#w-NktOm}NgU^K3hBWK3xlMhCwg*nyq& z+{LxbvD?e{E35PIWabIf=yU9ieusAEd5ra3wYW5x1I)cBT5yVC+cq5nAs8nJS3IqA6#OE)juFm58jzZ>4y@1co$Y;?1p!{nqJ zAsoeC9K#`;z%WkIx`)=in8IlfU2CcCGP)^xpGT6oE)Y#U0)Br>b6ZNKn*@GwEpU-$ zq8ge$2t7odfBFBx9x8z!75Ewrm35#0nDPW$yV~#j#2R+4+*d@45_+`qTWI-|@&-!* zjNo&YpTuY0x5QGMKPJEcJ2=ShpCjsFGBAQD(H|l95kgJk5+?WpWRQZ1E4at+rx;_D zaeP6ANp19b+IdiBk2K&zS_w%AJ+6A(KA_K4(v%~G<6J59F0rh5EN@3$K(gHWCu&67 z3cIGL5Gt+-J}o8YCosvNQrJ%!Na45F-}eLuE8I}sUtwL-^`@IIaci6`$(ydw;g`t1 z!i*2&_x&Q&{%GYFHYhKpU!q|LYPr724o+bsZlRs;&UT~`Ap6-9i jv%oxy%(KKicbVstcjfW142jxBk>4QkY~ugInJs?<+8hj* literal 1786 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 diff --git a/IOIOLib/bin/classes/ioio/lib/impl/SpiMasterImpl.class b/IOIOLib/bin/classes/ioio/lib/impl/SpiMasterImpl.class index 4b5af06f91572fbf7ab1d622786a41340a3e191d..73d8977dcfbf1c53699907f071717ff76d0d9313 100644 GIT binary patch delta 14 WcmbQQJ70Ih9bQI-&3AbZaR2};-37V; delta 14 WcmbQQJ70Ih9bQJo&3AbZaR2};;|03_ diff --git a/IOIOLib/bin/classes/ioio/lib/impl/TwiMasterImpl.class b/IOIOLib/bin/classes/ioio/lib/impl/TwiMasterImpl.class index 74ec6439a77408bbc7aa00f4ebd855698c6d3e01..47c7a4ad7e375c88f6523cfe0df950490a1ed513 100644 GIT binary patch delta 14 WcmX@3azd!(ajw;elwKT*_&{m@s)6rwwE3Vq)>O^h0}F(yXTV%4Zo5)*j!!AFfhi7#q2ny4}2cW1Xd+t_N;WY4|le&^it zf2UXfe*Yr?tMRyofS}#7EjyO7hGJRQO2sw~Wn9Ndy78Uyog0&`b;5E_HLA^?5yv!! zH3S8fM~xFkEM=sR#By9gsnT-4pgK`dXN+1gCD<(}Glp!(<+1D+gp+nUZ6;lFn2f6Y z#f#v=45=B(&~;_nZ}M8_&I%0_#P{5x$6Z@&He>*3$zRfg1z>z zSq|E%L&tStl%Ph(LevUMnp+0c$zmNELTYurj&g*;Sc)4p+#tBA5Q;f3(XkB61tBAu zG&7zr+vaDR@4(yl*zqoENPCq%u_}~i(q9T2C5v)>BE?x&xD-rV8`Z@LA1KzA5-Vxxu) zg8CVuQJs?S03Dmq%W};r7pyKSiCHwxa$mPf{ardrQ5M1$LCtJk9YS2t+GkiPb2#GK zk=%6=Wk_4ZYgj9N#M|bYydjM3*r6dozm4>;V_UE=;+5zwm0jF&E@BBx9dv%xn!hThRFeF$KFDeVq;mBdjIeyY`Oy%;ha*mGm%*(MNR~a_@ zq-w72mbkKnQCfrA5G=vcy=il7)Ek14LQ_PNM)t^v8^SR`tv_-jR)!eMWgwL~$91H= zSaTM$(yRSo7dk|P!ycXy)xsq)-&rfg*_*+F;;7dLhUsK07sK&QgL5@ys z)PY9aOd3>$PKhS2%Sf9M_3morUg5-7SmrT)EK@S3v!vcguN5c zqL;AGQ*-V23298^FoS{#1pA|d{hvmKy4tC$to`!GJgq-3ZIn5nQd~bFk-WqqYW39r zrzn2*Me&`o%wT;a_mO*eKH_f`iNC7EM{?rc&GR%NTcpF(2juFAY69h19-_Sk5-8;p7Guqs60bSm{xX{1|NVV;=3oAP%tX58^N^7JnL9j8Se3 z4|!CbkCT56FW~~|Yj_iH^Zad0dQ_cX!Grh$U$M!e_#Qvu7oPuuUp>lm3Bi`7(kM-c zN}Jp&oh+|Txm{6y4jhyO={U}L^a{RER6>+cbcYPcAmtA6)f?e{M2>p&jGUDx$sdInelspb18EcaF}V>PvW(Td99^;k zo1~F1`OVlax1gW)?jk)PEwr(d56W5`l`f=Z9Wtb+r3X*Q9XKoNF)mR&Ev@vS6Bmek zh3Bsk_Xg=D;yxtq6XLE&8?H(_K9`mBvlHJC`z`_G|5_NmM%tc9l2fn>yZt_ZYDO)s3v3$qtz*c#O>$lA>xvx$B1)?J3-uON}M6) zNn)Pj**G!JkUme$%f!4&%Rj-+FQ9?UFERu zXWfXDqc?wgf-+IGX@8+YgY408@|v24T&o|uyrv|sp*f+kWUf&>s17*qy;C2GicLi9 zoe%24!TF%O<;9@d_SG_?AGnpj5U(mpGsA p?JV>JBGO+p?;G+-WO6v;1N_1Z;0)IRJc36_)$7mCCOj4l{sXwBM~?si literal 3701 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 JYJd%-KLPG*noa-! diff --git a/IOIOLib/bin/classes/ioio/lib/util/IOIOConnectionDiscovery.class b/IOIOLib/bin/classes/ioio/lib/util/IOIOConnectionDiscovery.class deleted file mode 100644 index 1cc95f68f12cddeda46cfa075d2ce14b27a1ef72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 410 zcmbV|!D_-l5QhIzYfP%G^d|J!g1vO1r-IT;iVzTT2<>S`0A6q#phtMrPCH@rM%0xyB1w}pcGjxA($2nXpSztZzXJ>i z)2-Y|VWcfYy4kXVFe+4)eW~0NX7hDBZ`Z`mn7B7pYE*0V9v~IaR%g965 zV8dR#wTvipb(yP`HVnD%%Kq86bY# Y6^7gg-1RWx2-_;cHRp&YV~!hyN4o-d=l}o! diff --git a/IOIOLib/bin/classes/ioio/lib/util/SocketIOIOConnectionDiscovery.class b/IOIOLib/bin/classes/ioio/lib/util/SocketIOIOConnectionDiscovery.class deleted file mode 100644 index 884afb9217186139be3f61ad93b9e36e714279e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1186 zcmbtUU2oD*7=8{g3SH+0GIV}ShoEJ+)vpT^mfc`XNXSU6VwhK@oZy+IC8f;lXL%#h zMH6rRQO5Tawo%jV#*3cQ^Y(q8=lyt2fB*UZ6TmaPP>^7#2~T*2BYMUp5RPGb_9q@R z+s$^vb6su+!gF7VvF#mm|3pEGVeQB|whYH|2S&ShL>>y#43#L%B>)WRJ>iOApCPGN zI}E9Y*XJsdSk|B*!?4;CEV*bfVjf9D(FR-|HdBXQ*TWDz#G-y7}T)+)VvIvYR}`W;d7nHN|~% ziU;5DFoE}yBuStkh!m|F1+S1CG?a-#5*BA*b3%f}W9$&|Zs;wul@^(K0#E3az*G7c NfpAC&RO54G{{lr&H$(sc diff --git a/IOIOLib/bin/ioiolib.jar b/IOIOLib/bin/ioiolib.jar deleted file mode 100644 index 691991de9e96c43f2f5683f882e42753c7e23ee5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 97909 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ͤe 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 diff --git a/IOIOLib/src/ioio/lib/api/AnalogInput.java b/IOIOLib/src/ioio/lib/api/AnalogInput.java index dab37f8..1178a54 100644 --- a/IOIOLib/src/ioio/lib/api/AnalogInput.java +++ b/IOIOLib/src/ioio/lib/api/AnalogInput.java @@ -52,10 +52,42 @@ import ioio.lib.api.exception.ConnectionLostException; * Typical usage: * *
+ * {@code
  * AnalogInput potentiometer = ioio.openAnalogInput(40);
  * float value = potentiometer.read();
  * ...
  * potentiometer.close();  // pin 40 can now be used for something else.
+ * }
+ * 
+ *

+ * An alternate usage allows reading periodically sampled data without missing + * samples. The {@link #setBuffer(int)} method must first be called, for setting + * up an internal buffer for queuing samples. Then, samples can be obtained by + * calling {@link #readBuffered()} or {@link #getVoltageBuffered()}. These + * methods will block until a sample is available. If this is undesirable, the + * {@link #available()} method can be called first to check how many samples are + * ready in the buffer. In case the buffer overflows, as result of the client + * not reading fast enough, old samples will be dropped, and the client can + * check {@link #getOverflowCount()} to determine how many samples have been + * lost. The sample rate used for capturing samples can be obtained by calling + * {@link #getSampleRate()}. + *

+ * The non-buffered versions of the read methods will still behave normally when + * buffering is enabled. The {@link #read()} and {@link #getVoltage()} methods + * will always return the most recent value, regardless of the buffer state. + *

+ * Typical usage: + * + *

+ * AnalogInput potentiometer = ioio.openAnalogInput(40);
+ * potentiometer.setBuffer(256);
+ * for (int i = 0; i < 1024; ++i) { 
+ *   // next line will block until at least one sample is available
+ *   float sample = potentiometer.readBuffered();
+ *   ...
+ * }
+ * ...
+ * potentiometer.close();  // pin 40 can now be used for something else.
  * 
* * @see IOIO#openAnalogInput(int) @@ -69,11 +101,13 @@ public interface AnalogInput extends Closeable { * may block shortly. If this is a problem, the calling thread can be * interrupted. *

- * If a scaled value is desired, consider using {@link #read()}. + * If a scaled value is desired, consider using {@link #read()}. * * @return The voltage, in Volt units. - * @throws InterruptedException The calling thread has been interrupted. - * @throws ConnectionLostException The connection with the IOIO is lost. + * @throws InterruptedException + * The calling thread has been interrupted. + * @throws ConnectionLostException + * The connection with the IOIO is lost. * @see #read() */ public float getVoltage() throws InterruptedException, @@ -81,6 +115,7 @@ public interface AnalogInput extends Closeable { /** * Gets the maximum value against which {@link #read()} values are scaled. + * * @return The voltage, in Volts. */ public float getReference(); @@ -96,9 +131,95 @@ public interface AnalogInput extends Closeable { * If an absolute value is desired, consider using {@link #getVoltage()}. * * @return The voltage, in scaled units. - * @throws InterruptedException The calling thread has been interrupted. - * @throws ConnectionLostException The connection with the IOIO is lost. + * @throws InterruptedException + * The calling thread has been interrupted. + * @throws ConnectionLostException + * The connection with the IOIO is lost. * @see #getVoltage() */ public float read() throws InterruptedException, ConnectionLostException; + + /** + * Initializes or destroys an internal buffer, used for queuing sampled + * data. When called with a positive argument, an internal buffer will be + * created, and start storing sampled data. The client can then call + * {@link #readBuffered()} or {@link #getVoltageBuffered()} for obtaining + * buffered samples. + *

+ * When called with argument of 0, the internal buffer is destroyed. + * + * @param capacity + * The maximum number of unread samples that can be buffered + * before overflow occurs. + * @throws ConnectionLostException + * The connection with the IOIO is lost. + */ + public void setBuffer(int capacity) throws ConnectionLostException; + + /** + * Gets the number of samples that have been dropped as result of overflow, + * since {@link #setBuffer(int)} has been called. + * + * @return The number of dropped samples. + * @throws ConnectionLostException + * The connection with the IOIO is lost. + */ + public int getOverflowCount() throws ConnectionLostException; + + /** + * Gets the number of samples currently in the buffer. Reading that many + * samples is guaranteed not to block. + * + * @return The number of samples available in the buffer. + * @throws ConnectionLostException + * The connection with the IOIO is lost. + */ + public int available() throws ConnectionLostException; + + /** + * Read a sample from the internal buffer. This method will block until at + * least one sample is available, the instance is closed (via + * {@link #close()}), the thread is interrupted (via + * {@link Thread#interrupt()} or connection is lost. {@link #setBuffer(int)} + * must be called prior to this method for setting up an internal buffer for + * storing samples. + * + * @see #getVoltageBuffered() + * @return The earliest (oldest) sample available in the buffer, scaled to + * the range [0,1]. + * @throws InterruptedException + * The calling thread has been interrupted. + * @throws ConnectionLostException + * The connection with the IOIO is lost. + */ + public float readBuffered() throws InterruptedException, + ConnectionLostException; + + /** + * Read a sample from the internal buffer. This method will block until at + * least one sample is available, the instance is closed (via + * {@link #close()}), the thread is interrupted (via + * {@link Thread#interrupt()} or connection is lost. {@link #setBuffer(int)} + * must be called prior to this method for setting up an internal buffer for + * storing samples. + * + * @see #readBuffered() + * @return The earliest (oldest) sample available in the buffer, in Volt + * units. + * @throws InterruptedException + * The calling thread has been interrupted. + * @throws ConnectionLostException + * The connection with the IOIO is lost. + */ + public float getVoltageBuffered() throws InterruptedException, + ConnectionLostException; + + /** + * Gets the sample rate used for obtaining buffered samples. + * + * @return The sample rate, in Hz units. + * @throws ConnectionLostException + * The connection with the IOIO is lost. + */ + public float getSampleRate() throws ConnectionLostException; } diff --git a/IOIOLib/src/ioio/lib/api/DigitalInput.java b/IOIOLib/src/ioio/lib/api/DigitalInput.java index 63567e2..f773d88 100644 --- a/IOIOLib/src/ioio/lib/api/DigitalInput.java +++ b/IOIOLib/src/ioio/lib/api/DigitalInput.java @@ -1,140 +1,140 @@ -/* - * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. - * - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied. - */ -package ioio.lib.api; - -import ioio.lib.api.exception.ConnectionLostException; - -/** - * A pin used for digital input. - *

- * A digital input pin can be used to read logic-level signals. DigitalInput - * instances are obtained by calling {@link IOIO#openDigitalInput(Spec)}. - *

- * The value of the pin is obtained by calling {@link #read()}. It is also - * possible for the client to block until a certain level is sensed, by using - * {@link #waitForValue(boolean)}. - *

- * The instance is alive since its creation. The first {@link #read()} call - * block for a few milliseconds until the initial value is updated. If the - * connection with the IOIO drops at any point, the instance transitions to a - * disconnected state, in which every attempt to use the pin (except - * {@link #close()}) will throw a {@link ConnectionLostException}. Whenever - * {@link #close()} is invoked the instance may no longer be used. Any resources - * associated with it are freed and can be reused. - *

- * Typical usage: - * - *

- * DigitalInput button = ioio.openDigitalInput(10);  // used an external pull-up
- * button.waitForValue(false);  // wait for press
- * ...
- * button.close();  // pin 10 can now be used for something else.
- * 
- */ -public interface DigitalInput extends Closeable { - /** - * A digital input pin specification, used when opening digital inputs. - */ - static public class Spec { - /** Input pin mode. */ - public enum Mode { - /** - * Pin is floating. When the pin is left disconnected the value - * sensed is undefined. Use this mode when an external pull-up or - * pull-down resistor is used or when interfacing push-pull type - * logic circuits. - */ - FLOATING, - /** - * Internal pull-up resistor is used. When the pin is left - * disconnected, a logical "HIGH" (true) will be sensed. This is - * useful for interfacing with open drain circuits or for - * interacting with a switch connected between the pin and ground. - */ - PULL_UP, - /** - * Internal pull-down resistor is used. When the pin is left - * disconnected, a logical "LOW" (false) will be sensed. This is - * useful for interacting with a switch connected between the pin - * and Vdd. - */ - PULL_DOWN - } - - /** The pin number, as labeled on the board. */ - public int pin; - /** The pin mode. */ - public Mode mode; - - /** - * Constructor. - * - * @param pin - * Pin number, as labeled on the board. - * @param mode - * Pin mode. - */ - public Spec(int pin, Mode mode) { - this.pin = pin; - this.mode = mode; - } - - /** Shorthand for Spec(pin, Mode.FLOATING). */ - public Spec(int pin) { - this(pin, Mode.FLOATING); - } - } - - /** - * Read the value sensed on the pin. May block for a few milliseconds if - * called right after creation of the instance. If this is a problem, the - * calling thread may be interrupted. - * - * @return True for logical "HIGH", false for logical "LOW". - * @throws InterruptedException - * The calling thread has been interrupted. - * @throws ConnectionLostException - * The connection with the IOIO has been lost. - */ - public boolean read() throws InterruptedException, ConnectionLostException; - - /** - * Block until a desired logical level is sensed. The calling thread can be - * interrupted for aborting this operation. - * - * @param value - * The desired logical level. true for "HIGH", false for "LOW". - * @throws InterruptedException - * The calling thread has been interrupted. - * @throws ConnectionLostException - * The connection with the IOIO has been lost. - */ - public void waitForValue(boolean value) throws InterruptedException, - ConnectionLostException; -} +/* + * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ +package ioio.lib.api; + +import ioio.lib.api.exception.ConnectionLostException; + +/** + * A pin used for digital input. + *

+ * A digital input pin can be used to read logic-level signals. DigitalInput + * instances are obtained by calling {@link IOIO#openDigitalInput(DigitalInput.Spec)}. + *

+ * The value of the pin is obtained by calling {@link #read()}. It is also + * possible for the client to block until a certain level is sensed, by using + * {@link #waitForValue(boolean)}. + *

+ * The instance is alive since its creation. The first {@link #read()} call + * block for a few milliseconds until the initial value is updated. If the + * connection with the IOIO drops at any point, the instance transitions to a + * disconnected state, in which every attempt to use the pin (except + * {@link #close()}) will throw a {@link ConnectionLostException}. Whenever + * {@link #close()} is invoked the instance may no longer be used. Any resources + * associated with it are freed and can be reused. + *

+ * Typical usage: + * + *

+ * DigitalInput button = ioio.openDigitalInput(10);  // used an external pull-up
+ * button.waitForValue(false);  // wait for press
+ * ...
+ * button.close();  // pin 10 can now be used for something else.
+ * 
+ */ +public interface DigitalInput extends Closeable { + /** + * A digital input pin specification, used when opening digital inputs. + */ + static public class Spec { + /** Input pin mode. */ + public enum Mode { + /** + * Pin is floating. When the pin is left disconnected the value + * sensed is undefined. Use this mode when an external pull-up or + * pull-down resistor is used or when interfacing push-pull type + * logic circuits. + */ + FLOATING, + /** + * Internal pull-up resistor is used. When the pin is left + * disconnected, a logical "HIGH" (true) will be sensed. This is + * useful for interfacing with open drain circuits or for + * interacting with a switch connected between the pin and ground. + */ + PULL_UP, + /** + * Internal pull-down resistor is used. When the pin is left + * disconnected, a logical "LOW" (false) will be sensed. This is + * useful for interacting with a switch connected between the pin + * and Vdd. + */ + PULL_DOWN + } + + /** The pin number, as labeled on the board. */ + public int pin; + /** The pin mode. */ + public Mode mode; + + /** + * Constructor. + * + * @param pin + * Pin number, as labeled on the board. + * @param mode + * Pin mode. + */ + public Spec(int pin, Mode mode) { + this.pin = pin; + this.mode = mode; + } + + /** Shorthand for Spec(pin, Mode.FLOATING). */ + public Spec(int pin) { + this(pin, Mode.FLOATING); + } + } + + /** + * Read the value sensed on the pin. May block for a few milliseconds if + * called right after creation of the instance. If this is a problem, the + * calling thread may be interrupted. + * + * @return True for logical "HIGH", false for logical "LOW". + * @throws InterruptedException + * The calling thread has been interrupted. + * @throws ConnectionLostException + * The connection with the IOIO has been lost. + */ + public boolean read() throws InterruptedException, ConnectionLostException; + + /** + * Block until a desired logical level is sensed. The calling thread can be + * interrupted for aborting this operation. + * + * @param value + * The desired logical level. true for "HIGH", false for "LOW". + * @throws InterruptedException + * The calling thread has been interrupted. + * @throws ConnectionLostException + * The connection with the IOIO has been lost. + */ + public void waitForValue(boolean value) throws InterruptedException, + ConnectionLostException; +} diff --git a/IOIOLib/src/ioio/lib/api/DigitalOutput.java b/IOIOLib/src/ioio/lib/api/DigitalOutput.java index fe8a09d..6d6a2a5 100644 --- a/IOIOLib/src/ioio/lib/api/DigitalOutput.java +++ b/IOIOLib/src/ioio/lib/api/DigitalOutput.java @@ -1,120 +1,120 @@ -/* - * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. - * - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied. - */ -package ioio.lib.api; - -import ioio.lib.api.exception.ConnectionLostException; - -/** - * A pin used for digital output. - *

- * A digital input pin can be used to generate logic-level signals. - * DigitalOutput instances are obtained by calling - * {@link IOIO#openDigitalOutput(Spec, boolean)}. - *

- * The value of the pin is set by calling {@link #write(boolean)}. - *

- * The instance is alive since its creation. If the connection with the IOIO - * drops at any point, the instance transitions to a disconnected state, in - * which every attempt to use the pin (except {@link #close()}) will throw a - * {@link ConnectionLostException}. Whenever {@link #close()} is invoked the - * instance may no longer be used. Any resources associated with it are freed - * and can be reused. - *

- * Typical usage: - * - *

- * DigitalOutput led = ioio.openDigitalInput(2);  // LED anode on pin 2.
- * led.write(true);  // turn LED on.
- * ...
- * led.close();  // pin 2 can now be used for something else.
- * 
- */ -public interface DigitalOutput extends Closeable { - /** - * A digital output pin specification, used when opening digital outputs. - */ - public static class Spec { - /** Output pin mode. */ - public enum Mode { - /** - * Pin operates in push-pull mode, i.e. a logical "HIGH" is - * represented by a voltage of Vdd on the pin and a logical "LOW" by - * a voltage of 0 (ground). - */ - NORMAL, - /** - * Pin operates in open-drain mode, i.e. a logical "HIGH" is - * represented by a high impedance on the pin (as if it is - * disconnected) and a logical "LOW" by a voltage of 0 (ground). - * This mode is most commonly used for generating 5V logical signal - * on a 3.3V pin: 5V tolerant pins must be used; a pull-up resistor - * is connected between the pin and 5V, and the pin is used in open- - * drain mode. - */ - OPEN_DRAIN, - } - - /** The pin number, as labeled on the board. */ - public int pin; - /** The pin mode. */ - public Mode mode; - - /** - * Constructor. - * - * @param pin - * Pin number, as labeled on the board. - * @param mode - * Pin mode. - */ - public Spec(int pin, Mode mode) { - this.pin = pin; - this.mode = mode; - } - - /** - * Shorthand for Spec(pin, Mode.NORMAL). - * - * @see #Spec(int, Mode) - */ - public Spec(int pin) { - this(pin, Mode.NORMAL); - } - } - - /** - * Set the output of the pin. - * - * @param val - * The output. true is logical "HIGH", false is logical "LOW". - * @throws ConnectionLostException - * The connection with the IOIO has been lost. - */ - public void write(boolean val) throws ConnectionLostException; -} +/* + * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ +package ioio.lib.api; + +import ioio.lib.api.exception.ConnectionLostException; + +/** + * A pin used for digital output. + *

+ * A digital input pin can be used to generate logic-level signals. + * DigitalOutput instances are obtained by calling + * {@link IOIO#openDigitalOutput(DigitalOutput.Spec, boolean)}. + *

+ * The value of the pin is set by calling {@link #write(boolean)}. + *

+ * The instance is alive since its creation. If the connection with the IOIO + * drops at any point, the instance transitions to a disconnected state, in + * which every attempt to use the pin (except {@link #close()}) will throw a + * {@link ConnectionLostException}. Whenever {@link #close()} is invoked the + * instance may no longer be used. Any resources associated with it are freed + * and can be reused. + *

+ * Typical usage: + * + *

+ * DigitalOutput led = ioio.openDigitalInput(2);  // LED anode on pin 2.
+ * led.write(true);  // turn LED on.
+ * ...
+ * led.close();  // pin 2 can now be used for something else.
+ * 
+ */ +public interface DigitalOutput extends Closeable { + /** + * A digital output pin specification, used when opening digital outputs. + */ + public static class Spec { + /** Output pin mode. */ + public enum Mode { + /** + * Pin operates in push-pull mode, i.e. a logical "HIGH" is + * represented by a voltage of Vdd on the pin and a logical "LOW" by + * a voltage of 0 (ground). + */ + NORMAL, + /** + * Pin operates in open-drain mode, i.e. a logical "HIGH" is + * represented by a high impedance on the pin (as if it is + * disconnected) and a logical "LOW" by a voltage of 0 (ground). + * This mode is most commonly used for generating 5V logical signal + * on a 3.3V pin: 5V tolerant pins must be used; a pull-up resistor + * is connected between the pin and 5V, and the pin is used in open- + * drain mode. + */ + OPEN_DRAIN, + } + + /** The pin number, as labeled on the board. */ + public int pin; + /** The pin mode. */ + public Mode mode; + + /** + * Constructor. + * + * @param pin + * Pin number, as labeled on the board. + * @param mode + * Pin mode. + */ + public Spec(int pin, Mode mode) { + this.pin = pin; + this.mode = mode; + } + + /** + * Shorthand for Spec(pin, Mode.NORMAL). + * + * @see DigitalOutput.Spec#Spec(int, DigitalOutput.Spec.Mode) + */ + public Spec(int pin) { + this(pin, Mode.NORMAL); + } + } + + /** + * Set the output of the pin. + * + * @param val + * The output. true is logical "HIGH", false is logical "LOW". + * @throws ConnectionLostException + * The connection with the IOIO has been lost. + */ + public void write(boolean val) throws ConnectionLostException; +} diff --git a/IOIOLib/src/ioio/lib/api/IOIO.java b/IOIOLib/src/ioio/lib/api/IOIO.java index 4851f5c..627e2e7 100644 --- a/IOIOLib/src/ioio/lib/api/IOIO.java +++ b/IOIOLib/src/ioio/lib/api/IOIO.java @@ -1,638 +1,696 @@ -/* - * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. - * - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied. - */ -package ioio.lib.api; - -import ioio.lib.api.PulseInput.PulseMode; -import ioio.lib.api.TwiMaster.Rate; -import ioio.lib.api.Uart.Parity; -import ioio.lib.api.Uart.StopBits; -import ioio.lib.api.exception.ConnectionLostException; -import ioio.lib.api.exception.IncompatibilityException; -import ioio.lib.api.exception.OutOfResourceException; - -import java.io.Closeable; - -/** - * This interface provides control over all the IOIO board functions. - *

- * An instance of this interface is typically obtained by using the - * {@link IOIOFactory} class. Initially, a connection should be established, by - * calling {@link #waitForConnect()}. This method will block until the board is - * connected an a connection has been established. - *

- * During the connection process, this library verifies that the IOIO firmware - * is compatible with the required version. If not, {@link #waitForConnect()} - * will throw a {@link IncompatibilityException}, putting the {@link IOIO} - * instance in a "zombie" state: nothing could be done with it except calling - * {@link #disconnect()}, or waiting for the physical connection to drop via - * {@link #waitForDisconnect()}. - *

- * As soon as a connection is established, the IOIO can be used, typically, by - * calling the openXXX() functions to obtain additional interfaces for - * controlling specific function of the board. - *

- * Whenever a connection is lost as a result of physically disconnecting the - * board or as a result of calling {@link #disconnect()}, this instance and all - * the interfaces obtained from it become invalid, and will throw a - * {@link ConnectionLostException} on every operation. Once the connection is - * lost, those instances cannot be recycled, but rather it is required to create - * new ones and wait for a connection again. - *

- * Initially all pins are tri-stated (floating), and all functions are disabled. - * Whenever a connection is lost or dropped, the board will immediately return - * to the this initial state. - *

- * Typical usage: - * - *

- * IOIO ioio = IOIOFactory.create();
- * try {
- *   ioio.waitForConnect();
- *   DigitalOutput out = ioio.openDigitalOutput(10);
- *   out.write(true);
- *   ...
- * } catch (ConnectionLostException e) {
- * } catch (Exception e) {
- *   ioio.disconnect();
- * } finally {
- *   ioio.waitForDisconnect();
- * }
- * 
- * - * @see IOIOFactory#create() - */ -public interface IOIO { - /** An invalid pin number. */ - public static final int INVALID_PIN = -1; - /** The pin number used to designate the on-board 'stat' LED. */ - public static final int LED_PIN = 0; - - /** - * A versioned component in the system. - * - * @see IOIO#getImplVersion(VersionType) - */ - public enum VersionType { - /** Hardware version. */ - HARDWARE_VER, - /** Bootloader version. */ - BOOTLOADER_VER, - /** Application layer firmware version. */ - APP_FIRMWARE_VER, - /** IOIOLib version. */ - IOIOLIB_VER - } - - /** - * Establishes connection with the IOIO board. - *

- * This method is blocking until connection is established. This method can - * be aborted by calling {@link #disconnect()}. In this case, it will throw - * a {@link ConnectionLostException}. - * - * @throws ConnectionLostException - * An error occurred during connection or disconnect() has been - * called during connection. The instance state is disconnected. - * @throws IncompatibilityException - * An incompatible board firmware of hardware has been detected. - * The instance state is disconnected. - * @see #disconnect() - * @see #waitForDisconnect() - */ - public void waitForConnect() throws ConnectionLostException, - IncompatibilityException; - - /** - * Closes the connection to the board, or aborts a connection process - * started with waitForConnect(). - *

- * Once this method is called, this IOIO instance and all the instances - * obtain from it become invalid and will throw an exception on every - * operation. - *

- * This method is asynchronous, i.e. it returns immediately, but it is not - * guaranteed that all connection-related resources has already been freed - * and can be reused upon return. In cases when this is important, client - * can call {@link #waitForDisconnect()}, which will block until all - * resources have been freed. - */ - public void disconnect(); - - /** - * Blocks until IOIO has been disconnected and all connection-related - * resources have been freed, so that a new connection can be attempted. - * - * @throws InterruptedException - * When interrupt() has been called on this thread. This might - * mean that an immediate attempt to create and connect a new - * IOIO object might fail for resource contention. - * @see #disconnect() - * @see #waitForConnect() - */ - public void waitForDisconnect() throws InterruptedException; - - /** - * Resets the entire state (returning to initial state), without dropping - * the connection. - *

- * It is equivalent to calling {@link Closeable#close()} on every interface - * obtained from this instance. A connection must have been established - * prior to calling this method, by invoking {@link #waitForConnect()}. - * - * @throws ConnectionLostException - * Connection was lost before or during the execution of this - * method. - * @see #hardReset() - */ - public void softReset() throws ConnectionLostException; - - /** - * Equivalent to disconnecting and reconnecting the board power supply. - *

- * The connection will be dropped and not reestablished. Full boot sequence - * will take place, so firmware upgrades can be performed. A connection must - * have been established prior to calling this method, by invoking - * {@link #waitForConnect()}. - * - * @throws ConnectionLostException - * Connection was lost before or during the execution of this - * method. - * @see #softReset() - */ - public void hardReset() throws ConnectionLostException; - - /** - * Query the implementation version of the system's components. The - * implementation version uniquely identifies a hardware revision or a - * software build. Returned version IDs are always 8-character long, - * according to the IOIO versioning system: first 4 characters are the - * version authority and last 4 characters are the revision. - * - * @param v - * The component whose version we query. - * @return An 8-character implementation version ID. - * @throws ConnectionLostException - * Connection was lost before or during the execution of this - * method. - */ - public String getImplVersion(VersionType v) throws ConnectionLostException; - - /** - * Open a pin for digital input. - *

- * A digital input pin can be used to read logic-level signals. The pin will - * operate in this mode until close() is invoked on the returned interface. - * It is illegal to open a pin that has already been opened and has not been - * closed. A connection must have been established prior to calling this - * method, by invoking {@link #waitForConnect()}. - * - * @param spec - * Pin specification, consisting of the pin number, as labeled on - * the board, and the mode, which determines whether the pin will - * be floating, pull-up or pull-down. See - * {@link DigitalInput.Spec.Mode} for more information. - * @return Interface of the assigned pin. - * @throws ConnectionLostException - * Connection was lost before or during the execution of this - * method. - * @see DigitalInput - */ - public DigitalInput openDigitalInput(DigitalInput.Spec spec) - throws ConnectionLostException; - - /** - * Shorthand for openDigitalInput(new DigitalInput.Spec(pin)). - * - * @see #openDigitalInput(ioio.lib.api.DigitalInput.Spec) - */ - public DigitalInput openDigitalInput(int pin) - throws ConnectionLostException; - - /** - * Shorthand for openDigitalInput(new DigitalInput.Spec(pin, mode)). - * - * @see #openDigitalInput(ioio.lib.api.DigitalInput.Spec) - */ - public DigitalInput openDigitalInput(int pin, DigitalInput.Spec.Mode mode) - throws ConnectionLostException; - - /** - * Open a pin for digital output. - *

- * A digital output pin can be used to generate logic-level signals. The pin - * will operate in this mode until close() is invoked on the returned - * interface. It is illegal to open a pin that has already been opened and - * has not been closed. A connection must have been established prior to - * calling this method, by invoking {@link #waitForConnect()}. - * - * @param spec - * Pin specification, consisting of the pin number, as labeled on - * the board, and the mode, which determines whether the pin will - * be normal or open-drain. See {@link DigitalOutput.Spec.Mode} - * for more information. - * @param startValue - * The initial logic level this pin will generate as soon at it - * is open. - * @return Interface of the assigned pin. - * @throws ConnectionLostException - * Connection was lost before or during the execution of this - * method. - * @see DigitalOutput - */ - public DigitalOutput openDigitalOutput(DigitalOutput.Spec spec, - boolean startValue) throws ConnectionLostException; - - /** - * Shorthand for openDigitalOutput(new DigitalOutput.Spec(pin, mode), - * startValue). - * - * @see #openDigitalOutput(ioio.lib.api.DigitalOutput.Spec, boolean) - */ - public DigitalOutput openDigitalOutput(int pin, - DigitalOutput.Spec.Mode mode, boolean startValue) - throws ConnectionLostException; - - /** - * Shorthand for openDigitalOutput(new DigitalOutput.Spec(pin), startValue). - * Pin mode will be "normal" (as opposed to "open-drain". - * - * @see #openDigitalOutput(ioio.lib.api.DigitalOutput.Spec, boolean) - */ - public DigitalOutput openDigitalOutput(int pin, boolean startValue) - throws ConnectionLostException; - - /** - * Shorthand for openDigitalOutput(new DigitalOutput.Spec(pin), false). Pin - * mode will be "normal" (as opposed to "open-drain". - * - * @see #openDigitalOutput(ioio.lib.api.DigitalOutput.Spec, boolean) - */ - public DigitalOutput openDigitalOutput(int pin) - throws ConnectionLostException; - - /** - * Open a pin for analog input. - *

- * An analog input pin can be used to measure voltage. Note that not every - * pin can be used as an analog input. See board documentation for the legal - * pins and permitted voltage range. - *

- * The pin will operate in this mode until close() is invoked on the - * returned interface. It is illegal to open a pin that has already been - * opened and has not been closed. A connection must have been established - * prior to calling this method, by invoking {@link #waitForConnect()}. - * - * @param pin - * Pin number, as labeled on the board. - * @return Interface of the assigned pin. - * @throws ConnectionLostException - * Connection was lost before or during the execution of this - * method. - * @see AnalogInput - */ - public AnalogInput openAnalogInput(int pin) throws ConnectionLostException; - - /** - * Open a pin for PWM (Pulse-Width Modulation) output. - *

- * A PWM pin produces a logic-level PWM signal. These signals are typically - * used for simulating analog outputs for controlling the intensity of LEDs, - * the rotation speed of motors, etc. They are also frequently used for - * controlling hobby servo motors. - *

- * Note that not every pin can be used as PWM output. In addition, the total - * number of concurrent PWM modules in use is limited. See board - * documentation for the legal pins and limit on concurrent usage. - *

- * The pin will operate in this mode until close() is invoked on the - * returned interface. It is illegal to open a pin that has already been - * opened and has not been closed. A connection must have been established - * prior to calling this method, by invoking {@link #waitForConnect()}. - * - * @param spec - * Pin specification, consisting of the pin number, as labeled on - * the board, and the mode, which determines whether the pin will - * be normal or open-drain. See {@link DigitalOutput.Spec.Mode} - * for more information. - * @param freqHz - * PWM frequency, in Hertz. - * @return Interface of the assigned pin. - * @throws ConnectionLostException - * Connection was lost before or during the execution of this - * method. - * @throws OutOfResourceException - * This is a runtime exception, so it is not necessary to catch - * it if the client guarantees that the total number of - * concurrent PWM resources is not exceeded. - * @see PwmOutput - */ - public PwmOutput openPwmOutput(DigitalOutput.Spec spec, int freqHz) - throws ConnectionLostException; - - /** - * Shorthand for openPwmOutput(new DigitalOutput.Spec(pin), freqHz). - * - * @see #openPwmOutput(ioio.lib.api.DigitalOutput.Spec, int) - */ - public PwmOutput openPwmOutput(int pin, int freqHz) - throws ConnectionLostException; - - /** - * Open a pin for pulse input. - *

- * The pulse input module is quite flexible. It enables several kinds of - * timing measurements on a digital signal: pulse width measurement - * (positive or negative pulse), and frequency of a periodic signal. - *

- * Note that not every pin can be used as pulse input. In addition, the - * total number of concurrent pulse input modules in use is limited. See - * board documentation for the legal pins and limit on concurrent usage. - *

- * The pin will operate in this mode until close() is invoked on the - * returned interface. It is illegal to open a pin that has already been - * opened and has not been closed. A connection must have been established - * prior to calling this method, by invoking {@link #waitForConnect()}. - * - * @param spec - * Pin specification, consisting of the pin number, as labeled on - * the board, and the mode, which determines whether the pin will - * be floating, pull-up or pull-down. See - * {@link DigitalInput.Spec.Mode} for more information. - * @param rate - * The clock rate to use for timing the signal. A faster clock - * rate will result in better precision but will only be able to - * measure narrow pulses / high frequencies. - * @param mode - * The mode in which to operate. Determines whether the module - * will measure pulse durations or frequency. - * @param doublePrecision - * Whether to open a double-precision pulse input module. Double- - * precision modules enable reading of much longer pulses and - * lower frequencies with high accuracy than single precision - * modules. However, their number is limited, so when possible, - * and if the resources are all needed, use single-precision. For - * more details on the exact spec of single- vs. double- - * precision, see {@link PulseInput}. - * @return An instance of the {@link PulseInput}, which can be used to - * obtain the data. - * @throws ConnectionLostException - * Connection was lost before or during the execution of this - * method. - * @throws OutOfResourceException - * This is a runtime exception, so it is not necessary to catch - * it if the client guarantees that the total number of - * concurrent PWM resources is not exceeded. - * @see PulseInput - */ - public PulseInput openPulseInput(DigitalInput.Spec spec, - PulseInput.ClockRate rate, PulseInput.PulseMode mode, - boolean doublePrecision) throws ConnectionLostException; - - /** - * Shorthand for openPulseInput(new DigitalInput.Spec(pin), rate, mode, - * true), i.e. opens a double-precision, 16MHz pulse input on the given pin - * with the given mode. - * - * @see #openPulseInput(ioio.lib.api.DigitalInput.Spec, - * ioio.lib.api.PulseInput.ClockRate, PulseMode, boolean) - */ - public PulseInput openPulseInput(int pin, PulseMode mode) - throws ConnectionLostException; - - /** - * Open a UART module, enabling a bulk transfer of byte buffers. - *

- * UART is a very common hardware communication protocol, enabling full- - * duplex, asynchronous point-to-point data transfer. It typically serves - * for opening consoles or as a basis for higher-level protocols, such as - * MIDI RS-232, and RS-485. - *

- * Note that not every pin can be used for UART RX or TX. In addition, the - * total number of concurrent UART modules in use is limited. See board - * documentation for the legal pins and limit on concurrent usage. - *

- * The UART module will operate, and the pins will work in their respective - * modes until close() is invoked on the returned interface. It is illegal - * to use pins that have already been opened and has not been closed. A - * connection must have been established prior to calling this method, by - * invoking {@link #waitForConnect()}. - * - * @param rx - * Pin specification for the RX pin, consisting of the pin - * number, as labeled on the board, and the mode, which - * determines whether the pin will be floating, pull-up or - * pull-down. See {@link DigitalInput.Spec.Mode} for more - * information. null can be passed to designate that we do not - * want RX input to this module. - * @param tx - * Pin specification for the TX pin, consisting of the pin - * number, as labeled on the board, and the mode, which - * determines whether the pin will be normal or open-drain. See - * {@link DigitalOutput.Spec.Mode} for more information. null can - * be passed to designate that we do not want TX output to this - * module. - * @param baud - * The clock frequency of the UART module in Hz. - * @param parity - * The parity mode, as in {@link Parity}. - * @param stopbits - * Number of stop bits, as in {@link StopBits}. - * @return Interface of the assigned module. - * @throws ConnectionLostException - * Connection was lost before or during the execution of this - * method. - * @throws OutOfResourceException - * This is a runtime exception, so it is not necessary to catch - * it if the client guarantees that the total number of - * concurrent UART resources is not exceeded. - * @see Uart - */ - public Uart openUart(DigitalInput.Spec rx, DigitalOutput.Spec tx, int baud, - Parity parity, StopBits stopbits) throws ConnectionLostException; - - /** - * Shorthand for - * {@link #openUart(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.DigitalOutput.Spec, int, Parity, StopBits)} - * , where the input pins use their default specs. {@link #INVALID_PIN} can - * be used on either pin if a TX- or RX-only UART is needed. - * - * @see #openUart(ioio.lib.api.DigitalInput.Spec, - * ioio.lib.api.DigitalOutput.Spec, int, Parity, StopBits) - */ - public Uart openUart(int rx, int tx, int baud, Parity parity, - StopBits stopbits) throws ConnectionLostException; - - /** - * Open a SPI master module, enabling communication with multiple - * SPI-enabled slave modules. - *

- * SPI is a common hardware communication protocol, enabling full-duplex, - * synchronous point-to-multi-point data transfer. It requires MOSI, MISO - * and CLK lines shared by all nodes, as well as a SS line per slave, - * connected between this slave and a respective pin on the master. The MISO - * line should operate in pull-up mode, using either the internal pull-up or - * an external resistor. - *

- * Note that not every pin can be used for SPI MISO, MOSI or CLK. In - * addition, the total number of concurrent SPI modules in use is limited. - * See board documentation for the legal pins and limit on concurrent usage. - *

- * The SPI module will operate, and the pins will work in their respective - * modes until close() is invoked on the returned interface. It is illegal - * to use pins that have already been opened and has not been closed. A - * connection must have been established prior to calling this method, by - * invoking {@link #waitForConnect()}. - * - * @param miso - * Pin specification for the MISO (Master In Slave Out) pin, - * consisting of the pin number, as labeled on the board, and the - * mode, which determines whether the pin will be floating, - * pull-up or pull-down. See {@link DigitalInput.Spec.Mode} for - * more information. - * @param mosi - * Pin specification for the MOSI (Master Out Slave In) pin, - * consisting of the pin number, as labeled on the board, and the - * mode, which determines whether the pin will be normal or - * open-drain. See {@link DigitalOutput.Spec.Mode} for more - * information. - * @param clk - * Pin specification for the CLK pin, consisting of the pin - * number, as labeled on the board, and the mode, which - * determines whether the pin will be normal or open-drain. See - * {@link DigitalOutput.Spec.Mode} for more information. - * @param slaveSelect - * An array of pin specifications for each of the slaves' SS - * (Slave Select) pin. The index of this array designates the - * slave index, used later to refer to this slave. The spec is - * consisting of the pin number, as labeled on the board, and the - * mode, which determines whether the pin will be normal or - * open-drain. See {@link DigitalOutput.Spec.Mode} for more - * information. - * @param config - * The configuration of the SPI module. See - * {@link SpiMaster.Config} for details. - * @return Interface of the assigned module. - * @throws ConnectionLostException - * Connection was lost before or during the execution of this - * method. - * @throws OutOfResourceException - * This is a runtime exception, so it is not necessary to catch - * it if the client guarantees that the total number of - * concurrent SPI resources is not exceeded. - * @see SpiMaster - */ - public SpiMaster openSpiMaster(DigitalInput.Spec miso, - DigitalOutput.Spec mosi, DigitalOutput.Spec clk, - DigitalOutput.Spec[] slaveSelect, SpiMaster.Config config) - throws ConnectionLostException; - -/** - * Shorthand for {@link #openSpiMaster(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config), - * where the pins are all open with the default modes and default configuration values are used. - * @see #openSpiMaster(ioio.lib.api.DigitalInput.Spec, - * ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, - * ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config) - */ - public SpiMaster openSpiMaster(int miso, int mosi, int clk, - int[] slaveSelect, SpiMaster.Rate rate) - throws ConnectionLostException; - - /** - * Shorthand for {@link #openSpiMaster(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config), - * where the MISO pins is opened with pull up, and the other pins are open - * with the default modes and default configuration values are used. - * In this version, a single slave is used. - * @see #openSpiMaster(ioio.lib.api.DigitalInput.Spec, - * ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, - * ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config) - */ - public SpiMaster openSpiMaster(int miso, int mosi, int clk, - int slaveSelect, SpiMaster.Rate rate) - throws ConnectionLostException; - - /** - * Open a TWI (Two-Wire Interface, such as I2C/SMBus) master module, - * enabling communication with multiple TWI-enabled slave modules. - *

- * TWI is a common hardware communication protocol, enabling half-duplex, - * synchronous point-to-multi-point data transfer. It requires a physical - * connection of two lines (SDA, SCL) shared by all the bus nodes, where the - * SDA is open-drain and externally pulled-up. - *

- * Note that there is a fixed number of TWI modules, and the pins they use - * are static. Client has to make sure these pins are not already opened - * before calling this method. See board documentation for the number of - * modules and the respective pins they use. - *

- * The TWI module will operate, and the pins will work in their respective - * modes until close() is invoked on the returned interface. It is illegal - * to use pins that have already been opened and has not been closed. A - * connection must have been established prior to calling this method, by - * invoking {@link #waitForConnect()}. - * - * @param twiNum - * The TWI module index to use. Will also determine the pins - * used. - * @param rate - * The clock rate. Can be 100KHz / 400KHz / 1MHz. - * @param smbus - * When true, will use SMBus voltage levels. When false, I2C - * voltage levels. - * @return Interface of the assigned module. - * @throws ConnectionLostException - * Connection was lost before or during the execution of this - * method. - * @see TwiMaster - */ - public TwiMaster openTwiMaster(int twiNum, Rate rate, boolean smbus) - throws ConnectionLostException; - - /** - * Open an ICSP channel, enabling Flash programming of an external PIC MCU, - * and in particular, another IOIO board. - *

- * ICSP (In-Circuit Serial Programming) is a protocol intended for - * programming of PIC MCUs. It is a serial protocol over three wires: PGC - * (clock), PGD (data) and MCLR (reset), where PGC and MCLR are controlled - * by the master and PGD is shared by the master and slave, depending on the - * transaction state. - *

- * Note that there is only one ICSP modules, and the pins it uses are - * static. Client has to make sure that the ICSP module is not already in - * use, as well as those dedicated pins. See board documentation for the - * actual pins used for ICSP. - * - * @return Interface of the ICSP module. - * @see IcspMaster - * @throws ConnectionLostException - * Connection was lost before or during the execution of this - * method. - */ - public IcspMaster openIcspMaster() throws ConnectionLostException; -} \ No newline at end of file +/* + * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ +package ioio.lib.api; + +import ioio.lib.api.PulseInput.PulseMode; +import ioio.lib.api.TwiMaster.Rate; +import ioio.lib.api.Uart.Parity; +import ioio.lib.api.Uart.StopBits; +import ioio.lib.api.exception.ConnectionLostException; +import ioio.lib.api.exception.IncompatibilityException; +import ioio.lib.api.exception.OutOfResourceException; + +import java.io.Closeable; + +/** + * This interface provides control over all the IOIO board functions. + *

+ * An instance of this interface is typically obtained by using the + * {@link IOIOFactory} class. Initially, a connection should be established, by + * calling {@link #waitForConnect()}. This method will block until the board is + * connected an a connection has been established. + *

+ * During the connection process, this library verifies that the IOIO firmware + * is compatible with the required version. If not, {@link #waitForConnect()} + * will throw a {@link IncompatibilityException}, putting the {@link IOIO} + * instance in a "zombie" state: nothing could be done with it except calling + * {@link #disconnect()}, or waiting for the physical connection to drop via + * {@link #waitForDisconnect()}. + *

+ * As soon as a connection is established, the IOIO can be used, typically, by + * calling the openXXX() functions to obtain additional interfaces for + * controlling specific function of the board. + *

+ * Whenever a connection is lost as a result of physically disconnecting the + * board or as a result of calling {@link #disconnect()}, this instance and all + * the interfaces obtained from it become invalid, and will throw a + * {@link ConnectionLostException} on every operation. Once the connection is + * lost, those instances cannot be recycled, but rather it is required to create + * new ones and wait for a connection again. + *

+ * Initially all pins are tri-stated (floating), and all functions are disabled. + * Whenever a connection is lost or dropped, the board will immediately return + * to the this initial state. + *

+ * Typical usage: + * + *

+ * IOIO ioio = IOIOFactory.create();
+ * try {
+ *   ioio.waitForConnect();
+ *   DigitalOutput out = ioio.openDigitalOutput(10);
+ *   out.write(true);
+ *   ...
+ * } catch (ConnectionLostException e) {
+ * } catch (Exception e) {
+ *   ioio.disconnect();
+ * } finally {
+ *   ioio.waitForDisconnect();
+ * }
+ * 
+ * + * @see IOIOFactory#create() + */ +public interface IOIO { + /** An invalid pin number. */ + public static final int INVALID_PIN = -1; + /** The pin number used to designate the on-board 'stat' LED. */ + public static final int LED_PIN = 0; + + /** + * A versioned component in the system. + * + * @see IOIO#getImplVersion(VersionType) + */ + public enum VersionType { + /** Hardware version. */ + HARDWARE_VER, + /** Bootloader version. */ + BOOTLOADER_VER, + /** Application layer firmware version. */ + APP_FIRMWARE_VER, + /** IOIOLib version. */ + IOIOLIB_VER + } + + /** + * A state of a IOIO instance. + */ + public enum State { + /** Connection not yet established. */ + INIT, + /** Connected. */ + CONNECTED, + /** Connection established, incompatible firmware detected. */ + INCOMPATIBLE, + /** Disconnected. Instance is useless. */ + DEAD + } + + /** + * Establishes connection with the IOIO board. + *

+ * This method is blocking until connection is established. This method can + * be aborted by calling {@link #disconnect()}. In this case, it will throw + * a {@link ConnectionLostException}. + * + * @throws ConnectionLostException + * An error occurred during connection or disconnect() has been + * called during connection. The instance state is disconnected. + * @throws IncompatibilityException + * An incompatible board firmware of hardware has been detected. + * The instance state is disconnected. + * @see #disconnect() + * @see #waitForDisconnect() + */ + public void waitForConnect() throws ConnectionLostException, + IncompatibilityException; + + /** + * Closes the connection to the board, or aborts a connection process + * started with waitForConnect(). + *

+ * Once this method is called, this IOIO instance and all the instances + * obtain from it become invalid and will throw an exception on every + * operation. + *

+ * This method is asynchronous, i.e. it returns immediately, but it is not + * guaranteed that all connection-related resources has already been freed + * and can be reused upon return. In cases when this is important, client + * can call {@link #waitForDisconnect()}, which will block until all + * resources have been freed. + */ + public void disconnect(); + + /** + * Blocks until IOIO has been disconnected and all connection-related + * resources have been freed, so that a new connection can be attempted. + * + * @throws InterruptedException + * When interrupt() has been called on this thread. This might + * mean that an immediate attempt to create and connect a new + * IOIO object might fail for resource contention. + * @see #disconnect() + * @see #waitForConnect() + */ + public void waitForDisconnect() throws InterruptedException; + + /** + * Gets the connections state. + * + * @return The connection state. + */ + public State getState(); + + /** + * Resets the entire state (returning to initial state), without dropping + * the connection. + *

+ * It is equivalent to calling {@link Closeable#close()} on every interface + * obtained from this instance. A connection must have been established + * prior to calling this method, by invoking {@link #waitForConnect()}. + * + * @throws ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @see #hardReset() + */ + public void softReset() throws ConnectionLostException; + + /** + * Equivalent to disconnecting and reconnecting the board power supply. + *

+ * The connection will be dropped and not reestablished. Full boot sequence + * will take place, so firmware upgrades can be performed. A connection must + * have been established prior to calling this method, by invoking + * {@link #waitForConnect()}. + * + * @throws ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @see #softReset() + */ + public void hardReset() throws ConnectionLostException; + + /** + * Query the implementation version of the system's components. The + * implementation version uniquely identifies a hardware revision or a + * software build. Returned version IDs are always 8-character long, + * according to the IOIO versioning system: first 4 characters are the + * version authority and last 4 characters are the revision. + * + * @param v + * The component whose version we query. + * @return An 8-character implementation version ID. + * @throws ConnectionLostException + * Connection was lost before or during the execution of this + * method. + */ + public String getImplVersion(VersionType v) throws ConnectionLostException; + + /** + * Open a pin for digital input. + *

+ * A digital input pin can be used to read logic-level signals. The pin will + * operate in this mode until close() is invoked on the returned interface. + * It is illegal to open a pin that has already been opened and has not been + * closed. A connection must have been established prior to calling this + * method, by invoking {@link #waitForConnect()}. + * + * @param spec + * Pin specification, consisting of the pin number, as labeled on + * the board, and the mode, which determines whether the pin will + * be floating, pull-up or pull-down. See + * {@link DigitalInput.Spec.Mode} for more information. + * @return Interface of the assigned pin. + * @throws ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @see DigitalInput + */ + public DigitalInput openDigitalInput(DigitalInput.Spec spec) + throws ConnectionLostException; + + /** + * Shorthand for openDigitalInput(new DigitalInput.Spec(pin)). + * + * @see #openDigitalInput(ioio.lib.api.DigitalInput.Spec) + */ + public DigitalInput openDigitalInput(int pin) + throws ConnectionLostException; + + /** + * Shorthand for openDigitalInput(new DigitalInput.Spec(pin, mode)). + * + * @see #openDigitalInput(ioio.lib.api.DigitalInput.Spec) + */ + public DigitalInput openDigitalInput(int pin, DigitalInput.Spec.Mode mode) + throws ConnectionLostException; + + /** + * Open a pin for digital output. + *

+ * A digital output pin can be used to generate logic-level signals. The pin + * will operate in this mode until close() is invoked on the returned + * interface. It is illegal to open a pin that has already been opened and + * has not been closed. A connection must have been established prior to + * calling this method, by invoking {@link #waitForConnect()}. + * + * @param spec + * Pin specification, consisting of the pin number, as labeled on + * the board, and the mode, which determines whether the pin will + * be normal or open-drain. See {@link DigitalOutput.Spec.Mode} + * for more information. + * @param startValue + * The initial logic level this pin will generate as soon at it + * is open. + * @return Interface of the assigned pin. + * @throws ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @see DigitalOutput + */ + public DigitalOutput openDigitalOutput(DigitalOutput.Spec spec, + boolean startValue) throws ConnectionLostException; + + /** + * Shorthand for openDigitalOutput(new DigitalOutput.Spec(pin, mode), + * startValue). + * + * @see #openDigitalOutput(ioio.lib.api.DigitalOutput.Spec, boolean) + */ + public DigitalOutput openDigitalOutput(int pin, + DigitalOutput.Spec.Mode mode, boolean startValue) + throws ConnectionLostException; + + /** + * Shorthand for openDigitalOutput(new DigitalOutput.Spec(pin), startValue). + * Pin mode will be "normal" (as opposed to "open-drain". + * + * @see #openDigitalOutput(ioio.lib.api.DigitalOutput.Spec, boolean) + */ + public DigitalOutput openDigitalOutput(int pin, boolean startValue) + throws ConnectionLostException; + + /** + * Shorthand for openDigitalOutput(new DigitalOutput.Spec(pin), false). Pin + * mode will be "normal" (as opposed to "open-drain". + * + * @see #openDigitalOutput(ioio.lib.api.DigitalOutput.Spec, boolean) + */ + public DigitalOutput openDigitalOutput(int pin) + throws ConnectionLostException; + + /** + * Open a pin for analog input. + *

+ * An analog input pin can be used to measure voltage. Note that not every + * pin can be used as an analog input. See board documentation for the legal + * pins and permitted voltage range. + *

+ * The pin will operate in this mode until close() is invoked on the + * returned interface. It is illegal to open a pin that has already been + * opened and has not been closed. A connection must have been established + * prior to calling this method, by invoking {@link #waitForConnect()}. + * + * @param pin + * Pin number, as labeled on the board. + * @return Interface of the assigned pin. + * @throws ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @see AnalogInput + */ + public AnalogInput openAnalogInput(int pin) throws ConnectionLostException; + + /** + * Open a pin for PWM (Pulse-Width Modulation) output. + *

+ * A PWM pin produces a logic-level PWM signal. These signals are typically + * used for simulating analog outputs for controlling the intensity of LEDs, + * the rotation speed of motors, etc. They are also frequently used for + * controlling hobby servo motors. + *

+ * Note that not every pin can be used as PWM output. In addition, the total + * number of concurrent PWM modules in use is limited. See board + * documentation for the legal pins and limit on concurrent usage. + *

+ * The pin will operate in this mode until close() is invoked on the + * returned interface. It is illegal to open a pin that has already been + * opened and has not been closed. A connection must have been established + * prior to calling this method, by invoking {@link #waitForConnect()}. + * + * @param spec + * Pin specification, consisting of the pin number, as labeled on + * the board, and the mode, which determines whether the pin will + * be normal or open-drain. See {@link DigitalOutput.Spec.Mode} + * for more information. + * @param freqHz + * PWM frequency, in Hertz. + * @return Interface of the assigned pin. + * @throws ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @throws OutOfResourceException + * This is a runtime exception, so it is not necessary to catch + * it if the client guarantees that the total number of + * concurrent PWM resources is not exceeded. + * @see PwmOutput + */ + public PwmOutput openPwmOutput(DigitalOutput.Spec spec, int freqHz) + throws ConnectionLostException; + + /** + * Shorthand for openPwmOutput(new DigitalOutput.Spec(pin), freqHz). + * + * @see #openPwmOutput(ioio.lib.api.DigitalOutput.Spec, int) + */ + public PwmOutput openPwmOutput(int pin, int freqHz) + throws ConnectionLostException; + + /** + * Open a pin for pulse input. + *

+ * The pulse input module is quite flexible. It enables several kinds of + * timing measurements on a digital signal: pulse width measurement + * (positive or negative pulse), and frequency of a periodic signal. + *

+ * Note that not every pin can be used as pulse input. In addition, the + * total number of concurrent pulse input modules in use is limited. See + * board documentation for the legal pins and limit on concurrent usage. + *

+ * The pin will operate in this mode until close() is invoked on the + * returned interface. It is illegal to open a pin that has already been + * opened and has not been closed. A connection must have been established + * prior to calling this method, by invoking {@link #waitForConnect()}. + * + * @param spec + * Pin specification, consisting of the pin number, as labeled on + * the board, and the mode, which determines whether the pin will + * be floating, pull-up or pull-down. See + * {@link DigitalInput.Spec.Mode} for more information. + * @param rate + * The clock rate to use for timing the signal. A faster clock + * rate will result in better precision but will only be able to + * measure narrow pulses / high frequencies. + * @param mode + * The mode in which to operate. Determines whether the module + * will measure pulse durations or frequency. + * @param doublePrecision + * Whether to open a double-precision pulse input module. Double- + * precision modules enable reading of much longer pulses and + * lower frequencies with high accuracy than single precision + * modules. However, their number is limited, so when possible, + * and if the resources are all needed, use single-precision. For + * more details on the exact spec of single- vs. double- + * precision, see {@link PulseInput}. + * @return An instance of the {@link PulseInput}, which can be used to + * obtain the data. + * @throws ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @throws OutOfResourceException + * This is a runtime exception, so it is not necessary to catch + * it if the client guarantees that the total number of + * concurrent PWM resources is not exceeded. + * @see PulseInput + */ + public PulseInput openPulseInput(DigitalInput.Spec spec, + PulseInput.ClockRate rate, PulseInput.PulseMode mode, + boolean doublePrecision) throws ConnectionLostException; + + /** + * Shorthand for openPulseInput(new DigitalInput.Spec(pin), rate, mode, + * true), i.e. opens a double-precision, 16MHz pulse input on the given pin + * with the given mode. + * + * @see #openPulseInput(ioio.lib.api.DigitalInput.Spec, + * ioio.lib.api.PulseInput.ClockRate, + * ioio.lib.api.PulseInput.PulseMode, boolean) + */ + public PulseInput openPulseInput(int pin, PulseMode mode) + throws ConnectionLostException; + + /** + * Open a UART module, enabling a bulk transfer of byte buffers. + *

+ * UART is a very common hardware communication protocol, enabling full- + * duplex, asynchronous point-to-point data transfer. It typically serves + * for opening consoles or as a basis for higher-level protocols, such as + * MIDI RS-232, and RS-485. + *

+ * Note that not every pin can be used for UART RX or TX. In addition, the + * total number of concurrent UART modules in use is limited. See board + * documentation for the legal pins and limit on concurrent usage. + *

+ * The UART module will operate, and the pins will work in their respective + * modes until close() is invoked on the returned interface. It is illegal + * to use pins that have already been opened and has not been closed. A + * connection must have been established prior to calling this method, by + * invoking {@link #waitForConnect()}. + * + * @param rx + * Pin specification for the RX pin, consisting of the pin + * number, as labeled on the board, and the mode, which + * determines whether the pin will be floating, pull-up or + * pull-down. See {@link DigitalInput.Spec.Mode} for more + * information. null can be passed to designate that we do not + * want RX input to this module. + * @param tx + * Pin specification for the TX pin, consisting of the pin + * number, as labeled on the board, and the mode, which + * determines whether the pin will be normal or open-drain. See + * {@link DigitalOutput.Spec.Mode} for more information. null can + * be passed to designate that we do not want TX output to this + * module. + * @param baud + * The clock frequency of the UART module in Hz. + * @param parity + * The parity mode, as in {@link Parity}. + * @param stopbits + * Number of stop bits, as in {@link StopBits}. + * @return Interface of the assigned module. + * @throws ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @throws OutOfResourceException + * This is a runtime exception, so it is not necessary to catch + * it if the client guarantees that the total number of + * concurrent UART resources is not exceeded. + * @see Uart + */ + public Uart openUart(DigitalInput.Spec rx, DigitalOutput.Spec tx, int baud, + Parity parity, StopBits stopbits) throws ConnectionLostException; + + /** + * Shorthand for + * {@link #openUart(DigitalInput.Spec, DigitalOutput.Spec, int, Uart.Parity, Uart.StopBits)} + * , where the input pins use their default specs. {@link #INVALID_PIN} can + * be used on either pin if a TX- or RX-only UART is needed. + * + * @see #openUart(DigitalInput.Spec, DigitalOutput.Spec, int, Uart.Parity, + * Uart.StopBits) + */ + public Uart openUart(int rx, int tx, int baud, Parity parity, + StopBits stopbits) throws ConnectionLostException; + + /** + * Open a SPI master module, enabling communication with multiple + * SPI-enabled slave modules. + *

+ * SPI is a common hardware communication protocol, enabling full-duplex, + * synchronous point-to-multi-point data transfer. It requires MOSI, MISO + * and CLK lines shared by all nodes, as well as a SS line per slave, + * connected between this slave and a respective pin on the master. The MISO + * line should operate in pull-up mode, using either the internal pull-up or + * an external resistor. + *

+ * Note that not every pin can be used for SPI MISO, MOSI or CLK. In + * addition, the total number of concurrent SPI modules in use is limited. + * See board documentation for the legal pins and limit on concurrent usage. + *

+ * The SPI module will operate, and the pins will work in their respective + * modes until close() is invoked on the returned interface. It is illegal + * to use pins that have already been opened and has not been closed. A + * connection must have been established prior to calling this method, by + * invoking {@link #waitForConnect()}. + * + * @param miso + * Pin specification for the MISO (Master In Slave Out) pin, + * consisting of the pin number, as labeled on the board, and the + * mode, which determines whether the pin will be floating, + * pull-up or pull-down. See {@link DigitalInput.Spec.Mode} for + * more information. + * @param mosi + * Pin specification for the MOSI (Master Out Slave In) pin, + * consisting of the pin number, as labeled on the board, and the + * mode, which determines whether the pin will be normal or + * open-drain. See {@link DigitalOutput.Spec.Mode} for more + * information. + * @param clk + * Pin specification for the CLK pin, consisting of the pin + * number, as labeled on the board, and the mode, which + * determines whether the pin will be normal or open-drain. See + * {@link DigitalOutput.Spec.Mode} for more information. + * @param slaveSelect + * An array of pin specifications for each of the slaves' SS + * (Slave Select) pin. The index of this array designates the + * slave index, used later to refer to this slave. The spec is + * consisting of the pin number, as labeled on the board, and the + * mode, which determines whether the pin will be normal or + * open-drain. See {@link DigitalOutput.Spec.Mode} for more + * information. + * @param config + * The configuration of the SPI module. See + * {@link SpiMaster.Config} for details. + * @return Interface of the assigned module. + * @throws ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @throws OutOfResourceException + * This is a runtime exception, so it is not necessary to catch + * it if the client guarantees that the total number of + * concurrent SPI resources is not exceeded. + * @see SpiMaster + */ + public SpiMaster openSpiMaster(DigitalInput.Spec miso, + DigitalOutput.Spec mosi, DigitalOutput.Spec clk, + DigitalOutput.Spec[] slaveSelect, SpiMaster.Config config) + throws ConnectionLostException; + + /** + * Shorthand for + * {@link #openSpiMaster(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config)} + * , where the pins are all open with the default modes and default + * configuration values are used. + * + * @see #openSpiMaster(ioio.lib.api.DigitalInput.Spec, + * ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, + * ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config) + */ + public SpiMaster openSpiMaster(int miso, int mosi, int clk, + int[] slaveSelect, SpiMaster.Rate rate) + throws ConnectionLostException; + + /** + * Shorthand for + * {@link #openSpiMaster(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config)} + * , where the MISO pins is opened with pull up, and the other pins are open + * with the default modes and default configuration values are used. In this + * version, a single slave is used. + * + * @see #openSpiMaster(ioio.lib.api.DigitalInput.Spec, + * ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, + * ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config) + */ + public SpiMaster openSpiMaster(int miso, int mosi, int clk, + int slaveSelect, SpiMaster.Rate rate) + throws ConnectionLostException; + + /** + * Open a TWI (Two-Wire Interface, such as I2C/SMBus) master module, + * enabling communication with multiple TWI-enabled slave modules. + *

+ * TWI is a common hardware communication protocol, enabling half-duplex, + * synchronous point-to-multi-point data transfer. It requires a physical + * connection of two lines (SDA, SCL) shared by all the bus nodes, where the + * SDA is open-drain and externally pulled-up. + *

+ * Note that there is a fixed number of TWI modules, and the pins they use + * are static. Client has to make sure these pins are not already opened + * before calling this method. See board documentation for the number of + * modules and the respective pins they use. + *

+ * The TWI module will operate, and the pins will work in their respective + * modes until close() is invoked on the returned interface. It is illegal + * to use pins that have already been opened and has not been closed. A + * connection must have been established prior to calling this method, by + * invoking {@link #waitForConnect()}. + * + * @param twiNum + * The TWI module index to use. Will also determine the pins + * used. + * @param rate + * The clock rate. Can be 100KHz / 400KHz / 1MHz. + * @param smbus + * When true, will use SMBus voltage levels. When false, I2C + * voltage levels. + * @return Interface of the assigned module. + * @throws ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @see TwiMaster + */ + public TwiMaster openTwiMaster(int twiNum, Rate rate, boolean smbus) + throws ConnectionLostException; + + /** + * Open an ICSP channel, enabling Flash programming of an external PIC MCU, + * and in particular, another IOIO board. + *

+ * ICSP (In-Circuit Serial Programming) is a protocol intended for + * programming of PIC MCUs. It is a serial protocol over three wires: PGC + * (clock), PGD (data) and MCLR (reset), where PGC and MCLR are controlled + * by the master and PGD is shared by the master and slave, depending on the + * transaction state. + *

+ * Note that there is only one ICSP modules, and the pins it uses are + * static. Client has to make sure that the ICSP module is not already in + * use, as well as those dedicated pins. See board documentation for the + * actual pins used for ICSP. + * + * @return Interface of the ICSP module. + * @see IcspMaster + * @throws ConnectionLostException + * Connection was lost before or during the execution of this + * method. + */ + public IcspMaster openIcspMaster() throws ConnectionLostException; + + /** + * Start a batch of operations. This is strictly an optimization and will + * not change functionality: if the client knows that a sequence of several + * IOIO operations are going to be performed immediately following each + * other, a call to {@link #beginBatch()} before the sequence and + * {@link #endBatch()} after the sequence will cause the operations to be + * grouped into one transfer to the IOIO, thus reducing latency. A matching + * {@link #endBatch()} operation must always follow, or otherwise no + * operation will ever be actually executed. {@link #beginBatch()} / + * {@link #endBatch()} blocks may be nested - the transfer will occur when + * the outermost {@link #endBatch()} is invoked. Note that it is not + * guaranteed that no transfers will happen while inside a batch - it should + * be treated as a hint. Code running inside the block must be quick as it + * blocks all transfers to the IOIO, including those performed from + * other threads. + * + * @throws ConnectionLostException + * Connection was lost before or during the execution of this + * method. + */ + public void beginBatch() throws ConnectionLostException; + + /** + * End a batch of operations. For explanation, see {@link #beginBatch()}. + * + * @throws ConnectionLostException + * Connection was lost before or during the execution of this + * method. + */ + public void endBatch() throws ConnectionLostException; +} diff --git a/IOIOLib/src/ioio/lib/api/IOIOConnection.java b/IOIOLib/src/ioio/lib/api/IOIOConnection.java old mode 100755 new mode 100644 diff --git a/IOIOLib/src/ioio/lib/api/IOIOFactory.java b/IOIOLib/src/ioio/lib/api/IOIOFactory.java index 947c48e..ed3a70f 100644 --- a/IOIOLib/src/ioio/lib/api/IOIOFactory.java +++ b/IOIOLib/src/ioio/lib/api/IOIOFactory.java @@ -28,10 +28,14 @@ */ package ioio.lib.api; -import java.lang.reflect.Constructor; - import ioio.lib.impl.IOIOImpl; -import ioio.lib.impl.SocketIOIOConnection; +import ioio.lib.spi.IOIOConnectionFactory; +import ioio.lib.util.IOIOConnectionRegistry; + +import java.util.Collection; +import java.util.NoSuchElementException; + +import android.util.Log; /** * Factory class for creating instances of the IOIO interface. @@ -55,22 +59,21 @@ import ioio.lib.impl.SocketIOIOConnection; * */ public class IOIOFactory { - /** The TCP port used for communicating with the IOIO board. */ - public static final int IOIO_PORT = 4545; - /** * Create a IOIO instance. This specific implementation creates a IOIO * instance which works with the actual IOIO board connected via a TCP * connection (typically over a wired USB connection). - * + * * @return The IOIO instance. */ public static IOIO create() { + Collection factories = IOIOConnectionRegistry + .getConnectionFactories(); try { - return create(SocketIOIOConnection.class.getName(), IOIO_PORT); - } catch (ClassNotFoundException e) { - // we shouldn't get here - this class must always exist. - throw new RuntimeException("Something is very wrong here"); + return create(factories.iterator().next().createConnection()); + } catch (NoSuchElementException e) { + Log.e(TAG, "No connection is available. This shouldn't happen."); + throw e; } } @@ -78,44 +81,15 @@ public class IOIOFactory { * Create a IOIO instance with a user-provided underlying connection class. * This method should be used for establishing a non-standard connection to * the IOIO board. - * - * @param connectionClassName - * The name of the connection class. Must have a public default - * constructor. - * + * + * @param connection + * An instance of a IOIO connection. + * * @return The IOIO instance. - * @throws ClassNotFoundException The given class name was not found. */ - public static IOIO create(String connectionClassName, Object... args) throws ClassNotFoundException { - IOIOConnection connection = createConnectionDynamically(connectionClassName, args); - return create(connection); - } - public static IOIO create(IOIOConnection connection) { return new IOIOImpl(connection); } - public static IOIOConnection createConnectionDynamically( - String connectionClassName, Object... args) - throws ClassNotFoundException { - Class cls; - cls = Class.forName(connectionClassName); - Object instance; - try { - Class[] argTypes = new Class[args.length]; - for (int i = 0; i < args.length; ++i) { - argTypes[i] = args[i].getClass(); - } - Constructor constructor = cls.getConstructor(argTypes); - instance = constructor.newInstance(args); - } catch (Exception e) { - throw new IllegalArgumentException( - "Provided class does not have a public ctor with the right signature", e); - } - if (!(instance instanceof IOIOConnection)) { - throw new IllegalArgumentException( - "Provided class does not implement IOIOConnection"); - } - return (IOIOConnection) instance; - } + private static final String TAG = "IOIOFactory"; } diff --git a/IOIOLib/src/ioio/lib/api/IcspMaster.java b/IOIOLib/src/ioio/lib/api/IcspMaster.java old mode 100755 new mode 100644 diff --git a/IOIOLib/src/ioio/lib/api/PulseInput.java b/IOIOLib/src/ioio/lib/api/PulseInput.java old mode 100755 new mode 100644 index ca4d366..709f3cb --- a/IOIOLib/src/ioio/lib/api/PulseInput.java +++ b/IOIOLib/src/ioio/lib/api/PulseInput.java @@ -46,7 +46,7 @@ import ioio.lib.api.exception.ConnectionLostException; * for measuring a turning shaft's speed. *

* {@link PulseInput} instances are obtained by calling - * {@link IOIO#openPulseInput(ioio.lib.api.DigitalInput.Spec, ClockRate, PulseMode, boolean)} + * {@link IOIO#openPulseInput(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.PulseInput.ClockRate, ioio.lib.api.PulseInput.PulseMode, boolean)} * . When created, some important configuration decisions have to be made: the * precision (single or double), the clock rate and the mode of operation. Modes * are straightforward: {@link PulseMode#POSITIVE} is used for measuring a diff --git a/IOIOLib/src/ioio/lib/api/SpiMaster.java b/IOIOLib/src/ioio/lib/api/SpiMaster.java old mode 100755 new mode 100644 index 6cf0db4..94cd087 --- a/IOIOLib/src/ioio/lib/api/SpiMaster.java +++ b/IOIOLib/src/ioio/lib/api/SpiMaster.java @@ -28,7 +28,6 @@ */ package ioio.lib.api; -import ioio.lib.api.DigitalInput.Spec; import ioio.lib.api.exception.ConnectionLostException; /** @@ -41,7 +40,7 @@ import ioio.lib.api.exception.ConnectionLostException; * between this slave and a respective pin on the master. The MISO line should * operate in pull-up mode, using either the internal pull-up or an external * resistor. SpiMaster instances are obtained by calling - * {@link IOIO#openSpiMaster(Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec[], Config)}. + * {@link IOIO#openSpiMaster(DigitalInput.Spec, DigitalOutput.Spec, DigitalOutput.Spec, DigitalOutput.Spec[], SpiMaster.Config)}. *

* The SPI protocol is comprised of simultaneous sending and receiving of data * between the bus master and a single slave. By the very nature of this @@ -90,9 +89,11 @@ import ioio.lib.api.exception.ConnectionLostException; * spi.writeRead(request, 2, 4, response, 3); * ... * spi.close(); // free SPI module and pins - * } + * } + * * - * @see IOIO#openSpiMaster(Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec[], Config) + * @see IOIO#openSpiMaster(DigitalInput.Spec, DigitalOutput.Spec, + * DigitalOutput.Spec, DigitalOutput.Spec[], SpiMaster.Config) */ public interface SpiMaster extends Closeable { /** Possible data rates for SPI, in Hz. */ @@ -148,7 +149,7 @@ public interface SpiMaster extends Closeable { * Constructor with common defaults. Equivalent to Config(rate, false, * false) * - * @see #Config(Rate, boolean, boolean) + * @see SpiMaster.Config#Config(SpiMaster.Config.Rate, boolean, boolean) */ public Config(Rate rate) { this(rate, false, false); @@ -165,7 +166,7 @@ public interface SpiMaster extends Closeable { * @param slave * The slave index. It is determined by the index of its * slave-select pin, as per the array passed to - * {@link IOIO#openSpiMaster(Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec[], Config)} + * {@link IOIO#openSpiMaster(DigitalInput.Spec, DigitalOutput.Spec, DigitalOutput.Spec, DigitalOutput.Spec[], SpiMaster.Config)} * . * @param writeData * A byte array of data to write. May be null if writeSize is 0. diff --git a/IOIOLib/src/ioio/lib/api/TwiMaster.java b/IOIOLib/src/ioio/lib/api/TwiMaster.java old mode 100755 new mode 100644 index 9d25f12..317f27e --- a/IOIOLib/src/ioio/lib/api/TwiMaster.java +++ b/IOIOLib/src/ioio/lib/api/TwiMaster.java @@ -39,7 +39,7 @@ import ioio.lib.api.exception.ConnectionLostException; * requires a physical connection of two lines (SDA, SCL) shared by all the bus * nodes, where the SDA is open-drain and externally pulled-up. TwiMaster * instances are obtained by calling - * {@link IOIO#openTwiMaster(int, Rate, boolean)}. + * {@link IOIO#openTwiMaster(int, ioio.lib.api.TwiMaster.Rate, boolean)}. *

* TWI is the generic name for the specific I2C and SMBus protocols, differing * mostly by the voltage levels they require. This module supports both. @@ -79,7 +79,7 @@ import ioio.lib.api.exception.ConnectionLostException; * twi.close(); // free TWI module and pins * } * - * @see IOIO#openTwiMaster(int, Rate, boolean) + * @see IOIO#openTwiMaster(int, ioio.lib.api.TwiMaster.Rate, boolean) */ public interface TwiMaster extends Closeable { enum Rate { diff --git a/IOIOLib/src/ioio/lib/api/Uart.java b/IOIOLib/src/ioio/lib/api/Uart.java index 309f478..30faa91 100644 --- a/IOIOLib/src/ioio/lib/api/Uart.java +++ b/IOIOLib/src/ioio/lib/api/Uart.java @@ -1,108 +1,107 @@ -/* - * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. - * - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied. - */ -package ioio.lib.api; - -import ioio.lib.api.DigitalInput.Spec; -import ioio.lib.api.exception.ConnectionLostException; - -import java.io.InputStream; -import java.io.OutputStream; - -/** - * An interface for controlling a UART module. - *

- * UART is a very common hardware communication protocol, enabling full- duplex, - * asynchronous point-to-point data transfer. It typically serves for opening - * consoles or as a basis for higher-level protocols, such as MIDI, RS-232 and - * RS-485. Uart instances are obtained by calling - * {@link IOIO#openUart(Spec, ioio.lib.api.DigitalOutput.Spec, int, Parity, StopBits)}. - *

- * The UART protocol is completely symmetric - there is no "master" and "slave" - * at this layer. Each end may send any number of bytes at arbitrary times, - * making it very useful for terminals and terminal-controllable devices. - *

- * Working with UART is very intuitive - it just provides a standard InputStream - * and/or OutputStream. Optionally, one could create a read-only or write-only - * UART instances, by passing null (or INVALID_PIN) for either TX or RX pins. - *

- * The instance is alive since its creation. If the connection with the IOIO - * drops at any point, the instance transitions to a disconnected state, which - * every attempt to use it (except {@link #close()}) will throw a - * {@link ConnectionLostException}. Whenever {@link #close()} is invoked the - * instance may no longer be used. Any resources associated with it are freed - * and can be reused. - *

- * Typical usage: - * - *

- * Uart uart = ioio.openUart(3, 4, 19200, Parity.NONE, StopBits.ONE);
- * InputStream in = uart.getInputStream();
- * OutputStream out = uart.getOutputStream();
- * out.write(new String("Hello").getBytes());
- * int i = in.read();  // blocking
- * ...
- * uart.close();  // free UART module and pins
- * 
- * - * @see IOIO#openUart(Spec, ioio.lib.api.DigitalOutput.Spec, int, Parity, - * StopBits) - */ -public interface Uart extends Closeable { - /** Parity-bit mode. */ - enum Parity { - /** No parity. */ - NONE, - /** Even parity. */ - EVEN, - /** Odd parity. */ - ODD - } - - /** Number of stop-bits. */ - enum StopBits { - /** One stop bit. */ - ONE, - /** Two stop bits. */ - TWO - } - - /** - * Gets the input stream. - * - * @return An input stream. - */ - public InputStream getInputStream(); - - /** - * Gets the output stream. - * - * @return An output stream. - */ - public OutputStream getOutputStream(); -} +/* + * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ +package ioio.lib.api; + +import ioio.lib.api.exception.ConnectionLostException; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * An interface for controlling a UART module. + *

+ * UART is a very common hardware communication protocol, enabling full- duplex, + * asynchronous point-to-point data transfer. It typically serves for opening + * consoles or as a basis for higher-level protocols, such as MIDI, RS-232 and + * RS-485. Uart instances are obtained by calling + * {@link IOIO#openUart(DigitalInput.Spec, DigitalOutput.Spec, int, Uart.Parity, Uart.StopBits)}. + *

+ * The UART protocol is completely symmetric - there is no "master" and "slave" + * at this layer. Each end may send any number of bytes at arbitrary times, + * making it very useful for terminals and terminal-controllable devices. + *

+ * Working with UART is very intuitive - it just provides a standard InputStream + * and/or OutputStream. Optionally, one could create a read-only or write-only + * UART instances, by passing null (or INVALID_PIN) for either TX or RX pins. + *

+ * The instance is alive since its creation. If the connection with the IOIO + * drops at any point, the instance transitions to a disconnected state, which + * every attempt to use it (except {@link #close()}) will throw a + * {@link ConnectionLostException}. Whenever {@link #close()} is invoked the + * instance may no longer be used. Any resources associated with it are freed + * and can be reused. + *

+ * Typical usage: + * + *

+ * Uart uart = ioio.openUart(3, 4, 19200, Parity.NONE, StopBits.ONE);
+ * InputStream in = uart.getInputStream();
+ * OutputStream out = uart.getOutputStream();
+ * out.write(new String("Hello").getBytes());
+ * int i = in.read();  // blocking
+ * ...
+ * uart.close();  // free UART module and pins
+ * 
+ * + * @see IOIO#openUart(DigitalInput.Spec, DigitalOutput.Spec, int, Uart.Parity, + * Uart.StopBits) + */ +public interface Uart extends Closeable { + /** Parity-bit mode. */ + enum Parity { + /** No parity. */ + NONE, + /** Even parity. */ + EVEN, + /** Odd parity. */ + ODD + } + + /** Number of stop-bits. */ + enum StopBits { + /** One stop bit. */ + ONE, + /** Two stop bits. */ + TWO + } + + /** + * Gets the input stream. + * + * @return An input stream. + */ + public InputStream getInputStream(); + + /** + * Gets the output stream. + * + * @return An output stream. + */ + public OutputStream getOutputStream(); +} diff --git a/IOIOLib/src/ioio/lib/api/exception/IncompatibilityException.java b/IOIOLib/src/ioio/lib/api/exception/IncompatibilityException.java old mode 100755 new mode 100644 diff --git a/IOIOLib/src/ioio/lib/bluetooth/BluetoothIOIOConnection.java b/IOIOLib/src/ioio/lib/bluetooth/BluetoothIOIOConnection.java index 5e5f8de..4dbfb47 100644 --- a/IOIOLib/src/ioio/lib/bluetooth/BluetoothIOIOConnection.java +++ b/IOIOLib/src/ioio/lib/bluetooth/BluetoothIOIOConnection.java @@ -1,3 +1,32 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ + package ioio.lib.bluetooth; import ioio.lib.api.IOIOConnection; @@ -8,43 +37,41 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.UUID; -import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; +import android.os.Build; import android.util.Log; public class BluetoothIOIOConnection implements IOIOConnection { private static final String TAG = "BluetoothIOIOConnection"; private BluetoothSocket socket_ = null; private boolean disconnect_ = false; + private final BluetoothDevice device_; private final String name_; private final String address_; - public BluetoothIOIOConnection(String name, String address) { - name_ = name; - address_ = address; + public BluetoothIOIOConnection(BluetoothDevice device) { + device_ = device; + name_ = device.getName(); + address_ = device.getAddress(); } @Override public void waitForConnect() throws ConnectionLostException { - final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - final BluetoothDevice ioioDevice = adapter.getRemoteDevice(address_); synchronized (this) { if (disconnect_) { throw new ConnectionLostException(); } - Log.v(TAG, name_ + " Creating socket"); try { - socket_ = createSocket(ioioDevice); + socket_ = createSocket(device_); } catch (IOException e) { throw new ConnectionLostException(e); } - Log.v(TAG, name_ + " Created socket"); } // keep trying to connect as long as we're not aborting while (true) { try { - Log.v(TAG, name_ + "Connecting"); + Log.v(TAG, "Attempting to connect to Bluetooth device: " + name_); socket_.connect(); Log.v(TAG, "Established connection to device " + name_ + " address: " + address_); @@ -61,17 +88,15 @@ public class BluetoothIOIOConnection implements IOIOConnection { } } - public BluetoothSocket createSocket(final BluetoothDevice device) + public static BluetoothSocket createSocket(final BluetoothDevice device) throws IOException { - try { - // We're trying to create an insecure socket, which is - // only supported - // in API 10 and up. If we fail, we try a secure socket - // with is in API - // 7 and up. + if (Build.VERSION.SDK_INT >= 10 ) { + // We're trying to create an insecure socket, which is only + // supported in API 10 and up. Otherwise, we try a secure socket + // which is in API 7 and up. return device.createInsecureRfcommSocketToServiceRecord(UUID .fromString("00001101-0000-1000-8000-00805F9B34FB")); - } catch (NoSuchMethodError e) { + } else { return device.createRfcommSocketToServiceRecord(UUID .fromString("00001101-0000-1000-8000-00805F9B34FB")); } @@ -82,7 +107,7 @@ public class BluetoothIOIOConnection implements IOIOConnection { if (disconnect_) { return; } - Log.d(TAG, "Client initiated disconnect"); + Log.v(TAG, "Client initiated disconnect"); disconnect_ = true; if (socket_ != null) { try { diff --git a/IOIOLib/src/ioio/lib/bluetooth/BluetoothIOIOConnectionBootstrap.java b/IOIOLib/src/ioio/lib/bluetooth/BluetoothIOIOConnectionBootstrap.java new file mode 100644 index 0000000..0c910f4 --- /dev/null +++ b/IOIOLib/src/ioio/lib/bluetooth/BluetoothIOIOConnectionBootstrap.java @@ -0,0 +1,93 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ + +package ioio.lib.bluetooth; + +import ioio.lib.api.IOIOConnection; +import ioio.lib.spi.IOIOConnectionBootstrap; +import ioio.lib.spi.IOIOConnectionFactory; +import ioio.lib.spi.NoRuntimeSupportException; + +import java.util.Collection; +import java.util.Set; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.util.Log; + +public class BluetoothIOIOConnectionBootstrap implements + IOIOConnectionBootstrap { + + private static final String TAG = "BluetoothIOIOConnectionDiscovery"; + private final BluetoothAdapter adapter_; + + public BluetoothIOIOConnectionBootstrap() throws NoRuntimeSupportException { + try { + adapter_ = BluetoothAdapter.getDefaultAdapter(); + } catch (NoClassDefFoundError e) { + throw new NoRuntimeSupportException( + "Bluetooth is not supported on this device."); + } + } + + @Override + public void getFactories(Collection result) { + try { + Set bondedDevices = adapter_.getBondedDevices(); + for (final BluetoothDevice device : bondedDevices) { + if (device.getName().startsWith("IOIO")) { + result.add(new IOIOConnectionFactory() { + @Override + public String getType() { + return BluetoothIOIOConnection.class + .getCanonicalName(); + } + + @Override + public Object getExtra() { + return new Object[] { device.getName(), + device.getAddress() }; + } + + @Override + public IOIOConnection createConnection() { + return new BluetoothIOIOConnection(device); + } + }); + } + } + } catch (SecurityException e) { + Log.e(TAG, + "Did you forget to declare uses-permission of android.permission.BLUETOOTH?"); + throw e; + } catch (NoClassDefFoundError e) { + Log.w(TAG, "Bluetooth is not supported on this device.", e); + } + } +} diff --git a/IOIOLib/src/ioio/lib/bluetooth/BluetoothIOIOConnectionDiscovery.java b/IOIOLib/src/ioio/lib/bluetooth/BluetoothIOIOConnectionDiscovery.java deleted file mode 100644 index 0fb6b86..0000000 --- a/IOIOLib/src/ioio/lib/bluetooth/BluetoothIOIOConnectionDiscovery.java +++ /dev/null @@ -1,37 +0,0 @@ -package ioio.lib.bluetooth; - -import ioio.lib.util.IOIOConnectionDiscovery; - -import java.util.Collection; -import java.util.Set; - -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.util.Log; - -public class BluetoothIOIOConnectionDiscovery implements - IOIOConnectionDiscovery { - - private static final String TAG = "BluetoothIOIOConnectionDiscovery"; - - @Override - public void getSpecs(Collection result) { - try { - final BluetoothAdapter adapter = BluetoothAdapter - .getDefaultAdapter(); - Set bondedDevices = adapter.getBondedDevices(); - for (BluetoothDevice device : bondedDevices) { - if (device.getName().startsWith("IOIO")) { - result.add(new IOIOConnectionSpec( - BluetoothIOIOConnection.class.getName(), - new Object[] { device.getName(), - device.getAddress() })); - } - } - } catch (SecurityException e) { - Log.e(TAG, - "Did you forget to declare uses-permission of android.permission.BLUETOOTH?"); - throw e; - } - } -} diff --git a/IOIOLib/src/ioio/lib/impl/AbstractPin.java b/IOIOLib/src/ioio/lib/impl/AbstractPin.java old mode 100755 new mode 100644 index e10b0c0..6a411fa --- a/IOIOLib/src/ioio/lib/impl/AbstractPin.java +++ b/IOIOLib/src/ioio/lib/impl/AbstractPin.java @@ -30,7 +30,7 @@ package ioio.lib.impl; import ioio.lib.api.exception.ConnectionLostException; -public abstract class AbstractPin extends AbstractResource { +abstract class AbstractPin extends AbstractResource { protected final int pinNum_; AbstractPin(IOIOImpl ioio, int pinNum) throws ConnectionLostException { @@ -38,6 +38,7 @@ public abstract class AbstractPin extends AbstractResource { pinNum_ = pinNum; } + @Override synchronized public void close() { super.close(); ioio_.closePin(pinNum_); diff --git a/IOIOLib/src/ioio/lib/impl/AbstractResource.java b/IOIOLib/src/ioio/lib/impl/AbstractResource.java old mode 100755 new mode 100644 index f985674..53ef174 --- a/IOIOLib/src/ioio/lib/impl/AbstractResource.java +++ b/IOIOLib/src/ioio/lib/impl/AbstractResource.java @@ -32,7 +32,7 @@ import ioio.lib.api.exception.ConnectionLostException; import ioio.lib.impl.IncomingState.DisconnectListener; import ioio.lib.api.Closeable; -public class AbstractResource implements Closeable, DisconnectListener { +class AbstractResource implements Closeable, DisconnectListener { enum State { OPEN, CLOSED, @@ -53,6 +53,7 @@ public class AbstractResource implements Closeable, DisconnectListener { } } + @Override synchronized public void close() { if (state_ == State.CLOSED) { throw new IllegalStateException("Trying to use a closed resouce"); diff --git a/IOIOLib/src/ioio/lib/impl/AnalogInputImpl.java b/IOIOLib/src/ioio/lib/impl/AnalogInputImpl.java old mode 100755 new mode 100644 index c5a99ed..7ec8ae0 --- a/IOIOLib/src/ioio/lib/impl/AnalogInputImpl.java +++ b/IOIOLib/src/ioio/lib/impl/AnalogInputImpl.java @@ -28,22 +28,31 @@ */ package ioio.lib.impl; -import java.io.IOException; - import ioio.lib.api.AnalogInput; import ioio.lib.api.exception.ConnectionLostException; import ioio.lib.impl.IncomingState.InputPinListener; -public class AnalogInputImpl extends AbstractPin implements AnalogInput, InputPinListener { +import java.io.IOException; + +class AnalogInputImpl extends AbstractPin implements AnalogInput, + InputPinListener { private int value_; private boolean valid_ = false; - + + short[] buffer_; + int bufferSize_; + int bufferCapacity_; + int bufferReadCursor_; + int bufferWriteCursor_; + int bufferOverflowCount_ = 0; + AnalogInputImpl(IOIOImpl ioio, int pin) throws ConnectionLostException { super(ioio, pin); } - + @Override - public float getVoltage() throws InterruptedException, ConnectionLostException { + public float getVoltage() throws InterruptedException, + ConnectionLostException { return read() * getReference(); } @@ -55,18 +64,20 @@ public class AnalogInputImpl extends AbstractPin implements AnalogInput, InputPi @Override synchronized public void setValue(int value) { // Log.v("AnalogInputImpl", "Pin " + pinNum_ + " value is " + value); - assert(value >= 0 || value < 1024); - value_ = value; + assert (value >= 0 && value < 1024); + value_ = value; if (!valid_) { valid_ = true; notifyAll(); } + bufferPush((short) value); } @Override - synchronized public float read() throws InterruptedException, ConnectionLostException { + synchronized public float read() throws InterruptedException, + ConnectionLostException { checkState(); - while (!valid_ && state_ != State.DISCONNECTED) { + while (!valid_ && state_ == State.OPEN) { wait(); } checkState(); @@ -87,4 +98,82 @@ public class AnalogInputImpl extends AbstractPin implements AnalogInput, InputPi } catch (IOException e) { } } + + @Override + public synchronized void setBuffer(int capacity) + throws ConnectionLostException { + checkState(); + if (capacity <= 0) { + buffer_ = null; + } else { + buffer_ = new short[capacity]; + } + bufferCapacity_ = capacity; + bufferSize_ = 0; + bufferReadCursor_ = 0; + bufferWriteCursor_ = 0; + bufferOverflowCount_ = 0; + } + + @Override + public float readBuffered() throws InterruptedException, + ConnectionLostException { + checkState(); + return (float) bufferPull() / 1023.0f; + } + + @Override + public float getVoltageBuffered() throws InterruptedException, + ConnectionLostException { + return readBuffered() * getReference(); + } + + private void bufferPush(short value) { + if (buffer_ == null) { + return; + } + if (bufferSize_ == bufferCapacity_) { + ++bufferOverflowCount_; + } else { + ++bufferSize_; + } + buffer_[bufferWriteCursor_++] = value; + if (bufferWriteCursor_ == bufferCapacity_) { + bufferWriteCursor_ = 0; + } + notifyAll(); + } + + private synchronized short bufferPull() throws InterruptedException, + ConnectionLostException { + if (buffer_ == null) { + throw new IllegalStateException( + "Need to call setBuffer() before reading buffered values."); + } + while (bufferSize_ == 0 && state_ == State.OPEN) { + wait(); + } + checkState(); + short result = buffer_[bufferReadCursor_++]; + if (bufferReadCursor_ == bufferCapacity_) { + bufferReadCursor_ = 0; + } + --bufferSize_; + return result; + } + + @Override + public int getOverflowCount() throws ConnectionLostException { + return bufferOverflowCount_; + } + + @Override + public float getSampleRate() throws ConnectionLostException { + return 1000.0f; + } + + @Override + public int available() throws ConnectionLostException { + return bufferSize_; + } } diff --git a/IOIOLib/src/ioio/lib/impl/Constants.java b/IOIOLib/src/ioio/lib/impl/Constants.java old mode 100755 new mode 100644 index aa82221..abfefb4 --- a/IOIOLib/src/ioio/lib/impl/Constants.java +++ b/IOIOLib/src/ioio/lib/impl/Constants.java @@ -1,45 +1,45 @@ -/* - * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. - * - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied. - */ -package ioio.lib.impl; - -class Constants { - static final int NUM_PINS = 49; - static final int NUM_ANALOG_PINS = 16; - static final int NUM_PWM_MODULES = 9; - static final int NUM_UART_MODULES = 4; - static final int NUM_SPI_MODULES = 3; - static final int NUM_TWI_MODULES = 3; - static final int[] INCAP_MODULES_DOUBLE = new int[] { 0, 2, 4}; - static final int[] INCAP_MODULES_SINGLE = new int[] { 6, 7, 8}; - static final int BUFFER_SIZE = 1024; - static final int PACKET_BUFFER_SIZE = 256; - - static final int[][] TWI_PINS = new int[][] {{ 4, 5 }, { 47, 48 }, { 26, 25 }}; - static final int[] ICSP_PINS = new int[] { 36, 37, 38 }; -} +/* + * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ +package ioio.lib.impl; + +class Constants { + static final int NUM_PINS = 49; + static final int NUM_ANALOG_PINS = 16; + static final int NUM_PWM_MODULES = 9; + static final int NUM_UART_MODULES = 4; + static final int NUM_SPI_MODULES = 3; + static final int NUM_TWI_MODULES = 3; + static final int[] INCAP_MODULES_DOUBLE = new int[] { 0, 2, 4}; + static final int[] INCAP_MODULES_SINGLE = new int[] { 6, 7, 8}; + static final int BUFFER_SIZE = 1024; + static final int PACKET_BUFFER_SIZE = 256; + + static final int[][] TWI_PINS = new int[][] {{ 4, 5 }, { 47, 48 }, { 26, 25 }}; + static final int[] ICSP_PINS = new int[] { 36, 37, 38 }; +} diff --git a/IOIOLib/src/ioio/lib/impl/DigitalInputImpl.java b/IOIOLib/src/ioio/lib/impl/DigitalInputImpl.java old mode 100755 new mode 100644 index 65413d4..c99d518 --- a/IOIOLib/src/ioio/lib/impl/DigitalInputImpl.java +++ b/IOIOLib/src/ioio/lib/impl/DigitalInputImpl.java @@ -34,7 +34,7 @@ import ioio.lib.impl.IncomingState.InputPinListener; import java.io.IOException; -public class DigitalInputImpl extends AbstractPin implements DigitalInput, +class DigitalInputImpl extends AbstractPin implements DigitalInput, InputPinListener { private boolean value_; private boolean valid_ = false; @@ -54,6 +54,7 @@ public class DigitalInputImpl extends AbstractPin implements DigitalInput, notifyAll(); } + @Override synchronized public void waitForValue(boolean value) throws InterruptedException, ConnectionLostException { checkState(); diff --git a/IOIOLib/src/ioio/lib/impl/DigitalOutputImpl.java b/IOIOLib/src/ioio/lib/impl/DigitalOutputImpl.java old mode 100755 new mode 100644 index 2e20457..f192345 --- a/IOIOLib/src/ioio/lib/impl/DigitalOutputImpl.java +++ b/IOIOLib/src/ioio/lib/impl/DigitalOutputImpl.java @@ -33,18 +33,24 @@ import ioio.lib.api.exception.ConnectionLostException; import java.io.IOException; -public class DigitalOutputImpl extends AbstractPin implements DigitalOutput { - DigitalOutputImpl(IOIOImpl ioio, int pin) throws ConnectionLostException { +class DigitalOutputImpl extends AbstractPin implements DigitalOutput { + boolean value_; + + DigitalOutputImpl(IOIOImpl ioio, int pin, boolean startValue) throws ConnectionLostException { super(ioio, pin); + value_ = startValue; } @Override synchronized public void write(boolean val) throws ConnectionLostException { checkState(); - try { - ioio_.protocol_.setDigitalOutLevel(pinNum_, val); - } catch (IOException e) { - throw new ConnectionLostException(e); + if (val != value_) { + try { + ioio_.protocol_.setDigitalOutLevel(pinNum_, val); + value_ = val; + } catch (IOException e) { + throw new ConnectionLostException(e); + } } } } diff --git a/IOIOLib/src/ioio/lib/impl/FlowControlledOutputStream.java b/IOIOLib/src/ioio/lib/impl/FlowControlledOutputStream.java old mode 100755 new mode 100644 index 05cfce5..6c16e45 --- a/IOIOLib/src/ioio/lib/impl/FlowControlledOutputStream.java +++ b/IOIOLib/src/ioio/lib/impl/FlowControlledOutputStream.java @@ -33,7 +33,7 @@ import java.io.OutputStream; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; -public class FlowControlledOutputStream extends OutputStream { +class FlowControlledOutputStream extends OutputStream { interface Sender { void send(byte[] data, int size); } @@ -91,15 +91,14 @@ public class FlowControlledOutputStream extends OutputStream { @Override synchronized public void close() { + if (closed_) { + return; + } closed_ = true; notifyAll(); thread_.interrupt(); } - synchronized public void kill() { - thread_.interrupt(); - } - class FlushThread extends Thread { @Override public void run() { diff --git a/IOIOLib/src/ioio/lib/impl/FlowControlledPacketSender.java b/IOIOLib/src/ioio/lib/impl/FlowControlledPacketSender.java old mode 100755 new mode 100644 index 2546c91..b6e9528 --- a/IOIOLib/src/ioio/lib/impl/FlowControlledPacketSender.java +++ b/IOIOLib/src/ioio/lib/impl/FlowControlledPacketSender.java @@ -32,7 +32,7 @@ import java.io.IOException; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; -public class FlowControlledPacketSender { +class FlowControlledPacketSender { interface Packet { int getSize(); } diff --git a/IOIOLib/src/ioio/lib/impl/IOIOImpl.java b/IOIOLib/src/ioio/lib/impl/IOIOImpl.java old mode 100755 new mode 100644 index a115186..8731ea0 --- a/IOIOLib/src/ioio/lib/impl/IOIOImpl.java +++ b/IOIOLib/src/ioio/lib/impl/IOIOImpl.java @@ -1,654 +1,700 @@ -/* - * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. - * - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied. - */ -package ioio.lib.impl; - -import ioio.lib.api.AnalogInput; -import ioio.lib.api.DigitalInput; -import ioio.lib.api.DigitalInput.Spec; -import ioio.lib.api.DigitalInput.Spec.Mode; -import ioio.lib.api.DigitalOutput; -import ioio.lib.api.IOIO; -import ioio.lib.api.IOIOConnection; -import ioio.lib.api.IcspMaster; -import ioio.lib.api.PulseInput; -import ioio.lib.api.PulseInput.ClockRate; -import ioio.lib.api.PulseInput.PulseMode; -import ioio.lib.api.PwmOutput; -import ioio.lib.api.SpiMaster; -import ioio.lib.api.TwiMaster; -import ioio.lib.api.TwiMaster.Rate; -import ioio.lib.api.Uart; -import ioio.lib.api.exception.ConnectionLostException; -import ioio.lib.api.exception.IncompatibilityException; -import ioio.lib.impl.IOIOProtocol.PwmScale; -import ioio.lib.impl.IncomingState.DisconnectListener; - -import java.io.IOException; - -import android.util.Log; - -public class IOIOImpl implements IOIO, DisconnectListener { - private static final String TAG = "IOIOImpl"; - - enum State { - INIT, CONNECTED, INCOMPATIBLE, DEAD - } - - private static final byte[] REQUIRED_INTERFACE_ID = new byte[] { 'I', 'O', - 'I', 'O', '0', '0', '0', '3' }; - - private final IOIOConnection connection_; - private final IncomingState incomingState_ = new IncomingState(); - private final boolean openPins_[] = new boolean[Constants.NUM_PINS]; - private final boolean openTwi_[] = new boolean[Constants.NUM_TWI_MODULES]; - private boolean openIcsp_ = false; - private final ModuleAllocator pwmAllocator_ = new ModuleAllocator( - Constants.NUM_PWM_MODULES, "PWM"); - private final ModuleAllocator uartAllocator_ = new ModuleAllocator( - Constants.NUM_UART_MODULES, "UART"); - private final ModuleAllocator spiAllocator_ = new ModuleAllocator( - Constants.NUM_SPI_MODULES, "SPI"); - private final ModuleAllocator incapAllocatorDouble_ = new ModuleAllocator( - Constants.INCAP_MODULES_DOUBLE, "INCAP_DOUBLE"); - private final ModuleAllocator incapAllocatorSingle_ = new ModuleAllocator( - Constants.INCAP_MODULES_SINGLE, "INCAP_SINGLE"); - IOIOProtocol protocol_; - private State state_ = State.INIT; - - public IOIOImpl(IOIOConnection con) { - connection_ = con; - } - - @Override - synchronized public void waitForConnect() throws ConnectionLostException, - IncompatibilityException { - if (state_ == State.CONNECTED) { - return; - } - if (state_ == State.DEAD) { - throw new ConnectionLostException(); - } - addDisconnectListener(this); - Log.d(TAG, "Waiting for IOIO connection"); - try { - try { - Log.d(TAG, "Waiting for underlying connection"); - connection_.waitForConnect(); - protocol_ = new IOIOProtocol(connection_.getInputStream(), - connection_.getOutputStream(), incomingState_); - } catch (ConnectionLostException e) { - incomingState_.handleConnectionLost(); - throw e; - } - Log.d(TAG, "Waiting for handshake"); - incomingState_.waitConnectionEstablished(); - Log.d(TAG, "Querying for required interface ID"); - checkInterfaceVersion(); - Log.d(TAG, "Required interface ID is supported"); - state_ = State.CONNECTED; - Log.i(TAG, "IOIO connection established"); - } catch (ConnectionLostException e) { - Log.d(TAG, "Connection lost / aborted"); - state_ = State.DEAD; - throw e; - } catch (InterruptedException e) { - Log.e(TAG, "Unexpected exception", e); - } - } - - @Override - public void disconnect() { - connection_.disconnect(); - } - - @Override - public void disconnected() { - state_ = State.DEAD; - // The IOIOConnection doesn't necessarily know about the disconnect - disconnect(); - } - - public void waitForDisconnect() throws InterruptedException { - incomingState_.waitDisconnect(); - } - - private void checkInterfaceVersion() throws IncompatibilityException, - ConnectionLostException, InterruptedException { - try { - protocol_.checkInterface(REQUIRED_INTERFACE_ID); - } catch (IOException e) { - throw new ConnectionLostException(e); - } - if (!incomingState_.waitForInterfaceSupport()) { - state_ = State.INCOMPATIBLE; - Log.e(TAG, "Required interface ID is not supported"); - throw new IncompatibilityException( - "IOIO firmware does not support required firmware: " - + new String(REQUIRED_INTERFACE_ID)); - } - } - - synchronized void removeDisconnectListener(DisconnectListener listener) { - incomingState_.removeDisconnectListener(listener); - } - - synchronized void addDisconnectListener(DisconnectListener listener) - throws ConnectionLostException { - incomingState_.addDisconnectListener(listener); - } - - synchronized void closePin(int pin) { - try { - checkState(); - if (!openPins_[pin]) { - throw new IllegalStateException("Pin not open: " + pin); - } - protocol_.setPinDigitalIn(pin, DigitalInput.Spec.Mode.FLOATING); - openPins_[pin] = false; - } catch (IOException e) { - } catch (ConnectionLostException e) { - } - } - - synchronized void closePwm(int pwmNum) { - try { - checkState(); - pwmAllocator_.releaseModule(pwmNum); - protocol_.setPwmPeriod(pwmNum, 0, IOIOProtocol.PwmScale.SCALE_1X); - } catch (IOException e) { - } catch (ConnectionLostException e) { - } - } - - synchronized void closeUart(int uartNum) { - try { - checkState(); - uartAllocator_.releaseModule(uartNum); - protocol_.uartClose(uartNum); - } catch (IOException e) { - } catch (ConnectionLostException e) { - } - } - - synchronized void closeTwi(int twiNum) { - try { - checkState(); - if (!openTwi_[twiNum]) { - throw new IllegalStateException("TWI not open: " + twiNum); - } - openTwi_[twiNum] = false; - openPins_[Constants.TWI_PINS[twiNum][0]] = false; - openPins_[Constants.TWI_PINS[twiNum][1]] = false; - protocol_.i2cClose(twiNum); - } catch (IOException e) { - } catch (ConnectionLostException e) { - } - } - - synchronized void closeIcsp() { - try { - checkState(); - if (!openIcsp_) { - throw new IllegalStateException("ICSP not open"); - } - openIcsp_ = false; - openPins_[Constants.ICSP_PINS[0]] = false; - openPins_[Constants.ICSP_PINS[1]] = false; - protocol_.icspClose(); - } catch (ConnectionLostException e) { - } catch (IOException e) { - } - } - - synchronized void closeSpi(int spiNum) { - try { - checkState(); - spiAllocator_.releaseModule(spiNum); - protocol_.spiClose(spiNum); - } catch (IOException e) { - } catch (ConnectionLostException e) { - } - } - - synchronized void closeIncap(int incapNum, boolean doublePrecision) { - try { - checkState(); - if (doublePrecision) { - incapAllocatorDouble_.releaseModule(incapNum); - } else { - incapAllocatorSingle_.releaseModule(incapNum); - } - protocol_.incapClose(incapNum); - } catch (IOException e) { - } catch (ConnectionLostException e) { - } - } - - @Override - synchronized public void softReset() throws ConnectionLostException { - checkState(); - try { - protocol_.softReset(); - } catch (IOException e) { - throw new ConnectionLostException(e); - } - } - - @Override - synchronized public void hardReset() throws ConnectionLostException { - checkState(); - try { - protocol_.hardReset(); - } catch (IOException e) { - throw new ConnectionLostException(e); - } - } - - @Override - public String getImplVersion(VersionType v) throws ConnectionLostException { - checkState(); - switch (v) { - case HARDWARE_VER: - return incomingState_.hardwareId_; - case BOOTLOADER_VER: - return incomingState_.bootloaderId_; - case APP_FIRMWARE_VER: - return incomingState_.firmwareId_; - case IOIOLIB_VER: - return "IOIO0311"; - } - return null; - } - - @Override - public DigitalInput openDigitalInput(int pin) - throws ConnectionLostException { - return openDigitalInput(new DigitalInput.Spec(pin)); - } - - @Override - public DigitalInput openDigitalInput(int pin, Mode mode) - throws ConnectionLostException { - return openDigitalInput(new DigitalInput.Spec(pin, mode)); - } - - @Override - synchronized public DigitalInput openDigitalInput(DigitalInput.Spec spec) - throws ConnectionLostException { - checkState(); - PinFunctionMap.checkValidPin(spec.pin); - checkPinFree(spec.pin); - DigitalInputImpl result = new DigitalInputImpl(this, spec.pin); - addDisconnectListener(result); - openPins_[spec.pin] = true; - incomingState_.addInputPinListener(spec.pin, result); - try { - protocol_.setPinDigitalIn(spec.pin, spec.mode); - protocol_.setChangeNotify(spec.pin, true); - } catch (IOException e) { - result.close(); - throw new ConnectionLostException(e); - } - return result; - } - - @Override - public DigitalOutput openDigitalOutput(int pin, - ioio.lib.api.DigitalOutput.Spec.Mode mode, boolean startValue) - throws ConnectionLostException { - return openDigitalOutput(new DigitalOutput.Spec(pin, mode), startValue); - } - - @Override - synchronized public DigitalOutput openDigitalOutput( - DigitalOutput.Spec spec, boolean startValue) - throws ConnectionLostException { - checkState(); - PinFunctionMap.checkValidPin(spec.pin); - checkPinFree(spec.pin); - DigitalOutputImpl result = new DigitalOutputImpl(this, spec.pin); - addDisconnectListener(result); - openPins_[spec.pin] = true; - try { - protocol_.setPinDigitalOut(spec.pin, startValue, spec.mode); - } catch (IOException e) { - result.close(); - throw new ConnectionLostException(e); - } - return result; - } - - @Override - public DigitalOutput openDigitalOutput(int pin, boolean startValue) - throws ConnectionLostException { - return openDigitalOutput(new DigitalOutput.Spec(pin), startValue); - } - - @Override - public DigitalOutput openDigitalOutput(int pin) - throws ConnectionLostException { - return openDigitalOutput(new DigitalOutput.Spec(pin), false); - } - - @Override - synchronized public AnalogInput openAnalogInput(int pin) - throws ConnectionLostException { - checkState(); - PinFunctionMap.checkSupportsAnalogInput(pin); - checkPinFree(pin); - AnalogInputImpl result = new AnalogInputImpl(this, pin); - addDisconnectListener(result); - openPins_[pin] = true; - incomingState_.addInputPinListener(pin, result); - try { - protocol_.setPinAnalogIn(pin); - protocol_.setAnalogInSampling(pin, true); - } catch (IOException e) { - result.close(); - throw new ConnectionLostException(e); - } - return result; - } - - @Override - public PwmOutput openPwmOutput(int pin, int freqHz) - throws ConnectionLostException { - return openPwmOutput(new DigitalOutput.Spec(pin), freqHz); - } - - @Override - synchronized public PwmOutput openPwmOutput(DigitalOutput.Spec spec, - int freqHz) throws ConnectionLostException { - checkState(); - PinFunctionMap.checkSupportsPeripheralOutput(spec.pin); - checkPinFree(spec.pin); - int pwmNum = pwmAllocator_.allocateModule(); - - int scale = 0; - float baseUs; - int period; - while (true) { - final int clk = 16000000 / IOIOProtocol.PwmScale.values()[scale].scale; - period = clk / freqHz; - if (period <= 65536) { - baseUs = 1000000.0f / clk; - break; - } - if (++scale >= PwmScale.values().length) { - throw new IllegalArgumentException("Frequency too low: " - + freqHz); - } - } - - PwmImpl pwm = new PwmImpl(this, spec.pin, pwmNum, period, baseUs); - addDisconnectListener(pwm); - openPins_[spec.pin] = true; - try { - protocol_.setPinDigitalOut(spec.pin, false, spec.mode); - protocol_.setPinPwm(spec.pin, pwmNum, true); - protocol_.setPwmPeriod(pwmNum, period - 1, - IOIOProtocol.PwmScale.values()[scale]); - } catch (IOException e) { - pwm.close(); - throw new ConnectionLostException(e); - } - return pwm; - } - - @Override - public Uart openUart(int rx, int tx, int baud, Uart.Parity parity, - Uart.StopBits stopbits) throws ConnectionLostException { - return openUart(rx == INVALID_PIN ? null : new DigitalInput.Spec(rx), - tx == INVALID_PIN ? null : new DigitalOutput.Spec(tx), baud, - parity, stopbits); - } - - @Override - synchronized public Uart openUart(DigitalInput.Spec rx, - DigitalOutput.Spec tx, int baud, Uart.Parity parity, - Uart.StopBits stopbits) throws ConnectionLostException { - checkState(); - if (rx != null) { - PinFunctionMap.checkSupportsPeripheralInput(rx.pin); - checkPinFree(rx.pin); - } - if (tx != null) { - PinFunctionMap.checkSupportsPeripheralOutput(tx.pin); - checkPinFree(tx.pin); - } - int rxPin = rx != null ? rx.pin : INVALID_PIN; - int txPin = tx != null ? tx.pin : INVALID_PIN; - int uartNum = uartAllocator_.allocateModule(); - UartImpl uart = new UartImpl(this, txPin, rxPin, uartNum); - addDisconnectListener(uart); - incomingState_.addUartListener(uartNum, uart); - try { - if (rx != null) { - openPins_[rx.pin] = true; - protocol_.setPinDigitalIn(rx.pin, rx.mode); - protocol_.setPinUart(rx.pin, uartNum, false, true); - } - if (tx != null) { - openPins_[tx.pin] = true; - protocol_.setPinDigitalOut(tx.pin, true, tx.mode); - protocol_.setPinUart(tx.pin, uartNum, true, true); - } - boolean speed4x = true; - int rate = Math.round(4000000.0f / baud) - 1; - if (rate > 65535) { - speed4x = false; - rate = Math.round(1000000.0f / baud) - 1; - } - protocol_.uartConfigure(uartNum, rate, speed4x, stopbits, parity); - } catch (IOException e) { - uart.close(); - throw new ConnectionLostException(e); - } - return uart; - } - - @Override - synchronized public TwiMaster openTwiMaster(int twiNum, Rate rate, - boolean smbus) throws ConnectionLostException { - checkState(); - checkTwiFree(twiNum); - checkPinFree(Constants.TWI_PINS[twiNum][0]); - checkPinFree(Constants.TWI_PINS[twiNum][1]); - openPins_[Constants.TWI_PINS[twiNum][0]] = true; - openPins_[Constants.TWI_PINS[twiNum][1]] = true; - openTwi_[twiNum] = true; - TwiMasterImpl twi = new TwiMasterImpl(this, twiNum); - addDisconnectListener(twi); - incomingState_.addTwiListener(twiNum, twi); - try { - protocol_.i2cConfigureMaster(twiNum, rate, smbus); - } catch (IOException e) { - twi.close(); - throw new ConnectionLostException(e); - } - return twi; - } - - @Override - synchronized public IcspMaster openIcspMaster() - throws ConnectionLostException { - checkState(); - checkIcspFree(); - checkPinFree(Constants.ICSP_PINS[0]); - checkPinFree(Constants.ICSP_PINS[1]); - checkPinFree(Constants.ICSP_PINS[2]); - openPins_[Constants.ICSP_PINS[0]] = true; - openPins_[Constants.ICSP_PINS[1]] = true; - openPins_[Constants.ICSP_PINS[2]] = true; - openIcsp_ = true; - IcspMasterImpl icsp = new IcspMasterImpl(this); - addDisconnectListener(icsp); - incomingState_.addIcspListener(icsp); - try { - protocol_.icspOpen(); - } catch (IOException e) { - icsp.close(); - throw new ConnectionLostException(e); - } - return icsp; - } - - @Override - public SpiMaster openSpiMaster(int miso, int mosi, int clk, - int slaveSelect, SpiMaster.Rate rate) - throws ConnectionLostException { - return openSpiMaster(miso, mosi, clk, new int[] { slaveSelect }, rate); - } - - @Override - public SpiMaster openSpiMaster(int miso, int mosi, int clk, - int[] slaveSelect, SpiMaster.Rate rate) - throws ConnectionLostException { - DigitalOutput.Spec[] slaveSpecs = new DigitalOutput.Spec[slaveSelect.length]; - for (int i = 0; i < slaveSelect.length; ++i) { - slaveSpecs[i] = new DigitalOutput.Spec(slaveSelect[i]); - } - return openSpiMaster(new DigitalInput.Spec(miso, Mode.PULL_UP), - new DigitalOutput.Spec(mosi), new DigitalOutput.Spec(clk), - slaveSpecs, new SpiMaster.Config(rate)); - } - - @Override - synchronized public SpiMaster openSpiMaster(DigitalInput.Spec miso, - DigitalOutput.Spec mosi, DigitalOutput.Spec clk, - DigitalOutput.Spec[] slaveSelect, SpiMaster.Config config) - throws ConnectionLostException { - checkState(); - int ssPins[] = new int[slaveSelect.length]; - checkPinFree(miso.pin); - PinFunctionMap.checkSupportsPeripheralInput(miso.pin); - checkPinFree(mosi.pin); - PinFunctionMap.checkSupportsPeripheralOutput(mosi.pin); - checkPinFree(clk.pin); - PinFunctionMap.checkSupportsPeripheralOutput(clk.pin); - for (int i = 0; i < slaveSelect.length; ++i) { - checkPinFree(slaveSelect[i].pin); - ssPins[i] = slaveSelect[i].pin; - } - - int spiNum = spiAllocator_.allocateModule(); - SpiMasterImpl spi = new SpiMasterImpl(this, spiNum, mosi.pin, miso.pin, - clk.pin, ssPins); - addDisconnectListener(spi); - - openPins_[miso.pin] = true; - openPins_[mosi.pin] = true; - openPins_[clk.pin] = true; - for (int i = 0; i < slaveSelect.length; ++i) { - openPins_[slaveSelect[i].pin] = true; - } - - incomingState_.addSpiListener(spiNum, spi); - try { - protocol_.setPinDigitalIn(miso.pin, miso.mode); - protocol_.setPinSpi(miso.pin, 1, true, spiNum); - protocol_.setPinDigitalOut(mosi.pin, true, mosi.mode); - protocol_.setPinSpi(mosi.pin, 0, true, spiNum); - protocol_.setPinDigitalOut(clk.pin, config.invertClk, clk.mode); - protocol_.setPinSpi(clk.pin, 2, true, spiNum); - for (DigitalOutput.Spec spec : slaveSelect) { - protocol_.setPinDigitalOut(spec.pin, true, spec.mode); - } - protocol_.spiConfigureMaster(spiNum, config); - } catch (IOException e) { - spi.close(); - throw new ConnectionLostException(e); - } - return spi; - } - - @Override - public PulseInput openPulseInput(Spec spec, ClockRate rate, PulseMode mode, - boolean doublePrecision) throws ConnectionLostException { - checkState(); - checkPinFree(spec.pin); - PinFunctionMap.checkSupportsPeripheralInput(spec.pin); - int incapNum = doublePrecision ? incapAllocatorDouble_.allocateModule() - : incapAllocatorSingle_.allocateModule(); - IncapImpl incap = new IncapImpl(this, mode, incapNum, spec.pin, - rate.hertz, mode.scaling, doublePrecision); - addDisconnectListener(incap); - incomingState_.addIncapListener(incapNum, incap); - openPins_[spec.pin] = true; - try { - protocol_.setPinDigitalIn(spec.pin, spec.mode); - protocol_.setPinIncap(spec.pin, incapNum, true); - protocol_.incapConfigure(incapNum, doublePrecision, - mode.ordinal() + 1, rate.ordinal()); - } catch (IOException e) { - incap.close(); - throw new ConnectionLostException(e); - } - return incap; - } - - @Override - public PulseInput openPulseInput(int pin, PulseMode mode) - throws ConnectionLostException { - return openPulseInput(new DigitalInput.Spec(pin), ClockRate.RATE_16MHz, - mode, true); - } - - private void checkPinFree(int pin) { - if (openPins_[pin]) { - throw new IllegalArgumentException("Pin already open: " + pin); - } - } - - private void checkTwiFree(int twi) { - if (openTwi_[twi]) { - throw new IllegalArgumentException("TWI already open: " + twi); - } - } - - private void checkIcspFree() { - if (openIcsp_) { - throw new IllegalArgumentException("ICSP already open"); - } - } - - private void checkState() throws ConnectionLostException { - if (state_ == State.DEAD) { - throw new ConnectionLostException(); - } - if (state_ == State.INCOMPATIBLE) { - throw new IllegalStateException( - "Incompatibility has been reported - IOIO cannot be used"); - } - if (state_ != State.CONNECTED) { - throw new IllegalStateException( - "Connection has not yet been established"); - } - } -} +/* + * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ +package ioio.lib.impl; + +import ioio.lib.api.AnalogInput; +import ioio.lib.api.DigitalInput; +import ioio.lib.api.DigitalInput.Spec; +import ioio.lib.api.DigitalInput.Spec.Mode; +import ioio.lib.api.DigitalOutput; +import ioio.lib.api.IOIO; +import ioio.lib.api.IOIOConnection; +import ioio.lib.api.IcspMaster; +import ioio.lib.api.PulseInput; +import ioio.lib.api.PulseInput.ClockRate; +import ioio.lib.api.PulseInput.PulseMode; +import ioio.lib.api.PwmOutput; +import ioio.lib.api.SpiMaster; +import ioio.lib.api.TwiMaster; +import ioio.lib.api.TwiMaster.Rate; +import ioio.lib.api.Uart; +import ioio.lib.api.exception.ConnectionLostException; +import ioio.lib.api.exception.IncompatibilityException; +import ioio.lib.impl.IOIOProtocol.PwmScale; +import ioio.lib.impl.IncomingState.DisconnectListener; + +import java.io.IOException; + +import android.util.Log; + +public class IOIOImpl implements IOIO, DisconnectListener { + private static final String TAG = "IOIOImpl"; + private boolean disconnect_ = false; + + private static final byte[] REQUIRED_INTERFACE_ID = new byte[] { 'I', 'O', + 'I', 'O', '0', '0', '0', '3' }; + + private final IOIOConnection connection_; + private final IncomingState incomingState_ = new IncomingState(); + private final boolean openPins_[] = new boolean[Constants.NUM_PINS]; + private final boolean openTwi_[] = new boolean[Constants.NUM_TWI_MODULES]; + private boolean openIcsp_ = false; + private final ModuleAllocator pwmAllocator_ = new ModuleAllocator( + Constants.NUM_PWM_MODULES, "PWM"); + private final ModuleAllocator uartAllocator_ = new ModuleAllocator( + Constants.NUM_UART_MODULES, "UART"); + private final ModuleAllocator spiAllocator_ = new ModuleAllocator( + Constants.NUM_SPI_MODULES, "SPI"); + private final ModuleAllocator incapAllocatorDouble_ = new ModuleAllocator( + Constants.INCAP_MODULES_DOUBLE, "INCAP_DOUBLE"); + private final ModuleAllocator incapAllocatorSingle_ = new ModuleAllocator( + Constants.INCAP_MODULES_SINGLE, "INCAP_SINGLE"); + IOIOProtocol protocol_; + private State state_ = State.INIT; + + public IOIOImpl(IOIOConnection con) { + connection_ = con; + } + + @Override + public void waitForConnect() throws ConnectionLostException, + IncompatibilityException { + if (state_ == State.CONNECTED) { + return; + } + if (state_ == State.DEAD) { + throw new ConnectionLostException(); + } + addDisconnectListener(this); + Log.d(TAG, "Waiting for IOIO connection"); + try { + try { + Log.v(TAG, "Waiting for underlying connection"); + connection_.waitForConnect(); + synchronized (this) { + if (disconnect_) { + throw new ConnectionLostException(); + } + protocol_ = new IOIOProtocol(connection_.getInputStream(), + connection_.getOutputStream(), incomingState_); + // Once this block exits, a disconnect will also involve + // softClose(). + } + } catch (ConnectionLostException e) { + incomingState_.handleConnectionLost(); + throw e; + } + Log.v(TAG, "Waiting for handshake"); + incomingState_.waitConnectionEstablished(); + Log.v(TAG, "Querying for required interface ID"); + checkInterfaceVersion(); + Log.v(TAG, "Required interface ID is supported"); + state_ = State.CONNECTED; + Log.i(TAG, "IOIO connection established"); + } catch (ConnectionLostException e) { + Log.d(TAG, "Connection lost / aborted"); + state_ = State.DEAD; + throw e; + } catch (InterruptedException e) { + Log.e(TAG, "Unexpected exception", e); + } + } + + @Override + public synchronized void disconnect() { + Log.d(TAG, "Client requested disconnect."); + if (disconnect_) { + return; + } + disconnect_ = true; + try { + if (protocol_ != null) { + protocol_.softClose(); + } + } catch (IOException e) { + Log.e(TAG, "Soft close failed", e); + } + connection_.disconnect(); + } + + @Override + public synchronized void disconnected() { + state_ = State.DEAD; + if (disconnect_) { + return; + } + Log.d(TAG, "Physical disconnect."); + disconnect_ = true; + // The IOIOConnection doesn't necessarily know about the disconnect + connection_.disconnect(); + } + + @Override + public void waitForDisconnect() throws InterruptedException { + incomingState_.waitDisconnect(); + } + + @Override + public State getState() { + return state_; + } + + private void checkInterfaceVersion() throws IncompatibilityException, + ConnectionLostException, InterruptedException { + try { + protocol_.checkInterface(REQUIRED_INTERFACE_ID); + } catch (IOException e) { + throw new ConnectionLostException(e); + } + if (!incomingState_.waitForInterfaceSupport()) { + state_ = State.INCOMPATIBLE; + Log.e(TAG, "Required interface ID is not supported"); + throw new IncompatibilityException( + "IOIO firmware does not support required firmware: " + + new String(REQUIRED_INTERFACE_ID)); + } + } + + synchronized void removeDisconnectListener(DisconnectListener listener) { + incomingState_.removeDisconnectListener(listener); + } + + synchronized void addDisconnectListener(DisconnectListener listener) + throws ConnectionLostException { + incomingState_.addDisconnectListener(listener); + } + + synchronized void closePin(int pin) { + try { + checkState(); + if (!openPins_[pin]) { + throw new IllegalStateException("Pin not open: " + pin); + } + protocol_.setPinDigitalIn(pin, DigitalInput.Spec.Mode.FLOATING); + openPins_[pin] = false; + } catch (IOException e) { + } catch (ConnectionLostException e) { + } + } + + synchronized void closePwm(int pwmNum) { + try { + checkState(); + pwmAllocator_.releaseModule(pwmNum); + protocol_.setPwmPeriod(pwmNum, 0, IOIOProtocol.PwmScale.SCALE_1X); + } catch (IOException e) { + } catch (ConnectionLostException e) { + } + } + + synchronized void closeUart(int uartNum) { + try { + checkState(); + uartAllocator_.releaseModule(uartNum); + protocol_.uartClose(uartNum); + } catch (IOException e) { + } catch (ConnectionLostException e) { + } + } + + synchronized void closeTwi(int twiNum) { + try { + checkState(); + if (!openTwi_[twiNum]) { + throw new IllegalStateException("TWI not open: " + twiNum); + } + openTwi_[twiNum] = false; + openPins_[Constants.TWI_PINS[twiNum][0]] = false; + openPins_[Constants.TWI_PINS[twiNum][1]] = false; + protocol_.i2cClose(twiNum); + } catch (IOException e) { + } catch (ConnectionLostException e) { + } + } + + synchronized void closeIcsp() { + try { + checkState(); + if (!openIcsp_) { + throw new IllegalStateException("ICSP not open"); + } + openIcsp_ = false; + openPins_[Constants.ICSP_PINS[0]] = false; + openPins_[Constants.ICSP_PINS[1]] = false; + protocol_.icspClose(); + } catch (ConnectionLostException e) { + } catch (IOException e) { + } + } + + synchronized void closeSpi(int spiNum) { + try { + checkState(); + spiAllocator_.releaseModule(spiNum); + protocol_.spiClose(spiNum); + } catch (IOException e) { + } catch (ConnectionLostException e) { + } + } + + synchronized void closeIncap(int incapNum, boolean doublePrecision) { + try { + checkState(); + if (doublePrecision) { + incapAllocatorDouble_.releaseModule(incapNum); + } else { + incapAllocatorSingle_.releaseModule(incapNum); + } + protocol_.incapClose(incapNum); + } catch (IOException e) { + } catch (ConnectionLostException e) { + } + } + + @Override + synchronized public void softReset() throws ConnectionLostException { + checkState(); + try { + protocol_.softReset(); + } catch (IOException e) { + throw new ConnectionLostException(e); + } + } + + @Override + synchronized public void hardReset() throws ConnectionLostException { + checkState(); + try { + protocol_.hardReset(); + } catch (IOException e) { + throw new ConnectionLostException(e); + } + } + + @Override + public String getImplVersion(VersionType v) throws ConnectionLostException { + if (state_ == State.INIT) { + throw new IllegalStateException( + "Connection has not yet been established"); + } + switch (v) { + case HARDWARE_VER: + return incomingState_.hardwareId_; + case BOOTLOADER_VER: + return incomingState_.bootloaderId_; + case APP_FIRMWARE_VER: + return incomingState_.firmwareId_; + case IOIOLIB_VER: + return "IOIO0322"; + } + return null; + } + + @Override + public DigitalInput openDigitalInput(int pin) + throws ConnectionLostException { + return openDigitalInput(new DigitalInput.Spec(pin)); + } + + @Override + public DigitalInput openDigitalInput(int pin, Mode mode) + throws ConnectionLostException { + return openDigitalInput(new DigitalInput.Spec(pin, mode)); + } + + @Override + synchronized public DigitalInput openDigitalInput(DigitalInput.Spec spec) + throws ConnectionLostException { + checkState(); + PinFunctionMap.checkValidPin(spec.pin); + checkPinFree(spec.pin); + DigitalInputImpl result = new DigitalInputImpl(this, spec.pin); + addDisconnectListener(result); + openPins_[spec.pin] = true; + incomingState_.addInputPinListener(spec.pin, result); + try { + protocol_.setPinDigitalIn(spec.pin, spec.mode); + protocol_.setChangeNotify(spec.pin, true); + } catch (IOException e) { + result.close(); + throw new ConnectionLostException(e); + } + return result; + } + + @Override + public DigitalOutput openDigitalOutput(int pin, + ioio.lib.api.DigitalOutput.Spec.Mode mode, boolean startValue) + throws ConnectionLostException { + return openDigitalOutput(new DigitalOutput.Spec(pin, mode), startValue); + } + + @Override + synchronized public DigitalOutput openDigitalOutput( + DigitalOutput.Spec spec, boolean startValue) + throws ConnectionLostException { + checkState(); + PinFunctionMap.checkValidPin(spec.pin); + checkPinFree(spec.pin); + DigitalOutputImpl result = new DigitalOutputImpl(this, spec.pin, startValue); + addDisconnectListener(result); + openPins_[spec.pin] = true; + try { + protocol_.setPinDigitalOut(spec.pin, startValue, spec.mode); + } catch (IOException e) { + result.close(); + throw new ConnectionLostException(e); + } + return result; + } + + @Override + public DigitalOutput openDigitalOutput(int pin, boolean startValue) + throws ConnectionLostException { + return openDigitalOutput(new DigitalOutput.Spec(pin), startValue); + } + + @Override + public DigitalOutput openDigitalOutput(int pin) + throws ConnectionLostException { + return openDigitalOutput(new DigitalOutput.Spec(pin), false); + } + + @Override + synchronized public AnalogInput openAnalogInput(int pin) + throws ConnectionLostException { + checkState(); + PinFunctionMap.checkSupportsAnalogInput(pin); + checkPinFree(pin); + AnalogInputImpl result = new AnalogInputImpl(this, pin); + addDisconnectListener(result); + openPins_[pin] = true; + incomingState_.addInputPinListener(pin, result); + try { + protocol_.setPinAnalogIn(pin); + protocol_.setAnalogInSampling(pin, true); + } catch (IOException e) { + result.close(); + throw new ConnectionLostException(e); + } + return result; + } + + @Override + public PwmOutput openPwmOutput(int pin, int freqHz) + throws ConnectionLostException { + return openPwmOutput(new DigitalOutput.Spec(pin), freqHz); + } + + @Override + synchronized public PwmOutput openPwmOutput(DigitalOutput.Spec spec, + int freqHz) throws ConnectionLostException { + checkState(); + PinFunctionMap.checkSupportsPeripheralOutput(spec.pin); + checkPinFree(spec.pin); + int pwmNum = pwmAllocator_.allocateModule(); + + int scale = 0; + float baseUs; + int period; + while (true) { + final int clk = 16000000 / IOIOProtocol.PwmScale.values()[scale].scale; + period = clk / freqHz; + if (period <= 65536) { + baseUs = 1000000.0f / clk; + break; + } + if (++scale >= PwmScale.values().length) { + throw new IllegalArgumentException("Frequency too low: " + + freqHz); + } + } + + PwmImpl pwm = new PwmImpl(this, spec.pin, pwmNum, period, baseUs); + addDisconnectListener(pwm); + openPins_[spec.pin] = true; + try { + protocol_.setPinDigitalOut(spec.pin, false, spec.mode); + protocol_.setPinPwm(spec.pin, pwmNum, true); + protocol_.setPwmPeriod(pwmNum, period - 1, + IOIOProtocol.PwmScale.values()[scale]); + } catch (IOException e) { + pwm.close(); + throw new ConnectionLostException(e); + } + return pwm; + } + + @Override + public Uart openUart(int rx, int tx, int baud, Uart.Parity parity, + Uart.StopBits stopbits) throws ConnectionLostException { + return openUart(rx == INVALID_PIN ? null : new DigitalInput.Spec(rx), + tx == INVALID_PIN ? null : new DigitalOutput.Spec(tx), baud, + parity, stopbits); + } + + @Override + synchronized public Uart openUart(DigitalInput.Spec rx, + DigitalOutput.Spec tx, int baud, Uart.Parity parity, + Uart.StopBits stopbits) throws ConnectionLostException { + checkState(); + if (rx != null) { + PinFunctionMap.checkSupportsPeripheralInput(rx.pin); + checkPinFree(rx.pin); + } + if (tx != null) { + PinFunctionMap.checkSupportsPeripheralOutput(tx.pin); + checkPinFree(tx.pin); + } + int rxPin = rx != null ? rx.pin : INVALID_PIN; + int txPin = tx != null ? tx.pin : INVALID_PIN; + int uartNum = uartAllocator_.allocateModule(); + UartImpl uart = new UartImpl(this, txPin, rxPin, uartNum); + addDisconnectListener(uart); + incomingState_.addUartListener(uartNum, uart); + try { + if (rx != null) { + openPins_[rx.pin] = true; + protocol_.setPinDigitalIn(rx.pin, rx.mode); + protocol_.setPinUart(rx.pin, uartNum, false, true); + } + if (tx != null) { + openPins_[tx.pin] = true; + protocol_.setPinDigitalOut(tx.pin, true, tx.mode); + protocol_.setPinUart(tx.pin, uartNum, true, true); + } + boolean speed4x = true; + int rate = Math.round(4000000.0f / baud) - 1; + if (rate > 65535) { + speed4x = false; + rate = Math.round(1000000.0f / baud) - 1; + } + protocol_.uartConfigure(uartNum, rate, speed4x, stopbits, parity); + } catch (IOException e) { + uart.close(); + throw new ConnectionLostException(e); + } + return uart; + } + + @Override + synchronized public TwiMaster openTwiMaster(int twiNum, Rate rate, + boolean smbus) throws ConnectionLostException { + checkState(); + checkTwiFree(twiNum); + checkPinFree(Constants.TWI_PINS[twiNum][0]); + checkPinFree(Constants.TWI_PINS[twiNum][1]); + openPins_[Constants.TWI_PINS[twiNum][0]] = true; + openPins_[Constants.TWI_PINS[twiNum][1]] = true; + openTwi_[twiNum] = true; + TwiMasterImpl twi = new TwiMasterImpl(this, twiNum); + addDisconnectListener(twi); + incomingState_.addTwiListener(twiNum, twi); + try { + protocol_.i2cConfigureMaster(twiNum, rate, smbus); + } catch (IOException e) { + twi.close(); + throw new ConnectionLostException(e); + } + return twi; + } + + @Override + synchronized public IcspMaster openIcspMaster() + throws ConnectionLostException { + checkState(); + checkIcspFree(); + checkPinFree(Constants.ICSP_PINS[0]); + checkPinFree(Constants.ICSP_PINS[1]); + checkPinFree(Constants.ICSP_PINS[2]); + openPins_[Constants.ICSP_PINS[0]] = true; + openPins_[Constants.ICSP_PINS[1]] = true; + openPins_[Constants.ICSP_PINS[2]] = true; + openIcsp_ = true; + IcspMasterImpl icsp = new IcspMasterImpl(this); + addDisconnectListener(icsp); + incomingState_.addIcspListener(icsp); + try { + protocol_.icspOpen(); + } catch (IOException e) { + icsp.close(); + throw new ConnectionLostException(e); + } + return icsp; + } + + @Override + public SpiMaster openSpiMaster(int miso, int mosi, int clk, + int slaveSelect, SpiMaster.Rate rate) + throws ConnectionLostException { + return openSpiMaster(miso, mosi, clk, new int[] { slaveSelect }, rate); + } + + @Override + public SpiMaster openSpiMaster(int miso, int mosi, int clk, + int[] slaveSelect, SpiMaster.Rate rate) + throws ConnectionLostException { + DigitalOutput.Spec[] slaveSpecs = new DigitalOutput.Spec[slaveSelect.length]; + for (int i = 0; i < slaveSelect.length; ++i) { + slaveSpecs[i] = new DigitalOutput.Spec(slaveSelect[i]); + } + return openSpiMaster(new DigitalInput.Spec(miso, Mode.PULL_UP), + new DigitalOutput.Spec(mosi), new DigitalOutput.Spec(clk), + slaveSpecs, new SpiMaster.Config(rate)); + } + + @Override + synchronized public SpiMaster openSpiMaster(DigitalInput.Spec miso, + DigitalOutput.Spec mosi, DigitalOutput.Spec clk, + DigitalOutput.Spec[] slaveSelect, SpiMaster.Config config) + throws ConnectionLostException { + checkState(); + int ssPins[] = new int[slaveSelect.length]; + checkPinFree(miso.pin); + PinFunctionMap.checkSupportsPeripheralInput(miso.pin); + checkPinFree(mosi.pin); + PinFunctionMap.checkSupportsPeripheralOutput(mosi.pin); + checkPinFree(clk.pin); + PinFunctionMap.checkSupportsPeripheralOutput(clk.pin); + for (int i = 0; i < slaveSelect.length; ++i) { + checkPinFree(slaveSelect[i].pin); + ssPins[i] = slaveSelect[i].pin; + } + + int spiNum = spiAllocator_.allocateModule(); + SpiMasterImpl spi = new SpiMasterImpl(this, spiNum, mosi.pin, miso.pin, + clk.pin, ssPins); + addDisconnectListener(spi); + + openPins_[miso.pin] = true; + openPins_[mosi.pin] = true; + openPins_[clk.pin] = true; + for (int i = 0; i < slaveSelect.length; ++i) { + openPins_[slaveSelect[i].pin] = true; + } + + incomingState_.addSpiListener(spiNum, spi); + try { + protocol_.setPinDigitalIn(miso.pin, miso.mode); + protocol_.setPinSpi(miso.pin, 1, true, spiNum); + protocol_.setPinDigitalOut(mosi.pin, true, mosi.mode); + protocol_.setPinSpi(mosi.pin, 0, true, spiNum); + protocol_.setPinDigitalOut(clk.pin, config.invertClk, clk.mode); + protocol_.setPinSpi(clk.pin, 2, true, spiNum); + for (DigitalOutput.Spec spec : slaveSelect) { + protocol_.setPinDigitalOut(spec.pin, true, spec.mode); + } + protocol_.spiConfigureMaster(spiNum, config); + } catch (IOException e) { + spi.close(); + throw new ConnectionLostException(e); + } + return spi; + } + + @Override + public PulseInput openPulseInput(Spec spec, ClockRate rate, PulseMode mode, + boolean doublePrecision) throws ConnectionLostException { + checkState(); + checkPinFree(spec.pin); + PinFunctionMap.checkSupportsPeripheralInput(spec.pin); + int incapNum = doublePrecision ? incapAllocatorDouble_.allocateModule() + : incapAllocatorSingle_.allocateModule(); + IncapImpl incap = new IncapImpl(this, mode, incapNum, spec.pin, + rate.hertz, mode.scaling, doublePrecision); + addDisconnectListener(incap); + incomingState_.addIncapListener(incapNum, incap); + openPins_[spec.pin] = true; + try { + protocol_.setPinDigitalIn(spec.pin, spec.mode); + protocol_.setPinIncap(spec.pin, incapNum, true); + protocol_.incapConfigure(incapNum, doublePrecision, + mode.ordinal() + 1, rate.ordinal()); + } catch (IOException e) { + incap.close(); + throw new ConnectionLostException(e); + } + return incap; + } + + @Override + public PulseInput openPulseInput(int pin, PulseMode mode) + throws ConnectionLostException { + return openPulseInput(new DigitalInput.Spec(pin), ClockRate.RATE_16MHz, + mode, true); + } + + private void checkPinFree(int pin) { + if (openPins_[pin]) { + throw new IllegalArgumentException("Pin already open: " + pin); + } + } + + private void checkTwiFree(int twi) { + if (openTwi_[twi]) { + throw new IllegalArgumentException("TWI already open: " + twi); + } + } + + private void checkIcspFree() { + if (openIcsp_) { + throw new IllegalArgumentException("ICSP already open"); + } + } + + private void checkState() throws ConnectionLostException { + if (state_ == State.DEAD) { + throw new ConnectionLostException(); + } + if (state_ == State.INCOMPATIBLE) { + throw new IllegalStateException( + "Incompatibility has been reported - IOIO cannot be used"); + } + if (state_ != State.CONNECTED) { + throw new IllegalStateException( + "Connection has not yet been established"); + } + } + + @Override + public synchronized void beginBatch() throws ConnectionLostException { + checkState(); + protocol_.beginBatch(); + } + + @Override + public synchronized void endBatch() throws ConnectionLostException { + checkState(); + try { + protocol_.endBatch(); + } catch (IOException e) { + throw new ConnectionLostException(e); + } + } +} diff --git a/IOIOLib/src/ioio/lib/impl/IOIOProtocol.java b/IOIOLib/src/ioio/lib/impl/IOIOProtocol.java old mode 100755 new mode 100644 index 1975e10..95ac03a --- a/IOIOLib/src/ioio/lib/impl/IOIOProtocol.java +++ b/IOIOLib/src/ioio/lib/impl/IOIOProtocol.java @@ -1,798 +1,857 @@ -/* - * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. - * - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied. - */ -package ioio.lib.impl; - -import ioio.lib.api.DigitalInput; -import ioio.lib.api.DigitalOutput; -import ioio.lib.api.SpiMaster; -import ioio.lib.api.TwiMaster.Rate; -import ioio.lib.api.Uart; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -import android.util.Log; - -public class IOIOProtocol { - static final int HARD_RESET = 0x00; - static final int ESTABLISH_CONNECTION = 0x00; - static final int SOFT_RESET = 0x01; - static final int CHECK_INTERFACE = 0x02; - static final int CHECK_INTERFACE_RESPONSE = 0x02; - static final int SET_PIN_DIGITAL_OUT = 0x03; - static final int SET_DIGITAL_OUT_LEVEL = 0x04; - static final int REPORT_DIGITAL_IN_STATUS = 0x04; - static final int SET_PIN_DIGITAL_IN = 0x05; - static final int REPORT_PERIODIC_DIGITAL_IN_STATUS = 0x05; - static final int SET_CHANGE_NOTIFY = 0x06; - static final int REGISTER_PERIODIC_DIGITAL_SAMPLING = 0x07; - static final int SET_PIN_PWM = 0x08; - static final int SET_PWM_DUTY_CYCLE = 0x09; - static final int SET_PWM_PERIOD = 0x0A; - static final int SET_PIN_ANALOG_IN = 0x0B; - static final int REPORT_ANALOG_IN_STATUS = 0x0B; - static final int SET_ANALOG_IN_SAMPLING = 0x0C; - static final int REPORT_ANALOG_IN_FORMAT = 0x0C; - static final int UART_CONFIG = 0x0D; - static final int UART_STATUS = 0x0D; - static final int UART_DATA = 0x0E; - static final int SET_PIN_UART = 0x0F; - static final int UART_REPORT_TX_STATUS = 0x0F; - static final int SPI_CONFIGURE_MASTER = 0x10; - static final int SPI_STATUS = 0x10; - static final int SPI_MASTER_REQUEST = 0x11; - static final int SPI_DATA = 0x11; - static final int SET_PIN_SPI = 0x12; - static final int SPI_REPORT_TX_STATUS = 0x12; - static final int I2C_CONFIGURE_MASTER = 0x13; - static final int I2C_STATUS = 0x13; - static final int I2C_WRITE_READ = 0x14; - static final int I2C_RESULT = 0x14; - static final int I2C_REPORT_TX_STATUS = 0x15; - static final int ICSP_SIX = 0x16; - static final int ICSP_REPORT_RX_STATUS = 0x16; - static final int ICSP_REGOUT = 0x17; - static final int ICSP_RESULT = 0x17; - static final int ICSP_PROG_ENTER = 0x18; - static final int ICSP_PROG_EXIT = 0x19; - static final int ICSP_CONFIG = 0x1A; - static final int INCAP_CONFIGURE = 0x1B; - static final int INCAP_STATUS = 0x1B; - static final int SET_PIN_INCAP = 0x1C; - static final int INCAP_REPORT = 0x1C; - - static final int[] SCALE_DIV = new int[] { - 0x1F, // 31.25 - 0x1E, // 35.714 - 0x1D, // 41.667 - 0x1C, // 50 - 0x1B, // 62.5 - 0x1A, // 83.333 - 0x17, // 125 - 0x16, // 142.857 - 0x15, // 166.667 - 0x14, // 200 - 0x13, // 250 - 0x12, // 333.333 - 0x0F, // 500 - 0x0E, // 571.429 - 0x0D, // 666.667 - 0x0C, // 800 - 0x0B, // 1000 - 0x0A, // 1333.333 - 0x07, // 2000 - 0x06, // 2285.714 - 0x05, // 2666.667 - 0x04, // 3200 - 0x03, // 4000 - 0x02, // 5333.333 - 0x01 // 8000 - }; - - private static final String TAG = "IOIOProtocol"; - - enum PwmScale { - SCALE_1X(1, 0), SCALE_8X(8, 3), SCALE_64X(64, 2), SCALE_256X(256, 1); - - public final int scale; - private final int encoding; - - PwmScale(int scale, int encoding) { - this.scale = scale; - this.encoding = encoding; - } - } - - private byte[] outbuf_ = new byte[128]; - private int pos_ = 0; - - private void writeByte(int b) { - assert (b >= 0 && b < 256); - //Log.v(TAG, "sending: 0x" + Integer.toHexString(b)); - outbuf_[pos_++] = (byte) b; - } - - private void flush() throws IOException { - try { - out_.write(outbuf_, 0, pos_); - } finally { - pos_ = 0; - } - } - - private void writeTwoBytes(int i) throws IOException { - writeByte(i & 0xFF); - writeByte(i >> 8); - } - - private void writeThreeBytes(int i) throws IOException { - writeByte(i & 0xFF); - writeByte((i >> 8) & 0xFF); - writeByte((i >> 16) & 0xFF); - } - - synchronized public void hardReset() throws IOException { - writeByte(HARD_RESET); - writeByte('I'); - writeByte('O'); - writeByte('I'); - writeByte('O'); - flush(); - } - - synchronized public void softReset() throws IOException { - writeByte(SOFT_RESET); - flush(); - } - - synchronized public void checkInterface(byte[] interfaceId) - throws IOException { - if (interfaceId.length != 8) { - throw new IllegalArgumentException( - "interface ID must be exactly 8 bytes long"); - } - writeByte(CHECK_INTERFACE); - for (int i = 0; i < 8; ++i) { - writeByte(interfaceId[i]); - } - flush(); - } - - synchronized public void setDigitalOutLevel(int pin, boolean level) - throws IOException { - writeByte(SET_DIGITAL_OUT_LEVEL); - writeByte(pin << 2 | (level ? 1 : 0)); - flush(); - } - - synchronized public void setPinPwm(int pin, int pwmNum, boolean enable) - throws IOException { - writeByte(SET_PIN_PWM); - writeByte(pin & 0x3F); - writeByte((enable ? 0x80 : 0x00) | (pwmNum & 0x0F)); - flush(); - } - - synchronized public void setPwmDutyCycle(int pwmNum, int dutyCycle, - int fraction) throws IOException { - writeByte(SET_PWM_DUTY_CYCLE); - writeByte(pwmNum << 2 | fraction); - writeTwoBytes(dutyCycle); - flush(); - } - - synchronized public void setPwmPeriod(int pwmNum, int period, PwmScale scale) - throws IOException { - writeByte(SET_PWM_PERIOD); - writeByte(((scale.encoding & 0x02) << 6) | (pwmNum << 1) - | (scale.encoding & 0x01)); - writeTwoBytes(period); - flush(); - } - - synchronized public void setPinIncap(int pin, int incapNum, boolean enable) - throws IOException { - writeByte(SET_PIN_INCAP); - writeByte(pin); - writeByte(incapNum | (enable ? 0x80 : 0x00)); - flush(); - } - - synchronized public void incapClose(int incapNum) throws IOException { - writeByte(INCAP_CONFIGURE); - writeByte(incapNum); - writeByte(0x00); - flush(); - } - - synchronized public void incapConfigure(int incapNum, boolean double_prec, - int mode, int clock) throws IOException { - writeByte(INCAP_CONFIGURE); - writeByte(incapNum); - writeByte((double_prec ? 0x80 : 0x00) | (mode << 3) | clock); - flush(); - } - - synchronized public void i2cWriteRead(int i2cNum, boolean tenBitAddr, - int address, int writeSize, int readSize, byte[] writeData) - throws IOException { - writeByte(I2C_WRITE_READ); - writeByte(((address >> 8) << 6) | (tenBitAddr ? 0x20 : 0x00) | i2cNum); - writeByte(address & 0xFF); - writeByte(writeSize); - writeByte(readSize); - for (int i = 0; i < writeSize; ++i) { - writeByte(((int) writeData[i]) & 0xFF); - } - flush(); - } - - synchronized public void setPinDigitalOut(int pin, boolean value, - DigitalOutput.Spec.Mode mode) throws IOException { - writeByte(SET_PIN_DIGITAL_OUT); - writeByte((pin << 2) - | (mode == DigitalOutput.Spec.Mode.OPEN_DRAIN ? 0x01 : 0x00) - | (value ? 0x02 : 0x00)); - flush(); - } - - synchronized public void setPinDigitalIn(int pin, - DigitalInput.Spec.Mode mode) throws IOException { - int pull = 0; - if (mode == DigitalInput.Spec.Mode.PULL_UP) { - pull = 1; - } else if (mode == DigitalInput.Spec.Mode.PULL_DOWN) { - pull = 2; - } - writeByte(SET_PIN_DIGITAL_IN); - writeByte((pin << 2) | pull); - flush(); - } - - synchronized public void setChangeNotify(int pin, boolean changeNotify) - throws IOException { - writeByte(SET_CHANGE_NOTIFY); - writeByte((pin << 2) | (changeNotify ? 0x01 : 0x00)); - flush(); - } - - synchronized public void registerPeriodicDigitalSampling(int pin, - int freqScale) throws IOException { - // TODO: implement - } - - synchronized public void setPinAnalogIn(int pin) throws IOException { - writeByte(SET_PIN_ANALOG_IN); - writeByte(pin); - flush(); - } - - synchronized public void setAnalogInSampling(int pin, boolean enable) - throws IOException { - writeByte(SET_ANALOG_IN_SAMPLING); - writeByte((enable ? 0x80 : 0x00) | (pin & 0x3F)); - flush(); - } - - synchronized public void uartData(int uartNum, int numBytes, byte data[]) - throws IOException { - if (numBytes > 64) { - throw new IllegalArgumentException( - "A maximum of 64 bytes can be sent in one uartData message. Got: " - + numBytes); - } - writeByte(UART_DATA); - writeByte((numBytes - 1) | uartNum << 6); - for (int i = 0; i < numBytes; ++i) { - writeByte(((int) data[i]) & 0xFF); - } - flush(); - } - - synchronized public void uartConfigure(int uartNum, int rate, - boolean speed4x, Uart.StopBits stopbits, Uart.Parity parity) - throws IOException { - int parbits = parity == Uart.Parity.EVEN ? 1 - : (parity == Uart.Parity.ODD ? 2 : 0); - writeByte(UART_CONFIG); - writeByte((uartNum << 6) | (speed4x ? 0x08 : 0x00) - | (stopbits == Uart.StopBits.TWO ? 0x04 : 0x00) | parbits); - writeTwoBytes(rate); - flush(); - } - - synchronized public void uartClose(int uartNum) throws IOException { - writeByte(UART_CONFIG); - writeByte(uartNum << 6); - writeTwoBytes(0); - flush(); - } - - synchronized public void setPinUart(int pin, int uartNum, boolean tx, - boolean enable) throws IOException { - writeByte(SET_PIN_UART); - writeByte(pin); - writeByte((enable ? 0x80 : 0x00) | (tx ? 0x40 : 0x00) | uartNum); - flush(); - } - - synchronized public void spiConfigureMaster(int spiNum, - SpiMaster.Config config) throws IOException { - writeByte(SPI_CONFIGURE_MASTER); - writeByte((spiNum << 5) | SCALE_DIV[config.rate.ordinal()]); - writeByte((config.sampleOnTrailing ? 0x00 : 0x02) - | (config.invertClk ? 0x01 : 0x00)); - flush(); - } - - synchronized public void spiClose(int spiNum) throws IOException { - writeByte(SPI_CONFIGURE_MASTER); - writeByte(spiNum << 5); - writeByte(0x00); - flush(); - } - - synchronized public void setPinSpi(int pin, int mode, boolean enable, - int spiNum) throws IOException { - writeByte(SET_PIN_SPI); - writeByte(pin); - writeByte((1 << 4) | (mode << 2) | spiNum); - flush(); - } - - synchronized public void spiMasterRequest(int spiNum, int ssPin, - byte data[], int dataBytes, int totalBytes, int responseBytes) - throws IOException { - final boolean dataNeqTotal = (dataBytes != totalBytes); - final boolean resNeqTotal = (responseBytes != totalBytes); - writeByte(SPI_MASTER_REQUEST); - writeByte((spiNum << 6) | ssPin); - writeByte((dataNeqTotal ? 0x80 : 0x00) | (resNeqTotal ? 0x40 : 0x00) - | totalBytes - 1); - if (dataNeqTotal) { - writeByte(dataBytes); - } - if (resNeqTotal) { - writeByte(responseBytes); - } - for (int i = 0; i < dataBytes; ++i) { - writeByte(((int) data[i]) & 0xFF); - } - flush(); - } - - synchronized public void i2cConfigureMaster(int i2cNum, Rate rate, - boolean smbusLevels) throws IOException { - int rateBits = (rate == Rate.RATE_1MHz ? 3 - : (rate == Rate.RATE_400KHz ? 2 : 1)); - writeByte(I2C_CONFIGURE_MASTER); - writeByte((smbusLevels ? 0x80 : 0) | (rateBits << 5) | i2cNum); - flush(); - } - - synchronized public void i2cClose(int i2cNum) throws IOException { - writeByte(I2C_CONFIGURE_MASTER); - writeByte(i2cNum); - flush(); - } - - public void icspOpen() throws IOException { - writeByte(ICSP_CONFIG); - writeByte(0x01); - flush(); - } - - public void icspClose() throws IOException { - writeByte(ICSP_CONFIG); - writeByte(0x00); - flush(); - } - - public void icspEnter() throws IOException { - writeByte(ICSP_PROG_ENTER); - flush(); - } - - public void icspExit() throws IOException { - writeByte(ICSP_PROG_EXIT); - flush(); - } - - public void icspSix(int instruction) throws IOException { - writeByte(ICSP_SIX); - writeThreeBytes(instruction); - flush(); - } - - public void icspRegout() throws IOException { - writeByte(ICSP_REGOUT); - flush(); - } - - public interface IncomingHandler { - public void handleEstablishConnection(byte[] hardwareId, - byte[] bootloaderId, byte[] firmwareId); - - public void handleConnectionLost(); - - public void handleSoftReset(); - - public void handleCheckInterfaceResponse(boolean supported); - - public void handleSetChangeNotify(int pin, boolean changeNotify); - - public void handleReportDigitalInStatus(int pin, boolean level); - - public void handleRegisterPeriodicDigitalSampling(int pin, int freqScale); - - public void handleReportPeriodicDigitalInStatus(int frameNum, - boolean values[]); - - public void handleAnalogPinStatus(int pin, boolean open); - - public void handleReportAnalogInStatus(int pins[], int values[]); - - public void handleUartOpen(int uartNum); - - public void handleUartClose(int uartNum); - - public void handleUartData(int uartNum, int numBytes, byte data[]); - - public void handleUartReportTxStatus(int uartNum, int bytesRemaining); - - public void handleSpiOpen(int spiNum); - - public void handleSpiClose(int spiNum); - - public void handleSpiData(int spiNum, int ssPin, byte data[], - int dataBytes); - - public void handleSpiReportTxStatus(int spiNum, int bytesRemaining); - - public void handleI2cOpen(int i2cNum); - - public void handleI2cClose(int i2cNum); - - public void handleI2cResult(int i2cNum, int size, byte[] data); - - public void handleI2cReportTxStatus(int spiNum, int bytesRemaining); - - void handleIcspOpen(); - - void handleIcspClose(); - - void handleIcspReportRxStatus(int bytesRemaining); - - void handleIcspResult(int size, byte[] data); - - public void handleIncapReport(int incapNum, int size, byte[] data); - - public void handleIncapClose(int incapNum); - - public void handleIncapOpen(int incapNum); - } - - class IncomingThread extends Thread { - private int readOffset_ = 0; - private int validBytes_ = 0; - private byte[] inbuf_ = new byte[64]; - - private int[] analogFramePins_ = new int[0]; - private Set removedPins_ = new HashSet( - Constants.NUM_ANALOG_PINS); - private Set addedPins_ = new HashSet( - Constants.NUM_ANALOG_PINS); - - private void findDelta(int[] newPins) { - removedPins_.clear(); - addedPins_.clear(); - for (int i : analogFramePins_) { - removedPins_.add(i); - } - for (int i : newPins) { - addedPins_.add(i); - } - for (Iterator it = removedPins_.iterator(); it.hasNext();) { - Integer current = it.next(); - if (addedPins_.contains(current)) { - it.remove(); - addedPins_.remove(current); - } - } - } - - private void fillBuf() throws IOException { - try { - validBytes_ = in_.read(inbuf_, 0, inbuf_.length); - if (validBytes_ <= 0) { - throw new IOException("Unexpected stream closure"); - } - //Log.v(TAG, "received " + validBytes_ + " bytes"); - readOffset_ = 0; - } catch (IOException e) { - Log.i(TAG, "IOIO disconnected"); - throw e; - } - } - - private int readByte() throws IOException { - if (readOffset_ == validBytes_) { - fillBuf(); - } - int b = inbuf_[readOffset_++]; - b &= 0xFF; // make unsigned - //Log.v(TAG, "received: 0x" + Integer.toHexString(b)); - return b; - } - - private void readBytes(int size, byte[] buffer) throws IOException { - for (int i = 0; i < size; ++i) { - buffer[i] = (byte) readByte(); - } - } - - @Override - public void run() { - super.run(); - setPriority(MAX_PRIORITY); - int arg1; - int arg2; - int numPins; - int size; - byte[] data = new byte[256]; - try { - while (true) { - switch (arg1 = readByte()) { - case ESTABLISH_CONNECTION: - if (readByte() != 'I' || readByte() != 'O' - || readByte() != 'I' || readByte() != 'O') { - throw new IOException( - "Bad establish connection magic"); - } - byte[] hardwareId = new byte[8]; - byte[] bootloaderId = new byte[8]; - byte[] firmwareId = new byte[8]; - readBytes(8, hardwareId); - readBytes(8, bootloaderId); - readBytes(8, firmwareId); - - handler_.handleEstablishConnection(hardwareId, - bootloaderId, firmwareId); - break; - - case SOFT_RESET: - handler_.handleSoftReset(); - break; - - case REPORT_DIGITAL_IN_STATUS: - arg1 = readByte(); - handler_.handleReportDigitalInStatus(arg1 >> 2, - (arg1 & 0x01) == 1); - break; - - case SET_CHANGE_NOTIFY: - arg1 = readByte(); - handler_.handleSetChangeNotify(arg1 >> 2, - (arg1 & 0x01) == 1); - break; - - case REGISTER_PERIODIC_DIGITAL_SAMPLING: - // TODO: implement - break; - - case REPORT_PERIODIC_DIGITAL_IN_STATUS: - // TODO: implement - break; - - case REPORT_ANALOG_IN_FORMAT: - numPins = readByte(); - int[] newFormat = new int[numPins]; - for (int i = 0; i < numPins; ++i) { - newFormat[i] = readByte(); - } - findDelta(newFormat); - for (Integer i : removedPins_) { - handler_.handleAnalogPinStatus(i, false); - } - for (Integer i : addedPins_) { - handler_.handleAnalogPinStatus(i, true); - } - analogFramePins_ = newFormat; - break; - - case REPORT_ANALOG_IN_STATUS: - numPins = analogFramePins_.length; - int header = 0; - int[] values = new int[numPins]; - for (int i = 0; i < numPins; ++i) { - if (i % 4 == 0) { - header = readByte(); - } - values[i] = (readByte() << 2) | (header & 0x03); - header >>= 2; - } - handler_.handleReportAnalogInStatus(analogFramePins_, - values); - break; - - case UART_REPORT_TX_STATUS: - arg1 = readByte(); - arg2 = readByte(); - handler_.handleUartReportTxStatus(arg1 & 0x03, - (arg1 >> 2) | (arg2 << 6)); - break; - - case UART_DATA: - arg1 = readByte(); - for (int i = 0; i < (arg1 & 0x3F) + 1; ++i) { - data[i] = (byte) readByte(); - } - handler_.handleUartData(arg1 >> 6, (arg1 & 0x3F) + 1, - data); - break; - - case UART_STATUS: - arg1 = readByte(); - if ((arg1 & 0x80) != 0) { - handler_.handleUartOpen(arg1 & 0x03); - } else { - handler_.handleUartClose(arg1 & 0x03); - } - break; - - case SPI_DATA: - arg1 = readByte(); - arg2 = readByte(); - for (int i = 0; i < (arg1 & 0x3F) + 1; ++i) { - data[i] = (byte) readByte(); - } - handler_.handleSpiData(arg1 >> 6, arg2 & 0x3F, data, - (arg1 & 0x3F) + 1); - break; - - case SPI_REPORT_TX_STATUS: - arg1 = readByte(); - arg2 = readByte(); - handler_.handleSpiReportTxStatus(arg1 & 0x03, - (arg1 >> 2) | (arg2 << 6)); - break; - - case SPI_STATUS: - arg1 = readByte(); - if ((arg1 & 0x80) != 0) { - handler_.handleSpiOpen(arg1 & 0x03); - } else { - handler_.handleSpiClose(arg1 & 0x03); - } - break; - - case I2C_STATUS: - arg1 = readByte(); - if ((arg1 & 0x80) != 0) { - handler_.handleI2cOpen(arg1 & 0x03); - } else { - handler_.handleI2cClose(arg1 & 0x03); - } - break; - - case I2C_RESULT: - arg1 = readByte(); - arg2 = readByte(); - if (arg2 != 0xFF) { - for (int i = 0; i < arg2; ++i) { - data[i] = (byte) readByte(); - } - } - handler_.handleI2cResult(arg1 & 0x03, arg2, data); - break; - - case I2C_REPORT_TX_STATUS: - arg1 = readByte(); - arg2 = readByte(); - handler_.handleI2cReportTxStatus(arg1 & 0x03, - (arg1 >> 2) | (arg2 << 6)); - break; - - case CHECK_INTERFACE_RESPONSE: - arg1 = readByte(); - handler_.handleCheckInterfaceResponse((arg1 & 0x01) == 1); - break; - - case ICSP_REPORT_RX_STATUS: - arg1 = readByte(); - arg2 = readByte(); - handler_.handleIcspReportRxStatus(arg1 | (arg2 << 8)); - break; - - case ICSP_RESULT: - data[0] = (byte) readByte(); - data[1] = (byte) readByte(); - handler_.handleIcspResult(2, data); - break; - - case ICSP_CONFIG: - arg1 = readByte(); - if ((arg1 & 0x01) == 1) { - handler_.handleIcspOpen(); - } else { - handler_.handleIcspClose(); - } - break; - - case INCAP_STATUS: - arg1 = readByte(); - if ((arg1 & 0x80) != 0) { - handler_.handleIncapOpen(arg1 & 0x0F); - } else { - handler_.handleIncapClose(arg1 & 0x0F); - } - break; - - case INCAP_REPORT: - arg1 = readByte(); - size = arg1 >> 6; - if (size == 0) { - size = 4; - } - readBytes(size, data); - handler_.handleIncapReport(arg1 & 0x0F, size, data); - break; - - default: - in_.close(); - IOException e = new IOException( - "Received unexpected command: 0x" - + Integer.toHexString(arg1)); - Log.e("IOIOProtocol", "Protocol error", e); - throw e; - } - } - } catch (IOException e) { - handler_.handleConnectionLost(); - } - } - } - - private final InputStream in_; - private final OutputStream out_; - private final IncomingHandler handler_; - private final IncomingThread thread_ = new IncomingThread(); - - public IOIOProtocol(InputStream in, OutputStream out, - IncomingHandler handler) { - in_ = in; - out_ = out; - handler_ = handler; - thread_.start(); - } -} +/* + * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ +package ioio.lib.impl; + +import ioio.lib.api.DigitalInput; +import ioio.lib.api.DigitalOutput; +import ioio.lib.api.SpiMaster; +import ioio.lib.api.TwiMaster.Rate; +import ioio.lib.api.Uart; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import android.util.Log; + +class IOIOProtocol { + static final int HARD_RESET = 0x00; + static final int ESTABLISH_CONNECTION = 0x00; + static final int SOFT_RESET = 0x01; + static final int CHECK_INTERFACE = 0x02; + static final int CHECK_INTERFACE_RESPONSE = 0x02; + static final int SET_PIN_DIGITAL_OUT = 0x03; + static final int SET_DIGITAL_OUT_LEVEL = 0x04; + static final int REPORT_DIGITAL_IN_STATUS = 0x04; + static final int SET_PIN_DIGITAL_IN = 0x05; + static final int REPORT_PERIODIC_DIGITAL_IN_STATUS = 0x05; + static final int SET_CHANGE_NOTIFY = 0x06; + static final int REGISTER_PERIODIC_DIGITAL_SAMPLING = 0x07; + static final int SET_PIN_PWM = 0x08; + static final int SET_PWM_DUTY_CYCLE = 0x09; + static final int SET_PWM_PERIOD = 0x0A; + static final int SET_PIN_ANALOG_IN = 0x0B; + static final int REPORT_ANALOG_IN_STATUS = 0x0B; + static final int SET_ANALOG_IN_SAMPLING = 0x0C; + static final int REPORT_ANALOG_IN_FORMAT = 0x0C; + static final int UART_CONFIG = 0x0D; + static final int UART_STATUS = 0x0D; + static final int UART_DATA = 0x0E; + static final int SET_PIN_UART = 0x0F; + static final int UART_REPORT_TX_STATUS = 0x0F; + static final int SPI_CONFIGURE_MASTER = 0x10; + static final int SPI_STATUS = 0x10; + static final int SPI_MASTER_REQUEST = 0x11; + static final int SPI_DATA = 0x11; + static final int SET_PIN_SPI = 0x12; + static final int SPI_REPORT_TX_STATUS = 0x12; + static final int I2C_CONFIGURE_MASTER = 0x13; + static final int I2C_STATUS = 0x13; + static final int I2C_WRITE_READ = 0x14; + static final int I2C_RESULT = 0x14; + static final int I2C_REPORT_TX_STATUS = 0x15; + static final int ICSP_SIX = 0x16; + static final int ICSP_REPORT_RX_STATUS = 0x16; + static final int ICSP_REGOUT = 0x17; + static final int ICSP_RESULT = 0x17; + static final int ICSP_PROG_ENTER = 0x18; + static final int ICSP_PROG_EXIT = 0x19; + static final int ICSP_CONFIG = 0x1A; + static final int INCAP_CONFIGURE = 0x1B; + static final int INCAP_STATUS = 0x1B; + static final int SET_PIN_INCAP = 0x1C; + static final int INCAP_REPORT = 0x1C; + static final int SOFT_CLOSE = 0x1D; + + static final int[] SCALE_DIV = new int[] { + 0x1F, // 31.25 + 0x1E, // 35.714 + 0x1D, // 41.667 + 0x1C, // 50 + 0x1B, // 62.5 + 0x1A, // 83.333 + 0x17, // 125 + 0x16, // 142.857 + 0x15, // 166.667 + 0x14, // 200 + 0x13, // 250 + 0x12, // 333.333 + 0x0F, // 500 + 0x0E, // 571.429 + 0x0D, // 666.667 + 0x0C, // 800 + 0x0B, // 1000 + 0x0A, // 1333.333 + 0x07, // 2000 + 0x06, // 2285.714 + 0x05, // 2666.667 + 0x04, // 3200 + 0x03, // 4000 + 0x02, // 5333.333 + 0x01 // 8000 + }; + + private static final String TAG = "IOIOProtocol"; + + enum PwmScale { + SCALE_1X(1, 0), SCALE_8X(8, 3), SCALE_64X(64, 2), SCALE_256X(256, 1); + + public final int scale; + private final int encoding; + + PwmScale(int scale, int encoding) { + this.scale = scale; + this.encoding = encoding; + } + } + + private byte[] outbuf_ = new byte[256]; + private int pos_ = 0; + private int batchCounter_ = 0; + + private void writeByte(int b) throws IOException { + assert (b >= 0 && b < 256); + if (pos_ == outbuf_.length) { + // buffer is full + flush(); + } + //Log.v(TAG, "sending: 0x" + Integer.toHexString(b)); + outbuf_[pos_++] = (byte) b; + } + + public synchronized void beginBatch() { + ++batchCounter_; + } + + public synchronized void endBatch() throws IOException { + if (--batchCounter_ == 0) { + flush(); + } + } + + private void flush() throws IOException { + try { + out_.write(outbuf_, 0, pos_); + } finally { + pos_ = 0; + } + } + + private void writeTwoBytes(int i) throws IOException { + writeByte(i & 0xFF); + writeByte(i >> 8); + } + + private void writeThreeBytes(int i) throws IOException { + writeByte(i & 0xFF); + writeByte((i >> 8) & 0xFF); + writeByte((i >> 16) & 0xFF); + } + + synchronized public void hardReset() throws IOException { + beginBatch(); + writeByte(HARD_RESET); + writeByte('I'); + writeByte('O'); + writeByte('I'); + writeByte('O'); + endBatch(); + } + + synchronized public void softReset() throws IOException { + beginBatch(); + writeByte(SOFT_RESET); + endBatch(); + } + + synchronized public void softClose() throws IOException { + beginBatch(); + writeByte(SOFT_CLOSE); + endBatch(); + } + + synchronized public void checkInterface(byte[] interfaceId) + throws IOException { + if (interfaceId.length != 8) { + throw new IllegalArgumentException( + "interface ID must be exactly 8 bytes long"); + } + beginBatch(); + writeByte(CHECK_INTERFACE); + for (int i = 0; i < 8; ++i) { + writeByte(interfaceId[i]); + } + endBatch(); + } + + synchronized public void setDigitalOutLevel(int pin, boolean level) + throws IOException { + beginBatch(); + writeByte(SET_DIGITAL_OUT_LEVEL); + writeByte(pin << 2 | (level ? 1 : 0)); + endBatch(); + } + + synchronized public void setPinPwm(int pin, int pwmNum, boolean enable) + throws IOException { + beginBatch(); + writeByte(SET_PIN_PWM); + writeByte(pin & 0x3F); + writeByte((enable ? 0x80 : 0x00) | (pwmNum & 0x0F)); + endBatch(); + } + + synchronized public void setPwmDutyCycle(int pwmNum, int dutyCycle, + int fraction) throws IOException { + beginBatch(); + writeByte(SET_PWM_DUTY_CYCLE); + writeByte(pwmNum << 2 | fraction); + writeTwoBytes(dutyCycle); + endBatch(); + } + + synchronized public void setPwmPeriod(int pwmNum, int period, PwmScale scale) + throws IOException { + beginBatch(); + writeByte(SET_PWM_PERIOD); + writeByte(((scale.encoding & 0x02) << 6) | (pwmNum << 1) + | (scale.encoding & 0x01)); + writeTwoBytes(period); + endBatch(); + } + + synchronized public void setPinIncap(int pin, int incapNum, boolean enable) + throws IOException { + beginBatch(); + writeByte(SET_PIN_INCAP); + writeByte(pin); + writeByte(incapNum | (enable ? 0x80 : 0x00)); + endBatch(); + } + + synchronized public void incapClose(int incapNum) throws IOException { + beginBatch(); + writeByte(INCAP_CONFIGURE); + writeByte(incapNum); + writeByte(0x00); + endBatch(); + } + + synchronized public void incapConfigure(int incapNum, boolean double_prec, + int mode, int clock) throws IOException { + beginBatch(); + writeByte(INCAP_CONFIGURE); + writeByte(incapNum); + writeByte((double_prec ? 0x80 : 0x00) | (mode << 3) | clock); + endBatch(); + } + + synchronized public void i2cWriteRead(int i2cNum, boolean tenBitAddr, + int address, int writeSize, int readSize, byte[] writeData) + throws IOException { + beginBatch(); + writeByte(I2C_WRITE_READ); + writeByte(((address >> 8) << 6) | (tenBitAddr ? 0x20 : 0x00) | i2cNum); + writeByte(address & 0xFF); + writeByte(writeSize); + writeByte(readSize); + for (int i = 0; i < writeSize; ++i) { + writeByte(((int) writeData[i]) & 0xFF); + } + endBatch(); + } + + synchronized public void setPinDigitalOut(int pin, boolean value, + DigitalOutput.Spec.Mode mode) throws IOException { + beginBatch(); + writeByte(SET_PIN_DIGITAL_OUT); + writeByte((pin << 2) + | (mode == DigitalOutput.Spec.Mode.OPEN_DRAIN ? 0x01 : 0x00) + | (value ? 0x02 : 0x00)); + endBatch(); + } + + synchronized public void setPinDigitalIn(int pin, + DigitalInput.Spec.Mode mode) throws IOException { + int pull = 0; + if (mode == DigitalInput.Spec.Mode.PULL_UP) { + pull = 1; + } else if (mode == DigitalInput.Spec.Mode.PULL_DOWN) { + pull = 2; + } + beginBatch(); + writeByte(SET_PIN_DIGITAL_IN); + writeByte((pin << 2) | pull); + endBatch(); + } + + synchronized public void setChangeNotify(int pin, boolean changeNotify) + throws IOException { + beginBatch(); + writeByte(SET_CHANGE_NOTIFY); + writeByte((pin << 2) | (changeNotify ? 0x01 : 0x00)); + endBatch(); + } + + synchronized public void registerPeriodicDigitalSampling(int pin, + int freqScale) throws IOException { + // TODO: implement + } + + synchronized public void setPinAnalogIn(int pin) throws IOException { + beginBatch(); + writeByte(SET_PIN_ANALOG_IN); + writeByte(pin); + endBatch(); + } + + synchronized public void setAnalogInSampling(int pin, boolean enable) + throws IOException { + beginBatch(); + writeByte(SET_ANALOG_IN_SAMPLING); + writeByte((enable ? 0x80 : 0x00) | (pin & 0x3F)); + endBatch(); + } + + synchronized public void uartData(int uartNum, int numBytes, byte data[]) + throws IOException { + if (numBytes > 64) { + throw new IllegalArgumentException( + "A maximum of 64 bytes can be sent in one uartData message. Got: " + + numBytes); + } + beginBatch(); + writeByte(UART_DATA); + writeByte((numBytes - 1) | uartNum << 6); + for (int i = 0; i < numBytes; ++i) { + writeByte(((int) data[i]) & 0xFF); + } + endBatch(); + } + + synchronized public void uartConfigure(int uartNum, int rate, + boolean speed4x, Uart.StopBits stopbits, Uart.Parity parity) + throws IOException { + int parbits = parity == Uart.Parity.EVEN ? 1 + : (parity == Uart.Parity.ODD ? 2 : 0); + beginBatch(); + writeByte(UART_CONFIG); + writeByte((uartNum << 6) | (speed4x ? 0x08 : 0x00) + | (stopbits == Uart.StopBits.TWO ? 0x04 : 0x00) | parbits); + writeTwoBytes(rate); + endBatch(); + } + + synchronized public void uartClose(int uartNum) throws IOException { + beginBatch(); + writeByte(UART_CONFIG); + writeByte(uartNum << 6); + writeTwoBytes(0); + endBatch(); + } + + synchronized public void setPinUart(int pin, int uartNum, boolean tx, + boolean enable) throws IOException { + beginBatch(); + writeByte(SET_PIN_UART); + writeByte(pin); + writeByte((enable ? 0x80 : 0x00) | (tx ? 0x40 : 0x00) | uartNum); + endBatch(); + } + + synchronized public void spiConfigureMaster(int spiNum, + SpiMaster.Config config) throws IOException { + beginBatch(); + writeByte(SPI_CONFIGURE_MASTER); + writeByte((spiNum << 5) | SCALE_DIV[config.rate.ordinal()]); + writeByte((config.sampleOnTrailing ? 0x00 : 0x02) + | (config.invertClk ? 0x01 : 0x00)); + endBatch(); + } + + synchronized public void spiClose(int spiNum) throws IOException { + beginBatch(); + writeByte(SPI_CONFIGURE_MASTER); + writeByte(spiNum << 5); + writeByte(0x00); + endBatch(); + } + + synchronized public void setPinSpi(int pin, int mode, boolean enable, + int spiNum) throws IOException { + beginBatch(); + writeByte(SET_PIN_SPI); + writeByte(pin); + writeByte((1 << 4) | (mode << 2) | spiNum); + endBatch(); + } + + synchronized public void spiMasterRequest(int spiNum, int ssPin, + byte data[], int dataBytes, int totalBytes, int responseBytes) + throws IOException { + final boolean dataNeqTotal = (dataBytes != totalBytes); + final boolean resNeqTotal = (responseBytes != totalBytes); + beginBatch(); + writeByte(SPI_MASTER_REQUEST); + writeByte((spiNum << 6) | ssPin); + writeByte((dataNeqTotal ? 0x80 : 0x00) | (resNeqTotal ? 0x40 : 0x00) + | totalBytes - 1); + if (dataNeqTotal) { + writeByte(dataBytes); + } + if (resNeqTotal) { + writeByte(responseBytes); + } + for (int i = 0; i < dataBytes; ++i) { + writeByte(((int) data[i]) & 0xFF); + } + endBatch(); + } + + synchronized public void i2cConfigureMaster(int i2cNum, Rate rate, + boolean smbusLevels) throws IOException { + int rateBits = (rate == Rate.RATE_1MHz ? 3 + : (rate == Rate.RATE_400KHz ? 2 : 1)); + beginBatch(); + writeByte(I2C_CONFIGURE_MASTER); + writeByte((smbusLevels ? 0x80 : 0) | (rateBits << 5) | i2cNum); + endBatch(); + } + + synchronized public void i2cClose(int i2cNum) throws IOException { + beginBatch(); + writeByte(I2C_CONFIGURE_MASTER); + writeByte(i2cNum); + endBatch(); + } + + public void icspOpen() throws IOException { + beginBatch(); + writeByte(ICSP_CONFIG); + writeByte(0x01); + endBatch(); + } + + public void icspClose() throws IOException { + beginBatch(); + writeByte(ICSP_CONFIG); + writeByte(0x00); + endBatch(); + } + + public void icspEnter() throws IOException { + beginBatch(); + writeByte(ICSP_PROG_ENTER); + endBatch(); + } + + public void icspExit() throws IOException { + beginBatch(); + writeByte(ICSP_PROG_EXIT); + endBatch(); + } + + public void icspSix(int instruction) throws IOException { + beginBatch(); + writeByte(ICSP_SIX); + writeThreeBytes(instruction); + endBatch(); + } + + public void icspRegout() throws IOException { + beginBatch(); + writeByte(ICSP_REGOUT); + endBatch(); + } + + public interface IncomingHandler { + public void handleEstablishConnection(byte[] hardwareId, + byte[] bootloaderId, byte[] firmwareId); + + public void handleConnectionLost(); + + public void handleSoftReset(); + + public void handleCheckInterfaceResponse(boolean supported); + + public void handleSetChangeNotify(int pin, boolean changeNotify); + + public void handleReportDigitalInStatus(int pin, boolean level); + + public void handleRegisterPeriodicDigitalSampling(int pin, int freqScale); + + public void handleReportPeriodicDigitalInStatus(int frameNum, + boolean values[]); + + public void handleAnalogPinStatus(int pin, boolean open); + + public void handleReportAnalogInStatus(int pins[], int values[]); + + public void handleUartOpen(int uartNum); + + public void handleUartClose(int uartNum); + + public void handleUartData(int uartNum, int numBytes, byte data[]); + + public void handleUartReportTxStatus(int uartNum, int bytesRemaining); + + public void handleSpiOpen(int spiNum); + + public void handleSpiClose(int spiNum); + + public void handleSpiData(int spiNum, int ssPin, byte data[], + int dataBytes); + + public void handleSpiReportTxStatus(int spiNum, int bytesRemaining); + + public void handleI2cOpen(int i2cNum); + + public void handleI2cClose(int i2cNum); + + public void handleI2cResult(int i2cNum, int size, byte[] data); + + public void handleI2cReportTxStatus(int spiNum, int bytesRemaining); + + void handleIcspOpen(); + + void handleIcspClose(); + + void handleIcspReportRxStatus(int bytesRemaining); + + void handleIcspResult(int size, byte[] data); + + public void handleIncapReport(int incapNum, int size, byte[] data); + + public void handleIncapClose(int incapNum); + + public void handleIncapOpen(int incapNum); + } + + class IncomingThread extends Thread { + private int readOffset_ = 0; + private int validBytes_ = 0; + private byte[] inbuf_ = new byte[64]; + + private int[] analogFramePins_ = new int[0]; + private Set removedPins_ = new HashSet( + Constants.NUM_ANALOG_PINS); + private Set addedPins_ = new HashSet( + Constants.NUM_ANALOG_PINS); + + private void findDelta(int[] newPins) { + removedPins_.clear(); + addedPins_.clear(); + for (int i : analogFramePins_) { + removedPins_.add(i); + } + for (int i : newPins) { + addedPins_.add(i); + } + for (Iterator it = removedPins_.iterator(); it.hasNext();) { + Integer current = it.next(); + if (addedPins_.contains(current)) { + it.remove(); + addedPins_.remove(current); + } + } + } + + private void fillBuf() throws IOException { + try { + validBytes_ = in_.read(inbuf_, 0, inbuf_.length); + if (validBytes_ <= 0) { + throw new IOException("Unexpected stream closure"); + } + //Log.v(TAG, "received " + validBytes_ + " bytes"); + readOffset_ = 0; + } catch (IOException e) { + Log.i(TAG, "IOIO disconnected"); + throw e; + } + } + + private int readByte() throws IOException { + if (readOffset_ == validBytes_) { + fillBuf(); + } + int b = inbuf_[readOffset_++]; + b &= 0xFF; // make unsigned + //Log.v(TAG, "received: 0x" + Integer.toHexString(b)); + return b; + } + + private void readBytes(int size, byte[] buffer) throws IOException { + for (int i = 0; i < size; ++i) { + buffer[i] = (byte) readByte(); + } + } + + @Override + public void run() { + super.run(); + setPriority(MAX_PRIORITY); + int arg1; + int arg2; + int numPins; + int size; + byte[] data = new byte[256]; + try { + while (true) { + switch (arg1 = readByte()) { + case ESTABLISH_CONNECTION: + if (readByte() != 'I' || readByte() != 'O' + || readByte() != 'I' || readByte() != 'O') { + throw new IOException( + "Bad establish connection magic"); + } + byte[] hardwareId = new byte[8]; + byte[] bootloaderId = new byte[8]; + byte[] firmwareId = new byte[8]; + readBytes(8, hardwareId); + readBytes(8, bootloaderId); + readBytes(8, firmwareId); + + handler_.handleEstablishConnection(hardwareId, + bootloaderId, firmwareId); + break; + + case SOFT_RESET: + handler_.handleSoftReset(); + break; + + case REPORT_DIGITAL_IN_STATUS: + arg1 = readByte(); + handler_.handleReportDigitalInStatus(arg1 >> 2, + (arg1 & 0x01) == 1); + break; + + case SET_CHANGE_NOTIFY: + arg1 = readByte(); + handler_.handleSetChangeNotify(arg1 >> 2, + (arg1 & 0x01) == 1); + break; + + case REGISTER_PERIODIC_DIGITAL_SAMPLING: + // TODO: implement + break; + + case REPORT_PERIODIC_DIGITAL_IN_STATUS: + // TODO: implement + break; + + case REPORT_ANALOG_IN_FORMAT: + numPins = readByte(); + int[] newFormat = new int[numPins]; + for (int i = 0; i < numPins; ++i) { + newFormat[i] = readByte(); + } + findDelta(newFormat); + for (Integer i : removedPins_) { + handler_.handleAnalogPinStatus(i, false); + } + for (Integer i : addedPins_) { + handler_.handleAnalogPinStatus(i, true); + } + analogFramePins_ = newFormat; + break; + + case REPORT_ANALOG_IN_STATUS: + numPins = analogFramePins_.length; + int header = 0; + int[] values = new int[numPins]; + for (int i = 0; i < numPins; ++i) { + if (i % 4 == 0) { + header = readByte(); + } + values[i] = (readByte() << 2) | (header & 0x03); + header >>= 2; + } + handler_.handleReportAnalogInStatus(analogFramePins_, + values); + break; + + case UART_REPORT_TX_STATUS: + arg1 = readByte(); + arg2 = readByte(); + handler_.handleUartReportTxStatus(arg1 & 0x03, + (arg1 >> 2) | (arg2 << 6)); + break; + + case UART_DATA: + arg1 = readByte(); + for (int i = 0; i < (arg1 & 0x3F) + 1; ++i) { + data[i] = (byte) readByte(); + } + handler_.handleUartData(arg1 >> 6, (arg1 & 0x3F) + 1, + data); + break; + + case UART_STATUS: + arg1 = readByte(); + if ((arg1 & 0x80) != 0) { + handler_.handleUartOpen(arg1 & 0x03); + } else { + handler_.handleUartClose(arg1 & 0x03); + } + break; + + case SPI_DATA: + arg1 = readByte(); + arg2 = readByte(); + for (int i = 0; i < (arg1 & 0x3F) + 1; ++i) { + data[i] = (byte) readByte(); + } + handler_.handleSpiData(arg1 >> 6, arg2 & 0x3F, data, + (arg1 & 0x3F) + 1); + break; + + case SPI_REPORT_TX_STATUS: + arg1 = readByte(); + arg2 = readByte(); + handler_.handleSpiReportTxStatus(arg1 & 0x03, + (arg1 >> 2) | (arg2 << 6)); + break; + + case SPI_STATUS: + arg1 = readByte(); + if ((arg1 & 0x80) != 0) { + handler_.handleSpiOpen(arg1 & 0x03); + } else { + handler_.handleSpiClose(arg1 & 0x03); + } + break; + + case I2C_STATUS: + arg1 = readByte(); + if ((arg1 & 0x80) != 0) { + handler_.handleI2cOpen(arg1 & 0x03); + } else { + handler_.handleI2cClose(arg1 & 0x03); + } + break; + + case I2C_RESULT: + arg1 = readByte(); + arg2 = readByte(); + if (arg2 != 0xFF) { + for (int i = 0; i < arg2; ++i) { + data[i] = (byte) readByte(); + } + } + handler_.handleI2cResult(arg1 & 0x03, arg2, data); + break; + + case I2C_REPORT_TX_STATUS: + arg1 = readByte(); + arg2 = readByte(); + handler_.handleI2cReportTxStatus(arg1 & 0x03, + (arg1 >> 2) | (arg2 << 6)); + break; + + case CHECK_INTERFACE_RESPONSE: + arg1 = readByte(); + handler_.handleCheckInterfaceResponse((arg1 & 0x01) == 1); + break; + + case ICSP_REPORT_RX_STATUS: + arg1 = readByte(); + arg2 = readByte(); + handler_.handleIcspReportRxStatus(arg1 | (arg2 << 8)); + break; + + case ICSP_RESULT: + data[0] = (byte) readByte(); + data[1] = (byte) readByte(); + handler_.handleIcspResult(2, data); + break; + + case ICSP_CONFIG: + arg1 = readByte(); + if ((arg1 & 0x01) == 1) { + handler_.handleIcspOpen(); + } else { + handler_.handleIcspClose(); + } + break; + + case INCAP_STATUS: + arg1 = readByte(); + if ((arg1 & 0x80) != 0) { + handler_.handleIncapOpen(arg1 & 0x0F); + } else { + handler_.handleIncapClose(arg1 & 0x0F); + } + break; + + case INCAP_REPORT: + arg1 = readByte(); + size = arg1 >> 6; + if (size == 0) { + size = 4; + } + readBytes(size, data); + handler_.handleIncapReport(arg1 & 0x0F, size, data); + break; + + case SOFT_CLOSE: + Log.d(TAG, "Received soft close."); + throw new IOException("Soft close"); + + + default: + in_.close(); + IOException e = new IOException( + "Received unexpected command: 0x" + + Integer.toHexString(arg1)); + Log.e("IOIOProtocol", "Protocol error", e); + throw e; + } + } + } catch (IOException e) { + handler_.handleConnectionLost(); + } + } + } + + private final InputStream in_; + private final OutputStream out_; + private final IncomingHandler handler_; + private final IncomingThread thread_ = new IncomingThread(); + + public IOIOProtocol(InputStream in, OutputStream out, + IncomingHandler handler) { + in_ = in; + out_ = out; + handler_ = handler; + thread_.start(); + } +} diff --git a/IOIOLib/src/ioio/lib/impl/IcspMasterImpl.java b/IOIOLib/src/ioio/lib/impl/IcspMasterImpl.java old mode 100755 new mode 100644 index 35b7789..d0fec77 --- a/IOIOLib/src/ioio/lib/impl/IcspMasterImpl.java +++ b/IOIOLib/src/ioio/lib/impl/IcspMasterImpl.java @@ -36,7 +36,7 @@ import java.io.IOException; import java.util.LinkedList; import java.util.Queue; -public class IcspMasterImpl extends AbstractResource implements IcspMaster, +class IcspMasterImpl extends AbstractResource implements IcspMaster, DataModuleListener { private Queue resultQueue_ = new LinkedList(); private int rxRemaining_ = 0; diff --git a/IOIOLib/src/ioio/lib/impl/IncapImpl.java b/IOIOLib/src/ioio/lib/impl/IncapImpl.java old mode 100755 new mode 100644 index a4c56c5..b63525b --- a/IOIOLib/src/ioio/lib/impl/IncapImpl.java +++ b/IOIOLib/src/ioio/lib/impl/IncapImpl.java @@ -7,7 +7,7 @@ import ioio.lib.impl.IncomingState.DataModuleListener; import java.util.LinkedList; import java.util.Queue; -public class IncapImpl extends AbstractPin implements DataModuleListener, +class IncapImpl extends AbstractPin implements DataModuleListener, PulseInput { private static final int MAX_QUEUE_LEN = 32; private final PulseMode mode_; diff --git a/IOIOLib/src/ioio/lib/impl/IncomingState.java b/IOIOLib/src/ioio/lib/impl/IncomingState.java old mode 100755 new mode 100644 index 02c48c2..05cbccc --- a/IOIOLib/src/ioio/lib/impl/IncomingState.java +++ b/IOIOLib/src/ioio/lib/impl/IncomingState.java @@ -38,7 +38,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; import android.util.Log; -public class IncomingState implements IncomingHandler { +class IncomingState implements IncomingHandler { enum ConnectionState { INIT, ESTABLISHED, CONNECTED, DISCONNECTED, UNSUPPORTED_IID } diff --git a/IOIOLib/src/ioio/lib/impl/ModuleAllocator.java b/IOIOLib/src/ioio/lib/impl/ModuleAllocator.java old mode 100755 new mode 100644 index 41f7273..5f55b20 --- a/IOIOLib/src/ioio/lib/impl/ModuleAllocator.java +++ b/IOIOLib/src/ioio/lib/impl/ModuleAllocator.java @@ -44,7 +44,7 @@ import java.util.TreeSet; * * @author birmiwal */ -public class ModuleAllocator { +class ModuleAllocator { private final Set availableModuleIds_; private final Set allocatedModuleIds_; private final String name_; diff --git a/IOIOLib/src/ioio/lib/impl/PinFunctionMap.java b/IOIOLib/src/ioio/lib/impl/PinFunctionMap.java old mode 100755 new mode 100644 index 3bf8cce..4361ee5 --- a/IOIOLib/src/ioio/lib/impl/PinFunctionMap.java +++ b/IOIOLib/src/ioio/lib/impl/PinFunctionMap.java @@ -29,7 +29,7 @@ package ioio.lib.impl; -public class PinFunctionMap { +class PinFunctionMap { private static final boolean[] PERIPHERAL_OUT = new boolean[] { true, false, false, true, true, true, true, true, false, false, true, true, true, true, true, false, false, false, false, false, false, diff --git a/IOIOLib/src/ioio/lib/impl/PwmImpl.java b/IOIOLib/src/ioio/lib/impl/PwmImpl.java old mode 100755 new mode 100644 index ce0d440..d3e82e9 --- a/IOIOLib/src/ioio/lib/impl/PwmImpl.java +++ b/IOIOLib/src/ioio/lib/impl/PwmImpl.java @@ -33,7 +33,7 @@ import ioio.lib.api.exception.ConnectionLostException; import java.io.IOException; -public class PwmImpl extends AbstractResource implements PwmOutput { +class PwmImpl extends AbstractResource implements PwmOutput { private final int pwmNum_; private final int pinNum_; private final float baseUs_; diff --git a/IOIOLib/src/ioio/lib/impl/QueueInputStream.java b/IOIOLib/src/ioio/lib/impl/QueueInputStream.java old mode 100755 new mode 100644 index 42ce41a..3fe0ed4 --- a/IOIOLib/src/ioio/lib/impl/QueueInputStream.java +++ b/IOIOLib/src/ioio/lib/impl/QueueInputStream.java @@ -35,26 +35,60 @@ import java.util.concurrent.ArrayBlockingQueue; import android.util.Log; -public class QueueInputStream extends InputStream { +class QueueInputStream extends InputStream { + private enum State { + OPEN, CLOSED, KILLED + }; + private final Queue queue_ = new ArrayBlockingQueue( Constants.BUFFER_SIZE); - private boolean closed_ = false; + private State state_ = State.OPEN; @Override synchronized public int read() throws IOException { try { - while (!closed_ && queue_.isEmpty()) { + while (state_ == State.OPEN && queue_.isEmpty()) { wait(); } - if (closed_) { + if (state_ == State.KILLED) { throw new IOException("Stream has been closed"); } + if (state_ == State.CLOSED && queue_.isEmpty()) { + return -1; + } return ((int) queue_.remove()) & 0xFF; } catch (InterruptedException e) { throw new IOException("Interrupted"); } } + @Override + synchronized public int read(byte[] b, int off, int len) throws IOException { + if (len == 0) { + return 0; + } + try { + while (state_ == State.OPEN && queue_.isEmpty()) { + wait(); + } + if (state_ == State.KILLED) { + throw new IOException("Stream has been closed"); + } + if (state_ == State.CLOSED && queue_.isEmpty()) { + return -1; + } + if (len > queue_.size()) { + len = queue_.size(); + } + for (int i = 0; i < len; ++i) { + b[off++] = queue_.remove(); + } + return len; + } catch (InterruptedException e) { + throw new IOException("Interrupted"); + } + } + synchronized public void write(byte[] data, int size) { for (int i = 0; i < size; ++i) { if (queue_.size() == Constants.BUFFER_SIZE) { @@ -73,7 +107,19 @@ public class QueueInputStream extends InputStream { @Override synchronized public void close() { - closed_ = true; + if (state_ != State.OPEN) { + return; + } + state_ = State.CLOSED; + notifyAll(); + } + + synchronized public void kill() { + if (state_ != State.OPEN) { + return; + } + state_ = State.KILLED; + notifyAll(); } } diff --git a/IOIOLib/src/ioio/lib/impl/SocketIOIOConnection.java b/IOIOLib/src/ioio/lib/impl/SocketIOIOConnection.java old mode 100755 new mode 100644 index d712b27..e80fb53 --- a/IOIOLib/src/ioio/lib/impl/SocketIOIOConnection.java +++ b/IOIOLib/src/ioio/lib/impl/SocketIOIOConnection.java @@ -41,6 +41,7 @@ import java.net.SocketException; import android.util.Log; public class SocketIOIOConnection implements IOIOConnection { + private static final String TAG = "SocketIOIOConnection"; private final int port_; private ServerSocket server_ = null; private Socket socket_ = null; @@ -48,7 +49,7 @@ public class SocketIOIOConnection implements IOIOConnection { private boolean server_owned_by_connect_ = true; private boolean socket_owned_by_connect_ = true; - public SocketIOIOConnection(Integer port) { + public SocketIOIOConnection(int port) { port_ = port; } @@ -59,13 +60,13 @@ public class SocketIOIOConnection implements IOIOConnection { if (disconnect_) { throw new ConnectionLostException(); } - Log.d("SocketIOIOConnection", "Creating server socket"); + Log.v(TAG, "Creating server socket"); server_ = new ServerSocket(port_); server_owned_by_connect_ = false; } - Log.d("SocketIOIOConnection", "Waiting for TCP connection"); + Log.v(TAG, "Waiting for TCP connection"); socket_ = server_.accept(); - Log.d("SocketIOIOConnection", "TCP connected"); + Log.v(TAG, "TCP connected"); synchronized (this) { if (disconnect_) { socket_.close(); @@ -80,18 +81,18 @@ public class SocketIOIOConnection implements IOIOConnection { try { server_.close(); } catch (IOException e1) { - Log.e("SocketIOIOConnection", "Unexpected exception", e1); + Log.e(TAG, "Unexpected exception", e1); } } if (socket_owned_by_connect_ && socket_ != null) { try { socket_.close(); } catch (IOException e1) { - Log.e("SocketIOIOConnection", "Unexpected exception", e1); + Log.e(TAG, "Unexpected exception", e1); } } if (e instanceof SocketException && e.getMessage().equals("Permission denied")) { - Log.e("SocketIOIOConnection", "Did you forget to declare uses-permission of android.permission.INTERNET?"); + Log.e(TAG, "Did you forget to declare uses-permission of android.permission.INTERNET?"); } throw new ConnectionLostException(e); } @@ -103,13 +104,13 @@ public class SocketIOIOConnection implements IOIOConnection { if (disconnect_) { return; } - Log.d("SocketIOIOConnection", "Client initiated disconnect"); + Log.v(TAG, "Client initiated disconnect"); disconnect_ = true; if (!server_owned_by_connect_) { try { server_.close(); } catch (IOException e1) { - Log.e("SocketIOIOConnection", "Unexpected exception", e1); + Log.e(TAG, "Unexpected exception", e1); } } if (!socket_owned_by_connect_) { diff --git a/IOIOLib/src/ioio/lib/impl/SocketIOIOConnectionBootstrap.java b/IOIOLib/src/ioio/lib/impl/SocketIOIOConnectionBootstrap.java new file mode 100644 index 0000000..12fc96c --- /dev/null +++ b/IOIOLib/src/ioio/lib/impl/SocketIOIOConnectionBootstrap.java @@ -0,0 +1,63 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ + +package ioio.lib.impl; + +import ioio.lib.api.IOIOConnection; +import ioio.lib.spi.IOIOConnectionBootstrap; +import ioio.lib.spi.IOIOConnectionFactory; + +import java.util.Collection; + +public class SocketIOIOConnectionBootstrap implements IOIOConnectionBootstrap { + /** The TCP port used for communicating with the IOIO board. */ + public static final int IOIO_PORT = 4545; + + @Override + public void getFactories(Collection result) { + result.add(new IOIOConnectionFactory() { + private Integer port_ = new Integer(IOIO_PORT); + + @Override + public String getType() { + return SocketIOIOConnection.class.getCanonicalName(); + } + + @Override + public Object getExtra() { + return port_; + } + + @Override + public IOIOConnection createConnection() { + return new SocketIOIOConnection(IOIO_PORT); + } + }); + } +} diff --git a/IOIOLib/src/ioio/lib/impl/SpiMasterImpl.java b/IOIOLib/src/ioio/lib/impl/SpiMasterImpl.java old mode 100755 new mode 100644 index bdd88ef..da27f67 --- a/IOIOLib/src/ioio/lib/impl/SpiMasterImpl.java +++ b/IOIOLib/src/ioio/lib/impl/SpiMasterImpl.java @@ -42,7 +42,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; import android.util.Log; -public class SpiMasterImpl extends AbstractResource implements SpiMaster, +class SpiMasterImpl extends AbstractResource implements SpiMaster, DataModuleListener, Sender { public class SpiResult implements Result { boolean ready_; diff --git a/IOIOLib/src/ioio/lib/impl/TwiMasterImpl.java b/IOIOLib/src/ioio/lib/impl/TwiMasterImpl.java old mode 100755 new mode 100644 index bfe2023..638a5fc --- a/IOIOLib/src/ioio/lib/impl/TwiMasterImpl.java +++ b/IOIOLib/src/ioio/lib/impl/TwiMasterImpl.java @@ -40,7 +40,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; import android.util.Log; -public class TwiMasterImpl extends AbstractResource implements TwiMaster, +class TwiMasterImpl extends AbstractResource implements TwiMaster, DataModuleListener, Sender { class TwiResult implements Result { boolean ready_ = false; diff --git a/IOIOLib/src/ioio/lib/impl/UartImpl.java b/IOIOLib/src/ioio/lib/impl/UartImpl.java old mode 100755 new mode 100644 index 4cd7a28..531704f --- a/IOIOLib/src/ioio/lib/impl/UartImpl.java +++ b/IOIOLib/src/ioio/lib/impl/UartImpl.java @@ -40,7 +40,7 @@ import java.io.OutputStream; import android.util.Log; -public class UartImpl extends AbstractResource implements DataModuleListener, Sender, Uart { +class UartImpl extends AbstractResource implements DataModuleListener, Sender, Uart { private static final int MAX_PACKET = 64; private final int uartNum_; @@ -87,7 +87,8 @@ public class UartImpl extends AbstractResource implements DataModuleListener, Se @Override synchronized public void disconnected() { super.disconnected(); - outgoing_.kill(); + incoming_.kill(); + outgoing_.close(); } @Override diff --git a/IOIOLib/src/ioio/lib/spi/IOIOConnectionBootstrap.java b/IOIOLib/src/ioio/lib/spi/IOIOConnectionBootstrap.java new file mode 100644 index 0000000..1479b19 --- /dev/null +++ b/IOIOLib/src/ioio/lib/spi/IOIOConnectionBootstrap.java @@ -0,0 +1,41 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ + +package ioio.lib.spi; + +import java.util.Collection; + +/** + * Implementing class must have a default constructor. The default constructor + * must throw a NoRuntimeSupportException in case the required libraries for + * implementing the connections are not available in run-time. + */ +public interface IOIOConnectionBootstrap { + public void getFactories(Collection result); +} diff --git a/IOIOLib/src/ioio/lib/spi/IOIOConnectionFactory.java b/IOIOLib/src/ioio/lib/spi/IOIOConnectionFactory.java new file mode 100644 index 0000000..ce9fd40 --- /dev/null +++ b/IOIOLib/src/ioio/lib/spi/IOIOConnectionFactory.java @@ -0,0 +1,21 @@ +package ioio.lib.spi; + +import ioio.lib.api.IOIOConnection; + +public interface IOIOConnectionFactory { + /** + * A unique name of the connection type. Typically a fully-qualified + * name of the connection class. + */ + public String getType(); + + /** + * Extra information on the connection. This is specific to the + * connection type. For example, for a Bluetooth connection, this is an + * array containing the name and the Bluetooth address of the remote + * IOIO. + */ + public Object getExtra(); + + public IOIOConnection createConnection(); +} \ No newline at end of file diff --git a/IOIOLib/src/ioio/lib/spi/NoRuntimeSupportException.java b/IOIOLib/src/ioio/lib/spi/NoRuntimeSupportException.java new file mode 100644 index 0000000..1b6e901 --- /dev/null +++ b/IOIOLib/src/ioio/lib/spi/NoRuntimeSupportException.java @@ -0,0 +1,9 @@ +package ioio.lib.spi; + +public class NoRuntimeSupportException extends Exception { + private static final long serialVersionUID = -6559208663699429514L; + + public NoRuntimeSupportException(String desc) { + super(desc); + } +} \ No newline at end of file diff --git a/IOIOLib/src/ioio/lib/util/AbstractIOIOActivity.java b/IOIOLib/src/ioio/lib/util/AbstractIOIOActivity.java old mode 100755 new mode 100644 index e2e4c1b..8408048 --- a/IOIOLib/src/ioio/lib/util/AbstractIOIOActivity.java +++ b/IOIOLib/src/ioio/lib/util/AbstractIOIOActivity.java @@ -1,295 +1,369 @@ -package ioio.lib.util; - -import ioio.lib.api.IOIO; -import ioio.lib.api.IOIOFactory; -import ioio.lib.api.exception.ConnectionLostException; -import ioio.lib.api.exception.IncompatibilityException; -import ioio.lib.util.IOIOConnectionDiscovery.IOIOConnectionSpec; - -import java.util.Collection; -import java.util.LinkedList; - -import android.app.Activity; -import android.os.Looper; -import android.util.Log; - -/** - * A convenience class for easy creation of IOIO-based applications. - * - * It is used by creating a concrete Activity in your application, which extends - * this class. This class then takes care of proper creation and abortion of the - * IOIO connection and of a dedicated thread for IOIO communication. - * - * In the basic usage the client should extend this class and implement - * {@link #createIOIOThread()}, which should return an implementation of the - * {@link IOIOThread} abstract class. In this implementation, the client - * implements the {@link IOIOThread#setup()} method, which gets called as soon - * as communication with the IOIO is established, and the {@link IOIOThread - * #loop()} method, which gets called repetitively as long as the IOIO is - * connected. Both methods should access the {@link IOIOThread#ioio_} field for - * controlling the IOIO. - * - * In addition, the {@link IOIOThread#disconnected()} method may be overridden - * in order to execute logic as soon as a disconnection occurs for whichever - * reason. The {@link IOIOThread#incompatible()} method may be overridden in - * order to take action in case where a IOIO whose firmware is incompatible with - * the IOIOLib version that application is built with. - * - * In a more advanced use case, more than one IOIO is available. In this case, a - * thread will be created for each IOIO, whose semantics are as defined above. - * If the client needs to be able to distinguish between them, it is possible to - * override {@link #createIOIOThread(String, Object[])} instead of - * {@link #createIOIOThread()}. The first argument provided will contain the - * connection class name, such as ioio.lib.impl.SocketIOIOConnection for a - * connection established over a TCP socket (which is used over ADB). The second - * argument will contain information specific to the connection type. For - * example, in the case of SocketIOIOConnection, the array will contain an - * {@link Integer} representing the local port number. - * - */ -public abstract class AbstractIOIOActivity extends Activity { - private static final String TAG = "AbstractIOIOActivity"; - private IOIOConnectionSpec currentSpec_; - private Collection threads_ = new LinkedList(); - - /** - * Subclasses should call this method from their own onResume() if - * overloaded. It takes care of connecting with the IOIO. - */ - @Override - protected void onResume() { - super.onResume(); - createAllThreads(); - startAllThreads(); - } - - /** - * Subclasses should call this method from their own onPause() if - * overloaded. It takes care of disconnecting from the IOIO. - */ - @Override - protected void onPause() { - super.onPause(); - abortAllThreads(); - try { - joinAllThreads(); - } catch (InterruptedException e) { - } - } - - /** - * Subclasses should implement this method by returning a concrete subclass - * of {@link IOIOThread}. null may be returned if the client - * is not interested to connect a thread for this IOIO. In multi-IOIO - * scenarios, where you want to identify which IOIO the thread is for, - * consider using {@link #createIOIOThread()} instead. - * - * @return An implementation of {@link IOIOThread}, or null to - * skip. - */ - protected IOIOThread createIOIOThread() { - return null; - } - - /** - * Subclasses should implement this method by returning a concrete subclass - * of {@link IOIOThread}. This overload is useful in multi-IOIO scenarios, - * where you want to identify which IOIO the thread is for. The provided - * arguments should provide enough information to be unique per connection. - * null may be returned if the client is not interested to - * connect a thread for this IOIO. This can be used in order to filter out - * unwanted connections, for example if the application is only intended for - * wireless connection, any wired connection attempts may be rejected, thus - * saving resources used for listening for incoming wired connections. - * - * @param connectionClass - * The fully-qualified name of the connection class used to - * connect to the IOIO. - * @param connectionArgs - * A list of arguments passed to the constructor of the - * connection class. Should provide information that enables - * distinguishing between different IOIO instances using the same - * connection class. - * - * @return An implementation of {@link IOIOThread}, or null to - * skip. - */ - protected IOIOThread createIOIOThread(String connectionClass, - Object[] connectionArgs) { - return createIOIOThread(); - } - - /** - * An abstract class, which facilitates a thread dedicated for communication - * with a single physical IOIO device. - */ - protected abstract class IOIOThread extends Thread { - /** Subclasses should use this field for controlling the IOIO. */ - protected IOIO ioio_; - private boolean abort_ = false; - private boolean connected_ = true; - private final IOIOConnectionSpec spec_ = currentSpec_; - - /** - * Subclasses should override this method for performing operations to - * be done once as soon as IOIO communication is established. Typically, - * this will include opening pins and modules using the openXXX() - * methods of the {@link #ioio_} field. - */ - protected void setup() throws ConnectionLostException, - InterruptedException { - } - - /** - * Subclasses should override this method for performing operations to - * be done repetitively as long as IOIO communication persists. - * Typically, this will be the main logic of the application, processing - * inputs and producing outputs. - */ - protected void loop() throws ConnectionLostException, - InterruptedException { - sleep(100000); - } - - /** - * Subclasses should override this method for performing operations to - * be done once as soon as IOIO communication is lost or closed. - * Typically, this will include GUI changes corresponding to the change. - * This method will only be called if setup() has been called. The - * {@link #ioio_} member must not be used from within this method. - */ - protected void disconnected() throws InterruptedException { - } - - /** - * Subclasses should override this method for performing operations to - * be done if an incompatible IOIO firmware is detected. The - * {@link #ioio_} member must not be used from within this method. This - * method will only be called once, until a compatible IOIO is connected - * (i.e. {@link #setup()} gets called). - */ - protected void incompatible() { - } - - /** Not relevant to subclasses. */ - @Override - public final void run() { - super.run(); - Looper.prepare(); - while (true) { - try { - synchronized (this) { - if (abort_) { - break; - } - ioio_ = IOIOFactory.create(spec_.className, spec_.args); - } - ioio_.waitForConnect(); - connected_ = true; - setup(); - while (!abort_) { - loop(); - } - ioio_.disconnect(); - } catch (ConnectionLostException e) { - if (abort_) { - break; - } - } catch (InterruptedException e) { - ioio_.disconnect(); - break; - } catch (IncompatibilityException e) { - Log.e(TAG, "Incompatible IOIO firmware", e); - incompatible(); - // nothing to do - just wait until physical disconnection - try { - ioio_.waitForDisconnect(); - } catch (InterruptedException e1) { - ioio_.disconnect(); - } - } catch (Exception e) { - Log.e(TAG, "Unexpected exception caught", e); - ioio_.disconnect(); - break; - } finally { - try { - if (ioio_ != null) { - ioio_.waitForDisconnect(); - if (connected_) { - disconnected(); - } - } - } catch (InterruptedException e) { - } - } - } - } - - /** Not relevant to subclasses. */ - public synchronized final void abort() { - abort_ = true; - if (ioio_ != null) { - ioio_.disconnect(); - } - if (connected_) { - interrupt(); - } - } - } - - private void abortAllThreads() { - for (IOIOThread thread : threads_) { - thread.abort(); - } - } - - private void joinAllThreads() throws InterruptedException { - for (IOIOThread thread : threads_) { - thread.join(); - } - } - - private void createAllThreads() { - threads_.clear(); - Collection specs = getConnectionSpecs(); - for (IOIOConnectionSpec spec : specs) { - currentSpec_ = spec; - IOIOThread thread = createIOIOThread(spec.className, spec.args); - if (thread != null) { - threads_.add(thread); - } - } - } - - private void startAllThreads() { - for (IOIOThread thread : threads_) { - thread.start(); - } - } - - private Collection getConnectionSpecs() { - Collection result = new LinkedList(); - addConnectionSpecs("ioio.lib.util.SocketIOIOConnectionDiscovery", - result); - addConnectionSpecs( - "ioio.lib.bluetooth.BluetoothIOIOConnectionDiscovery", result); - return result; - } - - private void addConnectionSpecs(String discoveryClassName, - Collection result) { - try { - Class cls = Class.forName(discoveryClassName); - IOIOConnectionDiscovery discovery = (IOIOConnectionDiscovery) cls - .newInstance(); - discovery.getSpecs(result); - } catch (ClassNotFoundException e) { - Log.d(TAG, "Discovery class not found: " + discoveryClassName - + ". Not adding."); - } catch (Exception e) { - Log.w(TAG, - "Exception caught while discovering connections - not adding connections of class " - + discoveryClassName, e); - } - } -} +/* + * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ + +package ioio.lib.util; + +import ioio.lib.api.IOIO; +import ioio.lib.api.IOIOFactory; +import ioio.lib.api.exception.ConnectionLostException; +import ioio.lib.api.exception.IncompatibilityException; +import ioio.lib.spi.IOIOConnectionBootstrap; +import ioio.lib.spi.IOIOConnectionFactory; +import ioio.lib.util.android.ContextWrapperDependent; + +import java.util.Collection; +import java.util.LinkedList; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +/** + * A convenience class for easy creation of IOIO-based applications. + * + * It is used by creating a concrete Activity in your application, which extends + * this class. This class then takes care of proper creation and abortion of the + * IOIO connection and of a dedicated thread for IOIO communication. + * + * In the basic usage the client should extend this class and implement + * {@link #createIOIOThread()}, which should return an implementation of the + * {@link AbstractIOIOActivity.IOIOThread} abstract class. In this + * implementation, the client implements the + * {@link AbstractIOIOActivity.IOIOThread#setup()} method, which gets called as + * soon as communication with the IOIO is established, and the + * {@link AbstractIOIOActivity.IOIOThread#loop()} method, which gets called + * repetitively as long as the IOIO is connected. Both methods should access the + * {@link AbstractIOIOActivity.IOIOThread#ioio_} field for controlling the IOIO. + * + * In addition, the {@link AbstractIOIOActivity.IOIOThread#disconnected()} + * method may be overridden in order to execute logic as soon as a disconnection + * occurs for whichever reason. The + * {@link AbstractIOIOActivity.IOIOThread#incompatible()} method may be + * overridden in order to take action in case where a IOIO whose firmware is + * incompatible with the IOIOLib version that application is built with. + * + * In a more advanced use case, more than one IOIO is available. In this case, a + * thread will be created for each IOIO, whose semantics are as defined above. + * If the client needs to be able to distinguish between them, it is possible to + * override {@link #createIOIOThread(String, Object)} instead of + * {@link #createIOIOThread()}. The first argument provided will contain the + * connection class name, such as ioio.lib.impl.SocketIOIOConnection for a + * connection established over a TCP socket (which is used over ADB). The second + * argument will contain information specific to the connection type. For + * example, in the case of SocketIOIOConnection, the second argument will + * contain an {@link Integer} representing the local port number. + * + * @deprecated Please use {@link ioio.lib.util.android.IOIOActivity} instead. + */ +public abstract class AbstractIOIOActivity extends Activity { + private static final String TAG = "AbstractIOIOActivity"; + + static { + IOIOConnectionRegistry + .addBootstraps(new String[] { + "ioio.lib.android.accessory.AccessoryConnectionBootstrap", + "ioio.lib.android.bluetooth.BluetoothIOIOConnectionBootstrap" }); + } + + private Collection threads_ = new LinkedList(); + private Collection bootstraps_ = IOIOConnectionRegistry + .getBootstraps(); + private IOIOConnectionFactory currentConnectionFactory_; + + /** + * Subclasses should call this method from their own onCreate() if + * overloaded. It takes care of connecting with the IOIO. + */ + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + for (IOIOConnectionBootstrap bootstrap : bootstraps_) { + if (bootstrap instanceof ContextWrapperDependent) { + ((ContextWrapperDependent) bootstrap).onCreate(this); + } + } + } + + /** + * Subclasses should call this method from their own onDestroy() if + * overloaded. It takes care of connecting with the IOIO. + */ + @Override + protected void onDestroy() { + for (IOIOConnectionBootstrap bootstrap : bootstraps_) { + if (bootstrap instanceof ContextWrapperDependent) { + ((ContextWrapperDependent) bootstrap).onDestroy(); + } + } + super.onDestroy(); + } + + /** + * Subclasses should call this method from their own onStart() if + * overloaded. It takes care of connecting with the IOIO. + */ + @Override + protected void onStart() { + super.onStart(); + for (IOIOConnectionBootstrap bootstrap : bootstraps_) { + if (bootstrap instanceof ContextWrapperDependent) { + ((ContextWrapperDependent) bootstrap).open(); + } + } + createAllThreads(); + startAllThreads(); + } + + /** + * Subclasses should call this method from their own onStop() if overloaded. + * It takes care of disconnecting from the IOIO. + */ + @Override + protected void onStop() { + abortAllThreads(); + try { + joinAllThreads(); + } catch (InterruptedException e) { + } + for (IOIOConnectionBootstrap bootstrap : bootstraps_) { + if (bootstrap instanceof ContextWrapperDependent) { + ((ContextWrapperDependent) bootstrap).close(); + } + } + super.onStop(); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { + for (IOIOConnectionBootstrap bootstrap : bootstraps_) { + if (bootstrap instanceof ContextWrapperDependent) { + ((ContextWrapperDependent) bootstrap).open(); + } + } + } + } + + /** + * Subclasses must either implement this method or its other overload by + * returning a concrete subclass of {@link IOIOThread}. null + * may be returned if the client is not interested to create a thread for + * this IOIO. In multi-IOIO scenarios, where you want to identify which IOIO + * the thread is for, consider using {@link #createIOIOThread()} instead. + * + * @return An implementation of {@link IOIOThread}, or null to + * skip. + */ + protected IOIOThread createIOIOThread() { + throw new RuntimeException( + "Client must override on of the createIOIOThread overloads!"); + } + + /** + * Subclasses should implement this method by returning a concrete subclass + * of {@link IOIOThread}. This overload is useful in multi-IOIO scenarios, + * where you want to identify which IOIO the thread is for. The provided + * arguments should provide enough information to be unique per connection. + * null may be returned if the client is not interested to + * connect a thread for this IOIO. This can be used in order to filter out + * unwanted connections, for example if the application is only intended for + * wireless connection, any wired connection attempts may be rejected, thus + * saving resources used for listening for incoming wired connections. + * + * @param connectionType + * A unique name of the connection type. Typically, the + * fully-qualified name of the connection class used to connect + * to the IOIO. + * @param extra + * A connection-type-specific object with extra information on + * the specific connection. Should provide information that + * enables distinguishing between different IOIO instances using + * the same connection class. For example, a Bluetooth connection + * type, might have the remote IOIO's Bluetooth name as extra. + * + * @return An implementation of {@link IOIOThread}, or null to + * skip. + */ + protected IOIOThread createIOIOThread(String connectionType, Object extra) { + return createIOIOThread(); + } + + /** + * An abstract class, which facilitates a thread dedicated for communication + * with a single physical IOIO device. + */ + protected abstract class IOIOThread extends Thread { + /** Subclasses should use this field for controlling the IOIO. */ + protected IOIO ioio_; + private boolean abort_ = false; + private boolean connected_ = true; + private final IOIOConnectionFactory connectionFactory_ = currentConnectionFactory_; + + /** + * Subclasses should override this method for performing operations to + * be done once as soon as IOIO communication is established. Typically, + * this will include opening pins and modules using the openXXX() + * methods of the {@link #ioio_} field. + */ + protected void setup() throws ConnectionLostException, + InterruptedException { + } + + /** + * Subclasses should override this method for performing operations to + * be done repetitively as long as IOIO communication persists. + * Typically, this will be the main logic of the application, processing + * inputs and producing outputs. + */ + protected void loop() throws ConnectionLostException, + InterruptedException { + sleep(100000); + } + + /** + * Subclasses should override this method for performing operations to + * be done once as soon as IOIO communication is lost or closed. + * Typically, this will include GUI changes corresponding to the change. + * This method will only be called if setup() has been called. The + * {@link #ioio_} member must not be used from within this method. This + * method should not block for long, since it may cause an ANR. + */ + protected void disconnected() { + } + + /** + * Subclasses should override this method for performing operations to + * be done if an incompatible IOIO firmware is detected. The + * {@link #ioio_} member must not be used from within this method. This + * method will only be called once, until a compatible IOIO is connected + * (i.e. {@link #setup()} gets called). + */ + protected void incompatible() { + } + + /** Not relevant to subclasses. */ + @Override + public final void run() { + super.run(); + while (!abort_) { + try { + synchronized (this) { + if (abort_) { + break; + } + ioio_ = IOIOFactory.create(connectionFactory_ + .createConnection()); + } + } catch (Exception e) { + Log.e(TAG, "Failed to create IOIO, aborting IOIOThread!"); + return; + } + // if we got here, we have a ioio_! + try { + ioio_.waitForConnect(); + connected_ = true; + setup(); + while (!abort_) { + loop(); + } + } catch (ConnectionLostException e) { + } catch (InterruptedException e) { + ioio_.disconnect(); + } catch (IncompatibilityException e) { + Log.e(TAG, "Incompatible IOIO firmware", e); + incompatible(); + // nothing to do - just wait until physical disconnection + } catch (Exception e) { + Log.e(TAG, "Unexpected exception caught", e); + ioio_.disconnect(); + break; + } finally { + try { + ioio_.waitForDisconnect(); + } catch (InterruptedException e1) { + } + synchronized (this) { + ioio_ = null; + } + if (connected_) { + disconnected(); + connected_ = false; + } + } + } + Log.d(TAG, "IOIOThread is exiting"); + } + + /** Not relevant to subclasses. */ + public synchronized final void abort() { + abort_ = true; + if (ioio_ != null) { + ioio_.disconnect(); + } + if (connected_) { + interrupt(); + } + } + } + + private void abortAllThreads() { + for (IOIOThread thread : threads_) { + thread.abort(); + } + } + + private void joinAllThreads() throws InterruptedException { + for (IOIOThread thread : threads_) { + thread.join(); + } + } + + private void createAllThreads() { + threads_.clear(); + Collection factories = IOIOConnectionRegistry + .getConnectionFactories(); + for (IOIOConnectionFactory factory : factories) { + currentConnectionFactory_ = factory; + IOIOThread thread = createIOIOThread(factory.getType(), + factory.getExtra()); + if (thread != null) { + threads_.add(thread); + } + } + } + + private void startAllThreads() { + for (IOIOThread thread : threads_) { + thread.start(); + } + } + +} diff --git a/IOIOLib/src/ioio/lib/util/BaseIOIOLooper.java b/IOIOLib/src/ioio/lib/util/BaseIOIOLooper.java new file mode 100644 index 0000000..8f9b92b --- /dev/null +++ b/IOIOLib/src/ioio/lib/util/BaseIOIOLooper.java @@ -0,0 +1,48 @@ +package ioio.lib.util; + +import ioio.lib.api.IOIO; +import ioio.lib.api.exception.ConnectionLostException; + +/** + * A convenience implementation of {@link IOIOLooper}. + * + * This base class provides no-op implementations for all methods and provides + * the {@link #ioio_} field for subclasses. + * + */ +public class BaseIOIOLooper implements IOIOLooper { + protected IOIO ioio_; + + @Override + public final void setup(IOIO ioio) throws ConnectionLostException, + InterruptedException { + ioio_ = ioio; + setup(); + } + + /** + * This method will be called as soon as connection to the IOIO has been + * established. Typically, this will include opening pins and modules using + * the openXXX() methods of the {@link #ioio_} field. + * + * @throws ConnectionLostException + * The connection to the IOIO has been lost. + * @throws InterruptedException + * The thread has been interrupted. + */ + protected void setup() throws ConnectionLostException, InterruptedException { + } + + @Override + public void loop() throws ConnectionLostException, InterruptedException { + Thread.sleep(20); + } + + @Override + public void disconnected() { + } + + @Override + public void incompatible() { + } +} \ No newline at end of file diff --git a/IOIOLib/src/ioio/lib/util/IOIOApplicationHelper.java b/IOIOLib/src/ioio/lib/util/IOIOApplicationHelper.java new file mode 100644 index 0000000..1114a8d --- /dev/null +++ b/IOIOLib/src/ioio/lib/util/IOIOApplicationHelper.java @@ -0,0 +1,166 @@ +package ioio.lib.util; + +import ioio.lib.api.IOIO; +import ioio.lib.api.IOIOFactory; +import ioio.lib.api.exception.ConnectionLostException; +import ioio.lib.api.exception.IncompatibilityException; +import ioio.lib.spi.IOIOConnectionBootstrap; +import ioio.lib.spi.IOIOConnectionFactory; + +import java.util.Collection; +import java.util.LinkedList; + +import android.util.Log; + +/** + * A helper class for creating different kinds of IOIO based applications. + * + * This class implements a common life-cycle for applications interacting with + * IOIO devices. + *

+ * When the application starts, call {@link #start()}, which will in turn + * attempt to create a thread for each possible IOIO connection channel. Each + * thread will have a respective {@link IOIOLooper}, which the client provides, + * through which the client gets context for working with the IOIO. + *

+ * When the application exits, call {@link #stop()}, which will disconnect all + * open connections and will abort and join all the threads. + * + */ +public class IOIOApplicationHelper { + /** + * An abstract class, which facilitates a thread dedicated for communication + * with a single physical IOIO device. + */ + static private class IOIOThread extends Thread { + protected IOIO ioio_; + private boolean abort_ = false; + private boolean connected_ = true; + private final IOIOLooper looper_; + private final IOIOConnectionFactory connectionFactory_; + + IOIOThread(IOIOLooper looper, IOIOConnectionFactory factory) { + looper_ = looper; + connectionFactory_ = factory; + } + + @Override + public final void run() { + super.run(); + while (!abort_) { + try { + synchronized (this) { + if (abort_) { + break; + } + ioio_ = IOIOFactory.create(connectionFactory_ + .createConnection()); + } + } catch (Exception e) { + Log.e(TAG, "Failed to create IOIO, aborting IOIOThread!"); + return; + } + // if we got here, we have a ioio_! + try { + ioio_.waitForConnect(); + connected_ = true; + looper_.setup(ioio_); + while (!abort_ && ioio_.getState() == IOIO.State.CONNECTED) { + looper_.loop(); + } + } catch (ConnectionLostException e) { + } catch (InterruptedException e) { + ioio_.disconnect(); + } catch (IncompatibilityException e) { + Log.e(TAG, "Incompatible IOIO firmware", e); + looper_.incompatible(); + // nothing to do - just wait until physical + // disconnection + } catch (Exception e) { + Log.e(TAG, "Unexpected exception caught", e); + ioio_.disconnect(); + break; + } finally { + try { + ioio_.waitForDisconnect(); + } catch (InterruptedException e1) { + } + synchronized (this) { + ioio_ = null; + } + if (connected_) { + looper_.disconnected(); + connected_ = false; + } + } + } + Log.d(TAG, "IOIOThread is exiting"); + } + + /** Not relevant to subclasses. */ + public synchronized final void abort() { + abort_ = true; + if (ioio_ != null) { + ioio_.disconnect(); + } + if (connected_) { + interrupt(); + } + } + } + + protected static final String TAG = "IOIOAndroidApplicationHelper"; + protected final IOIOLooperProvider looperProvider_; + private Collection threads_ = new LinkedList(); + protected Collection bootstraps_ = IOIOConnectionRegistry + .getBootstraps(); + + public IOIOApplicationHelper(IOIOLooperProvider provider) { + looperProvider_ = provider; + } + + protected void abortAllThreads() { + for (IOIOThread thread : threads_) { + thread.abort(); + } + } + + protected void joinAllThreads() throws InterruptedException { + for (IOIOThread thread : threads_) { + thread.join(); + } + } + + protected void createAllThreads() { + threads_.clear(); + Collection factories = IOIOConnectionRegistry + .getConnectionFactories(); + for (IOIOConnectionFactory factory : factories) { + IOIOLooper looper = looperProvider_.createIOIOLooper( + factory.getType(), factory.getExtra()); + if (looper != null) { + threads_.add(new IOIOThread(looper, factory)); + } + } + } + + protected void startAllThreads() { + for (IOIOThread thread : threads_) { + thread.start(); + } + } + + public void start() { + createAllThreads(); + startAllThreads(); + } + + public void stop() { + abortAllThreads(); + try { + joinAllThreads(); + } catch (InterruptedException e) { + } + } + +} \ No newline at end of file diff --git a/IOIOLib/src/ioio/lib/util/IOIOConnectionDiscovery.java b/IOIOLib/src/ioio/lib/util/IOIOConnectionDiscovery.java deleted file mode 100644 index 3802fb4..0000000 --- a/IOIOLib/src/ioio/lib/util/IOIOConnectionDiscovery.java +++ /dev/null @@ -1,17 +0,0 @@ -package ioio.lib.util; - -import java.util.Collection; - -public interface IOIOConnectionDiscovery { - public static class IOIOConnectionSpec { - public final String className; - public final Object[] args; - - public IOIOConnectionSpec(String c, Object[] a) { - className = c; - args = a; - } - } - - public void getSpecs(Collection result); -} diff --git a/IOIOLib/src/ioio/lib/util/IOIOConnectionRegistry.java b/IOIOLib/src/ioio/lib/util/IOIOConnectionRegistry.java new file mode 100644 index 0000000..4168b51 --- /dev/null +++ b/IOIOLib/src/ioio/lib/util/IOIOConnectionRegistry.java @@ -0,0 +1,133 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ +package ioio.lib.util; + +import ioio.lib.api.IOIOConnection; +import ioio.lib.api.IOIOFactory; +import ioio.lib.spi.IOIOConnectionBootstrap; +import ioio.lib.spi.IOIOConnectionFactory; + +import java.util.Collection; +import java.util.LinkedList; + +import android.util.Log; + +/** + * A utility class for managing available connection types to IOIO. + *

+ * For advanced usage only! + *

+ * This class facilitates dynamic linkage and instantiation of different IOIO + * connection types. {@link IOIOConnectionBootstrap} classes enable creation of + * {@link IOIOConnectionFactory} instances, from which concrete + * {@link IOIOConnection}s are created. The binding to + * {@link IOIOConnectionBootstrap} is dynamic, thus enabling linkage to succeed + * with or without those bootstraps. Likewise, during runtime, the absence of + * bootstraps is handled gracefully. + * + * A typical usage will call {@link #addBootstraps(String[])} with a list of + * class names to be sought from a static initializer block. It may then call + * {@link #getBootstraps()} to obtain any bootstrap classes that are available + * in runtime, in case the bootstrap classes themselves need some runtime + * handling. Last, the {@link #getConnectionFactories()} will return a + * collection of {@link IOIOConnectionFactory}, each representing one possible + * communication channel to a IOIO. + * + */ +public class IOIOConnectionRegistry { + /** + * Get all available connection specifications. This is a list of all + * currently available communication channels in which a IOIO may be + * available. The client typically passes elements of this collection to + * {@link IOIOFactory#create(IOIOConnection)}, possibly after filtering based on the + * specification's properties. + * + * @return A collection of specifications. + */ + public static Collection getConnectionFactories() { + Collection result = new LinkedList(); + for (IOIOConnectionBootstrap bootstrap : bootstraps_) { + bootstrap.getFactories(result); + } + return result; + } + + /** + * For advanced usage only! Used for special runtime handling of bootstrap + * classes. + * + * @return The bootstraps. + */ + public static Collection getBootstraps() { + return bootstraps_; + } + + /** + * For advanced usage only! Add platform-specific connection bootstrap + * classes. Call before calling {@link #getConnectionFactories()}. + */ + public static void addBootstraps(String[] classNames) { + for (String className : classNames) { + addBootstrap(className); + } + } + + private static final String TAG = "IOIOConnectionRegistry"; + + private static Collection bootstraps_; + + static { + bootstraps_ = new LinkedList(); + String[] classNames = new String[] { "ioio.lib.impl.SocketIOIOConnectionBootstrap" }; + addBootstraps(classNames); + } + + private static void addBootstrap(String className) { + try { + Class bootstrapClass = Class + .forName(className).asSubclass( + IOIOConnectionBootstrap.class); + bootstraps_.add(bootstrapClass.newInstance()); + Log.d(TAG, "Successfully added bootstrap class: " + className); + } catch (ClassNotFoundException e) { + Log.d(TAG, "Bootstrap class not found: " + className + + ". Not adding."); + } catch (RuntimeException e) { + Log.e(TAG, + "Runtime exception caught while attempting to initialize accessory connection factory", + e); + throw e; + } catch (Exception e) { + Log.w(TAG, + "Exception caught while attempting to initialize accessory connection factory", + e); + } + } + +} diff --git a/IOIOLib/src/ioio/lib/util/IOIOLooper.java b/IOIOLib/src/ioio/lib/util/IOIOLooper.java new file mode 100644 index 0000000..aa843da --- /dev/null +++ b/IOIOLib/src/ioio/lib/util/IOIOLooper.java @@ -0,0 +1,56 @@ +package ioio.lib.util; + +import ioio.lib.api.IOIO; +import ioio.lib.api.exception.ConnectionLostException; + +/** + * A handler implementing interaction with a single IOIO over a single + * connection period. The interface utilizes a basic workflow for working with a + * IOIO instance: as soon as a connection is established, {@link #setup(IOIO)} + * will be called. Then, the {@link #loop()} method will be called repeatedly as + * long as the connection is alive. Last, the {@link #disconnected()} method + * will be called upon losing the connection (as result of physical + * disconnection, closing the application, etc). In case a IOIO with an + * incompatible firmware is encountered, {@link #incompatible()} will be called + * instead of {@link #setup(IOIO)}, and the IOIO instance is entirely useless, + * until eventually {@link #disconnected()} gets called. + * + */ +public interface IOIOLooper { + /** + * Subclasses should override this method for performing operations to be + * done once as soon as IOIO communication is established. + */ + public abstract void setup(IOIO ioio) throws ConnectionLostException, + InterruptedException; + + /** + * Subclasses should override this method for performing operations to be + * done repetitively as long as IOIO communication persists. Typically, this + * will be the main logic of the application, processing inputs and + * producing outputs. + */ + public abstract void loop() throws ConnectionLostException, + InterruptedException; + + /** + * Subclasses should override this method for performing operations to be + * done once as soon as IOIO communication is lost or closed. Typically, + * this will include GUI changes corresponding to the change. This method + * will only be called if setup() has been called. The ioio argument passed + * to {@link #setup(IOIO)} must not be used from within this method - it is + * invalid. This method should not block for long, since it may cause an + * ANR. + */ + public abstract void disconnected(); + + /** + * Subclasses should override this method for performing operations to be + * done if an incompatible IOIO firmware is detected. The ioio argument + * passed to {@link #setup(IOIO)} must not be used from within this method - + * it is invalid. This method will only be called once, until a compatible + * IOIO is connected (i.e. {@link #setup(IOIO)} gets called). + */ + public abstract void incompatible(); + +} \ No newline at end of file diff --git a/IOIOLib/src/ioio/lib/util/IOIOLooperProvider.java b/IOIOLib/src/ioio/lib/util/IOIOLooperProvider.java new file mode 100644 index 0000000..a0fae19 --- /dev/null +++ b/IOIOLib/src/ioio/lib/util/IOIOLooperProvider.java @@ -0,0 +1,36 @@ +package ioio.lib.util; + +/** + * An entity that provides {@link IOIOLooper} instances on demand, per + * connection specifications. + */ +public interface IOIOLooperProvider { + + /** + * Subclasses should implement this method by returning an implementation of + * {@link IOIOLooper}. The caller provide enough information to uniquely + * identify the connection, through the parameters. null may be + * returned if the client is not interested to create a thread for this + * IOIO. This can be used in order to filter out unwanted connections, for + * example if the application is only intended for wireless connection, any + * wired connection attempts may be rejected, thus saving resources used for + * listening for incoming wired connections. + * + * @param connectionType + * A unique name of the connection type. Typically, the + * fully-qualified name of the connection class used to connect + * to the IOIO. + * @param extra + * A connection-type-specific object with extra information on + * the specific connection. Should provide information that + * enables distinguishing between different IOIO instances using + * the same connection class. For example, a Bluetooth connection + * type, might have the remote IOIO's Bluetooth name as extra. + * + * @return An implementation of {@link IOIOLooper}, or null to + * skip. + */ + public abstract IOIOLooper createIOIOLooper(String connectionType, + Object extra); + +} \ No newline at end of file diff --git a/IOIOLib/src/ioio/lib/util/SocketIOIOConnectionDiscovery.java b/IOIOLib/src/ioio/lib/util/SocketIOIOConnectionDiscovery.java deleted file mode 100644 index 88f76ee..0000000 --- a/IOIOLib/src/ioio/lib/util/SocketIOIOConnectionDiscovery.java +++ /dev/null @@ -1,15 +0,0 @@ -package ioio.lib.util; - -import ioio.lib.api.IOIOFactory; -import ioio.lib.impl.SocketIOIOConnection; - -import java.util.Collection; - -public class SocketIOIOConnectionDiscovery implements IOIOConnectionDiscovery { - - @Override - public void getSpecs(Collection result) { - result.add(new IOIOConnectionSpec(SocketIOIOConnection.class.getName(), - new Object[] { new Integer(IOIOFactory.IOIO_PORT) })); - } -} diff --git a/IOIOLib/src/ioio/lib/util/android/ContextWrapperDependent.java b/IOIOLib/src/ioio/lib/util/android/ContextWrapperDependent.java new file mode 100644 index 0000000..d012651 --- /dev/null +++ b/IOIOLib/src/ioio/lib/util/android/ContextWrapperDependent.java @@ -0,0 +1,41 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ + +package ioio.lib.util.android; + +import android.content.ContextWrapper; + + +public interface ContextWrapperDependent { + public void onCreate(ContextWrapper wrapper); + public void onDestroy(); + public void open(); + public void reopen(); + public void close(); +} \ No newline at end of file diff --git a/IOIOLib/src/ioio/lib/util/android/IOIOActivity.java b/IOIOLib/src/ioio/lib/util/android/IOIOActivity.java new file mode 100644 index 0000000..5e33366 --- /dev/null +++ b/IOIOLib/src/ioio/lib/util/android/IOIOActivity.java @@ -0,0 +1,148 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ + +package ioio.lib.util.android; + +import ioio.lib.impl.SocketIOIOConnection; +import ioio.lib.util.IOIOLooper; +import ioio.lib.util.IOIOLooperProvider; +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; + +/** + * A convenience class for easy creation of IOIO-based activities. + * + * It is used by creating a concrete {@link Activity} in your application, which + * extends this class. This class then takes care of proper creation and + * abortion of the IOIO connection and of a dedicated thread for IOIO + * communication. + *

+ * In the basic usage the client should extend this class and implement + * {@link #createIOIOLooper()}, which should return an implementation of the + * {@link IOIOLooper} interface. In this implementation, the client implements + * the {@link IOIOLooper#setup(ioio.lib.api.IOIO)} method, which gets called as + * soon as communication with the IOIO is established, and the + * {@link IOIOLooper#loop()} method, which gets called repetitively as long as + * the IOIO is connected. + *

+ * In addition, the {@link IOIOLooper#disconnected()} method may be overridden + * in order to execute logic as soon as a disconnection occurs for whichever + * reason. The {@link IOIOLooper#incompatible()} method may be overridden in + * order to take action in case where a IOIO whose firmware is incompatible with + * the IOIOLib version that application is built with. + *

+ * In a more advanced use case, more than one IOIO is available. In this case, a + * thread will be created for each IOIO, whose semantics are as defined above. + * If the client needs to be able to distinguish between them, it is possible to + * override {@link #createIOIOLooper(String, Object)} instead of + * {@link #createIOIOLooper()}. The first argument provided will contain the + * connection class name, such as ioio.lib.impl.SocketIOIOConnection for a + * connection established over a TCP socket (which is used over ADB). The second + * argument will contain information specific to the connection type. For + * example, in the case of {@link SocketIOIOConnection}, the second argument + * will contain an {@link Integer} representing the local port number. + */ +public abstract class IOIOActivity extends Activity implements + IOIOLooperProvider { + private final IOIOAndroidApplicationHelper helper_ = new IOIOAndroidApplicationHelper( + this, this); + + /** + * Subclasses should call this method from their own onCreate() if + * overloaded. It takes care of connecting with the IOIO. + */ + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + helper_.create(); + } + + /** + * Subclasses should call this method from their own onDestroy() if + * overloaded. It takes care of connecting with the IOIO. + */ + @Override + protected void onDestroy() { + helper_.destroy(); + super.onDestroy(); + } + + /** + * Subclasses should call this method from their own onStart() if + * overloaded. It takes care of connecting with the IOIO. + */ + @Override + protected void onStart() { + super.onStart(); + helper_.start(); + } + + /** + * Subclasses should call this method from their own onStop() if overloaded. + * It takes care of disconnecting from the IOIO. + */ + @Override + protected void onStop() { + helper_.stop(); + super.onStop(); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { + helper_.restart(); + } + } + + /** + * Subclasses must either implement this method or its other overload by + * returning an implementation of {@link IOIOLooper}. A dedicated thread + * will be created for each available IOIO, from which the + * {@link IOIOLooper}'s methods will be invoked. null may be + * returned if the client is not interested to create a thread for this + * IOIO. In multi-IOIO scenarios, where you want to identify which IOIO the + * thread is for, consider overriding + * {@link #createIOIOLooper(String, Object)} instead. + * + * @return An implementation of {@link IOIOLooper}, or null to + * skip. + */ + protected IOIOLooper createIOIOLooper() { + throw new RuntimeException( + "Client must override one of the createIOIOLooper overloads!"); + } + + @Override + public IOIOLooper createIOIOLooper(String connectionType, Object extra) { + return createIOIOLooper(); + } + +} diff --git a/IOIOLib/src/ioio/lib/util/android/IOIOAndroidApplicationHelper.java b/IOIOLib/src/ioio/lib/util/android/IOIOAndroidApplicationHelper.java new file mode 100644 index 0000000..a229a63 --- /dev/null +++ b/IOIOLib/src/ioio/lib/util/android/IOIOAndroidApplicationHelper.java @@ -0,0 +1,69 @@ +package ioio.lib.util.android; + +import ioio.lib.spi.IOIOConnectionBootstrap; +import ioio.lib.util.IOIOApplicationHelper; +import ioio.lib.util.IOIOConnectionRegistry; +import ioio.lib.util.IOIOLooperProvider; + +import android.content.ContextWrapper; + +public class IOIOAndroidApplicationHelper extends IOIOApplicationHelper { + private final ContextWrapper contextWrapper_; + + public IOIOAndroidApplicationHelper(ContextWrapper wrapper, + IOIOLooperProvider provider) { + super(provider); + contextWrapper_ = wrapper; + } + + static { + IOIOConnectionRegistry + .addBootstraps(new String[] { + "ioio.lib.android.accessory.AccessoryConnectionBootstrap", + "ioio.lib.android.bluetooth.BluetoothIOIOConnectionBootstrap" }); + } + + public void create() { + for (IOIOConnectionBootstrap bootstrap : bootstraps_) { + if (bootstrap instanceof ContextWrapperDependent) { + ((ContextWrapperDependent) bootstrap).onCreate(contextWrapper_); + } + } + } + + public void destroy() { + for (IOIOConnectionBootstrap bootstrap : bootstraps_) { + if (bootstrap instanceof ContextWrapperDependent) { + ((ContextWrapperDependent) bootstrap).onDestroy(); + } + } + } + + @Override + public void start() { + for (IOIOConnectionBootstrap bootstrap : bootstraps_) { + if (bootstrap instanceof ContextWrapperDependent) { + ((ContextWrapperDependent) bootstrap).open(); + } + } + super.start(); + } + + @Override + public void stop() { + super.stop(); + for (IOIOConnectionBootstrap bootstrap : bootstraps_) { + if (bootstrap instanceof ContextWrapperDependent) { + ((ContextWrapperDependent) bootstrap).close(); + } + } + } + + public void restart() { + for (IOIOConnectionBootstrap bootstrap : bootstraps_) { + if (bootstrap instanceof ContextWrapperDependent) { + ((ContextWrapperDependent) bootstrap).reopen(); + } + } + } +} diff --git a/IOIOLib/src/ioio/lib/util/android/IOIOService.java b/IOIOLib/src/ioio/lib/util/android/IOIOService.java new file mode 100644 index 0000000..8e15062 --- /dev/null +++ b/IOIOLib/src/ioio/lib/util/android/IOIOService.java @@ -0,0 +1,149 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ + +package ioio.lib.util.android; + +import ioio.lib.impl.SocketIOIOConnection; +import ioio.lib.util.IOIOLooper; +import ioio.lib.util.IOIOLooperProvider; +import android.app.Service; +import android.content.Intent; + +/** + * A convenience class for easy creation of IOIO-based services. + * + * It is used by creating a concrete {@link Service} in your application, which + * extends this class. This class then takes care of proper creation and + * abortion of the IOIO connection and of a dedicated thread for IOIO + * communication. + *

+ * In the basic usage the client should extend this class and implement + * {@link #createIOIOLooper()}, which should return an implementation of the + * {@link IOIOLooper} interface. In this implementation, the client implements + * the {@link IOIOLooper#setup(ioio.lib.api.IOIO)} method, which gets called as + * soon as communication with the IOIO is established, and the + * {@link IOIOLooper#loop()} method, which gets called repetitively as long as + * the IOIO is connected. + *

+ * In addition, the {@link IOIOLooper#disconnected()} method may be overridden + * in order to execute logic as soon as a disconnection occurs for whichever + * reason. The {@link IOIOLooper#incompatible()} method may be overridden in + * order to take action in case where a IOIO whose firmware is incompatible with + * the IOIOLib version that application is built with. + *

+ * In a more advanced use case, more than one IOIO is available. In this case, a + * thread will be created for each IOIO, whose semantics are as defined above. + * If the client needs to be able to distinguish between them, it is possible to + * override {@link #createIOIOLooper(String, Object)} instead of + * {@link #createIOIOLooper()}. The first argument provided will contain the + * connection class name, such as ioio.lib.impl.SocketIOIOConnection for a + * connection established over a TCP socket (which is used over ADB). The second + * argument will contain information specific to the connection type. For + * example, in the case of {@link SocketIOIOConnection}, the second argument + * will contain an {@link Integer} representing the local port number. + */ +public abstract class IOIOService extends Service implements + IOIOLooperProvider { + private final IOIOAndroidApplicationHelper helper_ = new IOIOAndroidApplicationHelper( + this, this); + private boolean started_ = false; + + /** + * Subclasses should call this method from their own onCreate() if + * overloaded. It takes care of connecting with the IOIO. + */ + @Override + public void onCreate() { + super.onCreate(); + helper_.create(); + } + + /** + * Subclasses should call this method from their own onDestroy() if + * overloaded. It takes care of connecting with the IOIO. + */ + @Override + public void onDestroy() { + stop(); + helper_.destroy(); + super.onDestroy(); + } + + /** + * Subclasses should call this method from their own onStart() if + * overloaded. It takes care of connecting with the IOIO. + */ + @Override + public void onStart(Intent intent, int startId) { + super.onStart(intent, startId); + if (!started_) { + helper_.start(); + started_ = true; + } else { + if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { + helper_.restart(); + } + } + } + + /** + * Subclasses should call this method if they wish to disconnect from the + * IOIO(s) until the next onStart(). + */ + protected void stop() { + if (started_) { + helper_.stop(); + started_ = false; + } + } + + /** + * Subclasses must either implement this method or its other overload by + * returning an implementation of {@link IOIOLooper}. A dedicated thread + * will be created for each available IOIO, from which the + * {@link IOIOLooper}'s methods will be invoked. null may be + * returned if the client is not interested to create a thread for this + * IOIO. In multi-IOIO scenarios, where you want to identify which IOIO the + * thread is for, consider overriding + * {@link #createIOIOLooper(String, Object)} instead. + * + * @return An implementation of {@link IOIOLooper}, or null to + * skip. + */ + protected IOIOLooper createIOIOLooper() { + throw new RuntimeException( + "Client must override one of the createIOIOLooper overloads!"); + } + + @Override + public IOIOLooper createIOIOLooper(String connectionType, Object extra) { + return createIOIOLooper(); + } + +}