Skip to content

Commit b5a0e00

Browse files
committed
pybricks.iodevices.XboxController: Support rumble.
1 parent 2b0f6c9 commit b5a0e00

File tree

2 files changed

+92
-0
lines changed

2 files changed

+92
-0
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@
44

55
## [Unreleased]
66

7+
### Added
8+
- Added support for rumble in `XboxController` ([support#1024]).
9+
710
### Changed
811
- Allow single floating point value for brightness array ([support#1547]).
912

13+
[support#1024]: https://github.com/pybricks/support/issues/1024
1014
[support#1547]: https://github.com/pybricks/support/issues/1547
1115

1216
## [3.4.0] - 2024-03-11

pybricks/iodevices/pb_type_iodevices_xbox_controller.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,93 @@ STATIC mp_obj_t pb_xbox_triggers(mp_obj_t self_in) {
432432
}
433433
STATIC 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+
435522
STATIC 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
};
444532
STATIC MP_DEFINE_CONST_DICT(pb_type_xbox_locals_dict, pb_type_xbox_locals_dict_table);
445533

0 commit comments

Comments
 (0)