@@ -432,6 +432,93 @@ STATIC mp_obj_t pb_xbox_triggers(mp_obj_t self_in) {
432432}
433433STATIC MP_DEFINE_CONST_FUN_OBJ_1 (pb_xbox_triggers_obj , pb_xbox_triggers );
434434
435+ typedef struct {
436+ uint8_t activation_flags ;
437+ uint8_t power_left_trigger ;
438+ uint8_t power_right_trigger ;
439+ uint8_t power_left_handle ;
440+ uint8_t power_right_handle ;
441+ uint8_t duration_10ms ;
442+ uint8_t delay_10ms ;
443+ uint8_t repetitions ;
444+ } __attribute__((packed )) xbox_rumble_command_t ;
445+
446+ STATIC mp_obj_t pb_xbox_rumble (size_t n_args , const mp_obj_t * pos_args , mp_map_t * kw_args ) {
447+ PB_PARSE_ARGS_METHOD (n_args , pos_args , kw_args ,
448+ pb_type_xbox_obj_t , self ,
449+ PB_ARG_REQUIRED (power ),
450+ PB_ARG_REQUIRED (duration ),
451+ PB_ARG_REQUIRED (delay ),
452+ PB_ARG_REQUIRED (count )
453+ );
454+
455+ (void )self ;
456+ pb_xbox_assert_connected ();
457+ pb_xbox_t * xbox = & pb_xbox_singleton ;
458+
459+ // 1 unit is 10ms, max duration is 250=2500ms.
460+ mp_int_t duration = pb_obj_get_positive_int (duration_in ) / 10 ;
461+ mp_int_t delay = pb_obj_get_positive_int (delay_in ) / 10 ;
462+
463+ // Number of rumbles, capped at 100.
464+ mp_int_t count = pb_obj_get_pct (count_in );
465+
466+ // User order is left, right, left trigger, right trigger.
467+ int8_t intensity [4 ];
468+ pb_obj_get_pct_or_array (power_in , 4 , intensity );
469+
470+ // If a single value is given, rumble only the main actuators.
471+ if (!pb_obj_is_array (power_in )) {
472+ intensity [2 ] = 0 ;
473+ intensity [3 ] = 0 ;
474+ }
475+
476+ xbox_rumble_command_t command = {
477+ .activation_flags = 0 ,
478+ .power_left_trigger = intensity [2 ],
479+ .power_right_trigger = intensity [3 ],
480+ .power_left_handle = intensity [0 ],
481+ .power_right_handle = intensity [1 ],
482+ .duration_10ms = duration > 250 ? 250 : duration ,
483+ .delay_10ms = delay > 250 ? 250 : delay ,
484+ .repetitions = count - 1 ,
485+ };
486+
487+ if (command .power_right_handle ) {
488+ command .activation_flags |= 0x01 ;
489+ }
490+ if (command .power_left_handle ) {
491+ command .activation_flags |= 0x02 ;
492+ }
493+ if (command .power_right_trigger ) {
494+ command .activation_flags |= 0x04 ;
495+ }
496+ if (command .power_left_trigger ) {
497+ command .activation_flags |= 0x08 ;
498+ }
499+
500+ // If all intensities are zero or duration or number is invalid, do nothing.
501+ if (command .activation_flags == 0 || count < 1 || duration < 1 ) {
502+ return mp_const_none ;
503+ }
504+
505+ // REVISIT: Discover this handle dynamically.
506+ const uint16_t handle = 34 ;
507+
508+ static struct {
509+ pbdrv_bluetooth_value_t value ;
510+ char payload [sizeof (command )];
511+ } __attribute__((packed )) msg = {
512+ };
513+ msg .value .size = sizeof (command );
514+ memcpy (msg .payload , & command , sizeof (command ));
515+ pbio_set_uint16_le (msg .value .handle , handle );
516+
517+ pbdrv_bluetooth_peripheral_write (& xbox -> task , & msg .value );
518+ return pb_module_tools_pbio_task_wait_or_await (& xbox -> task );
519+ }
520+ STATIC MP_DEFINE_CONST_FUN_OBJ_KW (pb_xbox_rumble_obj , 1 , pb_xbox_rumble );
521+
435522STATIC const mp_rom_map_elem_t pb_type_xbox_locals_dict_table [] = {
436523 { MP_ROM_QSTR (MP_QSTR_name ), MP_ROM_PTR (& pb_xbox_name_obj ) },
437524 { MP_ROM_QSTR (MP_QSTR_state ), MP_ROM_PTR (& pb_xbox_state_obj ) },
@@ -440,6 +527,7 @@ STATIC const mp_rom_map_elem_t pb_type_xbox_locals_dict_table[] = {
440527 { MP_ROM_QSTR (MP_QSTR_joystick_left ), MP_ROM_PTR (& pb_xbox_joystick_left_obj ) },
441528 { MP_ROM_QSTR (MP_QSTR_joystick_right ), MP_ROM_PTR (& pb_xbox_joystick_right_obj ) },
442529 { MP_ROM_QSTR (MP_QSTR_triggers ), MP_ROM_PTR (& pb_xbox_triggers_obj ) },
530+ { MP_ROM_QSTR (MP_QSTR_rumble ), MP_ROM_PTR (& pb_xbox_rumble_obj ) },
443531};
444532STATIC MP_DEFINE_CONST_DICT (pb_type_xbox_locals_dict , pb_type_xbox_locals_dict_table );
445533
0 commit comments