Skip to content

Commit 505f3ab

Browse files
committed
pybricks.iodevices.I2CDevice: Generalize object interface.
Allows sensor definitions to have an I2DDevice instance with awaitable operations with a result mapped to objects.
1 parent 4e4ca6c commit 505f3ab

File tree

2 files changed

+46
-38
lines changed

2 files changed

+46
-38
lines changed

pybricks/iodevices/iodevices.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,21 @@ extern const mp_obj_type_t pb_type_iodevices_PUPDevice;
1414
extern const mp_obj_type_t pb_type_uart_device;
1515

1616
#if PYBRICKS_PY_IODEVICES_I2CDEVICE
17+
1718
extern const mp_obj_type_t pb_type_i2c_device;
19+
/**
20+
* Given a completed I2C operation, maps the resulting read buffer to an object
21+
* of a desired form. For example, it could map two bytes to a single floating
22+
* point value representing temperature.
23+
*
24+
* @param [in] data The data read.
25+
* @param [in] len The data length.
26+
*/
27+
typedef mp_obj_t (*pb_type_i2c_device_return_map_t)(const uint8_t *data, size_t len);
28+
29+
mp_obj_t pb_type_i2c_device_make_new(mp_obj_t port_in, mp_obj_t address_in, bool custom, bool powered, bool nxt_quirk);
30+
mp_obj_t pb_type_i2c_device_start_operation(mp_obj_t i2c_device_obj, const uint8_t *write_data, size_t write_len, size_t read_len, pb_type_i2c_device_return_map_t return_map);
31+
1832
#endif
1933

2034
#if PYBRICKS_PY_PUPDEVICES

pybricks/iodevices/pb_type_i2c_device.c

Lines changed: 32 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,16 @@
1313
#include <pbio/port_interface.h>
1414

1515
#include <pybricks/common.h>
16+
#include <pybricks/iodevices/iodevices.h>
1617
#include <pybricks/parameters.h>
1718

1819
#include <pybricks/util_mp/pb_kwarg_helper.h>
1920
#include <pybricks/util_mp/pb_obj_helper.h>
2021

2122
#include <pybricks/util_pb/pb_error.h>
2223

23-
typedef struct _device_obj_t device_obj_t;
24-
25-
/**
26-
* Given a completed I2C operation, maps the resulting read buffer to an object
27-
* of a desired form. For example, it could map two bytes to a single floating
28-
* point value representing temperature.
29-
*
30-
* @param [in] device The device object.
31-
*/
32-
typedef mp_obj_t (*return_map_t)(device_obj_t *device);
33-
3424
// Object representing a pybricks.iodevices.I2CDevice instance.
35-
struct _device_obj_t {
25+
typedef struct {
3626
mp_obj_base_t base;
3727
/**
3828
* The following are buffered parameters for one ongoing I2C operation, See
@@ -45,21 +35,14 @@ struct _device_obj_t {
4535
pbio_os_state_t state;
4636
uint8_t address;
4737
bool nxt_quirk;
48-
return_map_t return_map;
38+
pb_type_i2c_device_return_map_t return_map;
4939
size_t write_len;
5040
size_t read_len;
5141
uint8_t *read_buf;
52-
};
42+
} device_obj_t;
5343

5444
// pybricks.iodevices.I2CDevice.__init__
55-
static mp_obj_t make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
56-
PB_PARSE_ARGS_CLASS(n_args, n_kw, args,
57-
PB_ARG_REQUIRED(port),
58-
PB_ARG_REQUIRED(address),
59-
PB_ARG_DEFAULT_FALSE(custom),
60-
PB_ARG_DEFAULT_FALSE(powered),
61-
PB_ARG_DEFAULT_FALSE(nxt_quirk)
62-
);
45+
mp_obj_t pb_type_i2c_device_make_new(mp_obj_t port_in, mp_obj_t address_in, bool custom, bool powered, bool nxt_quirk) {
6346

6447
pb_module_tools_assert_blocking();
6548

@@ -69,7 +52,7 @@ static mp_obj_t make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
6952
pb_assert(pbio_port_get_port(port_id, &port));
7053

7154
// Set the port mode to LEGO DCM or raw I2C mode.
72-
pbio_error_t err = pbio_port_set_mode(port, mp_obj_is_true(custom_in) ? PBIO_PORT_MODE_I2C : PBIO_PORT_MODE_LEGO_DCM);
55+
pbio_error_t err = pbio_port_set_mode(port, custom ? PBIO_PORT_MODE_I2C : PBIO_PORT_MODE_LEGO_DCM);
7356
if (err == PBIO_ERROR_AGAIN) {
7457
// If coming from a different mode, give port some time to get started.
7558
// This happens when the user has a custom device and decides to switch
@@ -82,17 +65,30 @@ static mp_obj_t make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
8265
pbdrv_i2c_dev_t *i2c_dev;
8366
pb_assert(pbio_port_get_i2c_dev(port, &i2c_dev));
8467

85-
device_obj_t *device = mp_obj_malloc(device_obj_t, type);
68+
device_obj_t *device = mp_obj_malloc(device_obj_t, &pb_type_i2c_device);
8669
device->i2c_dev = i2c_dev;
8770
device->address = mp_obj_get_int(address_in);
88-
device->nxt_quirk = mp_obj_is_true(nxt_quirk_in);
89-
if (mp_obj_is_true(powered_in)) {
71+
device->nxt_quirk = nxt_quirk;
72+
if (powered) {
9073
pbio_port_p1p2_set_power(port, PBIO_PORT_POWER_REQUIREMENTS_BATTERY_VOLTAGE_P1_POS);
9174
}
9275

9376
return MP_OBJ_FROM_PTR(device);
9477
}
9578

79+
// wrapper to parse args for __init__
80+
static mp_obj_t make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
81+
PB_PARSE_ARGS_CLASS(n_args, n_kw, args,
82+
PB_ARG_REQUIRED(port),
83+
PB_ARG_REQUIRED(address),
84+
PB_ARG_DEFAULT_FALSE(custom),
85+
PB_ARG_DEFAULT_FALSE(powered),
86+
PB_ARG_DEFAULT_FALSE(nxt_quirk)
87+
);
88+
89+
return pb_type_i2c_device_make_new(port_in, address_in, mp_obj_is_true(custom_in), mp_obj_is_true(powered_in), mp_obj_is_true(nxt_quirk_in));
90+
}
91+
9692
// Object representing the iterable that is returned when calling an I2C
9793
// method. This object can then be awaited (iterated). It has a reference to
9894
// the device from which it was created. Only one operation can be active at
@@ -142,7 +138,7 @@ static mp_obj_t operation_iternext(mp_obj_t op_in) {
142138
}
143139

144140
// Set return value via stop iteration.
145-
return mp_make_stop_iteration(device->return_map(device));
141+
return mp_make_stop_iteration(device->return_map(device->read_buf, device->read_len));
146142
}
147143

148144
static const mp_rom_map_elem_t operation_locals_dict_table[] = {
@@ -156,7 +152,10 @@ MP_DEFINE_CONST_OBJ_TYPE(operation_type,
156152
iter, operation_iternext,
157153
locals_dict, &operation_locals_dict);
158154

159-
static mp_obj_t start_operation(device_obj_t *device, const uint8_t *write_data, size_t write_len, size_t read_len, return_map_t return_map) {
155+
mp_obj_t pb_type_i2c_device_start_operation(mp_obj_t i2c_device_obj, const uint8_t *write_data, size_t write_len, size_t read_len, pb_type_i2c_device_return_map_t return_map) {
156+
157+
pb_assert_type(i2c_device_obj, &pb_type_i2c_device);
158+
device_obj_t *device = MP_OBJ_TO_PTR(i2c_device_obj);
160159

161160
// Kick off the operation. This will immediately raise if a transaction is
162161
// in progress.
@@ -200,12 +199,7 @@ static mp_obj_t start_operation(device_obj_t *device, const uint8_t *write_data,
200199
if (!device->return_map) {
201200
return mp_const_none;
202201
}
203-
return device->return_map(device);
204-
}
205-
206-
static mp_obj_t return_map_bytes(device_obj_t *device) {
207-
// Operation was successfull, so data is not NULL if read len is nonzero.
208-
return mp_obj_new_bytes(device->read_buf, device->read_len);
202+
return device->return_map(device->read_buf, device->read_len);
209203
}
210204

211205
// pybricks.iodevices.I2CDevice.write_then_read
@@ -217,7 +211,7 @@ static mp_obj_t write_then_read(size_t n_args, const mp_obj_t *pos_args, mp_map_
217211
);
218212

219213
size_t write_len;
220-
return start_operation(device, (const uint8_t *)mp_obj_str_get_data(write_data_in, &write_len), write_len, pb_obj_get_positive_int(read_length_in), return_map_bytes);
214+
return pb_type_i2c_device_start_operation(MP_OBJ_FROM_PTR(device), (const uint8_t *)mp_obj_str_get_data(write_data_in, &write_len), write_len, pb_obj_get_positive_int(read_length_in), mp_obj_new_bytes);
221215
}
222216
static MP_DEFINE_CONST_FUN_OBJ_KW(write_then_read_obj, 0, write_then_read);
223217

@@ -236,7 +230,7 @@ static mp_obj_t read(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
236230
&(uint8_t) { mp_obj_get_int(reg_in) };
237231
size_t write_len = reg_in == mp_const_none ? 0 : 1;
238232

239-
return start_operation(device, write_data, write_len, pb_obj_get_positive_int(length_in), return_map_bytes);
233+
return pb_type_i2c_device_start_operation(MP_OBJ_FROM_PTR(device), write_data, write_len, pb_obj_get_positive_int(length_in), mp_obj_new_bytes);
240234
}
241235
static MP_DEFINE_CONST_FUN_OBJ_KW(read_obj, 0, read);
242236

@@ -259,7 +253,7 @@ static mp_obj_t write(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args
259253

260254
// No register given, write data as is.
261255
if (reg_in == mp_const_none) {
262-
return start_operation(device, (const uint8_t *)user_data, user_len, 0, NULL);
256+
return pb_type_i2c_device_start_operation(MP_OBJ_FROM_PTR(device), (const uint8_t *)user_data, user_len, 0, NULL);
263257
}
264258

265259
// Otherwise need to prefix write data with given register.
@@ -270,7 +264,7 @@ static mp_obj_t write(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args
270264
write_data[0] = pb_obj_get_positive_int(reg_in);
271265
memcpy(&write_data[1], user_data, user_len);
272266

273-
return start_operation(device, write_data, user_len + 1, 0, NULL);
267+
return pb_type_i2c_device_start_operation(MP_OBJ_FROM_PTR(device), write_data, user_len + 1, 0, NULL);
274268
}
275269
static MP_DEFINE_CONST_FUN_OBJ_KW(write_obj, 0, write);
276270

0 commit comments

Comments
 (0)