From 8a4957505d8093133860c1be1c1c31bf463dace4 Mon Sep 17 00:00:00 2001 From: Advait Vedant Date: Mon, 29 Sep 2025 02:21:59 -0400 Subject: [PATCH 01/12] created interface to read from mcp3028 via spi --- lib/interfaces/include/MCPSPIInterface.h | 46 ++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 lib/interfaces/include/MCPSPIInterface.h diff --git a/lib/interfaces/include/MCPSPIInterface.h b/lib/interfaces/include/MCPSPIInterface.h new file mode 100644 index 00000000..2225ed7f --- /dev/null +++ b/lib/interfaces/include/MCPSPIInterface.h @@ -0,0 +1,46 @@ +#ifndef MCPSPIINTERFACE_H +#define MCPSPIINTERFACE_H + +/* Interface Includes (keep it light and LTC-like) */ +#include +#include +#include + +namespace mcp_spi_interface { + + // Fixed SPI settings (simple & consistent) + 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_SINGLE = 0x03; + static constexpr uint8_t MCP_START_READING_DIFF = 0x02; + + // 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. + * Packing (MSB-first on the wire): + * byte0: 0000 001S (Start=1 at bit1; SGL=1 or 0 at bit0) + * byte1: D2 D1 D0 0 0000 (bit4 = Null=0; lower 4 bits 0) + * byte2: 0000 0000 (dummy clocks) + * + * @param channel 0..7 (channel for single-ended, pair code for differential) + * @param singleEnded true = SGL/DIFF=1 (single-ended), false = 0 (differential) + * @return {byte0, byte1, byte2} + */ + std::array make_cmd(uint8_t channel, bool singleEnded); + + /** + * Perform one MCP3208 read using the provided command. + * Sends 3 bytes, returns assembled 12-bit raw result (0..4095). + * + * @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, const std::array& cmd); + +} // namespace mcp_spi_interface + +#endif // MCPSPIINTERFACE_H From f63b84dce899cda1bbaa1606b04a8c7371f856cf Mon Sep 17 00:00:00 2001 From: Advait Vedant Date: Mon, 29 Sep 2025 02:25:53 -0400 Subject: [PATCH 02/12] implemented interface to read from channels on mcp via spi --- lib/interfaces/include/MCPSPIInterface.h | 5 +-- lib/interfaces/include/MCPSPIInterface.tpp | 40 ++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 lib/interfaces/include/MCPSPIInterface.tpp diff --git a/lib/interfaces/include/MCPSPIInterface.h b/lib/interfaces/include/MCPSPIInterface.h index 2225ed7f..a91e09fc 100644 --- a/lib/interfaces/include/MCPSPIInterface.h +++ b/lib/interfaces/include/MCPSPIInterface.h @@ -41,6 +41,7 @@ namespace mcp_spi_interface { */ uint16_t read_channel(int cs, const std::array& cmd); -} // namespace mcp_spi_interface +} -#endif // MCPSPIINTERFACE_H +#include +#endif diff --git a/lib/interfaces/include/MCPSPIInterface.tpp b/lib/interfaces/include/MCPSPIInterface.tpp new file mode 100644 index 00000000..ea047f85 --- /dev/null +++ b/lib/interfaces/include/MCPSPIInterface.tpp @@ -0,0 +1,40 @@ +#pragma once +#include +#include +#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); +} + +// --- Command builder --- +std::array +mcp_spi_interface::make_cmd(uint8_t channel, bool singleEnded) { + const uint8_t b0 = singleEnded ? MCP_START_READING_SINGLE : MCP_START_READING_DIFF; + const uint8_t b1 = static_cast((channel & 0x07) << 5); + const uint8_t b2 = 0x00; //placeholder + return { b0, b1, b2 }; +} + +// --- Read Once --- +uint16_t mcp_spi_interface::read_channel(int cs, const std::array& cmd) { + SPI.beginTransaction(SPISettings(MCP_SPI_HZ, MSBFIRST, MCP_SPI_MODE)); + _write_and_delay_low(cs, 1); + + + SPI.transfer(cmd[0]); // starts read + const uint8_t hi = SPI.transfer(cmd[1]); // sets which channel and reads high byte + const uint8_t lo = SPI.transfer(cmd[2]); // reads low byte + + _write_and_delay_high(cs, 1); + SPI.endTransaction(); + + return static_cast(((hi & 0x0F) << 8) | lo); //combines hi and low bits +} From 399a4212ab01218ad2c4f53cdb3594f131926a52 Mon Sep 17 00:00:00 2001 From: Advait Vedant Date: Mon, 29 Sep 2025 03:24:35 -0400 Subject: [PATCH 03/12] created and implemented interface PowerSensor to get current, voltage and power. --- lib/interfaces/include/PowerSensor.h | 81 ++++++++++++++++++++++++++++ lib/interfaces/src/PowerSensor.cpp | 41 ++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 lib/interfaces/include/PowerSensor.h create mode 100644 lib/interfaces/src/PowerSensor.cpp diff --git a/lib/interfaces/include/PowerSensor.h b/lib/interfaces/include/PowerSensor.h new file mode 100644 index 00000000..11f2910c --- /dev/null +++ b/lib/interfaces/include/PowerSensor.h @@ -0,0 +1,81 @@ +#include +#ifndef POWERSENSOR_H +#define POWERSENSOR_H + +#include + +// Channel codes (D2 D1 D0). We'll use the single-ended CH codes for this sensor. +enum class CHANNEL_CODES_e : uint8_t +{ + CH0 = 0, + CH1 = 1, + CH2 = 2, + CH3 = 3, + CH4 = 4, + CH5 = 5, + CH6 = 6, + CH7 = 7, + + // (Differential codes listed here for completeness but not used in this class) + CH0p_CH1n = 0, + CH1p_CH0n = 1, + CH2p_CH3n = 2, + CH3p_CH2n = 3, + CH4p_CH5n = 4, + CH5p_CH4n = 5, + CH6p_CH7n = 6, + CH7p_CH6n = 7 +}; + +struct ValidData_s +{ + bool valid_read_current_channel = true; + bool valid_read_voltage_channel = true; + bool all_invalid_reads = false; // true only if both reads failed (we don’t expect that with MCP3208) +}; + +struct PowerSensorData +{ + float current; // Amps + volt voltage; // Volts (pack/bus) + float power; // Power +}; + +// Constants you actually need (set once at construction) +struct Config { + int cs; // MCP3208 chip-select pin + float vref; // ADC reference (V) – MCP3208 VREF pin + float resolution; + float voltage_divider_gain; + float sensor_v_per_a; // V per A for your current sensor (e.g., 0.006667f) + CHANNEL_CODES_e ch_voltage = CHANNEL_CODES_e::CH0; // divider node channel + CHANNEL_CODES_e ch_current_out = CHANNEL_CODES_e::CH1p_CH0n; // sensor OUT channel + CHANNEL_CODES_e ch_current_ref = CHANNEL_CODES_e::CH2; // sensor REF channel +}; + +/** + * Minimal power sensor wrapped around one MCP3208 (U20 in your schematic). + * - Voltage from a divider on CH (default CH0) + * - Current from sensor OUT and REF on CH1 and CH2 (OUT - REF) / (V_per_A) + */ +class PowerSensor +{ +public: + + PowerSensor() = delete; + PowerSensor(const Config& cfg) : _cfg(cfg) {}; + + void init(); + // One-shot read: returns bus voltage and current. + PowerSensorData read_data(); + +private: + uint16_t _read_raw(CHANNEL_CODES_e ch); + +private: + Config _cfg; + PowerSensorData _data; +}; + +#include "PowerSensor.cpp" +#endif // POWERSENSOR_H diff --git a/lib/interfaces/src/PowerSensor.cpp b/lib/interfaces/src/PowerSensor.cpp new file mode 100644 index 00000000..3c8971f0 --- /dev/null +++ b/lib/interfaces/src/PowerSensor.cpp @@ -0,0 +1,41 @@ +#include "PowerSensor.h" +#include "MCPSPIInterface.h" +#include +#include + +using mcp_spi_interface::make_cmd; +using mcp_spi_interface::read_channel; + +// Initialize the ADC chip-select pin. +void PowerSensor::init() { + pinMode(_cfg.cs, OUTPUT); + digitalWrite(_cfg.cs, HIGH); +} + +//Reads single values not differential +uint16_t PowerSensor::_read_raw(CHANNEL_CODES_e ch) { + auto cmd = make_cmd(static_cast(ch), true); + return read_channel(_cfg.cs, cmd); +} + +// Public: perform one measurement and return bus voltage and current +PowerSensorData PowerSensor::read_data() { + // 1) Raw ADC reads from configured channels + const uint16_t raw_v = _read_raw(_cfg.ch_voltage); // divider node (e.g., CH0) + const uint16_t raw_out = _read_raw(_cfg.ch_current_out); // sensor OUT (e.g., CH1) + const uint16_t raw_ref = _read_raw(_cfg.ch_current_ref); // sensor REF (e.g., CH2) + + // 2) Convert ADC codes -> volts at the ADC pins + const float v_node = (static_cast(raw_v) / _cfg.resolution) * _cfg.vref; + const float v_out = (static_cast(raw_out) / _cfg.resolution) * _cfg.vref; + const float v_ref = (static_cast(raw_ref) / _cfg.resolution) * _cfg.vref; + + // 3) Rebuild pack/bus voltage from divider: Vbus = Vnode * (Ra + Rb) / Rb + const float v_bus = v_node * _cfg.voltage_divider_gain; + + // 4) Current from sensor differential: I = (Vout - Vref) / (V_per_A) + float current = (v_out - v_ref) / _cfg.sensor_v_per_a; + + _data = PowerSensorData{ current, v_bus, current*v_bus }; + return _data; +} From ecac6976246e5f009e35023b137eb9ef7bf46fbd Mon Sep 17 00:00:00 2001 From: Advait Vedant Date: Mon, 29 Sep 2025 03:37:32 -0400 Subject: [PATCH 04/12] setup error checking system --- lib/interfaces/include/PowerSensor.h | 3 +++ lib/interfaces/src/PowerSensor.cpp | 34 +++++++++++++++++----------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/lib/interfaces/include/PowerSensor.h b/lib/interfaces/include/PowerSensor.h index 11f2910c..8abb3d6d 100644 --- a/lib/interfaces/include/PowerSensor.h +++ b/lib/interfaces/include/PowerSensor.h @@ -43,6 +43,7 @@ struct PowerSensorData // Constants you actually need (set once at construction) struct Config { + float acceptable_error; int cs; // MCP3208 chip-select pin float vref; // ADC reference (V) – MCP3208 VREF pin float resolution; @@ -72,6 +73,8 @@ class PowerSensor private: uint16_t _read_raw(CHANNEL_CODES_e ch); + bool _is_valid(CHANNEL_CODES_e ch, uint16_t raw); + private: Config _cfg; PowerSensorData _data; diff --git a/lib/interfaces/src/PowerSensor.cpp b/lib/interfaces/src/PowerSensor.cpp index 3c8971f0..195cd724 100644 --- a/lib/interfaces/src/PowerSensor.cpp +++ b/lib/interfaces/src/PowerSensor.cpp @@ -18,24 +18,32 @@ uint16_t PowerSensor::_read_raw(CHANNEL_CODES_e ch) { return read_channel(_cfg.cs, cmd); } +bool PowerSensor::_is_valid(CHANNEL_CODES_e ch, uint16_t raw){ + if (abs(_read_raw(ch) - raw) <= _cfg.acceptable_error){ + return true; + } + return false; +} // Public: perform one measurement and return bus voltage and current PowerSensorData PowerSensor::read_data() { // 1) Raw ADC reads from configured channels const uint16_t raw_v = _read_raw(_cfg.ch_voltage); // divider node (e.g., CH0) + bool raw_v_is_valid = _is_valid(_cfg.ch_voltage, raw_v); const uint16_t raw_out = _read_raw(_cfg.ch_current_out); // sensor OUT (e.g., CH1) + bool raw_out_is_valid = _is_valid(_cfg.ch_current_out, raw_out); const uint16_t raw_ref = _read_raw(_cfg.ch_current_ref); // sensor REF (e.g., CH2) - - // 2) Convert ADC codes -> volts at the ADC pins - const float v_node = (static_cast(raw_v) / _cfg.resolution) * _cfg.vref; - const float v_out = (static_cast(raw_out) / _cfg.resolution) * _cfg.vref; - const float v_ref = (static_cast(raw_ref) / _cfg.resolution) * _cfg.vref; - - // 3) Rebuild pack/bus voltage from divider: Vbus = Vnode * (Ra + Rb) / Rb - const float v_bus = v_node * _cfg.voltage_divider_gain; - - // 4) Current from sensor differential: I = (Vout - Vref) / (V_per_A) - float current = (v_out - v_ref) / _cfg.sensor_v_per_a; - - _data = PowerSensorData{ current, v_bus, current*v_bus }; + bool raw_ref_is_valid = _is_valid(_cfg.ch_current_ref, raw_ref); + if (raw_out_is_valid && raw_ref_is_valid){ + const float v_out = (static_cast(raw_out) / _cfg.resolution) * _cfg.vref; + const float v_ref = (static_cast(raw_ref) / _cfg.resolution) * _cfg.vref; + _data.current = (v_out - v_ref) / _cfg.sensor_v_per_a; + } + if (raw_v_is_valid){ + const float v_node = (static_cast(raw_v) / _cfg.resolution) * _cfg.vref; + _data.voltage = v_node * _cfg.voltage_divider_gain; + } + if (raw_ref_is_valid && raw_out_is_valid && raw_v_is_valid){ + _data.power = _data.current * _data.voltage; + } return _data; } From b1eb7f4b4b58653f5e67d77c0288512fc826496a Mon Sep 17 00:00:00 2001 From: Advait Vedant Date: Mon, 29 Sep 2025 03:43:02 -0400 Subject: [PATCH 05/12] fixed issue error in powersensor.h preventing code from compiling --- lib/interfaces/include/PowerSensor.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/interfaces/include/PowerSensor.h b/lib/interfaces/include/PowerSensor.h index 8abb3d6d..f6b83368 100644 --- a/lib/interfaces/include/PowerSensor.h +++ b/lib/interfaces/include/PowerSensor.h @@ -1,9 +1,7 @@ -#include #ifndef POWERSENSOR_H #define POWERSENSOR_H - +#include #include - // Channel codes (D2 D1 D0). We'll use the single-ended CH codes for this sensor. enum class CHANNEL_CODES_e : uint8_t { @@ -80,5 +78,4 @@ class PowerSensor PowerSensorData _data; }; -#include "PowerSensor.cpp" #endif // POWERSENSOR_H From 7fba6cb4202e55eb04ce888898d4968ec89c65b7 Mon Sep 17 00:00:00 2001 From: Advait Vedant Date: Mon, 29 Sep 2025 03:46:42 -0400 Subject: [PATCH 06/12] cleaned up the powersensor.h --- lib/interfaces/include/PowerSensor.h | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/lib/interfaces/include/PowerSensor.h b/lib/interfaces/include/PowerSensor.h index f6b83368..86c3e613 100644 --- a/lib/interfaces/include/PowerSensor.h +++ b/lib/interfaces/include/PowerSensor.h @@ -2,7 +2,7 @@ #define POWERSENSOR_H #include #include -// Channel codes (D2 D1 D0). We'll use the single-ended CH codes for this sensor. + enum class CHANNEL_CODES_e : uint8_t { CH0 = 0, @@ -13,25 +13,7 @@ enum class CHANNEL_CODES_e : uint8_t CH5 = 5, CH6 = 6, CH7 = 7, - - // (Differential codes listed here for completeness but not used in this class) - CH0p_CH1n = 0, - CH1p_CH0n = 1, - CH2p_CH3n = 2, - CH3p_CH2n = 3, - CH4p_CH5n = 4, - CH5p_CH4n = 5, - CH6p_CH7n = 6, - CH7p_CH6n = 7 -}; - -struct ValidData_s -{ - bool valid_read_current_channel = true; - bool valid_read_voltage_channel = true; - bool all_invalid_reads = false; // true only if both reads failed (we don’t expect that with MCP3208) }; - struct PowerSensorData { float current; // Amps @@ -48,7 +30,7 @@ struct Config { float voltage_divider_gain; float sensor_v_per_a; // V per A for your current sensor (e.g., 0.006667f) CHANNEL_CODES_e ch_voltage = CHANNEL_CODES_e::CH0; // divider node channel - CHANNEL_CODES_e ch_current_out = CHANNEL_CODES_e::CH1p_CH0n; // sensor OUT channel + CHANNEL_CODES_e ch_current_out = CHANNEL_CODES_e::CH1; // sensor OUT channel CHANNEL_CODES_e ch_current_ref = CHANNEL_CODES_e::CH2; // sensor REF channel }; From 5c85b916fed15890dff540996e674e63036f2819 Mon Sep 17 00:00:00 2001 From: Advait Vedant Date: Mon, 29 Sep 2025 03:53:32 -0400 Subject: [PATCH 07/12] made acceptable error a integer to avoid bugs --- lib/interfaces/include/PowerSensor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interfaces/include/PowerSensor.h b/lib/interfaces/include/PowerSensor.h index 86c3e613..a441d6f7 100644 --- a/lib/interfaces/include/PowerSensor.h +++ b/lib/interfaces/include/PowerSensor.h @@ -23,7 +23,7 @@ struct PowerSensorData // Constants you actually need (set once at construction) struct Config { - float acceptable_error; + int acceptable_error; int cs; // MCP3208 chip-select pin float vref; // ADC reference (V) – MCP3208 VREF pin float resolution; From 196f04147506a4006025f09cd470cacc61bc5eeb Mon Sep 17 00:00:00 2001 From: Advait Vedant Date: Tue, 30 Sep 2025 19:28:55 -0400 Subject: [PATCH 08/12] added read channel voltage function to the MCP3208 interface and reorganized PowerSensor interface to improve readabliity --- lib/interfaces/include/MCPSPIInterface.h | 25 +++++---- lib/interfaces/include/MCPSPIInterface.tpp | 6 ++- lib/interfaces/include/PowerSensor.h | 38 +++++++------ lib/interfaces/src/PowerSensor.cpp | 63 +++++++++++++--------- 4 files changed, 81 insertions(+), 51 deletions(-) diff --git a/lib/interfaces/include/MCPSPIInterface.h b/lib/interfaces/include/MCPSPIInterface.h index a91e09fc..eac66e83 100644 --- a/lib/interfaces/include/MCPSPIInterface.h +++ b/lib/interfaces/include/MCPSPIInterface.h @@ -1,18 +1,20 @@ #ifndef MCPSPIINTERFACE_H #define MCPSPIINTERFACE_H -/* Interface Includes (keep it light and LTC-like) */ #include #include #include + namespace mcp_spi_interface { - // Fixed SPI settings (simple & consistent) + // 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_SINGLE = 0x03; static constexpr uint8_t MCP_START_READING_DIFF = 0x02; + 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); @@ -20,12 +22,7 @@ namespace mcp_spi_interface { /** * Build the 3-byte MCP3208 command. - * Packing (MSB-first on the wire): - * byte0: 0000 001S (Start=1 at bit1; SGL=1 or 0 at bit0) - * byte1: D2 D1 D0 0 0000 (bit4 = Null=0; lower 4 bits 0) - * byte2: 0000 0000 (dummy clocks) - * - * @param channel 0..7 (channel for single-ended, pair code for differential) + * @param channel 0..7 (channel code) * @param singleEnded true = SGL/DIFF=1 (single-ended), false = 0 (differential) * @return {byte0, byte1, byte2} */ @@ -33,7 +30,7 @@ namespace mcp_spi_interface { /** * Perform one MCP3208 read using the provided command. - * Sends 3 bytes, returns assembled 12-bit raw result (0..4095). + * Sends 3 bytes, returns assembled 12-bit raw result * * @param cs chip-select pin * @param cmd 3-byte command (from make_cmd) @@ -41,6 +38,16 @@ namespace mcp_spi_interface { */ uint16_t read_channel(int cs, const std::array& 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, const std::array& cmd); + } #include diff --git a/lib/interfaces/include/MCPSPIInterface.tpp b/lib/interfaces/include/MCPSPIInterface.tpp index ea047f85..bfa8931a 100644 --- a/lib/interfaces/include/MCPSPIInterface.tpp +++ b/lib/interfaces/include/MCPSPIInterface.tpp @@ -14,7 +14,6 @@ void mcp_spi_interface::_write_and_delay_high(int cs, int us) { delayMicroseconds(us); } -// --- Command builder --- std::array mcp_spi_interface::make_cmd(uint8_t channel, bool singleEnded) { const uint8_t b0 = singleEnded ? MCP_START_READING_SINGLE : MCP_START_READING_DIFF; @@ -38,3 +37,8 @@ uint16_t mcp_spi_interface::read_channel(int cs, const std::array& cm return static_cast(((hi & 0x0F) << 8) | lo); //combines hi and low bits } + +volt mcp_spi_interface::read_channel_voltage(int cs, const std::array& cmd) { + return static_cast(read_channel(cs, cmd)) * REFERENCE_VOLTAGE / RESOLUTION; + +} diff --git a/lib/interfaces/include/PowerSensor.h b/lib/interfaces/include/PowerSensor.h index a441d6f7..a52cd6ab 100644 --- a/lib/interfaces/include/PowerSensor.h +++ b/lib/interfaces/include/PowerSensor.h @@ -3,6 +3,15 @@ #include #include + +struct PowerSensorData +{ + float current; // Amps + volt voltage; // Volts (pack/bus) + float power; // Power +}; + +// The codes for each channel on the MCP3028 (D2 D1 D0) enum class CHANNEL_CODES_e : uint8_t { CH0 = 0, @@ -14,50 +23,45 @@ enum class CHANNEL_CODES_e : uint8_t CH6 = 6, CH7 = 7, }; -struct PowerSensorData -{ - float current; // Amps - volt voltage; // Volts (pack/bus) - float power; // Power -}; -// Constants you actually need (set once at construction) struct Config { int acceptable_error; int cs; // MCP3208 chip-select pin - float vref; // ADC reference (V) – MCP3208 VREF pin - float resolution; float voltage_divider_gain; - float sensor_v_per_a; // V per A for your current sensor (e.g., 0.006667f) + float sensor_v_per_a = 0.005; // V per A for your current sensor (e.g., 0.005 or 5mV/A) CHANNEL_CODES_e ch_voltage = CHANNEL_CODES_e::CH0; // divider node channel CHANNEL_CODES_e ch_current_out = CHANNEL_CODES_e::CH1; // sensor OUT channel CHANNEL_CODES_e ch_current_ref = CHANNEL_CODES_e::CH2; // sensor REF channel }; /** - * Minimal power sensor wrapped around one MCP3208 (U20 in your schematic). - * - Voltage from a divider on CH (default CH0) - * - Current from sensor OUT and REF on CH1 and CH2 (OUT - REF) / (V_per_A) + * Minimal power sensor wrapped around one MCP3208 */ class PowerSensor { public: + static constexpr volt ERROR_CODE = -1; PowerSensor() = delete; PowerSensor(const Config& cfg) : _cfg(cfg) {}; void init(); - // One-shot read: returns bus voltage and current. + + // One-shot read: returns and validates bus voltage and current. PowerSensorData read_data(); private: - uint16_t _read_raw(CHANNEL_CODES_e ch); - - bool _is_valid(CHANNEL_CODES_e ch, uint16_t raw); + volt _read_voltage(CHANNEL_CODES_e ch); + + float _calculate_current(uint16_t v_out, uint16_t v_ref); + volt _calculate_voltage(uint16_t v_raw); + float _calculate_power(); + volt _read_and_validate(CHANNEL_CODES_e ch); private: Config _cfg; PowerSensorData _data; + }; #endif // POWERSENSOR_H diff --git a/lib/interfaces/src/PowerSensor.cpp b/lib/interfaces/src/PowerSensor.cpp index 195cd724..cf9e520e 100644 --- a/lib/interfaces/src/PowerSensor.cpp +++ b/lib/interfaces/src/PowerSensor.cpp @@ -2,9 +2,10 @@ #include "MCPSPIInterface.h" #include #include +#include "SharedFirmwareTypes.h" using mcp_spi_interface::make_cmd; -using mcp_spi_interface::read_channel; +using mcp_spi_interface::read_channel_voltage; // Initialize the ADC chip-select pin. void PowerSensor::init() { @@ -13,37 +14,51 @@ void PowerSensor::init() { } //Reads single values not differential -uint16_t PowerSensor::_read_raw(CHANNEL_CODES_e ch) { - auto cmd = make_cmd(static_cast(ch), true); - return read_channel(_cfg.cs, cmd); +volt PowerSensor::_read_voltage(CHANNEL_CODES_e ch) { + auto cmd = make_cmd(static_cast(ch), true); + return read_channel_voltage(_cfg.cs, cmd); } -bool PowerSensor::_is_valid(CHANNEL_CODES_e ch, uint16_t raw){ - if (abs(_read_raw(ch) - raw) <= _cfg.acceptable_error){ - return true; +//Checks if the data from the channel is correct +volt PowerSensor::_read_and_validate(CHANNEL_CODES_e ch){ + uint16_t first_read = _read_voltage(ch); + uint16_t second_read = _read_voltage(ch); + if (abs(first_read - second_read) <= _cfg.acceptable_error){ + return second_read; } - return false; + return ERROR_CODE; } + +float PowerSensor::_calculate_current(uint16_t v_out, uint16_t v_ref){ + return (v_out - v_ref) / _cfg.sensor_v_per_a; +} + +volt PowerSensor::_calculate_voltage(uint16_t voltage){ + return voltage*_cfg.voltage_divider_gain; +} + +float PowerSensor::_calculate_power(){ + return _data.current * _data.power; +} + + // Public: perform one measurement and return bus voltage and current PowerSensorData PowerSensor::read_data() { - // 1) Raw ADC reads from configured channels - const uint16_t raw_v = _read_raw(_cfg.ch_voltage); // divider node (e.g., CH0) - bool raw_v_is_valid = _is_valid(_cfg.ch_voltage, raw_v); - const uint16_t raw_out = _read_raw(_cfg.ch_current_out); // sensor OUT (e.g., CH1) - bool raw_out_is_valid = _is_valid(_cfg.ch_current_out, raw_out); - const uint16_t raw_ref = _read_raw(_cfg.ch_current_ref); // sensor REF (e.g., CH2) - bool raw_ref_is_valid = _is_valid(_cfg.ch_current_ref, raw_ref); - if (raw_out_is_valid && raw_ref_is_valid){ - const float v_out = (static_cast(raw_out) / _cfg.resolution) * _cfg.vref; - const float v_ref = (static_cast(raw_ref) / _cfg.resolution) * _cfg.vref; - _data.current = (v_out - v_ref) / _cfg.sensor_v_per_a; + + const uint16_t voltage_ts = _read_and_validate(_cfg.ch_voltage); // divider node (e.g., CH0) + const uint16_t voltage_out = _read_and_validate(_cfg.ch_current_out); // sensor OUT (e.g., CH1) + const uint16_t voltage_ref = _read_and_validate(_cfg.ch_current_ref); // sensor REF (e.g., CH2) + + const bool has_valid_current_data = (voltage_ref != ERROR_CODE && voltage_out != ERROR_CODE); + const bool has_valid_voltage_data = (voltage_ts != ERROR_CODE); + if (has_valid_current_data){ + _data.current = _calculate_current(voltage_out, voltage_ref); } - if (raw_v_is_valid){ - const float v_node = (static_cast(raw_v) / _cfg.resolution) * _cfg.vref; - _data.voltage = v_node * _cfg.voltage_divider_gain; + if (has_valid_voltage_data){ + _data.voltage = _calculate_voltage(voltage_ts); } - if (raw_ref_is_valid && raw_out_is_valid && raw_v_is_valid){ - _data.power = _data.current * _data.voltage; + if (has_valid_current_data && has_valid_voltage_data){ + _data.power = _calculate_power(); } return _data; } From 2b0eccaf50cc0aeda1d7bc1af7f2c0666cdf30ca Mon Sep 17 00:00:00 2001 From: Advait Vedant Date: Thu, 9 Oct 2025 18:56:11 -0700 Subject: [PATCH 09/12] moved current to a single channel --- lib/interfaces/include/PowerSensor.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/interfaces/include/PowerSensor.h b/lib/interfaces/include/PowerSensor.h index a52cd6ab..397d50d3 100644 --- a/lib/interfaces/include/PowerSensor.h +++ b/lib/interfaces/include/PowerSensor.h @@ -30,8 +30,9 @@ struct Config { float voltage_divider_gain; float sensor_v_per_a = 0.005; // V per A for your current sensor (e.g., 0.005 or 5mV/A) CHANNEL_CODES_e ch_voltage = CHANNEL_CODES_e::CH0; // divider node channel - CHANNEL_CODES_e ch_current_out = CHANNEL_CODES_e::CH1; // sensor OUT channel - CHANNEL_CODES_e ch_current_ref = CHANNEL_CODES_e::CH2; // sensor REF channel + // CHANNEL_CODES_e ch_current_out = CHANNEL_CODES_e::CH1; // sensor OUT channel + // CHANNEL_CODES_e ch_current_ref = CHANNEL_CODES_e::CH2; // sensor REF channel + CHANNEL_CODES_e ch_current = CHANNEL_CODES_e::CH3; // channel to read current from }; /** @@ -51,12 +52,12 @@ class PowerSensor PowerSensorData read_data(); private: - volt _read_voltage(CHANNEL_CODES_e ch); + volt _read_voltage(CHANNEL_CODES_e ch, bool isSingleEnded = true); - float _calculate_current(uint16_t v_out, uint16_t v_ref); + float _calculate_current(uint16_t voltage); volt _calculate_voltage(uint16_t v_raw); float _calculate_power(); - volt _read_and_validate(CHANNEL_CODES_e ch); + volt _read_and_validate(CHANNEL_CODES_e ch, bool isSingleEnded = true); private: Config _cfg; From 002ce18d095641b3e5f0c4d8c4d6c8d450aadd69 Mon Sep 17 00:00:00 2001 From: Advait Vedant Date: Thu, 9 Oct 2025 18:56:55 -0700 Subject: [PATCH 10/12] updated powersensor implementation to use a single current channel for calculations --- lib/interfaces/src/PowerSensor.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/interfaces/src/PowerSensor.cpp b/lib/interfaces/src/PowerSensor.cpp index cf9e520e..abf1705f 100644 --- a/lib/interfaces/src/PowerSensor.cpp +++ b/lib/interfaces/src/PowerSensor.cpp @@ -14,23 +14,23 @@ void PowerSensor::init() { } //Reads single values not differential -volt PowerSensor::_read_voltage(CHANNEL_CODES_e ch) { - auto cmd = make_cmd(static_cast(ch), true); +volt PowerSensor::_read_voltage(CHANNEL_CODES_e ch, bool isSingleEnded) { + auto cmd = make_cmd(static_cast(ch), isSingleEnded); return read_channel_voltage(_cfg.cs, cmd); } //Checks if the data from the channel is correct -volt PowerSensor::_read_and_validate(CHANNEL_CODES_e ch){ - uint16_t first_read = _read_voltage(ch); - uint16_t second_read = _read_voltage(ch); +volt PowerSensor::_read_and_validate(CHANNEL_CODES_e ch, bool isSingleEnded){ + uint16_t first_read = _read_voltage(ch, isSingleEnded); + uint16_t second_read = _read_voltage(ch, isSingleEnded); if (abs(first_read - second_read) <= _cfg.acceptable_error){ return second_read; } return ERROR_CODE; } -float PowerSensor::_calculate_current(uint16_t v_out, uint16_t v_ref){ - return (v_out - v_ref) / _cfg.sensor_v_per_a; +float PowerSensor::_calculate_current(uint16_t voltage){ + return (voltage) / _cfg.sensor_v_per_a; } volt PowerSensor::_calculate_voltage(uint16_t voltage){ @@ -46,13 +46,14 @@ float PowerSensor::_calculate_power(){ PowerSensorData PowerSensor::read_data() { const uint16_t voltage_ts = _read_and_validate(_cfg.ch_voltage); // divider node (e.g., CH0) - const uint16_t voltage_out = _read_and_validate(_cfg.ch_current_out); // sensor OUT (e.g., CH1) - const uint16_t voltage_ref = _read_and_validate(_cfg.ch_current_ref); // sensor REF (e.g., CH2) + // const uint16_t voltage_out = _read_and_validate(_cfg.ch_current_out); // sensor OUT (e.g., CH1) + // const uint16_t voltage_ref = _read_and_validate(_cfg.ch_current_ref); // sensor REF (e.g., CH2) - const bool has_valid_current_data = (voltage_ref != ERROR_CODE && voltage_out != ERROR_CODE); + const uint16_t voltage_current = _read_and_validate(_cfg.ch_current); // divider node (e.g., CH0) + const bool has_valid_current_data = (voltage_current != ERROR_CODE); const bool has_valid_voltage_data = (voltage_ts != ERROR_CODE); if (has_valid_current_data){ - _data.current = _calculate_current(voltage_out, voltage_ref); + _data.current = _calculate_current(voltage_current); } if (has_valid_voltage_data){ _data.voltage = _calculate_voltage(voltage_ts); From 60513114f4ee847b5737d73e6c1dd76ab2efca4a Mon Sep 17 00:00:00 2001 From: Advait Vedant Date: Thu, 9 Oct 2025 19:06:47 -0700 Subject: [PATCH 11/12] fixed variable conversion issues --- lib/interfaces/include/PowerSensor.h | 6 +++--- lib/interfaces/src/PowerSensor.cpp | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/interfaces/include/PowerSensor.h b/lib/interfaces/include/PowerSensor.h index 397d50d3..ca3e000b 100644 --- a/lib/interfaces/include/PowerSensor.h +++ b/lib/interfaces/include/PowerSensor.h @@ -25,7 +25,7 @@ enum class CHANNEL_CODES_e : uint8_t }; struct Config { - int acceptable_error; + float acceptable_error; int cs; // MCP3208 chip-select pin float voltage_divider_gain; float sensor_v_per_a = 0.005; // V per A for your current sensor (e.g., 0.005 or 5mV/A) @@ -54,8 +54,8 @@ class PowerSensor private: volt _read_voltage(CHANNEL_CODES_e ch, bool isSingleEnded = true); - float _calculate_current(uint16_t voltage); - volt _calculate_voltage(uint16_t v_raw); + float _calculate_current(volt voltage); + volt _calculate_voltage(volt v_raw); float _calculate_power(); volt _read_and_validate(CHANNEL_CODES_e ch, bool isSingleEnded = true); diff --git a/lib/interfaces/src/PowerSensor.cpp b/lib/interfaces/src/PowerSensor.cpp index abf1705f..63ac399d 100644 --- a/lib/interfaces/src/PowerSensor.cpp +++ b/lib/interfaces/src/PowerSensor.cpp @@ -21,20 +21,20 @@ volt PowerSensor::_read_voltage(CHANNEL_CODES_e ch, bool isSingleEnded) { //Checks if the data from the channel is correct volt PowerSensor::_read_and_validate(CHANNEL_CODES_e ch, bool isSingleEnded){ - uint16_t first_read = _read_voltage(ch, isSingleEnded); - uint16_t second_read = _read_voltage(ch, isSingleEnded); + volt first_read = _read_voltage(ch, isSingleEnded); + volt second_read = _read_voltage(ch, isSingleEnded); if (abs(first_read - second_read) <= _cfg.acceptable_error){ return second_read; } return ERROR_CODE; } -float PowerSensor::_calculate_current(uint16_t voltage){ - return (voltage) / _cfg.sensor_v_per_a; +float PowerSensor::_calculate_current(volt voltage){ + return voltage / _cfg.sensor_v_per_a; } -volt PowerSensor::_calculate_voltage(uint16_t voltage){ - return voltage*_cfg.voltage_divider_gain; +volt PowerSensor::_calculate_voltage(volt voltage){ + return voltage * _cfg.voltage_divider_gain; } float PowerSensor::_calculate_power(){ @@ -45,11 +45,11 @@ float PowerSensor::_calculate_power(){ // Public: perform one measurement and return bus voltage and current PowerSensorData PowerSensor::read_data() { - const uint16_t voltage_ts = _read_and_validate(_cfg.ch_voltage); // divider node (e.g., CH0) - // const uint16_t voltage_out = _read_and_validate(_cfg.ch_current_out); // sensor OUT (e.g., CH1) - // const uint16_t voltage_ref = _read_and_validate(_cfg.ch_current_ref); // sensor REF (e.g., CH2) + const volt voltage_ts = _read_and_validate(_cfg.ch_voltage); // divider node (e.g., CH0) + // const volt voltage_out = _read_and_validate(_cfg.ch_current_out); // sensor OUT (e.g., CH1) + // const volt voltage_ref = _read_and_validate(_cfg.ch_current_ref); // sensor REF (e.g., CH2) - const uint16_t voltage_current = _read_and_validate(_cfg.ch_current); // divider node (e.g., CH0) + const volt voltage_current = _read_and_validate(_cfg.ch_current); // divider node (e.g., CH0) const bool has_valid_current_data = (voltage_current != ERROR_CODE); const bool has_valid_voltage_data = (voltage_ts != ERROR_CODE); if (has_valid_current_data){ From b644e807a63e3a573155943e1422b3a54653b961 Mon Sep 17 00:00:00 2001 From: Advait Vedant Date: Tue, 28 Oct 2025 13:46:15 -0700 Subject: [PATCH 12/12] changed powersensor to only currentsensor and adjusted mcp3208 spi calls to match VCR example --- .../include/CurrentSensorInterface.h | 41 +++++++++++ lib/interfaces/include/MCPSPIInterface.h | 23 +++++-- lib/interfaces/include/MCPSPIInterface.tpp | 30 ++++---- lib/interfaces/include/PowerSensor.h | 68 ------------------- lib/interfaces/src/CurrentSensorInterface.cpp | 45 ++++++++++++ lib/interfaces/src/PowerSensor.cpp | 65 ------------------ src/ACU_InterfaceTasks.cpp | 1 - src/main.cpp | 19 +++++- 8 files changed, 137 insertions(+), 155 deletions(-) create mode 100644 lib/interfaces/include/CurrentSensorInterface.h delete mode 100644 lib/interfaces/include/PowerSensor.h create mode 100644 lib/interfaces/src/CurrentSensorInterface.cpp delete mode 100644 lib/interfaces/src/PowerSensor.cpp diff --git a/lib/interfaces/include/CurrentSensorInterface.h b/lib/interfaces/include/CurrentSensorInterface.h new file mode 100644 index 00000000..bdd30a61 --- /dev/null +++ b/lib/interfaces/include/CurrentSensorInterface.h @@ -0,0 +1,41 @@ +#ifndef CURRENTSENSOR_H +#define CURRENTSENSOR_H +#include +#include +#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; +#endif // CurrentSENSOR_H diff --git a/lib/interfaces/include/MCPSPIInterface.h b/lib/interfaces/include/MCPSPIInterface.h index eac66e83..c6224fe6 100644 --- a/lib/interfaces/include/MCPSPIInterface.h +++ b/lib/interfaces/include/MCPSPIInterface.h @@ -5,14 +5,27 @@ #include #include +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_SINGLE = 0x03; - static constexpr uint8_t MCP_START_READING_DIFF = 0x02; + 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; @@ -26,7 +39,7 @@ namespace mcp_spi_interface { * @param singleEnded true = SGL/DIFF=1 (single-ended), false = 0 (differential) * @return {byte0, byte1, byte2} */ - std::array make_cmd(uint8_t channel, bool singleEnded); + byte make_cmd(uint8_t channel, bool singleEnded); /** * Perform one MCP3208 read using the provided command. @@ -36,7 +49,7 @@ namespace mcp_spi_interface { * @param cmd 3-byte command (from make_cmd) * @return 12-bit conversion code (0..4095) */ - uint16_t read_channel(int cs, const std::array& cmd); + uint16_t read_channel(int cs, byte cmd); /** * Perform one MCP3208 read using the provided command. @@ -46,7 +59,7 @@ namespace mcp_spi_interface { * @param cmd 3-byte command (from make_cmd) * @return volt */ - volt read_channel_voltage(int cs, const std::array& cmd); + volt read_channel_voltage(int cs, byte cmd); } diff --git a/lib/interfaces/include/MCPSPIInterface.tpp b/lib/interfaces/include/MCPSPIInterface.tpp index bfa8931a..05c62cf2 100644 --- a/lib/interfaces/include/MCPSPIInterface.tpp +++ b/lib/interfaces/include/MCPSPIInterface.tpp @@ -14,31 +14,31 @@ void mcp_spi_interface::_write_and_delay_high(int cs, int us) { delayMicroseconds(us); } -std::array -mcp_spi_interface::make_cmd(uint8_t channel, bool singleEnded) { - const uint8_t b0 = singleEnded ? MCP_START_READING_SINGLE : MCP_START_READING_DIFF; - const uint8_t b1 = static_cast((channel & 0x07) << 5); - const uint8_t b2 = 0x00; //placeholder - return { b0, b1, b2 }; +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, const std::array& cmd) { +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, 1); + _write_and_delay_low(cs, 0); + byte command, b0, b1, b2; - - SPI.transfer(cmd[0]); // starts read - const uint8_t hi = SPI.transfer(cmd[1]); // sets which channel and reads high byte - const uint8_t lo = SPI.transfer(cmd[2]); // reads low byte + b0 = SPI.transfer(command); + b1 = SPI.transfer(0x00); + b2 = SPI.transfer(0x00); _write_and_delay_high(cs, 1); SPI.endTransaction(); - - return static_cast(((hi & 0x0F) << 8) | lo); //combines hi and low bits + uint16_t read_data = (b0 & 0x01) << 11 | (b1 & 0xFF) << 3 | (b2 & 0xE0) >> 5; + return static_cast(read_data & 0x0FFF); } -volt mcp_spi_interface::read_channel_voltage(int cs, const std::array& cmd) { +volt mcp_spi_interface::read_channel_voltage(int cs, byte cmd) { return static_cast(read_channel(cs, cmd)) * REFERENCE_VOLTAGE / RESOLUTION; } diff --git a/lib/interfaces/include/PowerSensor.h b/lib/interfaces/include/PowerSensor.h deleted file mode 100644 index ca3e000b..00000000 --- a/lib/interfaces/include/PowerSensor.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef POWERSENSOR_H -#define POWERSENSOR_H -#include -#include - - -struct PowerSensorData -{ - float current; // Amps - volt voltage; // Volts (pack/bus) - float power; // Power -}; - -// The codes for each channel on the MCP3028 (D2 D1 D0) -enum class CHANNEL_CODES_e : uint8_t -{ - CH0 = 0, - CH1 = 1, - CH2 = 2, - CH3 = 3, - CH4 = 4, - CH5 = 5, - CH6 = 6, - CH7 = 7, -}; - -struct Config { - float acceptable_error; - int cs; // MCP3208 chip-select pin - float voltage_divider_gain; - float sensor_v_per_a = 0.005; // V per A for your current sensor (e.g., 0.005 or 5mV/A) - CHANNEL_CODES_e ch_voltage = CHANNEL_CODES_e::CH0; // divider node channel - // CHANNEL_CODES_e ch_current_out = CHANNEL_CODES_e::CH1; // sensor OUT channel - // CHANNEL_CODES_e ch_current_ref = CHANNEL_CODES_e::CH2; // sensor REF channel - CHANNEL_CODES_e ch_current = CHANNEL_CODES_e::CH3; // channel to read current from -}; - -/** - * Minimal power sensor wrapped around one MCP3208 - */ -class PowerSensor -{ -public: - static constexpr volt ERROR_CODE = -1; - - PowerSensor() = delete; - PowerSensor(const Config& cfg) : _cfg(cfg) {}; - - void init(); - - // One-shot read: returns and validates bus voltage and current. - PowerSensorData read_data(); - -private: - volt _read_voltage(CHANNEL_CODES_e ch, bool isSingleEnded = true); - - float _calculate_current(volt voltage); - volt _calculate_voltage(volt v_raw); - float _calculate_power(); - volt _read_and_validate(CHANNEL_CODES_e ch, bool isSingleEnded = true); - -private: - Config _cfg; - PowerSensorData _data; - -}; - -#endif // POWERSENSOR_H diff --git a/lib/interfaces/src/CurrentSensorInterface.cpp b/lib/interfaces/src/CurrentSensorInterface.cpp new file mode 100644 index 00000000..cd2dd570 --- /dev/null +++ b/lib/interfaces/src/CurrentSensorInterface.cpp @@ -0,0 +1,45 @@ +#include "CurrentSensorInterface.h" +#include "MCPSPIInterface.h" +#include +#include +#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(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; +} diff --git a/lib/interfaces/src/PowerSensor.cpp b/lib/interfaces/src/PowerSensor.cpp deleted file mode 100644 index 63ac399d..00000000 --- a/lib/interfaces/src/PowerSensor.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "PowerSensor.h" -#include "MCPSPIInterface.h" -#include -#include -#include "SharedFirmwareTypes.h" - -using mcp_spi_interface::make_cmd; -using mcp_spi_interface::read_channel_voltage; - -// Initialize the ADC chip-select pin. -void PowerSensor::init() { - pinMode(_cfg.cs, OUTPUT); - digitalWrite(_cfg.cs, HIGH); -} - -//Reads single values not differential -volt PowerSensor::_read_voltage(CHANNEL_CODES_e ch, bool isSingleEnded) { - auto cmd = make_cmd(static_cast(ch), isSingleEnded); - return read_channel_voltage(_cfg.cs, cmd); -} - -//Checks if the data from the channel is correct -volt PowerSensor::_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) <= _cfg.acceptable_error){ - return second_read; - } - return ERROR_CODE; -} - -float PowerSensor::_calculate_current(volt voltage){ - return voltage / _cfg.sensor_v_per_a; -} - -volt PowerSensor::_calculate_voltage(volt voltage){ - return voltage * _cfg.voltage_divider_gain; -} - -float PowerSensor::_calculate_power(){ - return _data.current * _data.power; -} - - -// Public: perform one measurement and return bus voltage and current -PowerSensorData PowerSensor::read_data() { - - const volt voltage_ts = _read_and_validate(_cfg.ch_voltage); // divider node (e.g., CH0) - // const volt voltage_out = _read_and_validate(_cfg.ch_current_out); // sensor OUT (e.g., CH1) - // const volt voltage_ref = _read_and_validate(_cfg.ch_current_ref); // sensor REF (e.g., CH2) - - const volt voltage_current = _read_and_validate(_cfg.ch_current); // divider node (e.g., CH0) - const bool has_valid_current_data = (voltage_current != ERROR_CODE); - const bool has_valid_voltage_data = (voltage_ts != ERROR_CODE); - if (has_valid_current_data){ - _data.current = _calculate_current(voltage_current); - } - if (has_valid_voltage_data){ - _data.voltage = _calculate_voltage(voltage_ts); - } - if (has_valid_current_data && has_valid_voltage_data){ - _data.power = _calculate_power(); - } - return _data; -} diff --git a/src/ACU_InterfaceTasks.cpp b/src/ACU_InterfaceTasks.cpp index 7fc44b78..6b5a6313 100644 --- a/src/ACU_InterfaceTasks.cpp +++ b/src/ACU_InterfaceTasks.cpp @@ -22,7 +22,6 @@ void initialize_all_interfaces() ACUAllDataInstance::instance().fw_version_info.fw_version_hash = convert_version_to_char_arr(device_status_t::firmware_version); ACUAllDataInstance::instance().fw_version_info.project_on_main_or_master = device_status_t::project_on_main_or_master; ACUAllDataInstance::instance().fw_version_info.project_is_dirty = device_status_t::project_is_dirty; - /* BMS Driver */ BMSDriverInstance::create(ACUConstants::CS, ACUConstants::CS_PER_CHIP, ACUConstants::ADDR); BMSDriverInstance::instance().init(); diff --git a/src/main.cpp b/src/main.cpp index dc4c0ef9..ec7179f4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,7 +10,7 @@ #include "WatchdogInterface.h" #include "SystemTimeInterface.h" #include "ACUCANInterfaceImpl.h" - +#include "CurrentSensorInterface.h" /* System Includes */ #include "ACUController.h" #include "ACUStateMachine.h" @@ -20,6 +20,7 @@ #include "ht_task.hpp" + /* Scheduler setup */ HT_SCHED::Scheduler& scheduler = HT_SCHED::Scheduler::getInstance(); @@ -41,7 +42,23 @@ ::HT_TASK::Task debug_prints_task(HT_TASK::DUMMY_FUNCTION, debug_print, ACUConst FlexCAN_Type ACUCANInterfaceImpl::CCU_CAN; FlexCAN_Type ACUCANInterfaceImpl::EM_CAN; +// void setup(){ +// SPI.begin(); +// SPI.setClockDivider(SPI_CLOCK_DIV8); // 16MHz (Arduino Clock Frequency) / 8 = 2MHz -> SPI Clock +// Serial.begin(ACUConstants::SERIAL_BAUDRATE); +// analogReadResolution(ACUConstants::ANALOG_READ_RESOLUTION); + +// CurrentSensorInstance::create(); +// CurrentSensorInstance::instance().init(CurrentSensorConfig{}); + +// } +// void loop(){ +// float current = CurrentSensorInstance::instance().read_current(); +// Serial.print("Current Reading: "); +// Serial.print(current); +// Serial.println(" "); +// } void setup() { /* Interface and System initialization */