@@ -31,7 +31,9 @@ typedef struct {
3131 /**
3232 * Object that owns this I2C device, such as an Ultrasonic Sensor instance.
3333 * Gets passed to all return mappings.
34- * Equals MP_OBJ_NULL when this is the standalone I2CDevice class instance.
34+ *
35+ * In case of the standalone I2CDevice class instance, this value is instead
36+ * used to store an optional user callable to map bytes to a return object.
3537 */
3638 mp_obj_t sensor_obj ;
3739 /**
@@ -105,7 +107,7 @@ static mp_obj_t make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
105107 );
106108
107109 return pb_type_i2c_device_make_new (
108- MP_OBJ_NULL ,
110+ MP_OBJ_NULL , // Not associated with any particular sensor instance.
109111 port_in ,
110112 mp_obj_get_int (address_in ),
111113 mp_obj_is_true (custom_in ),
@@ -210,16 +212,29 @@ void pb_type_i2c_device_assert_string_at_register(mp_obj_t i2c_device_obj, uint8
210212 }
211213}
212214
215+ /**
216+ * I2C result mapping that just returns a bytes object.
217+ */
213218static mp_obj_t pb_type_i2c_device_return_bytes (mp_obj_t self_in , const uint8_t * data , size_t len ) {
214219 return mp_obj_new_bytes (data , len );
215220}
216221
222+ /**
223+ * I2C result mapping that calls user provided callback with self and bytes as argument.
224+ */
225+ static mp_obj_t pb_type_i2c_device_return_user_map (mp_obj_t callable_obj , const uint8_t * data , size_t len ) {
226+ // If user provides bound method, MicroPython takes care of providing
227+ // self as the first argument. We just need to pass in data arg.
228+ return mp_call_function_1 (callable_obj , mp_obj_new_bytes (data , len ));
229+ }
230+
217231// pybricks.iodevices.I2CDevice.read
218232static mp_obj_t read (size_t n_args , const mp_obj_t * pos_args , mp_map_t * kw_args ) {
219233 PB_PARSE_ARGS_METHOD (n_args , pos_args , kw_args ,
220234 device_obj_t , device ,
221235 PB_ARG_DEFAULT_NONE (reg ),
222- PB_ARG_DEFAULT_INT (length , 1 )
236+ PB_ARG_DEFAULT_INT (length , 1 ),
237+ PB_ARG_DEFAULT_NONE (map )
223238 );
224239
225240 // Write payload is one byte representing the register we want to read,
@@ -229,12 +244,18 @@ static mp_obj_t read(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
229244 & (uint8_t ) { mp_obj_get_int (reg_in ) };
230245 size_t write_len = reg_in == mp_const_none ? 0 : 1 ;
231246
247+ // Optional user provided callback method of the form def my_method(self, data)
248+ // We can use sensor_obj for this since it isn't used by I2CDevice instances,
249+ // and we are already passing this to the mapping anyway, so we can conviently
250+ // use it to pass the callable object in this case.
251+ device -> sensor_obj = mp_obj_is_callable (map_in ) ? map_in : MP_OBJ_NULL ;
252+
232253 return pb_type_i2c_device_start_operation (
233254 MP_OBJ_FROM_PTR (device ),
234255 write_data ,
235256 write_len ,
236257 pb_obj_get_positive_int (length_in ),
237- pb_type_i2c_device_return_bytes
258+ mp_obj_is_callable ( map_in ) ? pb_type_i2c_device_return_user_map : pb_type_i2c_device_return_bytes
238259 );
239260}
240261static MP_DEFINE_CONST_FUN_OBJ_KW (read_obj , 0 , read ) ;
0 commit comments