Skip to content

Commit 19969d4

Browse files
committed
pybricks.pupdevices: Add MarioHub.
Connect and measure colors.
1 parent dff6095 commit 19969d4

5 files changed

Lines changed: 150 additions & 1 deletion

File tree

CHANGELOG.md

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

55
## [Unreleased]
66

7+
###
8+
- Added `pybricks.pupdevices.MarioHub` to control it as a peripheral. It
9+
cannot be used as a standalone device since it cannot ne updated.
710

811
### Changed
912
- The EV3 Color Sensor now returns `Color.NONE` instead of `None` when no color

lib/lego/lego/lwp3.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ typedef enum {
3333
LWP3_HUB_KIND_HANDSET = LWP3_HUB_KIND(LWP3_HUB_SYSTEM_SYSTEM, 2),
3434
LWP3_HUB_KIND_MARIO = LWP3_HUB_KIND(LWP3_HUB_SYSTEM_SYSTEM, 3),
3535
LWP3_HUB_KIND_LUIGI = LWP3_HUB_KIND(LWP3_HUB_SYSTEM_SYSTEM, 4),
36+
LWP3_HUB_KIND_PEACH = LWP3_HUB_KIND(LWP3_HUB_SYSTEM_SYSTEM, 5),
3637

3738
LWP3_HUB_KIND_TECHNIC_MEDIUM = LWP3_HUB_KIND(LWP3_HUB_SYSTEM_TECHNIC, 0),
3839
LWP3_HUB_KIND_TECHNIC_LARGE = LWP3_HUB_KIND(LWP3_HUB_SYSTEM_TECHNIC, 1),

pybricks/iodevices/pb_type_iodevices_lwp3device.c

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <pybricks/tools/pb_type_async.h>
2626
#include <pybricks/util_mp/pb_kwarg_helper.h>
2727
#include <pybricks/util_mp/pb_obj_helper.h>
28+
#include <pybricks/util_pb/pb_color_map.h>
2829
#include <pybricks/util_pb/pb_error.h>
2930

3031
#include "py/mphal.h"
@@ -747,6 +748,149 @@ MP_DEFINE_CONST_OBJ_TYPE(pb_type_technic_move_hub,
747748
make_new, pb_type_technic_move_hub_make_new,
748749
locals_dict, &pb_type_technic_move_hub_locals_dict);
749750

751+
// -----------------------------------------------------------------------------
752+
// pybricks.pupdevices.MarioHub (special case of LWP3).
753+
// -----------------------------------------------------------------------------
754+
755+
#define MARIO_PORT_COLOR (0x01)
756+
#define MARIO_PORT_COLOR_MODE_RGB (0x01)
757+
758+
static const uint8_t pb_type_mario_hub_setup1[] = {
759+
0x0a, 0x00, 0x41, MARIO_PORT_COLOR, MARIO_PORT_COLOR_MODE_RGB, 0x01, 0x00, 0x00, 0x00, 0x01,
760+
};
761+
762+
763+
static bool pb_type_mario_hub_advertisement_matches(void *user, const uint8_t *data, uint8_t length) {
764+
pb_type_lwp3device_obj_t *self = user;
765+
if (!self) {
766+
return false;
767+
}
768+
769+
// Same test as LWP3 advertisement, but allow 3 hub types
770+
return
771+
data[3] == 17 /* length */
772+
&& (data[4] == PBDRV_BLUETOOTH_AD_DATA_TYPE_128_BIT_SERV_UUID_COMPLETE_LIST
773+
|| data[4] == PBDRV_BLUETOOTH_AD_DATA_TYPE_128_BIT_SERV_UUID_INCOMPLETE_LIST)
774+
&& pbio_uuid128_reverse_compare(&data[5], lwp3_hub_service_uuid)
775+
&& (data[26] == LWP3_HUB_KIND_MARIO || data[26] == LWP3_HUB_KIND_LUIGI || data[26] == LWP3_HUB_KIND_PEACH);
776+
}
777+
778+
static void pb_type_mario_hub_handle_notification(void *user, const uint8_t *value, uint32_t size) {
779+
780+
pb_type_lwp3device_obj_t *self = user;
781+
782+
// Want only port value messages.
783+
if (!user || size < 4 || value[2] != 0x45) {
784+
return;
785+
}
786+
787+
// Get RGB values, one byte each.
788+
if (size == 7 && value[3] == MARIO_PORT_COLOR) {
789+
memcpy(&self->data[0], &value[4], 3);
790+
}
791+
}
792+
793+
static pbio_error_t pb_type_mario_hub_post_connect(pbio_os_state_t *state, mp_obj_t parent_obj) {
794+
795+
pbio_os_state_t unused;
796+
pbio_error_t err;
797+
pb_type_lwp3device_obj_t *self = MP_OBJ_TO_PTR(parent_obj);
798+
799+
PBIO_OS_ASYNC_BEGIN(state);
800+
801+
// Subscribe to RGB color info.
802+
err = pbdrv_bluetooth_peripheral_write_characteristic(self->peripheral,
803+
self->lwp3_char_handle, pb_type_mario_hub_setup1, sizeof(pb_type_mario_hub_setup1));
804+
if (err != PBIO_SUCCESS) {
805+
return err;
806+
}
807+
PBIO_OS_AWAIT(state, &unused, err = pbdrv_bluetooth_await_peripheral_command(&unused, self->peripheral));
808+
if (err != PBIO_SUCCESS) {
809+
return err;
810+
}
811+
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
812+
}
813+
814+
static void pb_type_mario_hub_color_get_hsv_data(pb_type_lwp3device_obj_t *self, pbio_color_hsv_t *hsv) {
815+
pbio_color_rgb_t rgb = {
816+
.r = self->data[0],
817+
.g = self->data[1],
818+
.b = self->data[2],
819+
};
820+
pb_color_map_rgb_to_hsv(&rgb, hsv);
821+
}
822+
823+
static mp_obj_t pb_type_mario_hub_color(mp_obj_t self_in) {
824+
pb_type_lwp3device_obj_t *self = MP_OBJ_TO_PTR(self_in);
825+
pbio_color_hsv_t hsv;
826+
pb_type_mario_hub_color_get_hsv_data(self, &hsv);
827+
return pb_color_map_get_color(&self->buttons, &hsv);
828+
}
829+
static MP_DEFINE_CONST_FUN_OBJ_1(pb_type_mario_hub_color_obj, pb_type_mario_hub_color);
830+
831+
static mp_obj_t pb_type_mario_hub_hsv(mp_obj_t self_in) {
832+
pb_type_lwp3device_obj_t *self = MP_OBJ_TO_PTR(self_in);
833+
pb_type_Color_obj_t *color = pb_type_Color_new_empty();
834+
pb_type_mario_hub_color_get_hsv_data(self, &color->hsv);
835+
return MP_OBJ_FROM_PTR(color);
836+
}
837+
static MP_DEFINE_CONST_FUN_OBJ_1(pb_type_mario_hub_hsv_obj, pb_type_mario_hub_hsv);
838+
839+
static mp_obj_t pb_type_mario_hub_detectable_colors(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
840+
PB_PARSE_ARGS_METHOD(n_args, pos_args, kw_args,
841+
pb_type_lwp3device_obj_t, self,
842+
PB_ARG_DEFAULT_NONE(colors));
843+
return pb_color_map_detectable_colors_method(&self->buttons, colors_in);
844+
}
845+
static MP_DEFINE_CONST_FUN_OBJ_KW(pb_type_mario_hub_detectable_colors_obj, 1, pb_type_mario_hub_detectable_colors);
846+
847+
static mp_obj_t pb_type_mario_hub_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
848+
PB_PARSE_ARGS_CLASS(n_args, n_kw, args,
849+
PB_ARG_DEFAULT_NONE(name),
850+
PB_ARG_DEFAULT_INT(timeout, 10000),
851+
PB_ARG_DEFAULT_TRUE(connect)
852+
);
853+
854+
pb_module_tools_assert_blocking();
855+
pb_type_lwp3device_obj_t *self = mp_obj_malloc_with_finaliser(pb_type_lwp3device_obj_t, type);
856+
self->iter = NULL;
857+
self->noti_num = 0;
858+
self->hub_kind = 0; // Not used, checks for 3 mario types instead.
859+
self->post_connect_setup_func = pb_type_mario_hub_post_connect;
860+
self->scan_config = (pbdrv_bluetooth_peripheral_connect_config_t) {
861+
.match_adv = pb_type_mario_hub_advertisement_matches,
862+
.match_adv_rsp = pb_type_lwp3device_advertisement_response_matches,
863+
.notification_handler = pb_type_mario_hub_handle_notification,
864+
// Newer Mario firmware versions require pairing. Older firmware
865+
// versions seem to accept it even if it wasn't required, so always
866+
// attempt it just in case.
867+
.options = PBDRV_BLUETOOTH_PERIPHERAL_OPTIONS_PAIR,
868+
};
869+
pb_type_lwp3device_set_name_filter_and_timeout(self, name_in, timeout_in);
870+
pb_type_lwp3device_intialize_connection(MP_OBJ_FROM_PTR(self), connect_in);
871+
872+
// Reusing the buttons object which is not used on Mario to hold the
873+
// detectable color mapping.
874+
pb_color_map_save_default(&self->buttons);
875+
876+
return MP_OBJ_FROM_PTR(self);
877+
}
878+
879+
static const mp_rom_map_elem_t pb_type_mario_hub_locals_dict_table[] = {
880+
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&pb_type_lwp3device_close_obj) },
881+
{ MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&pb_type_lwp3device_connect_obj) },
882+
{ MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&pb_type_lwp3device_disconnect_obj) },
883+
{ MP_ROM_QSTR(MP_QSTR_color), MP_ROM_PTR(&pb_type_mario_hub_color_obj) },
884+
{ MP_ROM_QSTR(MP_QSTR_hsv), MP_ROM_PTR(&pb_type_mario_hub_hsv_obj) },
885+
{ MP_ROM_QSTR(MP_QSTR_detectable_colors), MP_ROM_PTR(&pb_type_mario_hub_detectable_colors_obj) },
886+
};
887+
static MP_DEFINE_CONST_DICT(pb_type_mario_hub_locals_dict, pb_type_mario_hub_locals_dict_table);
888+
889+
MP_DEFINE_CONST_OBJ_TYPE(pb_type_mario_hub,
890+
MP_QSTR_MarioHub,
891+
MP_TYPE_FLAG_NONE,
892+
make_new, pb_type_mario_hub_make_new,
893+
locals_dict, &pb_type_mario_hub_locals_dict);
750894

751895
// -----------------------------------------------------------------------------
752896
// pybricks.pupdevices.DuploTrain (special case of LWP3).

pybricks/pupdevices.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ extern const mp_obj_type_t pb_type_pupdevices_TiltSensor;
2424
extern const mp_obj_type_t pb_type_pupdevices_UltrasonicSensor;
2525

2626
extern const mp_obj_type_t pb_type_duplo_train;
27+
extern const mp_obj_type_t pb_type_mario_hub;
2728
extern const mp_obj_type_t pb_type_remote;
2829
extern const mp_obj_type_t pb_type_technic_move_hub;
2930

pybricks/pupdevices/pb_module_pupdevices.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ static const mp_rom_map_elem_t pupdevices_globals_table[] = {
2626
{ MP_ROM_QSTR(MP_QSTR_DuploTrain), MP_ROM_PTR(&pb_type_duplo_train) },
2727
#endif
2828
#if PYBRICKS_PY_PUPDEVICES_MARIO
29-
{ MP_ROM_QSTR(MP_QSTR_Mario), MP_ROM_PTR(&pb_type_remote) },
29+
{ MP_ROM_QSTR(MP_QSTR_MarioHub), MP_ROM_PTR(&pb_type_mario_hub) },
3030
#endif
3131
#if PYBRICKS_PY_PUPDEVICES_REMOTE
3232
{ MP_ROM_QSTR(MP_QSTR_Remote), MP_ROM_PTR(&pb_type_remote) },

0 commit comments

Comments
 (0)