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
Binary file added .cache/clangd/index/apps.cpp.4A4888B58627EC67.idx
Binary file not shown.
Binary file added .cache/clangd/index/apps.h.E11F6CDFB32DD39D.idx
Binary file not shown.
Binary file added .cache/clangd/index/header.h.AF8E12C7DE81203E.idx
Binary file not shown.
Binary file added .cache/clangd/index/main.cpp.C1463423FB8DE48E.idx
Binary file not shown.
Binary file not shown.
Binary file added .cache/clangd/index/motor.h.5524FEED15B6403E.idx
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added .cache/clangd/index/units.h.74F3D0D83FD86752.idx
Binary file not shown.
Binary file not shown.
86 changes: 85 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,85 @@
# TEENSY
# TEENSY VCU System

This repository contains the Vehicle Control Unit (VCU) software for the University College Dublin Formula SAE team, designed to run on the Teensy 4.1 microcontroller.

## Overview

This is the new repository for our VCU system. We are migrating to the Teensy 4.1 platform for improved performance and capabilities. The Teensy 4.1 provides:

- 600 MHz ARM Cortex-M7 processor
- 8 MB Flash memory
- 1 MB RAM
- Multiple CAN bus interfaces
- High-speed serial communication
- Built-in Ethernet support

## Project Structure

```
TEENSY/
├── src/ # Source code files
│ └── main.cpp # Main application entry point
├── include/ # Header files
├── lib/ # Project-specific libraries
├── test/ # Unit tests
├── platformio.ini # PlatformIO configuration
└── README.md # This file
```

## Requirements

- [PlatformIO](https://platformio.org/) - Cross-platform build system
- [Visual Studio Code](https://code.visualstudio.com/) (recommended) with PlatformIO IDE extension
- Teensy 4.1 board

## Getting Started

### Installation

1. Install Visual Studio Code
2. Install the PlatformIO IDE extension
3. Clone this repository:
```bash
git clone https://github.com/UCDFS/TEENSY.git
cd TEENSY
```

### Building the Project

1. Open the project folder in Visual Studio Code
2. PlatformIO will automatically detect the project and install dependencies
3. Build the project using one of these methods:
- Click the checkmark (✓) icon in the PlatformIO toolbar
- Use the command palette: `PlatformIO: Build`
- Run in terminal: `pio run`

### Uploading to Teensy 4.1

1. Connect your Teensy 4.1 board via USB
2. Upload using one of these methods:
- Click the right arrow (→) icon in the PlatformIO toolbar
- Use the command palette: `PlatformIO: Upload`
- Run in terminal: `pio run --target upload`

### Serial Monitor

To view serial output from the Teensy:

- Click the plug icon in the PlatformIO toolbar
- Use the command palette: `PlatformIO: Serial Monitor`
- Run in terminal: `pio device monitor`

## Development

### Adding Libraries

Add library dependencies to `platformio.ini` under the `lib_deps` section:

```ini
[env:teensy41]
platform = teensy
board = teensy41
framework = arduino
lib_deps =
# Add libraries here
```
8 changes: 4 additions & 4 deletions compile_commands.json
Original file line number Diff line number Diff line change
Expand Up @@ -1032,9 +1032,9 @@
"output": ".pio/build/teensy41/src/main.cpp.o"
},
{
"command": "/Users/shanewhelan/.platformio/packages/toolchain-gccarmnoneeabi-teensy/bin/arm-none-eabi-g++ -o .pio/build/teensy41/src/motor_controller.cpp.o -c -fno-exceptions -felide-constructors -fno-rtti -std=gnu++17 -Wno-error=narrowing -fpermissive -fno-threadsafe-statics -Wall -ffunction-sections -fdata-sections -mthumb -mcpu=cortex-m7 -nostdlib -mfloat-abi=hard -mfpu=fpv5-d16 -O2 -DPLATFORMIO=60118 -D__IMXRT1062__ -DARDUINO_TEENSY41 -DUSB_SERIAL -DARDUINO=10805 -DTEENSYDUINO=159 -DCORE_TEENSY -DF_CPU=600000000 -DLAYOUT_US_ENGLISH -Iinclude -Isrc -Ilib/ITEADLIB_Arduino_Nextion-master -I/Users/shanewhelan/.platformio/packages/framework-arduinoteensy/libraries/SD/src -I/Users/shanewhelan/.platformio/packages/framework-arduinoteensy/libraries/SdFat/src -I/Users/shanewhelan/.platformio/packages/framework-arduinoteensy/libraries/SoftwareSerial -I/Users/shanewhelan/.platformio/packages/framework-arduinoteensy/libraries/FlexCAN_T4 \"-I.pio/libdeps/teensy41/Adafruit MPU6050\" \"-I.pio/libdeps/teensy41/Adafruit Unified Sensor\" \"-I.pio/libdeps/teensy41/Adafruit BusIO\" -I/Users/shanewhelan/.platformio/packages/framework-arduinoteensy/libraries/SPI -I/Users/shanewhelan/.platformio/packages/framework-arduinoteensy/libraries/Wire -I/Users/shanewhelan/.platformio/packages/framework-arduinoteensy/libraries/Wire/utility -I/Users/shanewhelan/.platformio/packages/framework-arduinoteensy/cores/teensy4 src/motor_controller.cpp",
"directory": "/Users/shanewhelan/Documents/GitHub/TEENSY",
"file": "src/motor_controller.cpp",
"output": ".pio/build/teensy41/src/motor_controller.cpp.o"
"command": "/home/laptop-main/.platformio/packages/toolchain-gccarmnoneeabi-teensy/bin/arm-none-eabi-g++ -o .pio/build/teensy41/src/motor.cpp.o -c -fno-exceptions -felide-constructors -fno-rtti -std=gnu++17 -Wno-error=narrowing -fpermissive -fno-threadsafe-statics -Wall -ffunction-sections -fdata-sections -mthumb -mcpu=cortex-m7 -nostdlib -mfloat-abi=hard -mfpu=fpv5-d16 -O2 -DPLATFORMIO=60118 -D__IMXRT1062__ -DARDUINO_TEENSY41 -DUSB_SERIAL -DARDUINO=10805 -DTEENSYDUINO=159 -DCORE_TEENSY -DF_CPU=600000000 -DLAYOUT_US_ENGLISH -Iinclude -Isrc -I/home/laptop-main/.platformio/packages/framework-arduinoteensy/libraries/FlexCAN_T4 -I/home/laptop-main/.platformio/packages/framework-arduinoteensy/cores/teensy4 src/motor.cpp",
"directory": "/home/laptop-main/repos/ucdfs/TEENSY",
"file": "src/motor.cpp",
"output": ".pio/build/teensy41/src/motor.cpp.o"
}
]
5 changes: 5 additions & 0 deletions include/main.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include "motor.h"

// This is a NULLPTR Placeholder and is not a safe permanant solution
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The comment states "This is a NULLPTR Placeholder and is not a safe permanant solution". The typo "permanant" should be corrected to "permanent".

Suggested change
// This is a NULLPTR Placeholder and is not a safe permanant solution
// This is a NULLPTR Placeholder and is not a safe permanent solution

Copilot uses AI. Check for mistakes.
bool *brake_active;
Comment on lines +3 to +4
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The Motor instance 'motor' is declared as a global variable with an uninitialized brake_active pointer. This creates a global static initialization ordering problem and will crash when the Motor constructor is called before main() executes. Consider moving this initialization into the setup() function or ensuring proper initialization order.

Suggested change
// This is a NULLPTR Placeholder and is not a safe permanant solution
bool *brake_active;
// Global brake state storage and pointer passed to Motor.
// The pointer is initialized to refer to this flag before Motor is constructed.
static bool brake_active_flag = false;
bool *brake_active = &brake_active_flag;

Copilot uses AI. Check for mistakes.
Comment on lines +3 to +4
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The brake_active pointer is uninitialized and set to nullptr. This will cause undefined behavior when dereferenced in Motor::tryEnterRTD() at line 145 of motor.cpp. The pointer should be initialized to point to a valid boolean variable before being passed to the Motor constructor.

Suggested change
// This is a NULLPTR Placeholder and is not a safe permanant solution
bool *brake_active;
// Backing storage for the brake active flag; initialized to a known value.
bool brake_active_flag = false;
bool *brake_active = &brake_active_flag;

Copilot uses AI. Check for mistakes.
Motor motor{brake_active};
225 changes: 225 additions & 0 deletions include/motor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
#include <cstdint>
#include <optional>

#ifndef INCLUDE_INCLUDE_MOTOR_H_
#define INCLUDE_INCLUDE_MOTOR_H_
/// This class should be used by the user directly

#include <FlexCAN_T4.h>
class Motor {
public:
enum class MotorResponse {
OK,
NEGATIVE_TORQUE,
OVERTORQUE,
CAN_ERROR,
DISABLED
};
///@argument brake_active A pointer to a variable representing if the brake
/// lights should be on. Should be updated periodicaly
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The comment at line 18 states "@argument brake_active A pointer to a variable representing if the brake lights should be on. Should be updated periodicaly". The typo "periodicaly" should be corrected to "periodically".

Suggested change
/// lights should be on. Should be updated periodicaly
/// lights should be on. Should be updated periodically

Copilot uses AI. Check for mistakes.
Motor(bool *brake_active);
Motor(Motor &&) = default;
Motor(const Motor &) = default;
Motor &operator=(Motor &&) = default;
Motor &operator=(const Motor &) = default;
~Motor() = default;
MotorResponse setTorque(double desired_Nm);

inline void setCanTimeout(uint16_t ms);
inline void clearErrors();
inline void lockDrive();
// Very similar to tryEnterRTD, but attempts to unlock the drive system as
// well
inline void enableDrive();
void update();

inline bool ready() { return readyToDrive; }
inline bool faulted() { return faultActive; }
inline int rpm() { return rpmFeedback; }
inline float dcBus() { return dcBusVoltage; }

// ---------- State ----------
private:
//---
static inline bool readyToDrive = false;
static inline bool faultActive = false;
static inline bool rtdRequestPending = false;
static inline uint32_t tPendingReady = 0;
static inline uint16_t statusWord = 0;
static inline int rpmFeedback = 0;
static inline float dcBusVoltage = 0.0f;
static inline int16_t lastTorque = 0;
static inline uint32_t tLastReissue = 0;
static inline uint32_t tBuzzerOff = 0;
//---

inline void requestCyclic(uint8_t reg, uint8_t periodMs) {
send3(ADDRS::READ, reg, periodMs);
}
static constexpr int BUZZER_PIN = 0; // CHANGEME

// CHANGEME
static constexpr double MAX_TORQUE = 0.0;
Comment on lines +61 to +62
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The MAX_TORQUE constant is set to 0.0 with a CHANGEME comment. This will cause all torque requests above 0.0 to return MotorResponse::OVERTORQUE, effectively disabling motor control. This must be configured with the correct value before the code can be used.

Suggested change
// CHANGEME
static constexpr double MAX_TORQUE = 0.0;
// Maximum allowed motor torque in newton-meters.
// Configure this to match the motor/inverter torque limit before deployment.
static constexpr double MAX_TORQUE = 300.0;

Copilot uses AI. Check for mistakes.

void transmit(int function, int val, int val2 = 0x00);

inline void send3(uint8_t b0, uint8_t b1, uint8_t b2) {
CAN_message_t m{};
m.id = BAMOCAR_RX_ID;
m.len = 3;
m.buf[0] = b0;
m.buf[1] = b1;
m.buf[2] = b2;
Can1.write(m);

// Logging::logCANFrame(m, "TX");
}
void set(int function, int val);
// uint16_t request_once(int field);
void tryEnterRTD();
bool getStatusField(uint8_t field);
bool getError(uint8_t field);
bool getWarning(uint8_t field);

struct ADDRS {
static constexpr uint8_t CANCEL_ERRORS = 0x8E; // Cannot confirm in docs
static constexpr uint8_t TIMEOUT = 0xD0; // pg 157
static constexpr uint8_t SETPOINT = 0x90; // pg 80
static constexpr uint8_t MODE_BIT = 0x51; // pg 29
static constexpr uint8_t READ = 0x3D; // pg 59
static constexpr uint8_t READ_DC = 0xEB;
static constexpr uint8_t STATUS = 0x40; // pg 133
static constexpr uint8_t RPM = 0x30; // pg 142
static constexpr uint8_t LOCK_DRIVE = 0x04;
static constexpr uint8_t SPECIAL_SETTINGS = 0xDC;
static constexpr uint8_t LOCK_DRIVE_BIT = 21; // pg 151
static constexpr uint8_t WARNING_ERROR = 0x8F;

static constexpr uint8_t STATUS_ENABLED = 0;
static constexpr uint8_t STATUS_OK = 4;
static constexpr uint8_t STATUS_STANDSTILL = 9;

// Errors occupy the 0x8F low bitmask, whereas warning occupy the 0x8F high
// bitmask
//
// All avalible on page 148
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The comment "All avalible on page 148" contains a spelling error. "avalible" should be corrected to "available".

Suggested change
// All avalible on page 148
// All available on page 148

Copilot uses AI. Check for mistakes.
static constexpr uint8_t ERROR_EPROM_READ = 0;
static constexpr uint8_t ERROR_HW_FAULT = 1;
static constexpr uint8_t ERROR_RFE_INPUT_MISSING = 2;
static constexpr uint8_t ERROR_CAN_TIMEOUT = 3;
static constexpr uint8_t ERROR_FEEDBACK_SIGNAL = 4;
static constexpr uint8_t ERROR_MAINS_VOLTAGE_MIN = 5;
static constexpr uint8_t ERROR_MOTOR_OVERHEAT = 6;
static constexpr uint8_t ERROR_ENDSTAGE_OVERHEAT = 7;
static constexpr uint8_t ERROR_MAINS_VOLTAGE_MAX = 8;
static constexpr uint8_t ERROR_CRITICAL_AC_CURRENT = 9;
static constexpr uint8_t ERROR_RACE_AWAY = 10;
static constexpr uint8_t ERROR_ECODE_TIMEOUT = 11;
static constexpr uint8_t ERROR_WATCHDOG_RESET = 12;
static constexpr uint8_t ERROR_AC_CURRENT_OFFSET = 13;
static constexpr uint8_t ERROR_INTERNAL_HW_VOLTAGE = 14;
static constexpr uint8_t ERROR_BLEED_RESISTOR_OVERLOAD = 15;

static constexpr uint8_t WARNING_PARAMETER_CONFLICT = 16;
static constexpr uint8_t WARNING_SPECIAL_CPU_FAULT = 17;
static constexpr uint8_t WARNING_RFE_INPUT_MISSING = 18;
static constexpr uint8_t WARNING_AUX_VOLTAGE_MIN = 19;
static constexpr uint8_t WARNING_FEEDBACK_SIGNAL = 20;
static constexpr uint8_t WARNING_5 = 21;
static constexpr uint8_t WARNING_MOTOR_TEMP_LIMIT = 22;
static constexpr uint8_t WARNING_IGBT_TEMP_LIMIT = 23;
static constexpr uint8_t WARNING_VOUT_SATURATION_MAX = 24;
static constexpr uint8_t WARNING_9 = 25;
static constexpr uint8_t WARNING_SPEED_RESOLUTION_LIMIT = 26;
static constexpr uint8_t WARNING_CHECK_ECODE_ID = 27;
static constexpr uint8_t WARNING_TRIPZONE_GLITCH = 28;
static constexpr uint8_t WARNING_ADC_SEQUENCER = 29;
static constexpr uint8_t WARNING_ADC_MEASUREMENT = 30;
static constexpr uint8_t WARNING_BLEEDER_RESISTOR_LOAD = 31;

}; // struct ADDRS

String generateFilename();

static constexpr int USB_DEBUGGING_PORT = 115200;
static constexpr int ESP8266_PORT = 115200;
static constexpr int BAUDRATE = 500000;
static constexpr int ANALOG_RESOLUTION = 12;
static constexpr int CAN_TIMEOUT = 2000;
static constexpr int RTD_BUTTON_PIN = 0; // CHANGEME
static constexpr int STATUS_REQ_INTERVAL_MS = 0; // CHANGEME
static constexpr int RPM_REQ_INTERVAL_MS = 0; // CHANGEME
Comment on lines +149 to +151
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

BUZZER_PIN, RTD_BUTTON_PIN, STATUS_REQ_INTERVAL_MS, and RPM_REQ_INTERVAL_MS are all set to 0 with CHANGEME comments. These placeholder values will cause incorrect behavior: the buzzer and button will use pin 0 (which may conflict with other hardware), and the CAN cyclic requests will be sent every 0ms (as fast as possible), potentially flooding the CAN bus. These must be configured before use.

Suggested change
static constexpr int RTD_BUTTON_PIN = 0; // CHANGEME
static constexpr int STATUS_REQ_INTERVAL_MS = 0; // CHANGEME
static constexpr int RPM_REQ_INTERVAL_MS = 0; // CHANGEME
static constexpr int RTD_BUTTON_PIN = 2;
static constexpr int STATUS_REQ_INTERVAL_MS = 100;
static constexpr int RPM_REQ_INTERVAL_MS = 100;

Copilot uses AI. Check for mistakes.

// ---------- CAN ----------
static inline FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can1;
static constexpr uint32_t BAMOCAR_TX_ID = 0x181; // inverter -> us
static constexpr uint32_t BAMOCAR_RX_ID = 0x201; // us -> inverter

static constexpr bool DEBUG_MODE = true;

bool rtdButtonPressed();

// Currently risks blocking everything and starving the thread
std::optional<bool> getFromBitfield(uint16_t bitfieldAddr, uint8_t bit);

bool *brake_active;

// Represents the state of a can.read() in a much more human-readable way
struct FullCANFrame {
uint16_t rawStatusWord;

int16_t rpmFeedback;
float dcBusVoltage;
bool warning_present;

struct ProcessedStatusWord {
bool enableBit;
bool readyBit;
bool faultActive;
} statusWord;
struct Warnings {
bool parameter_conflict;
bool special_cpu_fault;
bool rfe_input_missing;
bool aux_voltage_min;
bool feedback_signal;
bool warn_5;
bool motor_temp_limit;
bool igbt_temp_limit;
bool vout_saturation_max;
bool warn_9;
bool speed_resolution_limit;
bool check_ecode_id;
bool tripzone_glitch;
bool adc_sequencer;
bool adc_measurement;
bool bleeder_resistor_load;
} warnings;

bool error_present;
struct Errors {
bool eprom_read;
bool hw_fault;
bool rfe_input_missing;
bool can_timeout;
bool feedback_signal;
bool mains_voltage_min;
bool motor_overheat;
bool endstage_overheat;
bool mains_voltage_max;
bool critical_ac_current;
bool race_away;
bool ecode_timeout;
bool watchdog_reset;
bool ac_current_offset;
bool internal_hw_voltage;
bool bleed_resistor_overload;
} errors;
Comment on lines +169 to +217
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The FullCANFrame struct defines many fields (warnings, errors, etc.) that are never populated in the readCAN() method. Only rawStatusWord, statusWord, rpmFeedback, and dcBusVoltage are set. The unused fields should either be populated with actual data or removed to avoid confusion.

Suggested change
uint16_t rawStatusWord;
int16_t rpmFeedback;
float dcBusVoltage;
bool warning_present;
struct ProcessedStatusWord {
bool enableBit;
bool readyBit;
bool faultActive;
} statusWord;
struct Warnings {
bool parameter_conflict;
bool special_cpu_fault;
bool rfe_input_missing;
bool aux_voltage_min;
bool feedback_signal;
bool warn_5;
bool motor_temp_limit;
bool igbt_temp_limit;
bool vout_saturation_max;
bool warn_9;
bool speed_resolution_limit;
bool check_ecode_id;
bool tripzone_glitch;
bool adc_sequencer;
bool adc_measurement;
bool bleeder_resistor_load;
} warnings;
bool error_present;
struct Errors {
bool eprom_read;
bool hw_fault;
bool rfe_input_missing;
bool can_timeout;
bool feedback_signal;
bool mains_voltage_min;
bool motor_overheat;
bool endstage_overheat;
bool mains_voltage_max;
bool critical_ac_current;
bool race_away;
bool ecode_timeout;
bool watchdog_reset;
bool ac_current_offset;
bool internal_hw_voltage;
bool bleed_resistor_overload;
} errors;
uint16_t rawStatusWord{0};
int16_t rpmFeedback{0};
float dcBusVoltage{0.0F};
bool warning_present{false};
struct ProcessedStatusWord {
bool enableBit{false};
bool readyBit{false};
bool faultActive{false};
} statusWord{};
struct Warnings {
bool parameter_conflict{false};
bool special_cpu_fault{false};
bool rfe_input_missing{false};
bool aux_voltage_min{false};
bool feedback_signal{false};
bool warn_5{false};
bool motor_temp_limit{false};
bool igbt_temp_limit{false};
bool vout_saturation_max{false};
bool warn_9{false};
bool speed_resolution_limit{false};
bool check_ecode_id{false};
bool tripzone_glitch{false};
bool adc_sequencer{false};
bool adc_measurement{false};
bool bleeder_resistor_load{false};
} warnings{};
bool error_present{false};
struct Errors {
bool eprom_read{false};
bool hw_fault{false};
bool rfe_input_missing{false};
bool can_timeout{false};
bool feedback_signal{false};
bool mains_voltage_min{false};
bool motor_overheat{false};
bool endstage_overheat{false};
bool mains_voltage_max{false};
bool critical_ac_current{false};
bool race_away{false};
bool ecode_timeout{false};
bool watchdog_reset{false};
bool ac_current_offset{false};
bool internal_hw_voltage{false};
bool bleed_resistor_overload{false};
} errors{};

Copilot uses AI. Check for mistakes.
};

FullCANFrame readCAN();

FullCANFrame::ProcessedStatusWord handleStatusWord(uint16_t word);
};

#endif // INCLUDE_INCLUDE_MOTOR_H_
Loading