Skip to content

Commit 246ebea

Browse files
committed
pybricks.common.System: Support user data storage.
This allows end-users to save small amounts of data on the hub. This is useful for settings like train speed or counters in great ball contraption modules. Fixes pybricks/support#85
1 parent 16401c7 commit 246ebea

File tree

3 files changed

+83
-0
lines changed

3 files changed

+83
-0
lines changed

lib/pbio/include/pbsys/program_load.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,24 @@ typedef struct _pbsys_program_load_data_header_t {
5353

5454
#define PBSYS_PROGRAM_LOAD_MAX_PROGRAM_SIZE (PBSYS_CONFIG_PROGRAM_LOAD_ROM_SIZE - sizeof(pbsys_program_load_data_header_t))
5555

56+
pbio_error_t pbsys_program_load_set_user_data(uint32_t offset, const uint8_t *data, uint32_t size);
57+
58+
pbio_error_t pbsys_program_load_get_user_data(uint32_t offset, uint8_t **data, uint32_t size);
59+
5660
#else
5761

5862
#define PBSYS_PROGRAM_LOAD_MAX_PROGRAM_SIZE (0)
5963

64+
static inline pbio_error_t pbsys_program_load_set_user_data(uint32_t offset, const uint8_t *data, uint32_t size) {
65+
return PBIO_ERROR_NOT_SUPPORTED;
66+
}
67+
68+
static inline pbio_error_t pbsys_program_load_get_user_data(uint32_t offset, uint8_t **data, uint32_t size) {
69+
*data = NULL;
70+
return PBIO_ERROR_NOT_SUPPORTED;
71+
}
72+
73+
6074
#endif // PBSYS_CONFIG_PROGRAM_LOAD
6175

6276
#endif // _PBSYS_PROGRAM_LOAD_H_

lib/pbio/sys/program_load.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,42 @@ static void update_write_size(void) {
4646
map->header.write_size = sizeof(pbsys_program_load_data_header_t) + map->header.program_size;
4747
}
4848

49+
/**
50+
* Saves user data. This will be saved during power off, like program data.
51+
*
52+
* @param [in] offset Offset from the base address.
53+
* @param [in] data The data to be stored (copied).
54+
* @param [in] size Data size.
55+
* @returns ::PBIO_ERROR_INVALID_ARG if the data won't fit.
56+
* Otherwise, ::PBIO_SUCCESS.
57+
*/
58+
pbio_error_t pbsys_program_load_set_user_data(uint32_t offset, const uint8_t *data, uint32_t size) {
59+
if (offset + size > sizeof(map->header.user_data)) {
60+
return PBIO_ERROR_INVALID_ARG;
61+
}
62+
// Update data and write size to request write on poweroff.
63+
memcpy(map->header.user_data + offset, data, size);
64+
update_write_size();
65+
return PBIO_SUCCESS;
66+
}
67+
68+
/**
69+
* Gets pointer to user data.
70+
*
71+
* @param [in] offset Offset from the base address.
72+
* @param [in] data The data reference.
73+
* @param [in] size Data size.
74+
* @returns ::PBIO_ERROR_INVALID_ARG if reading out of range.
75+
* Otherwise, ::PBIO_SUCCESS.
76+
*/
77+
pbio_error_t pbsys_program_load_get_user_data(uint32_t offset, uint8_t **data, uint32_t size) {
78+
if (offset + size > sizeof(map->header.user_data)) {
79+
return PBIO_ERROR_INVALID_ARG;
80+
}
81+
*data = map->header.user_data + offset;
82+
return PBIO_SUCCESS;
83+
}
84+
4985
static bool pbsys_program_load_start_user_program_requested;
5086
static bool pbsys_program_load_start_repl_requested;
5187

pybricks/common/pb_type_system.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@
88
#include <string.h>
99

1010
#include <pbdrv/bluetooth.h>
11+
#include <pbsys/program_load.h>
1112

1213
#include "py/obj.h"
14+
#include "py/objstr.h"
1315
#include "py/runtime.h"
1416

1517
#include <pybricks/common.h>
1618
#include <pybricks/util_pb/pb_error.h>
19+
#include <pybricks/util_mp/pb_kwarg_helper.h>
20+
#include <pybricks/util_mp/pb_obj_helper.h>
1721

1822
STATIC mp_obj_t pb_type_System_name(void) {
1923
const char *hub_name = pbdrv_bluetooth_get_hub_name();
@@ -101,6 +105,34 @@ STATIC mp_obj_t pb_type_System_shutdown(void) {
101105
}
102106
STATIC MP_DEFINE_CONST_FUN_OBJ_0(pb_type_System_shutdown_obj, pb_type_System_shutdown);
103107

108+
STATIC mp_obj_t pb_type_System_storage(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
109+
PB_PARSE_ARGS_FUNCTION(n_args, pos_args, kw_args,
110+
PB_ARG_REQUIRED(offset),
111+
PB_ARG_DEFAULT_NONE(read),
112+
PB_ARG_DEFAULT_NONE(write));
113+
114+
// Get offset and confirm integer type.
115+
mp_int_t offset = mp_obj_get_int(offset_in);
116+
117+
// Handle read.
118+
if (write_in == mp_const_none) {
119+
byte *data;
120+
mp_uint_t size = mp_obj_get_int(read_in);
121+
pb_assert(pbsys_program_load_get_user_data(offset, &data, size));
122+
return mp_obj_new_bytes(data, size);
123+
}
124+
125+
// Handle write.
126+
if (read_in == mp_const_none && !mp_obj_is_str(write_in) && mp_obj_is_str_or_bytes(write_in)) {
127+
mp_obj_str_t *obj = ((mp_obj_str_t *)MP_OBJ_TO_PTR(write_in));
128+
pbsys_program_load_set_user_data(offset, obj->data, obj->len);
129+
return mp_const_none;
130+
}
131+
132+
mp_raise_ValueError(MP_ERROR_TEXT("Must set either read (int) or write (bytes)."));
133+
}
134+
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pb_type_System_storage_obj, 0, pb_type_System_storage);
135+
104136
#endif // PBIO_CONFIG_ENABLE_SYS
105137

106138
// dir(pybricks.common.System)
@@ -113,6 +145,7 @@ STATIC const mp_rom_map_elem_t common_System_locals_dict_table[] = {
113145
#if PBIO_CONFIG_ENABLE_SYS
114146
{ MP_ROM_QSTR(MP_QSTR_set_stop_button), MP_ROM_PTR(&pb_type_System_set_stop_button_obj) },
115147
{ MP_ROM_QSTR(MP_QSTR_shutdown), MP_ROM_PTR(&pb_type_System_shutdown_obj) },
148+
{ MP_ROM_QSTR(MP_QSTR_storage), MP_ROM_PTR(&pb_type_System_storage_obj) },
116149
#endif
117150
};
118151
STATIC MP_DEFINE_CONST_DICT(common_System_locals_dict, common_System_locals_dict_table);

0 commit comments

Comments
 (0)