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
41 changes: 41 additions & 0 deletions lib/interfaces/include/CurrentSensorInterface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#ifndef CURRENTSENSOR_H
#define CURRENTSENSOR_H
#include <SharedFirmwareTypes.h>
#include <stdint.h>
#include "etl/singleton.h"
#include "MCPSPIInterface.h"

struct CurrentSensorConfig {
float acceptable_error = 0.02f; // Volts
int cs = 0; // MCP3208 chip-select pin
float voltage_divider_gain = 1.0f;
float sensor_v_per_a = 0.005f; // V per A for your current sensor (e.g., 0.005 or 5mV/A)
CHANNEL_CODES_e ch_current = CHANNEL_CODES_e::CH3; // channel to read current from
};

/**
* Minimal Current sensor wrapped around one MCP3208
*/
class CurrentSensorInterface
{
public:
static constexpr volt ERROR_CODE = -1;

void init(const CurrentSensorConfig& cfg);

// One-shot read: returns and validates bus voltage and current.
float read_current();

private:
volt _read_voltage(CHANNEL_CODES_e ch, bool isSingleEnded = true);

float _calculate_current(volt voltage);
volt _read_and_validate(CHANNEL_CODES_e ch, bool isSingleEnded = true);

private:
CurrentSensorConfig _config;
float _current;

};
using CurrentSensorInstance = etl::singleton<CurrentSensorInterface>;
#endif // CurrentSENSOR_H
67 changes: 67 additions & 0 deletions lib/interfaces/include/MCPSPIInterface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#ifndef MCPSPIINTERFACE_H
#define MCPSPIINTERFACE_H

#include <SPI.h>
#include <array>
#include <stdint.h>

enum class CHANNEL_CODES_e : uint8_t
{
CH0 = 0,
CH1 = 1,
CH2 = 2,
CH3 = 3,
CH4 = 4,
CH5 = 5,
CH6 = 6,
CH7 = 7,
};


namespace mcp_spi_interface {

// Fixed SPI settings
static constexpr uint32_t MCP_SPI_HZ = 1'000'000; // 1 MHz
static constexpr uint8_t MCP_SPI_MODE = SPI_MODE0; // MCP3208 supports Mode 0 or 1
static constexpr uint8_t MCP_START_READING = 0x01;
static constexpr uint8_t MCP_READING_SINGLE = 0x01;
static constexpr uint8_t MCP_READING_DIFF = 0x00;
static constexpr uint16_t RESOLUTION = 4095;
static constexpr float REFERENCE_VOLTAGE = 3.3;

// CS helpers (matching the LTC style)
void _write_and_delay_low (int cs, int delay_microSeconds);
void _write_and_delay_high(int cs, int delay_microSeconds);

/**
* Build the 3-byte MCP3208 command.
* @param channel 0..7 (channel code)
* @param singleEnded true = SGL/DIFF=1 (single-ended), false = 0 (differential)
* @return {byte0, byte1, byte2}
*/
byte make_cmd(uint8_t channel, bool singleEnded);

/**
* Perform one MCP3208 read using the provided command.
* Sends 3 bytes, returns assembled 12-bit raw result
*
* @param cs chip-select pin
* @param cmd 3-byte command (from make_cmd)
* @return 12-bit conversion code (0..4095)
*/
uint16_t read_channel(int cs, byte cmd);

/**
* Perform one MCP3208 read using the provided command.
* Sends 3 bytes, returns voltage
*
* @param cs chip-select pin
* @param cmd 3-byte command (from make_cmd)
* @return volt
*/
volt read_channel_voltage(int cs, byte cmd);

}

#include <MCPSPIInterface.tpp>
#endif
44 changes: 44 additions & 0 deletions lib/interfaces/include/MCPSPIInterface.tpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#pragma once
#include <Arduino.h>
#include <SPI.h>
#include "MCPSPIInterface.h"

// --- CS helpers ---
void mcp_spi_interface::_write_and_delay_low(int cs, int us) {
digitalWrite(cs, LOW);
delayMicroseconds(us);
}

void mcp_spi_interface::_write_and_delay_high(int cs, int us) {
digitalWrite(cs, HIGH);
delayMicroseconds(us);
}

byte mcp_spi_interface::make_cmd(uint8_t channel, bool singleEnded) {
const uint8_t read_type = singleEnded ? MCP_READING_SINGLE : MCP_READING_DIFF;
const byte command = ((MCP_START_READING << 7) | // start bit
(read_type << 6) | // single or differential
((channel & 0x07) << 3)); // channel number
return command;
}

// --- Read Once ---
uint16_t mcp_spi_interface::read_channel(int cs, byte cmd) {
SPI.beginTransaction(SPISettings(MCP_SPI_HZ, MSBFIRST, MCP_SPI_MODE));
_write_and_delay_low(cs, 0);
byte command, b0, b1, b2;

b0 = SPI.transfer(command);
b1 = SPI.transfer(0x00);
b2 = SPI.transfer(0x00);

_write_and_delay_high(cs, 1);
SPI.endTransaction();
uint16_t read_data = (b0 & 0x01) << 11 | (b1 & 0xFF) << 3 | (b2 & 0xE0) >> 5;
return static_cast<uint16_t>(read_data & 0x0FFF);
}

volt mcp_spi_interface::read_channel_voltage(int cs, byte cmd) {
return static_cast<float>(read_channel(cs, cmd)) * REFERENCE_VOLTAGE / RESOLUTION;

}
45 changes: 45 additions & 0 deletions lib/interfaces/src/CurrentSensorInterface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include "CurrentSensorInterface.h"
#include "MCPSPIInterface.h"
#include <Arduino.h>
#include <SPI.h>
#include "SharedFirmwareTypes.h"

using mcp_spi_interface::make_cmd;
using mcp_spi_interface::read_channel_voltage;

// Initialize the ADC chip-select pin.
void CurrentSensorInterface::init(const CurrentSensorConfig& cfg){
_config = cfg;
pinMode(_config.cs, OUTPUT);
digitalWrite(_config.cs, HIGH);
}

//Reads single values not differential
volt CurrentSensorInterface::_read_voltage(CHANNEL_CODES_e ch, bool isSingleEnded) {
auto cmd = make_cmd(static_cast<uint8_t>(ch), isSingleEnded);
return read_channel_voltage(_config.cs, cmd);
}

//Checks if the data from the channel is correct
volt CurrentSensorInterface::_read_and_validate(CHANNEL_CODES_e ch, bool isSingleEnded){
volt first_read = _read_voltage(ch, isSingleEnded);
volt second_read = _read_voltage(ch, isSingleEnded);
if (abs(first_read - second_read) <= _config.acceptable_error){
return second_read;
}
return ERROR_CODE;
}

float CurrentSensorInterface::_calculate_current(volt voltage){
return (voltage / _config.voltage_divider_gain) / _config.sensor_v_per_a;
}


float CurrentSensorInterface::read_current() {
const volt voltage_current = _read_and_validate(_config.ch_current);
const bool has_valid_current_data = (voltage_current != ERROR_CODE);
if (has_valid_current_data){
_current = _calculate_current(voltage_current);
}
return _current;
}
3 changes: 2 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include "BMSDriverGroup.h"
#include "WatchdogInterface.h"
#include "ACUCANInterfaceImpl.h"

#include "CurrentSensorInterface.h"
/* System Includes */
#include "ACUController.h"
#include "ACUStateMachine.h"
Expand All @@ -18,6 +18,7 @@
#include "ht_task.hpp"



/* Scheduler setup */
HT_SCHED::Scheduler& scheduler = HT_SCHED::Scheduler::getInstance();

Expand Down