7
7
// except according to those terms.
8
8
9
9
// Reads data from Wii Nunchuck
10
+ //
11
+ // NOTE: This code is provided as an example. Driver developers are encouraged
12
+ // to use the embedded-hal traits if possible rather than coupling directly
13
+ // to this library. An implementation of the embedded-hal I2C traits based
14
+ // on this library may be found in the embedded-hal-linux project.
10
15
11
- extern crate i2cdev;
12
16
extern crate docopt;
17
+ extern crate i2cdev;
13
18
14
19
#[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
15
20
use i2cdev:: linux:: * ;
21
+
22
+ #[ cfg( any( target_os = "linux" , taret_os = "android" ) ) ]
23
+ mod nunchuck {
24
+ use std:: error:: Error ;
25
+ use std:: thread;
26
+ use std:: time:: Duration ;
27
+ use std:: fmt;
28
+
29
+ use i2cdev:: core:: I2CDevice ;
30
+
31
+ pub const NUNCHUCK_SLAVE_ADDR : u16 = 0x52 ;
32
+
33
+ #[ derive( Debug ) ]
34
+ pub enum NunchuckError < E > {
35
+ Error ( E ) ,
36
+ ParseError ,
37
+ }
38
+
39
+ impl < E : Error > fmt:: Display for NunchuckError < E > {
40
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
41
+ match * self {
42
+ NunchuckError :: Error ( ref e) => fmt:: Display :: fmt ( e, f) ,
43
+ NunchuckError :: ParseError => fmt:: Display :: fmt ( self . description ( ) , f) ,
44
+ }
45
+ }
46
+ }
47
+
48
+ impl < E : Error > Error for NunchuckError < E > {
49
+ fn description ( & self ) -> & str {
50
+ match * self {
51
+ NunchuckError :: Error ( ref e) => e. description ( ) ,
52
+ NunchuckError :: ParseError => "Unable to Parse Data" ,
53
+ }
54
+ }
55
+
56
+ fn cause ( & self ) -> Option < & Error > {
57
+ match * self {
58
+ NunchuckError :: Error ( ref e) => Some ( e) ,
59
+ NunchuckError :: ParseError => None ,
60
+ }
61
+ }
62
+ }
63
+
64
+ // TODO: Move Nunchuck code out to be an actual sensor and add tests
65
+
66
+ #[ derive( Debug ) ]
67
+ pub struct NunchuckReading {
68
+ joystick_x : u8 ,
69
+ joystick_y : u8 ,
70
+ accel_x : u16 , // 10-bit
71
+ accel_y : u16 , // 10-bit
72
+ accel_z : u16 , // 10-bit
73
+ c_button_pressed : bool ,
74
+ z_button_pressed : bool ,
75
+ }
76
+
77
+ impl NunchuckReading {
78
+ pub fn from_data ( data : & [ u8 ] ) -> Option < NunchuckReading > {
79
+ if data. len ( ) < 6 {
80
+ None
81
+ } else {
82
+ Some ( NunchuckReading {
83
+ joystick_x : data[ 0 ] ,
84
+ joystick_y : data[ 1 ] ,
85
+ accel_x : ( data[ 2 ] as u16 ) << 2 | ( ( data[ 5 ] as u16 >> 6 ) & 0b11 ) ,
86
+ accel_y : ( data[ 3 ] as u16 ) << 2 | ( ( data[ 5 ] as u16 >> 4 ) & 0b11 ) ,
87
+ accel_z : ( data[ 4 ] as u16 ) << 2 | ( ( data[ 5 ] as u16 >> 2 ) & 0b11 ) ,
88
+ c_button_pressed : ( data[ 5 ] & 0b10 ) == 0 ,
89
+ z_button_pressed : ( data[ 5 ] & 0b01 ) == 0 ,
90
+ } )
91
+ }
92
+ }
93
+ }
94
+
95
+ pub struct Nunchuck < T : I2CDevice > {
96
+ i2cdev : T ,
97
+ }
98
+
99
+ impl < T > Nunchuck < T >
100
+ where
101
+ T : I2CDevice ,
102
+ {
103
+ /// Create a new Wii Nunchuck
104
+ ///
105
+ /// This method will open the provide i2c device file and will
106
+ /// send the required init sequence in order to read data in
107
+ /// the future.
108
+ pub fn new ( i2cdev : T ) -> Result < Nunchuck < T > , T :: Error > {
109
+ let mut nunchuck = Nunchuck { i2cdev : i2cdev } ;
110
+ try!( nunchuck. init ( ) ) ;
111
+ Ok ( nunchuck)
112
+ }
113
+
114
+ #[ cfg( test) ]
115
+ pub fn get_i2cdev ( & mut self ) -> & mut T {
116
+ & mut self . i2cdev
117
+ }
118
+
119
+ /// Send the init sequence to the Wii Nunchuck
120
+ pub fn init ( & mut self ) -> Result < ( ) , T :: Error > {
121
+ // These registers must be written; the documentation is a bit
122
+ // lacking but it appears this is some kind of handshake to
123
+ // perform unencrypted data tranfers
124
+ try!( self . i2cdev . smbus_write_byte_data ( 0xF0 , 0x55 ) ) ;
125
+ try!( self . i2cdev . smbus_write_byte_data ( 0xFB , 0x00 ) ) ;
126
+ Ok ( ( ) )
127
+ }
128
+
129
+ pub fn read ( & mut self ) -> Result < NunchuckReading , NunchuckError < T :: Error > > {
130
+ let mut buf: [ u8 ; 6 ] = [ 0 ; 6 ] ;
131
+
132
+ // tell the nunchuck to prepare a sample
133
+ try!(
134
+ self . i2cdev
135
+ . smbus_write_byte ( 0x00 )
136
+ . map_err ( NunchuckError :: Error )
137
+ ) ;
138
+
139
+ // now, read it!
140
+ thread:: sleep ( Duration :: from_millis ( 10 ) ) ;
141
+ try!( self . i2cdev . read ( & mut buf) . map_err ( NunchuckError :: Error ) ) ;
142
+ NunchuckReading :: from_data ( & buf) . ok_or ( NunchuckError :: ParseError )
143
+ }
144
+ }
145
+
146
+ #[ cfg( test) ]
147
+ mod test {
148
+ use super :: * ;
149
+ use i2cdev:: core:: I2CDevice ;
150
+ use i2cdev:: mock:: MockI2CDevice ;
151
+
152
+ #[ test]
153
+ fn test_intialization ( ) {
154
+ // write out some "bad" values to start out with so we know the
155
+ // write happens
156
+ let mut i2cdev = MockI2CDevice :: new ( ) ;
157
+ i2cdev. smbus_write_byte_data ( 0xF0 , 0xFF ) . unwrap ( ) ;
158
+ i2cdev. smbus_write_byte_data ( 0xFB , 0xFF ) . unwrap ( ) ;
159
+
160
+ // these values must be written out for things to work
161
+ let mut dev = Nunchuck :: new ( i2cdev) . unwrap ( ) ;
162
+ assert_eq ! ( dev. get_i2cdev( ) . smbus_read_byte_data( 0xF0 ) . unwrap( ) , 0x55 ) ;
163
+ assert_eq ! ( dev. get_i2cdev( ) . smbus_read_byte_data( 0xFB ) . unwrap( ) , 0x00 ) ;
164
+ }
165
+
166
+ #[ test]
167
+ fn test_read_zeroed_out ( ) {
168
+ let mut dev = Nunchuck :: new ( MockI2CDevice :: new ( ) ) . unwrap ( ) ;
169
+ let reading = dev. read ( ) . unwrap ( ) ;
170
+ assert_eq ! ( reading. joystick_x, 0 ) ;
171
+ assert_eq ! ( reading. joystick_y, 0 ) ;
172
+ assert_eq ! ( reading. accel_x, 0 ) ;
173
+ assert_eq ! ( reading. accel_y, 0 ) ;
174
+ assert_eq ! ( reading. accel_z, 0 ) ;
175
+ assert_eq ! ( reading. c_button_pressed, true ) ;
176
+ assert_eq ! ( reading. z_button_pressed, true ) ;
177
+ }
178
+
179
+ #[ test]
180
+ fn test_read_sample_data ( ) {
181
+ let mut i2cdev = MockI2CDevice :: new ( ) ;
182
+ i2cdev. write ( & [ 0 , 127 , 128 , 191 , 129 , 144 , 71 ] ) . unwrap ( ) ;
183
+ let mut dev = Nunchuck :: new ( i2cdev) . unwrap ( ) ;
184
+ let reading = dev. read ( ) . unwrap ( ) ;
185
+ assert_eq ! ( reading. joystick_x, 127 ) ;
186
+ assert_eq ! ( reading. joystick_y, 128 ) ;
187
+ assert_eq ! ( reading. accel_x, 765 ) ;
188
+ assert_eq ! ( reading. accel_y, 516 ) ;
189
+ assert_eq ! ( reading. accel_z, 577 ) ;
190
+ assert_eq ! ( reading. c_button_pressed, false ) ;
191
+ assert_eq ! ( reading. z_button_pressed, false ) ;
192
+ }
193
+ }
194
+ }
195
+
16
196
#[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
17
- use i2cdev :: sensors :: nunchuck:: * ;
197
+ use nunchuck:: * ;
18
198
19
199
use std:: env:: args;
20
200
use docopt:: Docopt ;
@@ -38,21 +218,19 @@ fn main() {}
38
218
#[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
39
219
fn main ( ) {
40
220
let args = Docopt :: new ( USAGE )
41
- . and_then ( |d| d. argv ( args ( ) . into_iter ( ) ) . parse ( ) )
42
- . unwrap_or_else ( |e| e. exit ( ) ) ;
221
+ . and_then ( |d| d. argv ( args ( ) . into_iter ( ) ) . parse ( ) )
222
+ . unwrap_or_else ( |e| e. exit ( ) ) ;
43
223
let device = args. get_str ( "<device>" ) ;
44
224
let i2cdev = LinuxI2CDevice :: new ( device, NUNCHUCK_SLAVE_ADDR ) . unwrap ( ) ;
45
225
match Nunchuck :: new ( i2cdev) {
46
226
Err ( err) => {
47
227
println ! ( "Unable to open {:?}, {:?}" , device, err) ;
48
228
}
49
- Ok ( mut nunchuck) => {
50
- loop {
51
- match nunchuck. read ( ) {
52
- Ok ( reading) => println ! ( "{:?}" , reading) ,
53
- Err ( err) => println ! ( "Error: {:?}" , err) ,
54
- } ;
55
- }
56
- }
229
+ Ok ( mut nunchuck) => loop {
230
+ match nunchuck. read ( ) {
231
+ Ok ( reading) => println ! ( "{:?}" , reading) ,
232
+ Err ( err) => println ! ( "Error: {:?}" , err) ,
233
+ } ;
234
+ } ,
57
235
}
58
236
}
0 commit comments