Skip to content
Merged
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
81 changes: 65 additions & 16 deletions firmware/src/cell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,22 +119,6 @@ float Cell::getLDOVoltage()
return volts;
}

uint16_t Cell::calculateSetpoint(float voltage, bool useBuckCalibration)
{
// Select calibration points based on parameter
const std::pair<float, float>* calibration_points = useBuckCalibration ? BUCK_SETPOINTS : LDO_SETPOINTS;

// Calculate slope using the two points
float m = (calibration_points[1].first - calibration_points[0].first) /
(calibration_points[1].second - calibration_points[0].second);

// Calculate intercept
float b = calibration_points[0].first - m * calibration_points[0].second;

// Calculate and return the setpoint for the desired voltage
return static_cast<uint16_t>(m * voltage + b);
}

float Cell::getBuckVoltage()
{
setMuxChannel();
Expand Down Expand Up @@ -185,3 +169,68 @@ void Cell::setMuxChannel()
wire.write(1 << mux_channel);
wire.endTransmission();
}

void Cell::calibrate()
{
// Calibrate the buck between 234 and 2625
float delta = 2625 - 234;
int step = round(delta / NUM_POINTS);
for (int i = 0; i < NUM_POINTS; i++) {
enable();
turnOnOutputRelay();
float setpoint = 234 + i * step;
buck_dac.setVoltage(setpoint, false);
delay(100);
float voltage = getBuckVoltage();
BUCK_SETPOINTS[i] = {voltage, setpoint};
}

// Set buck output to max
buck_dac.setVoltage(234, false);
delay(50);
// Calibrate the ldo between 42 and 3760
delta = 3760 - 42;
step = round(delta / NUM_POINTS);
for (int i = 0; i < NUM_POINTS; i++) {
enable();
turnOnOutputRelay();
float setpoint = 42 + i * step;
ldo_dac.setVoltage(setpoint, false);
delay(100);
float voltage = getVoltage();
LDO_SETPOINTS[i] = {voltage, setpoint};
}
}

uint16_t Cell::calculateSetpoint(float voltage, bool useBuckCalibration)
{
// Use the appropriate calibration array; each element is {measured voltage, DAC setpoint}
const std::pair<float, float>* calibration_points = useBuckCalibration ? BUCK_SETPOINTS : LDO_SETPOINTS;
const int numPoints = NUM_POINTS;
// For descending data: clamp if voltage is above the maximum or below the minimum.
if (voltage >= calibration_points[0].first) {
return calibration_points[0].second;
}
if (voltage <= calibration_points[numPoints - 1].first) {
return calibration_points[numPoints - 1].second;
}
// Find the first index where the measured voltage becomes less than or equal to the target.
int index = 1;
while (index < numPoints && calibration_points[index].first > voltage) {
index++;
}
int above_index = index - 1;
int below_index = index;
if (above_index == below_index) {
return calibration_points[above_index].second;
}
// Linear interpolation:
// DAC setpoint = s1 + (voltage - v1) * (s2 - s1) / (v2 - v1)
float v1 = calibration_points[above_index].first;
float v2 = calibration_points[below_index].first;
float s1 = calibration_points[above_index].second;
float s2 = calibration_points[below_index].second;
float m = (s2 - s1) / (v2 - v1);
float b = s1 - m * v1;
return static_cast<uint16_t>(m * voltage + b);
}
11 changes: 9 additions & 2 deletions firmware/src/cell.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class Cell
void setLDOVoltage(float voltage);
float getBuckVoltage();
void setBuckVoltage(float voltage);
void calibrate();
uint8_t GPIO_STATE = 0b00000000;

private:
Expand Down Expand Up @@ -80,8 +81,14 @@ class Cell
const float MAX_LDO_VOLTAGE = 4.5;

// Calibration points
const std::pair<float, float> BUCK_SETPOINTS[2] = {{234, 4.5971}, {2625, 1.5041}};
const std::pair<float, float> LDO_SETPOINTS[2] = {{42, 4.5176}, {3760, 0.3334}};
static const int NUM_POINTS = 32;
// Each pair is defined as {measured voltage, DAC setpoint}
std::pair<float, float> BUCK_SETPOINTS[NUM_POINTS] = {
{4.5971, 234}, {1.5041, 2625}
};
std::pair<float, float> LDO_SETPOINTS[NUM_POINTS] = {
{4.5176, 42}, {0.3334, 3760}
};
};

#endif // CELL_H
18 changes: 16 additions & 2 deletions firmware/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ void setup()
cell.init();
cell.enable();
cell.turnOnOutputRelay();
delay(10);
cell.calibrate();
}

FastLED.addLeds<NEOPIXEL, ledPin>(leds, NUM_LEDS);
Expand Down Expand Up @@ -175,7 +175,7 @@ void processUARTCommands() {
}
float volt = cells[cellNumber - 1].getVoltage();
USBSerial.print("OK:voltage:");
USBSerial.println(volt);
USBSerial.println(volt, 5);
} else if (command == "ENABLE_OUTPUT") {
int cellNumber = args.toInt();
if (cellNumber < 1 || cellNumber > 16) {
Expand Down Expand Up @@ -281,6 +281,20 @@ void processUARTCommands() {
USBSerial.println("OK:all_load_switches_disabled");
} else if (command == "PING") {
USBSerial.println("OK:PONG");
} else if (command == "CALIBRATE") {
int cell_num = args.toInt();
if (cell_num < 1 || cell_num > 16) {
USBSerial.println("Error:cell number must be between 1 and 16");
return;
}
cells[cell_num - 1].calibrate();

USBSerial.println("OK:calibrated");
} else if (command == "CALIBRATE_ALL") {
for (int i = 0; i < 16; i++) {
cells[i].calibrate();
}
USBSerial.println("OK:all_calibrated");
} else {
USBSerial.println("Error:unknown command");
}
Expand Down
20 changes: 18 additions & 2 deletions lib/cellsim.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def getVoltage(self, channel: int):
stripped = line.strip()
if stripped.startswith("OK:voltage:"):
try:
return float(stripped[len("OK:voltage:"):])
return round(float(stripped[len("OK:voltage:"):]), 6)
except Exception:
pass
elif stripped.startswith("Error:"):
Expand Down Expand Up @@ -127,4 +127,20 @@ def disableLoadSwitch(self, channel: int):

def close(self):
"""Close the underlying serial connection."""
self.client.close()
self.client.close()

def calibrate(self, channel: int):
"""Calibrate the given cell channel (1-16)."""
cmd = f"CALIBRATE {channel}"
response = self.client.send_command(cmd)
if response and "OK:calibrated" in response[0]:
return response
raise Exception("Calibration failed")

def calibrateAll(self):
"""Calibrate all 16 cells."""
cmd = "CALIBRATE_ALL"
response = self.client.send_command(cmd)
if response and "OK:all_calibrated" in response[0]:
return response
raise Exception("Calibration failed")