|
25 | 25 | #include <pybricks/tools/pb_type_async.h> |
26 | 26 | #include <pybricks/util_mp/pb_kwarg_helper.h> |
27 | 27 | #include <pybricks/util_mp/pb_obj_helper.h> |
| 28 | +#include <pybricks/util_pb/pb_color_map.h> |
28 | 29 | #include <pybricks/util_pb/pb_error.h> |
29 | 30 |
|
30 | 31 | #include "py/mphal.h" |
@@ -747,6 +748,149 @@ MP_DEFINE_CONST_OBJ_TYPE(pb_type_technic_move_hub, |
747 | 748 | make_new, pb_type_technic_move_hub_make_new, |
748 | 749 | locals_dict, &pb_type_technic_move_hub_locals_dict); |
749 | 750 |
|
| 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); |
750 | 894 |
|
751 | 895 | // ----------------------------------------------------------------------------- |
752 | 896 | // pybricks.pupdevices.DuploTrain (special case of LWP3). |
|
0 commit comments