• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pybricks / pybricks-micropython / 16158381375

09 Jul 2025 01:49AM UTC coverage: 57.157% (+0.04%) from 57.122%
16158381375

Pull #313

github

web-flow
Merge 5d9a88eac into 88c8c6b88
Pull Request #313: Update to MicroPython v1.25

1 of 6 new or added lines in 2 files covered. (16.67%)

6 existing lines in 1 file now uncovered.

3913 of 6846 relevant lines covered (57.16%)

20535043.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/pybricks/tools/pb_type_app_data.c
1
// SPDX-License-Identifier: MIT
2
// Copyright (c) 2024 The Pybricks Authors
3

4
#include "py/mpconfig.h"
5

6
#if PYBRICKS_PY_TOOLS_APP_DATA
7

8
#include <string.h>
9

10
#include <pbsys/command.h>
11
#include <pbdrv/bluetooth.h>
12

13
#include "py/mphal.h"
14
#include "py/objstr.h"
15

16
#include <pybricks/tools.h>
17
#include <pybricks/tools/pb_type_awaitable.h>
18

19
#include <pybricks/util_mp/pb_kwarg_helper.h>
20
#include <pybricks/util_mp/pb_obj_helper.h>
21
#include <pybricks/util_pb/pb_error.h>
22

23
typedef struct _pb_type_app_data_obj_t {
24
    mp_obj_base_t base;
25
    pbio_task_t tx_task;
26
    pbdrv_bluetooth_send_context_t tx_context;
27
    mp_obj_t rx_format;
28
    mp_obj_str_t rx_bytes_obj;
29
    uint8_t tx_buffer[20]; // REVISIT: Could be the negotiated MTU - 3 for better throughput https://github.com/pybricks/support/issues/1727
30
    uint8_t rx_buffer[] __attribute__((aligned(4)));
31
} pb_type_app_data_obj_t;
32

33
// pointer to dynamically allocated app_data singleton for driver callback.
34
static pb_type_app_data_obj_t *app_data_instance;
35

36
static pbio_error_t handle_incoming_app_data(uint16_t offset, uint32_t size, const uint8_t *data) {
×
37
    // Can't write if rx_buffer does not exist or isn't big enough.
38
    if (!app_data_instance || offset + size > app_data_instance->rx_bytes_obj.len) {
×
39
        return PBIO_ERROR_INVALID_ARG;
40
    }
41
    memcpy(app_data_instance->rx_buffer + offset, data, size);
×
42
    return PBIO_SUCCESS;
×
43
}
44

NEW
45
static mp_obj_t pb_type_app_data_get_bytes(mp_obj_t self_in) {
×
46
    pb_type_app_data_obj_t *self = MP_OBJ_TO_PTR(self_in);
×
47
    // Don't return internal bytes object but make a copy so the user bytes
48
    // object is constant as would be expected. Revisit: enable and return
49
    // a memoryview, especially if using large buffers.
50
    return mp_obj_new_bytes(self->rx_bytes_obj.data, self->rx_bytes_obj.len);
×
51
}
52
static MP_DEFINE_CONST_FUN_OBJ_1(pb_type_app_data_get_bytes_obj, pb_type_app_data_get_bytes);
53

NEW
54
static mp_obj_t pb_type_app_data_get_values(mp_obj_t self_in) {
×
55

56
    // Implementation in MicroPython is static, so import from ustruct.unpack.
57
    mp_obj_t ustruct_unpack = pb_function_import_helper(MP_QSTR_ustruct, MP_QSTR_unpack);
×
58

59
    // Host (sender) is responsible for making sure that each individual
60
    // value remains valid, i.e. is written in a single chunk, since the
61
    // following may allocate, and thus be updated between unpacking values.
62
    pb_type_app_data_obj_t *self = MP_OBJ_TO_PTR(self_in);
×
63
    return mp_call_function_2(ustruct_unpack, self->rx_format, MP_OBJ_FROM_PTR(&self->rx_bytes_obj));
×
64
}
65
static MP_DEFINE_CONST_FUN_OBJ_1(pb_type_app_data_get_values_obj, pb_type_app_data_get_values);
66

NEW
67
static mp_obj_t pb_type_app_data_write_bytes(mp_obj_t self_in, mp_obj_t data_in) {
×
68
    pb_type_app_data_obj_t *self = MP_OBJ_TO_PTR(self_in);
×
69

70
    // Copy data to local buffer. Needs to remain valid while sending.
71
    size_t len;
×
72
    const char *data = mp_obj_str_get_data(data_in, &len);
×
73

74
    if (len > sizeof(self->tx_buffer) - 1) {
×
75
        mp_raise_msg_varg(&mp_type_ValueError,
×
76
            MP_ERROR_TEXT("Cannot send more than %d bytes\n"), sizeof(self->tx_buffer) - 1);
×
77
    }
78

79
    memcpy(self->tx_buffer + 1, data, len);
×
80
    self->tx_context.size = len + 1;
×
81

82
    pbdrv_bluetooth_send_queued(&self->tx_task, &self->tx_context);
×
83
    return pb_module_tools_pbio_task_wait_or_await(&self->tx_task);
×
84
}
85
static MP_DEFINE_CONST_FUN_OBJ_2(pb_type_app_data_write_bytes_obj, pb_type_app_data_write_bytes);
86

87
static const mp_obj_str_t pb_const_empty_str_obj = {{&mp_type_str}, 0, 0, (const byte *)""};
88

NEW
89
static mp_obj_t pb_type_app_data_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
×
90

91
    PB_PARSE_ARGS_CLASS(n_args, n_kw, args,
×
92
        PB_ARG_DEFAULT_OBJ(rx_format, pb_const_empty_str_obj));
×
93

94
    // Use ustruct.calcsize to parse user rx_format for size.
95
    mp_obj_t ustruct_calcsize = pb_function_import_helper(MP_QSTR_ustruct, MP_QSTR_calcsize);
×
96
    size_t size = mp_obj_get_int(mp_call_function_1(ustruct_calcsize, rx_format_in));
×
97

98
    // Can only create one instance for now.
99
    if (app_data_instance) {
×
100
        mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("host rx_buffer already allocated"));
×
101
    }
102

103
    // Use finalizer so we can deactivate the data callback when rx_buffer is garbage collected.
NEW
104
    app_data_instance = mp_obj_malloc_var_with_finaliser(pb_type_app_data_obj_t, uint8_t, size, type);
×
105
    app_data_instance->rx_format = rx_format_in;
×
106

107
    // Keep rx_buffer in bytes object rx_format for compatibility with unpack.
108
    app_data_instance->rx_bytes_obj.base.type = &mp_type_bytes;
×
109
    app_data_instance->rx_bytes_obj.len = size;
×
110
    app_data_instance->rx_bytes_obj.data = app_data_instance->rx_buffer;
×
111

112
    // Activate callback now that we have allocated the rx_buffer.
113
    pbsys_command_set_write_app_data_callback(handle_incoming_app_data);
×
114

115
    // Prepare tx context. Only the length and data is variable.
116
    app_data_instance->tx_context.done = NULL;
×
117
    app_data_instance->tx_context.connection = PBDRV_BLUETOOTH_CONNECTION_PYBRICKS;
×
118
    app_data_instance->tx_context.data = app_data_instance->tx_buffer;
×
119
    app_data_instance->tx_buffer[0] = PBIO_PYBRICKS_EVENT_WRITE_APP_DATA;
×
120

121
    return MP_OBJ_FROM_PTR(app_data_instance);
×
122
}
123

124
mp_obj_t pb_type_app_data_close(mp_obj_t stream) {
×
125
    if (app_data_instance) {
×
126
        pbsys_command_set_write_app_data_callback(NULL);
×
127
        app_data_instance = NULL;
×
128
    }
129
    return mp_const_none;
×
130
}
131
MP_DEFINE_CONST_FUN_OBJ_1(pb_type_app_data_close_obj, pb_type_app_data_close);
132

133
static const mp_rom_map_elem_t pb_type_app_data_locals_dict_table[] = {
134
    { MP_ROM_QSTR(MP_QSTR___del__),      MP_ROM_PTR(&pb_type_app_data_close_obj) },
135
    { MP_ROM_QSTR(MP_QSTR_close),        MP_ROM_PTR(&pb_type_app_data_close_obj) },
136
    { MP_ROM_QSTR(MP_QSTR_get_bytes),    MP_ROM_PTR(&pb_type_app_data_get_bytes_obj) },
137
    { MP_ROM_QSTR(MP_QSTR_get_values),   MP_ROM_PTR(&pb_type_app_data_get_values_obj) },
138
    { MP_ROM_QSTR(MP_QSTR_write_bytes),    MP_ROM_PTR(&pb_type_app_data_write_bytes_obj) },
139
};
140
static MP_DEFINE_CONST_DICT(pb_type_app_data_locals_dict, pb_type_app_data_locals_dict_table);
141

142
MP_DEFINE_CONST_OBJ_TYPE(pb_type_app_data,
143
    MP_QSTR_AppData,
144
    MP_TYPE_FLAG_NONE,
145
    make_new, pb_type_app_data_make_new,
146
    locals_dict, &pb_type_app_data_locals_dict);
147

148
#endif // PYBRICKS_PY_TOOLS_APP_DATA
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc