Skip to content

Commit 6d086bc

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

5 files changed

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

751900
// -----------------------------------------------------------------------------
752901
// 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)