Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Cell Data Logging with Timestamping #176

Open
wants to merge 29 commits into
base: develop
Choose a base branch
from

Conversation

KSMehta11
Copy link

@KSMehta11 KSMehta11 commented Feb 11, 2025

Changes

  • Added cell data logging functionality using a ring buffer.
  • Implemented timestamping for cell data using TIM2 in microseconds.
  • Stored the last N measurements in a static array.
  • Provided functions for retrieving data: last log, last n logs.
  • Implemented functions to view logs on serial terminal.

Notes

The buffer size can be adjusted in the header file to store more readings.

Test Cases

  • Test A: Fake BMS Data on STM32F4 Nucleo
    The logging functionality was first tested on an STM32 Nucleo board using fake BMS data. Simulated cell voltages and temperatures were logged, retrieved, and verified using serial printouts.
  • Test B: Real BMS Data on Shepherd BMS Master Board
    The logging functionality was tested on the Shepherd BMS Master Board, configured with one chip Alpha. The inputs were connected to a PSU to simulate real cell voltages.

To Do

  • Validate logging behavior with real BMS data instead of test cases.
  • Validate functionality with additional chips and segments.

Checklist

It can be helpful to check the Checks and Files changed tabs.
Please reach out to your Project Lead if anything is unclear.
Please request reviewers and ping on slack only after you've gone through this whole checklist.

  • No merge conflicts
  • All checks passing
  • Remove any non-applicable sections of this template
  • Assign the PR to yourself
  • Request reviewers & ping on Slack
  • PR is linked to the ticket (fill in the closes line below)

Closes # (issue #175 )

@KSMehta11 KSMehta11 self-assigned this Feb 11, 2025
@KSMehta11 KSMehta11 linked an issue Feb 11, 2025 that may be closed by this pull request
@KSMehta11 KSMehta11 requested a review from Sabramz February 11, 2025 21:49

void cell_data_logger_init(void)
{
rb_init(&cell_log_ring_buff, cell_data_storage, NUM_OF_READINGS,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can all of this be wrapped in a ptr that the user controls? So the arg to this func (and all other funcs) would be like struct *bms_logger. If you don't want them modifying it, you can use C stuff to make a struct that is defined in the header but its fields are defined in the source file and the fields cannot be accessed from outside the source.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also this struct needs a mutex, and all the getters and setters will need mutexes as well.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I can wrap this in a pointer-controlled struct, and that approach makes sense. I will do it that way and add the mutex as well.

new_entry.cell_temperatures[chip_num][cell] =
bms_data->chip_data[chip_num].cell_temp[cell];

new_entry.timestamp[chip_num][cell] =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the source of this timestamp be right after the measurement is taken, rather than when it is logged?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to log the data much later than the reading happened.


CellDataEntry_t *cell_data_log_get_last(void)
{
return (CellDataEntry_t *)rb_get_head(&cell_log_ring_buff);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you protect against getting a NULLPTR from this? or say in docs that NULL PTR can be returned and should be checked and that a NULL ptr return means the rb has not been initted / populated with data yet?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add protection against returning a nullptr and will likely include a message.

@KSMehta11 KSMehta11 requested a review from Sabramz February 16, 2025 02:50
osMutexId_t mutex;
};

static BMSLogger bms_logger = { .is_initialized = false };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't want this. The user should be creating the logger. They will make the struct, and then pass it to an init function.


static BMSLogger bms_logger = { .is_initialized = false };

BMSLogger *get_logger(void)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Get rid of this with the static global variable

* @param logger Pointer to the logger instance.
* @return false on success, true on failure.
*/
bool cell_data_logger_init(BMSLogger *logger);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The standard in C is that the return type is an int. Make every error code return an int.

extern TIM_HandleTypeDef htim2;

struct BMSLogger {
bool is_initialized;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unnecessary. You will know if the logger is uninitialized if a NULLPTR is passed into a func in the interface.

return __HAL_TIM_GET_COUNTER(&htim2);
}

static bool get_logger_status(const BMSLogger *logger)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add doc comment and put private functions before all public functions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you are also checking if acc_data_t is null in other functions, make this function able to determine if any pointer is a nullptr, not just loggers.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tbh you can just get rid of this entirely and use asserts. Just at the beginning of every function call assert(ptr) for every ptr.

#include "cmsis_os.h"

// Number of stored cell data readings in ring buffer.
#define NUM_OF_READINGS 10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The user should decide how many readings will be logged, and should be able to do this when initializing the logger.

* @brief Serial prints the most recent data log.
* @param logger Pointer to the logger instance.
*/
void print_latest_cell_data_log(const BMSLogger *logger);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is not needed since the user can just call print last n cell data logs with an n of 1.

log_entries[entry_idx].cell_temperature_timestamp);

for (int chip_num = 0; chip_num < NUM_CHIPS; chip_num++) {
int cell_count = (chip_num % 2 == 0) ? NUM_CELLS_ALPHA :
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be broken out into a helper function. Triple nested for loops is going too far lolz.

@@ -142,6 +142,10 @@ typedef struct {

bool is_charger_connected;

/* Cell Data Logging Timestamps */
uint32_t voltage_timestamp;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My mistake on the previous review. Lets limit the scope of this to just the interface. Application will come later, so get rid of these. Everything should be in cell data logging.

@KSMehta11 KSMehta11 requested a review from Sabramz February 17, 2025 21:07
* @brief Assigns a timestamp to the voltage measurement field of a cell data entry.
* @param entry Pointer to the CellDataEntry_t structure to update.
*/
void cell_data_set_voltage_timestamp(CellDataEntry_t *entry)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we timestamp something, we should just timestamp the next log entry, cuz that is where the next reading will be written to. Do some auto incrementing here so the user simply calls timestamp_next_reading and passes in the logger.

* @param entry Pointer to a CellDataEntry_t structure that holds the measurement data.
* @return 0 on success, -1 on failure.
*/
int cell_data_log_measurement(struct BMSLogger *logger, acc_data_t *bms_data,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this not take an entry as an argument. Just a logger and data to insert.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

24A - Storing Cell Data Readings and Timestamping Them
2 participants