Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
# ESP32 Race Car Core Library
# BYUI Formula-Hybrid Race Car Core Library

[![CI Pipeline](https://github.com/byui-formula-hybrid/Core/actions/workflows/ci.yml/badge.svg)](https://github.com/byui-formula-hybrid/Core/actions/workflows/ci.yml)

This is a PlatformIO-based ESP32 project for a race car with multiple modular components. Each component is implemented as a separate library for clean organization and easy testing.
This is a PlatformIO-based project for a race car with multiple modular components. Each component is implemented as a separate library for clean organization and easy testing. This library is designed to be microcontroller agnostic, which means that we design the general hardware layout and implement hardware agnostic logic.

## Project Structure

<!-- I think we can safely remove
- core (from tests)
- imd
- pedals -->
```
Core/
├── .github/ # GitHub Actions workflows and templates
Expand Down Expand Up @@ -44,6 +48,7 @@ Core/

**New to the project? Start here:**

<!-- TODO: Building should be done in the hardware repository -->

#### For Linux/macOS
```bash
Expand All @@ -57,7 +62,7 @@ cd Core
# 3. Test that everything works
./.scripts/test.sh

# 4. Build for ESP32
# 4. Build
./.scripts/build.sh
```

Expand Down Expand Up @@ -99,7 +104,7 @@ cd Core
> # 3. Test that everything works
> .\.scripts\powershell\test.ps1
>
> # 4. Build for ESP32
> # 4. Build
> .\.scripts\powershell\build.ps1
> ```
>
Expand All @@ -120,8 +125,8 @@ cd Core
>
> # 3. Test that everything works
> ./.scripts/test.sh
>
> # 4. Build for ESP32
>
> # 4. Build
> ./.scripts/build.sh
> ```
>
Expand Down Expand Up @@ -177,6 +182,7 @@ This project uses GitHub Actions for continuous integration with smart safeguard

## License

<!-- TODO: I think that this can be removed -->
This project contains STM32CubeMX generated code © STMicroelectronics, licensed under ST’s software license - see [LICENSE](LICENSE_ST) file for details.

All original source code and project documentation is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License - see the [LICENSE](LICENSE) file for details.
Expand Down
8 changes: 8 additions & 0 deletions include/battery.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// This is an umbrella header for the battery library. It includes all the necessary headers for using the battery library.
#ifndef BATTERY_H
#define BATTERY_H

// NOTE: Due to the inclusion of multiple lib folders in hardware, these need to be relative
#include "../lib/battery/messages.h"

#endif // BATTERY_H
6 changes: 4 additions & 2 deletions lib/core/core.h → include/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
#ifndef CORE_H
#define CORE_H

#include "lock.h"
#include "thread.h"
#include "core/lock.h"
#include "core/thread.h"
#include "core/queue.h"
#include "core/logger.h"

#endif // CORE_H
File renamed without changes.
File renamed without changes.
File renamed without changes.
9 changes: 9 additions & 0 deletions include/core/logger.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef CORE_LOGGER_UMBRELLA_H
#define CORE_LOGGER_UMBRELLA_H

#include "logger/i_logger.h"
#include "logger/i_timestamp_provider.h"
#include "logger/types.h"
#include "logger/logger.h"

#endif // CORE_LOGGER_UMBRELLA_H
32 changes: 32 additions & 0 deletions include/core/logger/i_logger.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef CORE_LOGGER_I_LOGGER_H
#define CORE_LOGGER_I_LOGGER_H

#include "types.h"

namespace Core {

/**
* @brief Interface for hardware-specific log outputs (Serial, SD Card, etc.)
*/
class ILogger {
public:
/**
* @brief Destructor for the logger backend.
*/
virtual ~ILogger() = default;

/**
* @brief Consumes a pre-filled LogEntry.
* @param entry The entry containing msg, timestamp, file, line, and level.
*/
virtual void log(const LogEntry& entry) = 0;

/**
* @brief Optional: Flush any pending writes (useful for SD cards before a crash)
*/
virtual void flush() {}
};

} // namespace Core

#endif // CORE_LOGGER_I_LOGGER_H
12 changes: 12 additions & 0 deletions include/core/logger/i_timestamp_provider.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#ifndef I_TIME_STAMP_PROVIDER_H
#define I_TIME_STAMP_PROVIDER_H

#include <stdint.h>

class ITimeStampProvider {
public:
virtual ~ITimeStampProvider() = default;
virtual uint64_t get_timestamp() = 0;
};

#endif // I_TIME_STAMP_PROVIDER_H
138 changes: 138 additions & 0 deletions include/core/logger/logger.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#ifndef CORE_LOGGER_H
#define CORE_LOGGER_H

#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <core/queue.h>

#include "i_logger.h"
#include "i_timestamp_provider.h"
#include "types.h"

namespace Core {

// TODO: Use the flush method as necessary
/**
* @brief A simple logger class for handling log entries
*/
class Logger {
public:
/**
* @brief Logs a message with the specified level and metadata
* @param level: The log level
* @param file: The file name where the log is called
* @param line: The line number where the log is called
* @param tag: A tag for categorizing the log
* @param format: The format string for the log message
* @param ...: Variable arguments for the format string
*/
static void log(LogLevel level, const char* file, int line, const char* tag, const char* format, ...) {
Logger instance = get_instance();
LogEntry entry;
entry.level = level;
entry.file = file;
entry.line = line;
entry.timestamp = instance.time_provider->get_timestamp(); // Implement based on ESP32 or STM32

strncpy(entry.tag, tag, sizeof(entry.tag));

va_list args;
va_start(args, format);
vsnprintf(entry.msg, sizeof(entry.msg), format, args);
va_end(args);

// Fire and forget: Push to the non-blocking queue
instance.queue->enqueue(entry);
}

/**
* @brief Sets the output backend for the logger
* @param backend: The logger backend to use
*/
static void set_output(ILogger* backend) {
get_instance().backend = backend;
}

/**
* @brief Sets the queue for the logger
* @param queue: The queue to use
*/
static void set_queue(IQueue<LogEntry>* queue) {
get_instance().queue = queue;
}

static void set_time_provider(ITimeStampProvider* provider) {
get_instance().time_provider = provider;
}

/**
* @brief Processes the log queue
* This should be called in a loop in the main thread to ensure logs are printed.
*/
static void process() {
Logger instance = get_instance();
LogEntry entry;
// Block until a log arrives
if (instance.queue->dequeue(entry, instance.max_timeout_ms)) {
if (instance.backend) {
// The backend handles the actual string printing
instance.backend->log(entry);
}
}
}

/**
* @brief Sets the maximum timeout for log processing
* @param timeout_ms: The maximum timeout in milliseconds
*/
static void set_max_timeout(uint32_t timeout_ms) {
get_instance().max_timeout_ms = timeout_ms;
}

private:
/**
* @brief Constructor for the logger.
*/
Logger() {};

/**
* @brief Returns the singleton instance of the logger.
* @return instance: the singleton instance of the logger.
*/
static Logger& get_instance() {
static Logger instance;
return instance;
}

/**
* @brief The queue for storing log entries.
*/
IQueue<LogEntry>* queue;

/**
* @brief The logger backend for outputting log entries.
*/
ILogger* backend = nullptr;

/**
* @brief The time stamp provider for generating time stamps.
*/
ITimeStampProvider* time_provider;

/**
* @brief The maximum timeout for log processing.
*/
uint32_t max_timeout_ms = 100;
};

} // namespace Core

// --- THE MACRO INTERFACE ---
// This replaces the call site with the correct File and Line info
#define LOG_DEBUG(tag, fmt, ...) Core::Logger::log(Core::LogLevel::DEBUG, __FILE__, __LINE__, tag, fmt, ##__VA_ARGS__)
#define LOG_INFO(tag, fmt, ...) Core::Logger::log(Core::LogLevel::INFO, __FILE__, __LINE__, tag, fmt, ##__VA_ARGS__)
#define LOG_WARN(tag, fmt, ...) Core::Logger::log(Core::LogLevel::WARNING, __FILE__, __LINE__, tag, fmt, ##__VA_ARGS__)
#define LOG_ERR(tag, fmt, ...) Core::Logger::log(Core::LogLevel::CRITICAL, __FILE__, __LINE__, tag, fmt, ##__VA_ARGS__)

#endif // CORE_LOGGER_H
32 changes: 32 additions & 0 deletions include/core/logger/types.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef CORE_LOGGER_TYPES_H
#define CORE_LOGGER_TYPES_H

#include <stdint.h>

namespace Core {

/**
* @brief Enumerate representing different log levels
*/
enum class LogLevel {
DEBUG,
INFO,
WARNING,
CRITICAL
};

/**
* @brief Represents a single log entry
*/
struct LogEntry {
LogLevel level;
uint32_t timestamp; // System millis
int line;
const char* file; // Pointer to string literal (Flash)
char tag[16];
char msg[64]; // Fixed size to avoid heap allocation
};

} // namespace Core

#endif // CORE_LOGGER_TYPES_H
7 changes: 7 additions & 0 deletions include/core/queue.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// This is an umbrella header for the queue module.
#ifndef QUEUE_H
#define QUEUE_H

#include "queue/i_queue.h"

#endif // QUEUE_H
47 changes: 47 additions & 0 deletions include/core/queue/i_queue.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#ifndef CORE_I_QUEUE_H
#define CORE_I_QUEUE_H

#include <cstddef>
#include <cstdint>

namespace Core {

template<typename T>
class IQueue {
public:
/**
* @brief Destructor for the queue.
*/
virtual ~IQueue() = default;

/**
* @brief Adds an item to the queue.
* @param data The item to copy into the queue.
* @return true if successful, false if the queue is full.
*/
virtual bool enqueue(const T data) = 0;

/**
* @brief Removes an item from the queue.
* @param outData Reference to store the popped item.
* @param timeout_ms How long to wait if the queue is empty (0 for non-blocking).
* @return true if an item was retrieved, false if it timed out.
*/
virtual bool dequeue(T& outData, uint32_t timeout_ms = 0) = 0;

/**
* @brief Returns the number of items in the queue.
* @return size: the number of items in the queue.
*/
virtual size_t size() const = 0;

/**
* @brief Checks if the queue is full.
* @return true if the queue is full, false otherwise.
*/
virtual bool is_full() = 0;
};

} // namespace Core

#endif // CORE_I_QUEUE_H
6 changes: 6 additions & 0 deletions include/core/task.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#ifndef CORE_TASK_H
#define CORE_TASK_H

#include "../../lib/core/task/controller.h"

#endif // CORE_TASK_H
File renamed without changes.
Loading