Program Listing for File cli_bms.c

Return to documentation for file (mainboard/Src/cli_bms.c)

#include "cli_bms.h"

#include "bal_fsm.h"
#include "bms_fsm.h"
#include "error/error.h"
#include "feedback.h"
#include "pack/pack.h"
#include "pack/temperature.h"
#include "pack/voltage.h"
#include "soc.h"
#include "usart.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// TODO: don't count manually
#define N_COMMANDS 12

cli_command_func_t _cli_volts;
cli_command_func_t _cli_volts_all;
cli_command_func_t _cli_temps;
cli_command_func_t _cli_temps_all;
cli_command_func_t _cli_status;
cli_command_func_t _cli_balance;
cli_command_func_t _cli_soc;
cli_command_func_t _cli_errors;
cli_command_func_t _cli_ts;
cli_command_func_t _cli_current;
cli_command_func_t _cli_dmesg;
cli_command_func_t _cli_reset;
cli_command_func_t _cli_help;
cli_command_func_t _cli_taba;

const char *bms_state_names[BMS_NUM_STATES] =
    {[BMS_IDLE] = "idle", [BMS_PRECHARGE] = "precharge", [BMS_ON] = "run", [BMS_HALT] = "halt"};

const char *bal_state_names[BAL_NUM_STATES] =
    {[BAL_OFF] = "off", [BAL_COMPUTE] = "computing", [BAL_DISCHARGE] = "discharging", [BAL_COOLDOWN] = "cooldown"};

const char *error_names[ERROR_NUM_ERRORS] = {
    [ERROR_CELL_UNDER_VOLTAGE]    = "under-voltage",
    [ERROR_CELL_OVER_VOLTAGE]     = "over-voltage",
    [ERROR_CELL_OVER_TEMPERATURE] = "over-temperature",
    [ERROR_OVER_CURRENT]          = "over-current",
    [ERROR_CAN]                   = "CAN",
    [ERROR_ADC_INIT]              = "ADC init",
    [ERROR_ADC_TIMEOUT]           = "ADC timeout",
    [ERROR_INT_VOLTAGE_MISMATCH]  = "internal voltage mismatch",
    [ERROR_CELLBOARD_COMM]        = "cellboard communication",
    [ERROR_CELLBOARD_INTERNAL]    = "cellboard internal",
    [ERROR_FEEDBACK]              = "feedback",
    [ERROR_EEPROM_COMM]           = "EEPROM communication",
    [ERROR_EEPROM_WRITE]          = "EEPROM write"};

char const *const feedback_names[FEEDBACK_N] = {
    [FEEDBACK_VREF_POS]          = "VREF",
    [FEEDBACK_FROM_TSMS_POS]     = "FROM TSMS",
    [FEEDBACK_TO_TSMS_POS]       = "TO TSMS",
    [FEEDBACK_FROM_SHUTDOWN_POS] = "FROM SHUTDOWN",
    [FEEDBACK_LATCH_IMD_POS]     = "LATCH IMD",
    [FEEDBACK_LATCH_BMS_POS]     = "LATCH BMS",
    [FEEDBACK_IMD_FAULT_POS]     = "IMD FAULT",
    [FEEDBACK_BMS_FAULT_POS]     = "BMS FAULT",
    [FEEDBACK_TSAL_HV_POS]       = "TSAL OVER 60V",
    [FEEDBACK_AIR_POSITIVE_POS]  = "AIR POSITIVE",
    [FEEDBACK_AIR_NEGATIVE_POS]  = "AIR NEGATIVE",
    [FEEDBACK_PC_END_POS]        = "PRE-CHARGE END",
    [FEEDBACK_RELAY_LV_POS]      = "RELAY LV",
    [FEEDBACK_IMD_SHUTDOWN_POS]  = "IMD SHUTDOWN",
    [FEEDBACK_BMS_SHUTDOWN_POS]  = "BMS SHUTDOWN",
    [FEEDBACK_TS_ON_POS]         = "TS ON"};

char *command_names[N_COMMANDS] =
    {"volt", "temp", "status", "errors", "ts", "bal", "soc", "current", "dmesg", "reset", "?", "\ta"};

cli_command_func_t *commands[N_COMMANDS] = {
    &_cli_volts,
    &_cli_temps,
    &_cli_status,
    &_cli_errors,
    &_cli_ts,
    &_cli_balance,
    &_cli_soc,
    &_cli_current,
    &_cli_dmesg,
    &_cli_reset,
    &_cli_help,
    &_cli_taba};

cli_t cli_bms;
bool dmesg_ena = true;

void cli_bms_init() {
    cli_bms.uart           = &CLI_UART;
    cli_bms.cmds.functions = commands;
    cli_bms.cmds.names     = command_names;
    cli_bms.cmds.count     = N_COMMANDS;

    char init[94];
    sprintf(
        init,
        "\r\n\n********* Fenice BMS *********\r\n"
        " build: %s @ %s\r\n\n type ? for commands\r\n\n",
        __DATE__,
        __TIME__);

    strcat(init, cli_ps);

    cli_init(&cli_bms);
    cli_print(&cli_bms, init, strlen(init));
}

void cli_bms_debug(char *text, size_t length) {
    if (dmesg_ena) {
        char out[3000] = {'\0'};
        // add prefix
        sprintf(out, "[%.3f] ", (float)HAL_GetTick() / 1000);

        strcat(out, text);
        length += strlen(out);

        // add suffix
        sprintf(out + length, "\r\n");
        length += 2;

        cli_print(&cli_bms, out, length);
    }
}

void _cli_volts(uint16_t argc, char **argv, char *out) {
    if (strcmp(argv[1], "") == 0) {
        voltage_t max = voltage_get_cell_max();
        voltage_t min = voltage_get_cell_min();
        sprintf(
            out,
            "bus.......%.2f V\r\n"
            "internal..%.2f V\r\n"
            "average...%.2f V\r\n"
            "max.......%.3f V\r\n"
            "min.......%.3f V\r\n"
            "delta.....%.3f V\r\n",
            (float)voltage_get_bus() / 100,
            (float)voltage_get_internal() / 100,
            (float)voltage_get_internal() / PACK_CELL_COUNT / 100,
            (float)max / 10000,
            (float)min / 10000,
            (float)(max - min) / 10000);
    } else if (strcmp(argv[1], "all") == 0) {
        _cli_volts_all(argc, &argv[1], out);
    } else {
        sprintf(
            out,
            "Unknown parameter: %s\r\n"
            "valid parameters:\r\n"
            "- all: returns voltages for all cells\r\n",
            argv[1]);
    }
}

void _cli_volts_all(uint16_t argc, char **argv, char *out) {
    out[0] = '\0';

    for (uint8_t i = 0; i < PACK_CELL_COUNT; i++) {
        if (i % LTC6813_CELL_COUNT == 0) {
            sprintf(out + strlen(out), "\r\n%-3d", i / LTC6813_CELL_COUNT);
        } else if (i % (LTC6813_CELL_COUNT / 2) == 0 && i > 0) {
            sprintf(out + strlen(out), "\r\n%-3s", "");
        }

        sprintf(out + strlen(out), "[%3u %-.3fv] ", i, (float)voltage_get_cells()[i] / 10000);
    }

    sprintf(out + strlen(out), "\r\n");
}

void _cli_temps(uint16_t argc, char **argv, char *out) {
    //if (strcmp(argv[1], "") == 0) {
    //    sprintf(
    //        out,
    //        "average.....%.1f C\r\nmax.........%2u "
    //        "C\r\nmin.........%2u C\r\n"
    //        "delta.......%2u C\r\n",
    //        (float)pack_get_mean_temperature() / 10,
    //        pack_get_max_temperature(),
    //        pack_get_min_temperature(),
    //        pack_get_max_temperature() - pack_get_min_temperature());
    //} else if (strcmp(argv[1], "all") == 0) {
    //    _cli_temps_all(argc, &argv[1], out);
    //} else {
    //    sprintf(
    //        out,
    //        "Unknown parameter: %s\r\n"
    //        "valid parameters:\r\n"
    //        "- all: returns temperature for all cells\r\n",
    //        argv[1]);
    //}
}

void _cli_temps_all(uint16_t argc, char **argv, char *out) {
    out[0] = '\0';

    for (uint8_t i = 0; i < PACK_TEMP_COUNT; i++) {
        if (i % TEMP_SENSOR_COUNT == 0) {
            sprintf(out + strlen(out), "\r\n%-3d", i / TEMP_SENSOR_COUNT);
        } else if (i % (TEMP_SENSOR_COUNT / 2) == 0 && i > 0) {
            sprintf(out + strlen(out), "\r\n%-3s", "");
        }
        sprintf(out + strlen(out), "[%3u %2uc] ", i, temperature_get_all()[i]);
    }

    sprintf(out + strlen(out), "\r\n");
}

void _cli_status(uint16_t argc, char **argv, char *out) {
#define n_items 3

    char thresh[5] = {'\0'};
    itoa((float)bal_get_threshold() / 10, thresh, 10);

    char er_count[3] = {'\0'};
    itoa(error_count(), er_count, 10);

    // TODO: Fix this
    const char *values[n_items][2] = {
        {"BMS state", bms_state_names[fsm_get_state(bms.fsm)]},
        {"error count", er_count},
        {"balancing state", bal_state_names[fsm_get_state(bal.fsm)]}};
    //{"BMS state", (char *)fsm_bms.state_names[fsm_bms.current_state]}, {"error
    // count", er_count}, {"balancing", bal}, {"balancing threshold", thresh}};

    out[0] = '\0';
    for (uint8_t i = 0; i < n_items; i++) {
        sprintf(
            out + strlen(out),
            "%s%s%s\r\n",
            values[i][0],
            "........................" + strlen(values[i][0]),
            values[i][1]);
    }
}

void _cli_balance(uint16_t argc, char **argv, char *out) {
    if (strcmp(argv[1], "on") == 0) {
        fsm_trigger_event(bal.fsm, EV_BAL_START);
        sprintf(out, "enabling balancing\r\n");
    } else if (strcmp(argv[1], "off") == 0) {
        fsm_trigger_event(bal.fsm, EV_BAL_STOP);
        sprintf(out, "disabling balancing\r\n");
    } else if (strcmp(argv[1], "thr") == 0) {
        if (argv[2] != NULL) {
            voltage_t thresh = atoi(argv[2]) * 10;
            if (thresh <= BAL_MAX_VOLTAGE_THRESHOLD) {
                bal_set_threshold(thresh);
            }
        }
        sprintf(out, "balancing threshold is %u mV\r\n", bal_get_threshold() / 10);
    } else {
        sprintf(
            out,
            "Unknown parameter: %s\r\n\n"
            "valid parameters:\r\n"
            "- on\r\n"
            "- off\r\n"
            "- thr <millivolts>\r\n",
            argv[1]);
    }
}
void _cli_soc(uint16_t argc, char **argv, char *out) {
    if (strcmp(argv[1], "reset") == 0) {
        soc_reset_soc();
        sprintf(out, "Resetting energy meter\r\n");
    } else {
        sprintf(
            out,
            "SoC: %.2f %%\r\n"
            "Energy: %.1f Wh\r\n"
            "Energy total: %.1f Wh\r\n",
            soc_get_soc(),
            soc_get_energy_last_charge(),
            soc_get_energy_total());
    }
}

void _cli_errors(uint16_t argc, char **argv, char *out) {
    uint16_t count = error_count();
    error_t errors[count];
    error_dump(errors);

    uint32_t now = HAL_GetTick();
    sprintf(out, "total %u\r\n", count);
    for (uint16_t i = 0; i < count; i++) {
        sprintf(
            out + strlen(out),
            "\r\nid..........%i (%s)\r\n"
            "timestamp...T+%lu (%lums ago)\r\n"
            "offset......%u\r\n"
            "state.......%s\r\n",
            errors[i].id,
            error_names[errors[i].id],
            errors[i].timestamp,
            now - errors[i].timestamp,
            errors[i].offset,
            errors[i].state == STATE_WARNING ? "warning" : "fatal");
    }
}

void _cli_ts(uint16_t argc, char **argv, char *out) {
    if (strcmp(argv[1], "on") == 0) {
        fsm_trigger_event(bms.fsm, BMS_EV_TS_ON);
        sprintf(out, "triggered TS ON event\r\n");
    } else if (strcmp(argv[1], "off") == 0) {
        fsm_trigger_event(bms.fsm, BMS_EV_TS_OFF);
        sprintf(out, "triggered TS OFF event\r\n");
    } else {
        sprintf(
            out,
            "Unknown parameter: %s\r\n\n"
            "valid parameters:\r\n"
            "- on\r\n"
            "- off\r\n",
            argv[1]);
    }
}

void _cli_current(uint16_t argc, char **argv, char *out) {
    if (argc == 0) {
        sprintf(
            out,
            "Hall 50A:\t%.1fA\r\n"
            "Hall 300A:\t%.1fA\r\n"
            "Shunt:\t\t%.1fA\r\n",
            current_get_current_sensors()[CURRENT_SENSOR_50],
            current_get_current_sensors()[CURRENT_SENSOR_300],
            current_get_current_sensors()[CURRENT_SENSOR_SHUNT]);
    } else if (strcmp(argv[1], "zero") == 0) {
        current_zero();
        sprintf(out, "Current zeroed\r\n");
    } else {
        sprintf(
            out,
            "Unknown parameter: %s\r\n\n"
            "valid parameters:\r\n"
            "- zero: zeroes the hall-sensor measurement\r\n",
            argv[1]);
    }
}

void _cli_dmesg(uint16_t argc, char **argv, char *out) {
    dmesg_ena = !dmesg_ena;
    sprintf(out, "dmesg output is %s\r\n", dmesg_ena ? "enabled" : "disabled");
}

void _cli_reset(uint16_t argc, char **argv, char *out) {
    HAL_NVIC_SystemReset();
}

void _cli_taba(uint16_t argc, char **argv, char *out) {
    sprintf(
        out,
        " #######    #    ######     #    ######     #    ####### ####### ######  \r\n"
        "    #      # #   #     #   # #   #     #   # #      #    #       #     # \r\n"
        "    #     #   #  #     #  #   #  #     #  #   #     #    #       #     # \r\n"
        "    #    #     # ######  #     # ######  #     #    #    #####   #     # \r\n"
        "    #    ####### #     # ####### #   #   #######    #    #       #     # \r\n"
        "    #    #     # #     # #     # #    #  #     #    #    #       #     # \r\n"
        "    #    #     # ######  #     # #     # #     #    #    ####### ######  \r\n");
}

void _cli_help(uint16_t argc, char **argv, char *out) {
    sprintf(out, "command list:\r\n");
    for (uint8_t i = 0; i < N_COMMANDS - 1; i++) {
        sprintf(out + strlen(out), "- %s\r\n", cli_bms.cmds.names[i]);
    }
}