Command Module

despite being part of the UART task, this documentation has been broken out into its own listing because it does so much, it would tripple the size of the UART task documentation.

The command module is provides convinient interfaces for performing RPC-like operations on the PSOC over the UART.

the majority of the functions provided this way take the form:

bool <command_name>(uart_channel_t channel, <command_args>);

where channel is the UART channel to use, and command_args are any arguments for the RPC.

for simplicity, the result callbacks for these functions are implemented internally, in future, it may be worth alowing the user to provide their own however this level of complexity may defeat the purpose of the command module.

Non Command Public Functions

handle_command

handles a command received from the UART, this function is called by the UART currently the ESP is only used as a master device, so this should never be called and is a dummy function which send an empty response.

void handle_command(const uint8_t* buffer, size_t length, uint8_t id, uart_channel_t channel)
{
    packet_header_t* header = (packet_header_t*)buffer;
    uint8_t response[64] = {0};
    size_t resp_len = 0;
    switch (header->cmd)
    {
        default:
            break;
    }
    send_response(channel, id, response, resp_len);
}

Command Public Functions

get_metrics

gets the metrics struct from the PSOC, and store them in the 'metrics' global variable

void update_metrics(const metrics_t* _metrics)
{
    memcpy(&metrics, _metrics, sizeof(metrics_t));
}

void metrics_callback(void* user_data, bool timeout, const uint8_t* resp, size_t resp_len)
{
    if (!timeout && resp && resp_len >= sizeof(metrics_t))
    {
        update_metrics((metrics_t*)resp);
    }
}

bool get_metrics(uart_channel_t channel)
{
    uint8_t send_buffer[64] = {0};
    uint8_t len = prep_command(CMD_GET_METRICS, send_buffer, NULL, 0);
    uart_send_async(channel, PACKET_TYPE_CMD, send_buffer, len, metrics_callback, NULL, 40);
    return true;
}

set_resistance

sets the resistance (and FTP for now) on the PSOC, no response is expected

bool set_resistance(uart_channel_t channel, uint8_t resistance)
{
    cmd_set_resistance_t cmd = {.resistance = resistance,
    .ftp = global_ftp};
    uint8_t send_buffer[64] = {0};
    uint8_t len = prep_command(CMD_SET_RESISTANCE, send_buffer, &cmd, sizeof(cmd));
    uart_send_async(channel, PACKET_TYPE_CMD, send_buffer, len, NULL, NULL, 40);
    return true;
}

get_session

gets the session struct from the PSOC, and store them in the 'session' global variable

void update_session(const session_t* _session)
{
    memcpy(&session, _session, sizeof(session_t));
}

void session_callback(void* user_data, bool timeout, const uint8_t* resp, size_t resp_len)
{
    if (!timeout && resp && resp_len >= sizeof(session_t))
    {
        update_session((session_t*)resp);
    }
}

bool get_session(uart_channel_t channel)
{
    uint8_t send_buffer[64] = {0};
    uint8_t len = prep_command(CMD_GET_SESSION, send_buffer, NULL, 0);
    uart_send_async(channel, PACKET_TYPE_CMD, send_buffer, len, session_callback, NULL, 40);
    return true;
}

cmd_session

sends session commands to the PSOC to start, stop, or pause a session

bool cmd_session(uart_channel_t channel, session_cmd_t command)
{
    cmd_edit_session_t cmd = {.cmd = command};
    uint8_t send_buffer[64] = {0};
    uint8_t len = prep_command(CMD_EDIT_SESSION, send_buffer, &cmd, sizeof(cmd));
    uart_send_async(channel, PACKET_TYPE_CMD, send_buffer, len, NULL, NULL, 40);
    return true;
}

cmd_reset

sends a reset command to the PSOC, used for reboots and for OTA updates

bool cmd_reset(uart_channel_t channel)
{
    uint8_t send_buffer[64] = {0};
    uint8_t len = prep_command(CMD_RESET, send_buffer, NULL, 0);
    uart_send_async(channel, PACKET_TYPE_CMD, send_buffer, len, NULL, NULL, 40);
    return true;
}

cmd_delay_watchdog

sends a delay watchdog command to the PSOC, this command will dellay the watchdog check for 250 seconds, used during OTA's the PSOC by default will reset us if it does not receive any messages for 6 seconds when updating ourselves or the PSOC, we will be busy for longer than 6 seconds

this command has also been hijacked to also send the "STAY_ALIVE" command to the OHM battery (if one is connected) this is because if we are a self-powered device (domestic or ohm commercial) then if the ohm battery times itself out, it will not turn back on. to prevent this, we send the "STAY_ALIVE" command to the ohm battery if we fail to do this there is a small chance that we will soft brick the bike. and the user will need to manualy pedal and OTA the bike to fix it.

bool cmd_delay_watchdog(uart_channel_t channel)
{
    uint8_t send_buffer[64] = {0};
    uint8_t len = prep_command(CMD_DELAY_WATCHDOG, send_buffer, NULL, 0);
    uart_send_async(channel, PACKET_TYPE_CMD, send_buffer, len, NULL, NULL, 40);
    return true;
}

get_battery_present

gets the battery present status from the PSOC, and store them in the 'battery_present' global variable used to determine if the OHM battery is connected

void battery_present_callback(void* user_data, bool timeout, const uint8_t* resp, size_t resp_len)
{
    if (!timeout && resp && resp_len >= sizeof(resp_battery_present_t))
    {
        battery_present = ((resp_battery_present_t*)resp)->battery_present;
    }
}

bool get_battery_present(uart_channel_t channel)
{
    uint8_t send_buffer[64] = {0};
    uint8_t len = prep_command(CMD_BATTERY_PRESENT, send_buffer, NULL, 0);
    uart_send_async(channel, PACKET_TYPE_CMD, send_buffer, len, battery_present_callback, NULL, 40);
    return true;
}

cmd_get_debug

gets the debug metadata from the PSOC, and store them in the 'debug_info' global variable

this debug metadata contains:

  • version : the version of the PSOC firmware
  • uptime : the time the PSOC has been running in seconds
  • objects : the number of debug_objects available to be queried
  • running_app : the partition index of the currently running app
void get_debug_callback(void* user_data, bool timeout, const uint8_t* resp, size_t resp_len)
{
    if (!timeout && resp && resp_len >= sizeof(resp_get_debug_t))
    {
        memcpy(&debug_info, resp, sizeof(resp_get_debug_t));
    }
}

bool cmd_get_debug(uart_channel_t channel)
{
    uint8_t send_buffer[64] = {0};
    uint8_t len = prep_command(CMD_GET_DEBUG_INFO, send_buffer, NULL, 0);
    uart_send_async(channel, PACKET_TYPE_CMD, send_buffer, len, get_debug_callback, NULL, 40);
    return true;
}

cmd_get_debug_metadata

gets the debud data from a specific debug object, and stores it in the 'debug_metadata' global variable, at the index specified

this debug metadata is a generic binary blob with a header to identify the type of data the ESP (us) does not have the knowledge to interpret this data, it simply stores it, and forwards it to the debug client which can interpret it

the data format is "resp_get_debug_metadata_t" in lib_comms and is defined as:

  • type : the type of debug object
  • version : the version of the debug object
  • data_size : the size of the data
  • data : the data

data is a variable length binary blob, but for convinience we assume it is no more than 64 bytes so we can allocate a fixed size buffer to store it


void cmd_get_debug_metadata_callback(void* user_data, bool timeout, const uint8_t* resp, size_t resp_len)
{
    if (!timeout && resp && resp_len >= sizeof(resp_get_debug_metadata_t))
    {
        resp_get_debug_metadata_t* meta = (resp_get_debug_metadata_t*)resp;
        if(meta->type < debug_object_max)
        {
            memcpy(&debug_metadata[meta->type], meta, sizeof(resp_get_debug_metadata_t) + meta->data_size);
        }
    }
}

bool cmd_get_debug_metadata(uart_channel_t channel, uint8_t index)
{
    cmd_get_debug_metadata_t cmd = {.type = index};
    uint8_t send_buffer[64] = {0};
    uint8_t len = prep_command(CMD_GET_DEBUG_META, send_buffer, &cmd, sizeof(cmd));
    uart_send_async(channel, PACKET_TYPE_CMD, send_buffer, len, cmd_get_debug_metadata_callback, NULL, 40);
    return true;
}