restructured i2c-max split, added BMS unit test

This commit is contained in:
2026-03-09 16:09:28 -05:00
parent b03b0c0f43
commit b17ac96f2f
8 changed files with 137 additions and 63 deletions

View File

@@ -14,10 +14,6 @@ extern "C" {
#define I2C_MASTER_TIMEOUT_MS 1000
esp_err_t i2c_init();
esp_err_t max17048_read_reg(uint8_t reg_addr, uint8_t *MSB, uint8_t *LSB);
esp_err_t max17048_write_reg(uint8_t reg_addr, uint8_t MSB, uint8_t LSB);
esp_err_t max17048_friendly_write_reg(uint8_t reg_addr, uint8_t MSB, uint8_t LSB,
uint8_t MSBmask, uint8_t LSBmask);
#ifdef __cplusplus
}

View File

@@ -61,6 +61,11 @@ void bms_checker_task(void *pvParameters);
#define bms_clear_status() max17048_write_reg(MAX17048_REG_STATUS, 0, 0)
#define bms_clear_alrt() max17048_friendly_write_reg(MAX17048_REG_CONFIG, 0, 0, 0, 1<<5)
esp_err_t max17048_read_reg(uint8_t reg_addr, uint8_t *MSB, uint8_t *LSB);
esp_err_t max17048_write_reg(uint8_t reg_addr, uint8_t MSB, uint8_t LSB);
esp_err_t max17048_friendly_write_reg(uint8_t reg_addr, uint8_t MSB, uint8_t LSB,
uint8_t MSBmask, uint8_t LSBmask);
#ifdef __cplusplus
}
#endif

View File

@@ -1,8 +1,8 @@
# This file was automatically generated for projects
# without default 'CMakeLists.txt' file.
FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*)
FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.* ${CMAKE_SOURCE_DIR}/test/*.*)
idf_component_register(SRCS ${app_sources}
INCLUDE_DIRS "."
INCLUDE_DIRS "." "${CMAKE_SOURCE_DIR}/test"
REQUIRES nvs_flash esp-nimble-cpp esp_socketio_client)

View File

@@ -18,58 +18,3 @@ esp_err_t i2c_init() {
return (i2c_param_config(I2C_MASTER_NUM, &conf) ||
i2c_driver_install(I2C_MASTER_NUM, conf.mode, 0, 0, 0));
}
// Helper: Read 16-bit register to 2-byte array (MSB first big endian)
esp_err_t max17048_read_reg(uint8_t reg_addr, uint8_t *MSB, uint8_t *LSB) {
// this is better than converting to little endian for my application
// since I usually need to handle bytes individually.
uint8_t data[2];
// Write register address
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX17048_ADDR << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg_addr, true);
// Restart and Read 2 bytes
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX17048_ADDR << 1) | I2C_MASTER_READ, true);
i2c_master_read(cmd, data, 2, I2C_MASTER_LAST_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS));
i2c_cmd_link_delete(cmd);
if (ret == ESP_OK) {
*MSB = data[0];
*LSB = data[1];
}
return ret;
}
// Write big endian 2-byte array to a 16-bit register
esp_err_t max17048_write_reg(uint8_t reg_addr, uint8_t MSB, uint8_t LSB) {
// Write register address
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX17048_ADDR << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg_addr, true);
i2c_master_write_byte(cmd, MSB, true);
i2c_master_write_byte(cmd, LSB, true);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS));
i2c_cmd_link_delete(cmd);
return ret;
}
esp_err_t max17048_friendly_write_reg(uint8_t reg_addr, uint8_t MSB, uint8_t LSB,
uint8_t MSBmask, uint8_t LSBmask) {
uint8_t origMSB, origLSB;
esp_err_t err = max17048_read_reg(reg_addr, &origMSB, &origLSB);
MSB &= MSBmask;
LSB &= LSBmask;
MSB |= origMSB & ~MSBmask;
LSB |= origLSB & ~LSBmask;
return err | max17048_write_reg(reg_addr, MSB, LSB);
}

View File

@@ -11,6 +11,7 @@
#include "esp_pm.h"
#include "mainEventLoop.hpp"
#include "max17048.h"
#include "bms_test.hpp"
// Global encoder instances
Encoder* topEnc = new Encoder(ENCODER_PIN_A, ENCODER_PIN_B);
@@ -53,6 +54,7 @@ void pm_init() {
}
extern "C" void app_main() {
pm_init();
mainApp();
// pm_init();
// mainApp();
bms_test_app();
}

View File

@@ -118,3 +118,58 @@ void bms_checker_task(void *pvParameters) {
}
}
}
// Helper: Read 16-bit register to 2-byte array (MSB first big endian)
esp_err_t max17048_read_reg(uint8_t reg_addr, uint8_t *MSB, uint8_t *LSB) {
// this is better than converting to little endian for my application
// since I usually need to handle bytes individually.
uint8_t data[2];
// Write register address
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX17048_ADDR << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg_addr, true);
// Restart and Read 2 bytes
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX17048_ADDR << 1) | I2C_MASTER_READ, true);
i2c_master_read(cmd, data, 2, I2C_MASTER_LAST_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS));
i2c_cmd_link_delete(cmd);
if (ret == ESP_OK) {
*MSB = data[0];
*LSB = data[1];
}
return ret;
}
// Write big endian 2-byte array to a 16-bit register
esp_err_t max17048_write_reg(uint8_t reg_addr, uint8_t MSB, uint8_t LSB) {
// Write register address
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX17048_ADDR << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg_addr, true);
i2c_master_write_byte(cmd, MSB, true);
i2c_master_write_byte(cmd, LSB, true);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS));
i2c_cmd_link_delete(cmd);
return ret;
}
esp_err_t max17048_friendly_write_reg(uint8_t reg_addr, uint8_t MSB, uint8_t LSB,
uint8_t MSBmask, uint8_t LSBmask) {
uint8_t origMSB, origLSB;
esp_err_t err = max17048_read_reg(reg_addr, &origMSB, &origLSB);
MSB &= MSBmask;
LSB &= LSBmask;
MSB |= origMSB & ~MSBmask;
LSB |= origLSB & ~LSBmask;
return err | max17048_write_reg(reg_addr, MSB, LSB);
}

65
test/bms_test.cpp Normal file
View File

@@ -0,0 +1,65 @@
#include "bms_test.hpp"
#include "i2c.h"
#include "max17048.h"
void bms_test_app() {
vTaskDelay(pdMS_TO_TICKS(5000));
esp_err_t err = i2c_init();
printf("Error after init: %d\n", err);
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << maxALRT),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&io_conf);
printf("BMS test running. Polling ALRT pin every 1s...\n");
uint8_t msb, lsb;
if (max17048_read_reg(MAX17048_REG_STATUS, &msb, &lsb) == ESP_OK) {
printf(" STATUS before clear: RI=%d\n", !!(msb & RIbit));
printf(" STATUS reg before clear: RI=%x, %x\n", msb, lsb);
} else {
printf(" STATUS read FAILED\n");
}
err = bms_set_alert_bound_voltages(3.3f, 4.2f);
printf("Error after boundV: %d\n", err);
err |= bms_clear_status();
printf("Error after clearStatus: %d\n", err);
err |= bms_clear_alrt();
printf("Error after clearALRT: %d\n", err);
if (max17048_read_reg(MAX17048_REG_STATUS, &msb, &lsb) == ESP_OK) {
printf(" STATUS after clear: RI=%d\n", !!(msb & RIbit));
printf(" STATUS reg after clear: RI=%x, %x\n", msb, lsb);
} else {
printf(" STATUS read FAILED\n");
}
while (true) {
int level = gpio_get_level(maxALRT);
printf("ALRT pin: %s\n", level ? "HIGH (no alert)" : "LOW (alert asserted)");
if (level == 0) {
if (max17048_read_reg(MAX17048_REG_STATUS, &msb, &lsb) == ESP_OK) {
printf(" STATUS: VH=%d VL=%d HD=%d SC=%d\n",
!!(msb & VHbit),
!!(msb & VLbit),
!!(msb & HDbit),
!!(msb & SCbit));
bms_clear_status();
bms_clear_alrt();
} else {
printf(" STATUS read FAILED\n");
}
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}

6
test/bms_test.hpp Normal file
View File

@@ -0,0 +1,6 @@
#ifndef BMS_TEST_H
#define BMS_TEST_H
void bms_test_app();
#endif