Skip to content

Commit

Permalink
pybricks.common.System: Support user data storage.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
laurensvalk committed Oct 21, 2022
1 parent c5ce343 commit ef59eba
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 0 deletions.
14 changes: 14 additions & 0 deletions lib/pbio/include/pbsys/program_load.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,24 @@ typedef struct _pbsys_program_load_data_header_t {

#define PBSYS_PROGRAM_LOAD_MAX_PROGRAM_SIZE (PBSYS_CONFIG_PROGRAM_LOAD_ROM_SIZE - sizeof(pbsys_program_load_data_header_t))

pbio_error_t pbsys_program_load_set_user_data(uint32_t offset, const uint8_t *data, uint32_t size);

pbio_error_t pbsys_program_load_get_user_data(uint32_t offset, uint8_t **data, uint32_t size);

#else

#define PBSYS_PROGRAM_LOAD_MAX_PROGRAM_SIZE (0)

static inline pbio_error_t pbsys_program_load_set_user_data(uint32_t offset, const uint8_t *data, uint32_t size) {
return PBIO_ERROR_NOT_SUPPORTED;
}

static inline pbio_error_t pbsys_program_load_get_user_data(uint32_t offset, uint8_t **data, uint32_t size) {
*data = NULL;
return PBIO_ERROR_NOT_SUPPORTED;
}


#endif // PBSYS_CONFIG_PROGRAM_LOAD

#endif // _PBSYS_PROGRAM_LOAD_H_
Expand Down
36 changes: 36 additions & 0 deletions lib/pbio/sys/program_load.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,42 @@ static void update_write_size(void) {
map->header.write_size = sizeof(pbsys_program_load_data_header_t) + map->header.program_size;
}

/**
* Saves user data. This will be saved during power off, like program data.
*
* @param [in] offset Offset from the base address.
* @param [in] data The data to be stored (copied).
* @param [in] size Data size.
* @returns ::PBIO_ERROR_INVALID_ARG if the data won't fit.
* Otherwise, ::PBIO_SUCCESS.
*/
pbio_error_t pbsys_program_load_set_user_data(uint32_t offset, const uint8_t *data, uint32_t size) {
if (offset + size > sizeof(map->header.user_data)) {
return PBIO_ERROR_INVALID_ARG;
}
// Update data and write size to request write on poweroff.
memcpy(map->header.user_data + offset, data, size);
update_write_size();
return PBIO_SUCCESS;
}

/**
* Gets pointer to user data.
*
* @param [in] offset Offset from the base address.
* @param [in] data The data reference.
* @param [in] size Data size.
* @returns ::PBIO_ERROR_INVALID_ARG if reading out of range.
* Otherwise, ::PBIO_SUCCESS.
*/
pbio_error_t pbsys_program_load_get_user_data(uint32_t offset, uint8_t **data, uint32_t size) {
if (offset + size > sizeof(map->header.user_data)) {
return PBIO_ERROR_INVALID_ARG;
}
*data = map->header.user_data + offset;
return PBIO_SUCCESS;
}

static bool pbsys_program_load_start_user_program_requested;
static bool pbsys_program_load_start_repl_requested;

Expand Down
34 changes: 34 additions & 0 deletions pybricks/common/pb_type_system.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@
#include <string.h>

#include <pbdrv/bluetooth.h>
#include <pbsys/program_load.h>

#include "py/obj.h"
#include "py/objstr.h"
#include "py/runtime.h"

#include <pybricks/common.h>
#include <pybricks/util_pb/pb_error.h>
#include <pybricks/util_mp/pb_kwarg_helper.h>
#include <pybricks/util_mp/pb_obj_helper.h>

STATIC mp_obj_t pb_type_System_name(void) {
const char *hub_name = pbdrv_bluetooth_get_hub_name();
Expand Down Expand Up @@ -101,6 +105,35 @@ STATIC mp_obj_t pb_type_System_shutdown(void) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(pb_type_System_shutdown_obj, pb_type_System_shutdown);

STATIC mp_obj_t pb_type_System_storage(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
PB_PARSE_ARGS_FUNCTION(n_args, pos_args, kw_args,
PB_ARG_REQUIRED(offset),
PB_ARG_DEFAULT_NONE(read),
PB_ARG_DEFAULT_NONE(write));

// Get offset and confirm integer type.
mp_int_t offset = mp_obj_get_int(offset_in);
(void)offset;

// Handle read.
if (write_in == mp_const_none) {
byte *data;
mp_uint_t size = mp_obj_get_int(read_in);
pb_assert(pbsys_program_load_get_user_data(offset, &data, size));
return mp_obj_new_bytes(data, size);
}

// Handle write.
if (read_in == mp_const_none && !mp_obj_is_str(write_in) && mp_obj_is_str_or_bytes(write_in)) {
mp_obj_str_t *obj = ((mp_obj_str_t *)MP_OBJ_TO_PTR(write_in));
pbsys_program_load_set_user_data(offset, obj->data, obj->len);
return mp_const_none;
}

mp_raise_ValueError(MP_ERROR_TEXT("Must set either read (int) or write (bytes)."));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pb_type_System_storage_obj, 0, pb_type_System_storage);

#endif // PBIO_CONFIG_ENABLE_SYS

// dir(pybricks.common.System)
Expand All @@ -113,6 +146,7 @@ STATIC const mp_rom_map_elem_t common_System_locals_dict_table[] = {
#if PBIO_CONFIG_ENABLE_SYS
{ MP_ROM_QSTR(MP_QSTR_set_stop_button), MP_ROM_PTR(&pb_type_System_set_stop_button_obj) },
{ MP_ROM_QSTR(MP_QSTR_shutdown), MP_ROM_PTR(&pb_type_System_shutdown_obj) },
{ MP_ROM_QSTR(MP_QSTR_storage), MP_ROM_PTR(&pb_type_System_storage_obj) },
#endif
};
STATIC MP_DEFINE_CONST_DICT(common_System_locals_dict, common_System_locals_dict_table);
Expand Down

0 comments on commit ef59eba

Please sign in to comment.