3838/**
3939 * The main HID Characteristic.
4040 */
41- static pbdrv_bluetooth_peripheral_char_t pb_xbox_char_hid_report = {
41+ static pbdrv_bluetooth_peripheral_char_t pb_type_xbox_char_hid_report = {
4242 .handle = 0 , // Will be set during discovery.
4343 // Even with the property filter, there are still 3 matches for this
4444 // characteristic on the Elite Series 2 controller. For now limit discovery
@@ -54,7 +54,7 @@ static pbdrv_bluetooth_peripheral_char_t pb_xbox_char_hid_report = {
5454/**
5555 * Unused characteristic that needs to be read for controller to become active.
5656 */
57- static pbdrv_bluetooth_peripheral_char_t pb_xbox_char_hid_map = {
57+ static pbdrv_bluetooth_peripheral_char_t pb_type_xbox_char_hid_map = {
5858 .uuid16 = 0x2a4b ,
5959 .request_notification = false,
6060};
@@ -75,17 +75,40 @@ typedef struct __attribute__((packed)) {
7575 uint8_t paddles ;
7676} xbox_input_map_t ;
7777
78- typedef struct {
79- xbox_input_map_t state ;
80- } pb_xbox_t ;
81-
82- static pb_xbox_t pb_xbox_singleton ;
78+ typedef struct _pb_type_xbox_obj_t {
79+ mp_obj_base_t base ;
80+ /**
81+ * The peripheral instance associated with this MicroPython object.
82+ */
83+ pbdrv_bluetooth_peripheral_t * peripheral ;
84+ /**
85+ * Buttons object on the Xbox Controller instance.
86+ */
87+ mp_obj_t buttons ;
88+ /**
89+ * Threshold below which joystick reports zero to avoid drift.
90+ */
91+ mp_int_t joystick_deadzone ;
92+ /**
93+ * State of awaitable used for connecting and writing.
94+ */
95+ pb_type_async_t * iter ;
96+ /**
97+ * Button and joystick state (populated by notification handler).
98+ */
99+ xbox_input_map_t input_map ;
100+ } pb_type_xbox_obj_t ;
83101
84102// Handles LEGO Wireless protocol messages from the XBOX Device.
85103static void handle_notification (void * user , const uint8_t * value , uint32_t size ) {
86- pb_xbox_t * xbox = & pb_xbox_singleton ;
104+
105+ pb_type_xbox_obj_t * self = user ;
106+ if (!self ) {
107+ return ;
108+ }
109+
87110 if (size <= sizeof (xbox_input_map_t )) {
88- memcpy (& xbox -> state , & value [0 ], size );
111+ memcpy (& self -> input_map , & value [0 ], size );
89112 }
90113}
91114
@@ -159,27 +182,20 @@ static pbdrv_bluetooth_ad_match_result_flags_t xbox_advertisement_response_match
159182 return flags ;
160183}
161184
162- static void pb_xbox_assert_connected (void ) {
185+ static xbox_input_map_t * pb_type_xbox_get_input (mp_obj_t self_in ) {
186+
163187 if (!pbdrv_bluetooth_is_connected (PBDRV_BLUETOOTH_CONNECTION_PERIPHERAL )) {
164188 mp_raise_OSError (MP_ENODEV );
165189 }
166- }
167190
168- typedef struct _pb_type_xbox_obj_t {
169- mp_obj_base_t base ;
170- mp_obj_t buttons ;
171- mp_int_t joystick_deadzone ;
172- pb_type_async_t * iter ;
173- } pb_type_xbox_obj_t ;
191+ pb_type_xbox_obj_t * self = MP_OBJ_TO_PTR (self_in );
174192
175- static xbox_input_map_t * pb_xbox_get_buttons (void ) {
176- xbox_input_map_t * buttons = & pb_xbox_singleton .state ;
177- pb_xbox_assert_connected ();
178- return buttons ;
193+ return & self -> input_map ;
179194}
180195
181- static mp_obj_t pb_xbox_button_pressed (mp_obj_t self_in ) {
182- xbox_input_map_t * buttons = pb_xbox_get_buttons ();
196+ static mp_obj_t pb_type_xbox_button_pressed (mp_obj_t self_in ) {
197+
198+ xbox_input_map_t * buttons = pb_type_xbox_get_input (self_in );
183199
184200 // At most 16 simultaneous button presses, plus up to two dpad directions.
185201 mp_obj_t items [16 + 2 ];
@@ -276,6 +292,11 @@ static pbio_error_t xbox_connect_thread(pbio_os_state_t *state, mp_obj_t parent_
276292
277293 PBIO_OS_ASYNC_BEGIN (state );
278294
295+ pb_type_xbox_obj_t * self = MP_OBJ_TO_PTR (parent_obj );
296+
297+ // Get available peripheral instance.
298+ pb_assert (pbdrv_bluetooth_peripheral_get_available (& self -> peripheral , self ));
299+
279300 // Connect with bonding enabled. On some computers, the pairing step will
280301 // fail if the hub is still connected to Pybricks Code. Since it is unclear
281302 // which computer will have this problem, recommend to disconnect the hub
@@ -296,24 +317,24 @@ static pbio_error_t xbox_connect_thread(pbio_os_state_t *state, mp_obj_t parent_
296317 // catch the case where user might not have done this at least once.
297318 // Connecting takes about a second longer this way, but we can provide
298319 // better error messages.
299- pb_assert (pbdrv_bluetooth_peripheral_discover_characteristic (& pb_xbox_char_hid_map ));
320+ pb_assert (pbdrv_bluetooth_peripheral_discover_characteristic (& pb_type_xbox_char_hid_map ));
300321 PBIO_OS_AWAIT (state , & unused , err = pbdrv_bluetooth_await_peripheral_command (& unused , NULL ));
301322 if (err != PBIO_SUCCESS ) {
302323 goto disconnect ;
303324 }
304- pb_assert (pbdrv_bluetooth_peripheral_read_characteristic (& pb_xbox_char_hid_map ));
325+ pb_assert (pbdrv_bluetooth_peripheral_read_characteristic (& pb_type_xbox_char_hid_map ));
305326 PBIO_OS_AWAIT (state , & unused , err = pbdrv_bluetooth_await_peripheral_command (& unused , NULL ));
306327 if (err != PBIO_SUCCESS ) {
307328 goto disconnect ;
308329 }
309330
310331 // This is the main characteristic that notifies us of button state.
311- pb_assert (pbdrv_bluetooth_peripheral_discover_characteristic (& pb_xbox_char_hid_report ));
332+ pb_assert (pbdrv_bluetooth_peripheral_discover_characteristic (& pb_type_xbox_char_hid_report ));
312333 PBIO_OS_AWAIT (state , & unused , err = pbdrv_bluetooth_await_peripheral_command (& unused , NULL ));
313334 if (err != PBIO_SUCCESS ) {
314335 goto disconnect ;
315336 }
316- pb_assert (pbdrv_bluetooth_peripheral_read_characteristic (& pb_xbox_char_hid_report ));
337+ pb_assert (pbdrv_bluetooth_peripheral_read_characteristic (& pb_type_xbox_char_hid_report ));
317338 PBIO_OS_AWAIT (state , & unused , err = pbdrv_bluetooth_await_peripheral_command (& unused , NULL ));
318339 if (err != PBIO_SUCCESS ) {
319340 goto disconnect ;
@@ -360,6 +381,24 @@ static mp_obj_t pb_type_xbox_await_operation(mp_obj_t self_in) {
360381 return pb_type_async_wait_or_await (& config , & self -> iter , true);
361382}
362383
384+ static mp_obj_t pb_type_xbox_close (mp_obj_t self_in ) {
385+ pb_type_xbox_obj_t * self = MP_OBJ_TO_PTR (self_in );
386+ // Disables notification handler from accessing allocated memory.
387+ pbdrv_bluetooth_peripheral_release (self -> peripheral , self );
388+ return mp_const_none ;
389+ }
390+ MP_DEFINE_CONST_FUN_OBJ_1 (pb_type_xbox_close_obj , pb_type_xbox_close );
391+
392+ static mp_obj_t pb_type_xbox_disconnect (mp_obj_t self_in ) {
393+ // Needed to release claim on allocated data so we can make a new
394+ // connection later.
395+ pb_type_xbox_close (self_in );
396+ pb_assert (pbdrv_bluetooth_peripheral_disconnect ());
397+ return pb_type_xbox_await_operation (self_in );
398+ }
399+ static MP_DEFINE_CONST_FUN_OBJ_1 (pb_type_xbox_disconnect_obj , pb_type_xbox_disconnect ) ;
400+
401+
363402static mp_obj_t pb_type_xbox_make_new (const mp_obj_type_t * type , size_t n_args , size_t n_kw , const mp_obj_t * args ) {
364403
365404 PB_PARSE_ARGS_CLASS (n_args , n_kw , args ,
@@ -373,17 +412,17 @@ static mp_obj_t pb_type_xbox_make_new(const mp_obj_type_t *type, size_t n_args,
373412
374413 pb_module_tools_assert_blocking ();
375414
376- pb_type_xbox_obj_t * self = mp_obj_malloc (pb_type_xbox_obj_t , type );
415+ pb_type_xbox_obj_t * self = mp_obj_malloc_with_finaliser (pb_type_xbox_obj_t , type );
377416 self -> joystick_deadzone = pb_obj_get_pct (joystick_deadzone_in );
378417 self -> iter = NULL ;
379418
380- pb_xbox_t * xbox = & pb_xbox_singleton ;
381- self -> buttons = pb_type_Keypad_obj_new (MP_OBJ_FROM_PTR (self ), pb_xbox_button_pressed );
419+ self -> buttons = pb_type_Keypad_obj_new (MP_OBJ_FROM_PTR (self ), pb_type_xbox_button_pressed );
382420
383- // needed to ensure that no buttons are "pressed" after reconnecting since
384- // we are using static memory
385- memset (& xbox -> state , 0 , sizeof (xbox_input_map_t ));
386- xbox -> state .x = xbox -> state .y = xbox -> state .z = xbox -> state .rz = INT16_MAX ;
421+ // needed to ensure that no buttons are "pressed" since we are using
422+ // allocated memory
423+ xbox_input_map_t * input = & self -> input_map ;
424+ memset (input , 0 , sizeof (xbox_input_map_t ));
425+ input -> x = input -> y = input -> z = input -> rz = INT16_MAX ;
387426
388427 // Xbox Controller requires pairing.
389428 scan_config .options = PBDRV_BLUETOOTH_PERIPHERAL_OPTIONS_PAIR ;
@@ -403,16 +442,18 @@ static mp_obj_t pb_type_xbox_make_new(const mp_obj_type_t *type, size_t n_args,
403442 return MP_OBJ_FROM_PTR (self );
404443}
405444
406- static mp_obj_t pb_xbox_name (size_t n_args , const mp_obj_t * args ) {
407- pb_xbox_assert_connected ();
445+ static mp_obj_t pb_type_xbox_name (size_t n_args , const mp_obj_t * args ) {
446+ // Asserts connection.
447+ pb_type_xbox_get_input (args [0 ]);
448+
408449 const char * name = pbdrv_bluetooth_peripheral_get_name ();
409450 return mp_obj_new_str (name , strlen (name ));
410451}
411- static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (pb_xbox_name_obj , 1 , 2 , pb_xbox_name ) ;
452+ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (pb_type_xbox_name_obj , 1 , 2 , pb_type_xbox_name ) ;
412453
413- static mp_obj_t pb_xbox_state (mp_obj_t self_in ) {
454+ static mp_obj_t pb_type_xbox_state (mp_obj_t self_in ) {
414455
415- xbox_input_map_t * buttons = pb_xbox_get_buttons ( );
456+ xbox_input_map_t * buttons = pb_type_xbox_get_input ( self_in );
416457
417458 mp_obj_t state [] = {
418459 mp_obj_new_int (buttons -> x - INT16_MAX ),
@@ -430,21 +471,21 @@ static mp_obj_t pb_xbox_state(mp_obj_t self_in) {
430471 };
431472 return mp_obj_new_tuple (MP_ARRAY_SIZE (state ), state );
432473}
433- static MP_DEFINE_CONST_FUN_OBJ_1 (pb_xbox_state_obj , pb_xbox_state ) ;
474+ static MP_DEFINE_CONST_FUN_OBJ_1 (pb_type_xbox_state_obj , pb_type_xbox_state ) ;
434475
435- static mp_obj_t pb_xbox_dpad (mp_obj_t self_in ) {
436- xbox_input_map_t * buttons = pb_xbox_get_buttons ( );
476+ static mp_obj_t pb_type_xbox_dpad (mp_obj_t self_in ) {
477+ xbox_input_map_t * buttons = pb_type_xbox_get_input ( self_in );
437478 return mp_obj_new_int (buttons -> dpad );
438479}
439- static MP_DEFINE_CONST_FUN_OBJ_1 (pb_xbox_dpad_obj , pb_xbox_dpad ) ;
480+ static MP_DEFINE_CONST_FUN_OBJ_1 (pb_type_xbox_dpad_obj , pb_type_xbox_dpad ) ;
440481
441- static mp_obj_t pb_xbox_profile (mp_obj_t self_in ) {
442- xbox_input_map_t * buttons = pb_xbox_get_buttons ( );
482+ static mp_obj_t pb_type_xbox_profile (mp_obj_t self_in ) {
483+ xbox_input_map_t * buttons = pb_type_xbox_get_input ( self_in );
443484 return mp_obj_new_int (buttons -> profile );
444485}
445- static MP_DEFINE_CONST_FUN_OBJ_1 (pb_xbox_profile_obj , pb_xbox_profile ) ;
486+ static MP_DEFINE_CONST_FUN_OBJ_1 (pb_type_xbox_profile_obj , pb_type_xbox_profile ) ;
446487
447- static mp_obj_t pb_xbox_joystick (mp_obj_t self_in , uint16_t x_raw , uint16_t y_raw ) {
488+ static mp_obj_t pb_type_xbox_joystick (mp_obj_t self_in , uint16_t x_raw , uint16_t y_raw ) {
448489 pb_type_xbox_obj_t * self = MP_OBJ_TO_PTR (self_in );
449490
450491 mp_int_t x = (x_raw - INT16_MAX ) * 100 / INT16_MAX ;
@@ -464,27 +505,27 @@ static mp_obj_t pb_xbox_joystick(mp_obj_t self_in, uint16_t x_raw, uint16_t y_ra
464505 return mp_obj_new_tuple (MP_ARRAY_SIZE (directions ), directions );
465506}
466507
467- static mp_obj_t pb_xbox_joystick_left (mp_obj_t self_in ) {
468- xbox_input_map_t * buttons = pb_xbox_get_buttons ( );
469- return pb_xbox_joystick (self_in , buttons -> x , buttons -> y );
508+ static mp_obj_t pb_type_xbox_joystick_left (mp_obj_t self_in ) {
509+ xbox_input_map_t * buttons = pb_type_xbox_get_input ( self_in );
510+ return pb_type_xbox_joystick (self_in , buttons -> x , buttons -> y );
470511}
471- static MP_DEFINE_CONST_FUN_OBJ_1 (pb_xbox_joystick_left_obj , pb_xbox_joystick_left ) ;
512+ static MP_DEFINE_CONST_FUN_OBJ_1 (pb_type_xbox_joystick_left_obj , pb_type_xbox_joystick_left ) ;
472513
473- static mp_obj_t pb_xbox_joystick_right (mp_obj_t self_in ) {
474- xbox_input_map_t * buttons = pb_xbox_get_buttons ( );
475- return pb_xbox_joystick (self_in , buttons -> z , buttons -> rz );
514+ static mp_obj_t pb_type_xbox_joystick_right (mp_obj_t self_in ) {
515+ xbox_input_map_t * buttons = pb_type_xbox_get_input ( self_in );
516+ return pb_type_xbox_joystick (self_in , buttons -> z , buttons -> rz );
476517}
477- static MP_DEFINE_CONST_FUN_OBJ_1 (pb_xbox_joystick_right_obj , pb_xbox_joystick_right ) ;
518+ static MP_DEFINE_CONST_FUN_OBJ_1 (pb_type_xbox_joystick_right_obj , pb_type_xbox_joystick_right ) ;
478519
479- static mp_obj_t pb_xbox_triggers (mp_obj_t self_in ) {
480- xbox_input_map_t * buttons = pb_xbox_get_buttons ( );
520+ static mp_obj_t pb_type_xbox_triggers (mp_obj_t self_in ) {
521+ xbox_input_map_t * buttons = pb_type_xbox_get_input ( self_in );
481522 mp_obj_t tiggers [] = {
482523 mp_obj_new_int (buttons -> left_trigger * 100 / 1023 ),
483524 mp_obj_new_int (buttons -> right_trigger * 100 / 1023 ),
484525 };
485526 return mp_obj_new_tuple (MP_ARRAY_SIZE (tiggers ), tiggers );
486527}
487- static MP_DEFINE_CONST_FUN_OBJ_1 (pb_xbox_triggers_obj , pb_xbox_triggers ) ;
528+ static MP_DEFINE_CONST_FUN_OBJ_1 (pb_type_xbox_triggers_obj , pb_type_xbox_triggers ) ;
488529
489530typedef struct {
490531 uint8_t activation_flags ;
@@ -497,7 +538,7 @@ typedef struct {
497538 uint8_t repetitions ;
498539} __attribute__((packed )) xbox_rumble_command_t ;
499540
500- static mp_obj_t pb_xbox_rumble (size_t n_args , const mp_obj_t * pos_args , mp_map_t * kw_args ) {
541+ static mp_obj_t pb_type_xbox_rumble (size_t n_args , const mp_obj_t * pos_args , mp_map_t * kw_args ) {
501542 PB_PARSE_ARGS_METHOD (n_args , pos_args , kw_args ,
502543 pb_type_xbox_obj_t , self ,
503544 PB_ARG_DEFAULT_INT (power , 100 ),
@@ -564,17 +605,19 @@ static mp_obj_t pb_xbox_rumble(size_t n_args, const mp_obj_t *pos_args, mp_map_t
564605 pbdrv_bluetooth_peripheral_write_characteristic (handle , (const uint8_t * )& command , sizeof (command ));
565606 return pb_type_xbox_await_operation (MP_OBJ_TO_PTR (self ));
566607}
567- static MP_DEFINE_CONST_FUN_OBJ_KW (pb_xbox_rumble_obj , 1 , pb_xbox_rumble ) ;
608+ static MP_DEFINE_CONST_FUN_OBJ_KW (pb_type_xbox_rumble_obj , 1 , pb_type_xbox_rumble ) ;
568609
569610static const mp_rom_map_elem_t pb_type_xbox_locals_dict_table [] = {
570- { MP_ROM_QSTR (MP_QSTR_name ), MP_ROM_PTR (& pb_xbox_name_obj ) },
571- { MP_ROM_QSTR (MP_QSTR_state ), MP_ROM_PTR (& pb_xbox_state_obj ) },
572- { MP_ROM_QSTR (MP_QSTR_dpad ), MP_ROM_PTR (& pb_xbox_dpad_obj ) },
573- { MP_ROM_QSTR (MP_QSTR_profile ), MP_ROM_PTR (& pb_xbox_profile_obj ) },
574- { MP_ROM_QSTR (MP_QSTR_joystick_left ), MP_ROM_PTR (& pb_xbox_joystick_left_obj ) },
575- { MP_ROM_QSTR (MP_QSTR_joystick_right ), MP_ROM_PTR (& pb_xbox_joystick_right_obj ) },
576- { MP_ROM_QSTR (MP_QSTR_triggers ), MP_ROM_PTR (& pb_xbox_triggers_obj ) },
577- { MP_ROM_QSTR (MP_QSTR_rumble ), MP_ROM_PTR (& pb_xbox_rumble_obj ) },
611+ { MP_ROM_QSTR (MP_QSTR_name ), MP_ROM_PTR (& pb_type_xbox_name_obj ) },
612+ { MP_ROM_QSTR (MP_QSTR___del__ ), MP_ROM_PTR (& pb_type_xbox_close_obj ) },
613+ { MP_ROM_QSTR (MP_QSTR_disconnect ), MP_ROM_PTR (& pb_type_xbox_disconnect_obj ) },
614+ { MP_ROM_QSTR (MP_QSTR_state ), MP_ROM_PTR (& pb_type_xbox_state_obj ) },
615+ { MP_ROM_QSTR (MP_QSTR_dpad ), MP_ROM_PTR (& pb_type_xbox_dpad_obj ) },
616+ { MP_ROM_QSTR (MP_QSTR_profile ), MP_ROM_PTR (& pb_type_xbox_profile_obj ) },
617+ { MP_ROM_QSTR (MP_QSTR_joystick_left ), MP_ROM_PTR (& pb_type_xbox_joystick_left_obj ) },
618+ { MP_ROM_QSTR (MP_QSTR_joystick_right ), MP_ROM_PTR (& pb_type_xbox_joystick_right_obj ) },
619+ { MP_ROM_QSTR (MP_QSTR_triggers ), MP_ROM_PTR (& pb_type_xbox_triggers_obj ) },
620+ { MP_ROM_QSTR (MP_QSTR_rumble ), MP_ROM_PTR (& pb_type_xbox_rumble_obj ) },
578621};
579622static MP_DEFINE_CONST_DICT (pb_type_xbox_locals_dict , pb_type_xbox_locals_dict_table ) ;
580623
0 commit comments