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 30 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
001d630
Added cell data logging header with function prototypes.
Feb 10, 2025
4efa3ac
Added cell data logger initialization and updated Makefile.
Feb 11, 2025
932fcb8
Implemented function to log cell voltage and temperature data.
Feb 11, 2025
81838cd
Implemented functions to retrieve logged cell data.
Feb 11, 2025
2ddecdc
Implemented serial debugging functions.
Feb 11, 2025
5202ac7
Initialized the data logging ring buffer and enabled logging.
Feb 11, 2025
d7c9364
Feature: Add BMSLogger struct.
Feb 14, 2025
e311d8d
Refactor: Encapsulate BMSLogger
Feb 15, 2025
6e58239
Add separate timestamps for voltage and temperature measurements.
Feb 15, 2025
84adf2a
Feat: Capture timestamps at measurement instead of storage.
Feb 15, 2025
36117e1
Add error messages for user diagnostics.
Feb 15, 2025
30e9c2b
Fix: prevent multiple initializations of BMSLogger.
Feb 15, 2025
01f4922
Add Mutex and Error Handling.
Feb 16, 2025
01d0110
Refactor: Mutex wait time.
Feb 16, 2025
70a66a1
Refactor: Mutex
Feb 16, 2025
d7e576f
Merge branch '166-24a---storing-cell-data-readings-and-timestamping-t…
Feb 16, 2025
05d5e7b
Refactor: Remove static global logger and use getLogger.
Feb 16, 2025
04e7ba5
Refactor: Use int return type and remove is_initialized flag.
Feb 16, 2025
7cfa7bb
Refactor: Replace manual NULL checks with assert.
Feb 16, 2025
6b656cc
Implement single return point using goto.
Feb 16, 2025
dcd5678
Refactor: Mutex wait time.
Feb 16, 2025
30f2846
Add print_cell_data helper func.
Feb 16, 2025
2010a5d
Add Doc Comments
Feb 16, 2025
ead1d0a
Add global bms logger instance.
Feb 16, 2025
da715db
Remove: Timestamp during measurement.
Feb 16, 2025
0822ce1
Refactor: Made bms logger public.
Feb 17, 2025
eb059c8
Add: Timestamp set functions.
Feb 17, 2025
fc12135
Refactor: CellDataEntry struct.
Feb 17, 2025
6f7ae82
Refactored: Data Insertion and Timestamping Functions.
Feb 20, 2025
56d2fd6
Refactor: Centralized Timestamping and Logging.
Feb 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions Core/Inc/cell_data_logging.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* @file cell_data_logging.h
* @brief Functionality for logging and retrieving cell voltage and temperature data.
*
* This module provides an interface for logging cell voltage and temperature
* data using a ring buffer. Users must ensure proper synchronization when
* handling logging operations, as well as setting timestamps before logging.
*
* @warning The `BMSLogger` structure is *managed internally by the logging system.
* Users should *only pass its address to logging functions and must not modify
* its contents directly.
*/

#ifndef CELL_DATA_LOGGING_H
#define CELL_DATA_LOGGING_H

#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "datastructs.h"
#include "ringbuffer.h"
#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.


/**
* @struct CellDataEntry_t
* @brief Structure to store logged cell voltage and temperature data.
*
* This structure is used to store the timestamp, cell voltages,
* and cell temperatures for each measurement cycle. Each entry
* contains data for all chips and their respective cells.
*/
typedef struct {
uint32_t cell_voltage_timestamp;
uint32_t cell_temperature_timestamp;
float cell_voltages[NUM_CHIPS][NUM_CELLS_ALPHA];
float cell_temperatures[NUM_CHIPS][NUM_CELLS_ALPHA];
} CellDataEntry_t;

/**
* @struct BMSLogger
* @brief Structure to manage logging system
*
* @warning The `BMSLogger` structure is *managed internally by the logging system.
* Users should *only pass its address to logging functions and must not modify
* its contents directly.
*/
struct BMSLogger {
ringbuf_t ring_buff;
CellDataEntry_t cell_data_storage[NUM_OF_READINGS];
osMutexId_t mutex;
};

/**
* @brief Initializes a BMSLogger instance.
* @param logger Pointer to the logger instance.
* @return 0 on success, -1 on failure.
*/
int cell_data_logger_init(struct BMSLogger *logger);

/**
* @brief Assigns a voltage timestamp to the current log entry.
* @param logger Pointer to the BMSLogger instance.
* @return 0 on success, -1 on failure.
*/
int cell_data_logger_timestamp_voltage(struct BMSLogger *logger);

/**
* @brief Assigns a temperature timestamp to the current log entry.
* @param logger Pointer to the BMSLogger instance.
* @return 0 on success, -1 on failure.
*/
int cell_data_logger_timestamp_therms(struct BMSLogger *logger);

/**
* @brief Logs a new measurement and inserts it into the ring buffer.
*
* @note The user must ensure timestamps are set before calling this function.
*
* @param logger Pointer to the BMSLogger instance managing the ring buffer.
* @param bms_data Pointer to the BMS data structure containing cell voltages and temperatures.
* @return 0 on success, -1 on failure.
*/
int cell_data_log_measurement(struct BMSLogger *logger, acc_data_t *bms_data);

/**
* @brief Retrieves the last n cell data logs from the buffer.
* @param logger Pointer to the logger instance.
* @param n Number of previous logs to retrieve.
* @param out_buffer Pointer to the buffer where the readings will be stored.
* @return 0 on success, -1 on failure.
*/
int cell_data_log_get_last_n(const struct BMSLogger *logger, size_t n,
CellDataEntry_t *out_buffer);

/**
* @brief Serial prints the last n cell data logs.
* @param logger Pointer to the logger instance.
* @param n Number of previous logs to print.
* @return 0 on success, -1 on failure.
*/
int print_last_n_cell_data_logs(const struct BMSLogger *logger, size_t n);

#endif
257 changes: 257 additions & 0 deletions Core/Src/cell_data_logging.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
/**
* @file cell_data_logging.c
* @brief Implementation of cell voltage and temperature data logging.
*/

#include "cell_data_logging.h"
#include "analyzer.h"
#include "bmsConfig.h"
#include "stm32f4xx_hal.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>

// Used to get microsecond timestamps.
extern TIM_HandleTypeDef htim2;

/**
* @brief Retrieves the current timestamp in microseconds from TIM2.
* @return The current timestamp in microseconds.
*/
static inline uint32_t get_us_timestamp(void)
{
return __HAL_TIM_GET_COUNTER(&htim2);
}

/**
* @brief Retrieves the next writable log entry from the buffer.
* @param logger Pointer to the BMSLogger instance.
* @return Pointer to the writable log entry.
*/
static CellDataEntry_t *get_writable_log_entry(struct BMSLogger *logger)
{
assert(logger != NULL);

size_t next_idx = logger->ring_buff.head_idx;

CellDataEntry_t *entry = &logger->cell_data_storage[next_idx];

return entry;
}

/**
* @brief Prints a single cell data entry.
* @param entry Pointer to the cell data entry to print.
* @param entry_idx Index of the entry.
*/
static void print_cell_data(const CellDataEntry_t *entry, size_t entry_idx)
{
assert(entry != NULL);

printf("\r\n--- Log Entry %u ---\r\n", entry_idx + 1);
printf("Voltage Measurement Timestamp: %lu µs\r\n",
entry->cell_voltage_timestamp);
printf("Temperature Measurement Timestamp: %lu µs\r\n",
entry->cell_temperature_timestamp);

for (int chip_num = 0; chip_num < NUM_CHIPS; chip_num++) {
int cell_count = (chip_num % 2 == 0) ? NUM_CELLS_ALPHA :
NUM_CELLS_BETA;
printf("\r\nChip %d (%s):\r\n", chip_num,
(chip_num % 2 == 0) ? "Alpha" : "Beta");

for (int cell = 0; cell < cell_count; cell++) {
printf(" Cell %d: Voltage: %.3f V, Temperature: %.2f C\r\n",
cell + 1, entry->cell_voltages[chip_num][cell],
entry->cell_temperatures[chip_num][cell]);
}
}
}

/**
* @brief Initializes a BMSLogger instance.
* @param logger Pointer to the logger instance.
* @return 0 on success, -1 on failure.
*/
int cell_data_logger_init(struct BMSLogger *logger)
{
assert(logger != NULL);

memset(logger, 0, sizeof(struct BMSLogger));

rb_init(&logger->ring_buff, logger->cell_data_storage, NUM_OF_READINGS,
sizeof(CellDataEntry_t));

logger->mutex = osMutexNew(NULL);

if (logger->mutex == NULL) {
printf("ERROR: Data Logger Mutex initialization failed!\r\n");
return -1;
}

return 0;
}

/**
* @brief Assigns a voltage timestamp to the current log entry.
* @param logger Pointer to the BMSLogger instance.
* @return 0 on success, -1 on failure.
*/
int cell_data_logger_timestamp_voltage(struct BMSLogger *logger)
{
assert(logger != NULL);

if (osMutexAcquire(logger->mutex, osWaitForever) != osOK) {
printf("ERROR: Failed to acquire data logging mutex!\r\n");
return -1;
}

CellDataEntry_t *entry = get_writable_log_entry(logger);

if (!entry)
return -1;

entry->cell_voltage_timestamp = get_us_timestamp();

osMutexRelease(logger->mutex);

return 0;
}

/**
* @brief Assigns a temperature timestamp to the current log entry.
* @param logger Pointer to the BMSLogger instance.
* @return 0 on success, -1 on failure.
*/
int cell_data_logger_timestamp_therms(struct BMSLogger *logger)
{
assert(logger != NULL);

if (osMutexAcquire(logger->mutex, osWaitForever) != osOK) {
printf("ERROR: Failed to acquire data logging mutex!\r\n");
return -1;
}

CellDataEntry_t *entry = get_writable_log_entry(logger);

if (!entry)
return -1;

entry->cell_temperature_timestamp = get_us_timestamp();

osMutexRelease(logger->mutex);

return 0;
}

/**
* @brief Logs a new measurement and inserts it into the ring buffer.
*
* @note The user must ensure timestamps are set before calling this function.
*
* @param logger Pointer to the BMSLogger instance managing the ring buffer.
* @param bms_data Pointer to the BMS data structure containing cell voltages and temperatures.
* @return 0 on success, -1 on failure.
*/
int cell_data_log_measurement(struct BMSLogger *logger, acc_data_t *bms_data)
{
int status = -1;
assert(logger != NULL);
assert(bms_data != NULL);

if (osMutexAcquire(logger->mutex, osWaitForever) != osOK) {
printf("ERROR: Failed to acquire data logging mutex!\r\n");
goto exit;
}

CellDataEntry_t *entry = get_writable_log_entry(logger);
if (!entry) {
printf("ERROR: No writable log entry available!\r\n");
goto exit;
}

for (int chip_num = 0; chip_num < NUM_CHIPS; chip_num++) {
int cell_count = get_num_cells(&bms_data->chip_data[chip_num]);

for (int cell = 0; cell < cell_count; cell++) {
entry->cell_voltages[chip_num][cell] =
bms_data->chip_data[chip_num]
.cell_voltages[cell];
entry->cell_temperatures[chip_num][cell] =
bms_data->chip_data[chip_num].cell_temp[cell];
}
}

rb_insert(&logger->ring_buff, entry);

status = 0;

osMutexRelease(logger->mutex);

return status;

exit:
return status;
}

/**
* @brief Retrieves the last n cell data logs from the buffer.
* @param logger Pointer to the logger instance.
* @param n Number of previous logs to retrieve.
* @param out_buffer Pointer to the buffer where the readings will be stored.
* @return 0 on success, -1 on failure.
*/
int cell_data_log_get_last_n(const struct BMSLogger *logger, size_t n,
CellDataEntry_t *out_buffer)
{
int status = -1;
assert(logger != NULL);

if (n > logger->ring_buff.curr_elements) {
printf("ERROR: Not enough logs available!\r\n");
goto exit;
}

if (osMutexAcquire(logger->mutex, osWaitForever) != osOK) {
printf("ERROR: Failed to acquire data logging mutex!\r\n");
goto exit;
}

rb_get_last_n(&logger->ring_buff, out_buffer, n);

status = 0;

osMutexRelease(logger->mutex);

exit:
return status;
}

/**
* @brief Serial prints the last n cell data logs.
* @param logger Pointer to the logger instance.
* @param n Number of previous logs to print.
* @return 0 on success, -1 on failure.
*/
int print_last_n_cell_data_logs(const struct BMSLogger *logger, size_t n)
{
int status = -1;
assert(logger != NULL);

CellDataEntry_t log_entries[n];

if (cell_data_log_get_last_n(logger, n, log_entries)) {
printf("Error retrieving log entries!\r\n");
goto exit;
}

printf("\r\nPrinting Last %u Cell Data Logs:\r\n", n);
for (size_t entry_idx = 0; entry_idx < n; entry_idx++) {
print_cell_data(&log_entries[entry_idx], entry_idx);
}

status = 0;

exit:
return status;
}
1 change: 1 addition & 0 deletions Core/Src/segment.c
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ void segment_retrieve_data(acc_data_t *bmsdata)
// read all therms using AUX 2
adc_and_read_aux2_registers(bmsdata->chips);
}

void segment_retrieve_debug_data(acc_data_t *bmsdata)
{
// poll stuff like vref, etc.
Expand Down
2 changes: 1 addition & 1 deletion Drivers/Embedded-Base
Submodule Embedded-Base updated 47 files
+3 −16 .github/ISSUE_TEMPLATE/bug-form.yml
+1 −1 .github/ISSUE_TEMPLATE/config.yml
+0 −64 .github/ISSUE_TEMPLATE/epic.yml
+0 −26 .github/ISSUE_TEMPLATE/feature-request.yml
+0 −18 .github/ISSUE_TEMPLATE/other.yml
+0 −38 .github/ISSUE_TEMPLATE/spike.yml
+5 −4 .github/ISSUE_TEMPLATE/task.yml
+0 −1 .github/pull_request_template.md
+5 −8 .github/workflows/build_cerberus.yml
+51 −78 cangen/README.md
+0 −0 cangen/archive/CANField.py
+0 −0 cangen/archive/CANMsg.py
+0 −0 cangen/archive/Format.py
+0 −0 cangen/archive/Messages.py
+0 −0 cangen/archive/Result.py
+0 −0 cangen/archive/RustSynth.py
+0 −0 cangen/archive/RustSynthFromJSON.py
+0 −0 cangen/archive/YAMLParser.py
+0 −0 cangen/archive/__init__.py
+0 −0 cangen/archive/jsongen
+0 −0 cangen/archive/templates/decoders.c.j2
+0 −0 cangen/archive/templates/encoders.c.j2
+0 −0 cangen/archive/templates/imports.c.j2
+0 −0 cangen/archive/templates/macros.j2
+0 −0 cangen/archive/templates/routers.c.j2
+1,800 −1,016 cangen/can-messages/bms.json
+168 −67 cangen/can-messages/calypso_cmd.json
+116 −100 cangen/can-messages/charger.json
+622 −528 cangen/can-messages/dti.json
+1,022 −456 cangen/can-messages/mpu.json
+857 −437 cangen/can-messages/msb.json
+35 −18 cangen/can-messages/wheel.json
+90 −0 cangen/change_spec.py
+41 −0 middleware/include/bitstream.h
+6 −0 middleware/include/c_utils.h
+23 −0 middleware/include/heap_ringbuffer.h
+34 −0 middleware/include/high_pass.h
+58 −15 middleware/include/ringbuffer.h
+0 −0 middleware/include/simple_ema.h
+44 −0 middleware/src/bitstream.c
+89 −0 middleware/src/heap_ringbuffer.c
+22 −0 middleware/src/high_pass.c
+58 −59 middleware/src/ringbuffer.c
+0 −0 middleware/src/simple_ema.c
+1 −1 ner_environment/build_system/build_system.py
+12 −1 platforms/stm32f405/include/can.h
+4 −0 platforms/stm32f405/src/can.c
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ Core/Src/stateMachine.c \
Core/Src/can_handler.c \
Core/Src/can_messages.c \
Core/Src/shep_tasks.c \
Core/Src/cell_data_logging.c \
Core/Src/stm32f4xx_it.c \
Core/Src/stm32f4xx_hal_msp.c \
Drivers/Embedded-Base/platforms/stm32f405/src/can.c \
Expand Down