Files
watch-watch/main/ina226_monitor.c

173 lines
5.4 KiB
C

#include "ina226_monitor.h"
#include <math.h>
#include "driver/i2c.h"
#include "esp_check.h"
#include "esp_log.h"
#include "sdkconfig.h"
#ifndef CONFIG_WATCH_INA226_ENABLED
#define CONFIG_WATCH_INA226_ENABLED 0
#endif
#ifndef CONFIG_WATCH_INA226_I2C_PORT
#define CONFIG_WATCH_INA226_I2C_PORT 0
#endif
#ifndef CONFIG_WATCH_INA226_I2C_SDA
#define CONFIG_WATCH_INA226_I2C_SDA 6
#endif
#ifndef CONFIG_WATCH_INA226_I2C_SCL
#define CONFIG_WATCH_INA226_I2C_SCL 7
#endif
#ifndef CONFIG_WATCH_INA226_I2C_FREQ_HZ
#define CONFIG_WATCH_INA226_I2C_FREQ_HZ 400000
#endif
#ifndef CONFIG_WATCH_INA226_CURRENT_LSB_uA
#define CONFIG_WATCH_INA226_CURRENT_LSB_uA 100
#endif
#ifndef CONFIG_WATCH_INA226_SHUNT_MILLIOHM
#define CONFIG_WATCH_INA226_SHUNT_MILLIOHM 10
#endif
#ifndef CONFIG_WATCH_INA226_ADDR
#define CONFIG_WATCH_INA226_ADDR 0x40
#endif
#define INA226_REG_CONFIG 0x00
#define INA226_REG_BUS 0x02
#define INA226_REG_CURRENT 0x04
#define INA226_REG_CALIBRATION 0x05
#define INA226_CONFIG_AVG_16 (0x04 << 9)
#define INA226_CONFIG_VBUS_1100US (0x04 << 6)
#define INA226_CONFIG_VSH_1100US (0x04 << 3)
#define INA226_CONFIG_MODE_SHUNT_BUS_CONT 0x07
#if CONFIG_WATCH_INA226_ENABLED
static const char *TAG = "ina226";
static bool s_initialized;
static float s_current_lsb_ma;
static uint8_t s_address = CONFIG_WATCH_INA226_ADDR;
static ina226_reading_t s_last_reading;
#endif
esp_err_t ina226_monitor_init(void)
{
#if !CONFIG_WATCH_INA226_ENABLED
return ESP_ERR_NOT_SUPPORTED;
#else
if (s_initialized) {
return ESP_OK;
}
i2c_config_t config = {
.mode = I2C_MODE_MASTER,
.sda_io_num = CONFIG_WATCH_INA226_I2C_SDA,
.scl_io_num = CONFIG_WATCH_INA226_I2C_SCL,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = CONFIG_WATCH_INA226_I2C_FREQ_HZ,
};
ESP_RETURN_ON_ERROR(i2c_param_config(CONFIG_WATCH_INA226_I2C_PORT, &config), TAG, "i2c config failed");
ESP_RETURN_ON_ERROR(i2c_driver_install(CONFIG_WATCH_INA226_I2C_PORT, config.mode, 0, 0, 0),
TAG, "i2c driver install failed");
double current_lsb_a = ((double)CONFIG_WATCH_INA226_CURRENT_LSB_uA) / 1000000.0;
double shunt_ohms = ((double)CONFIG_WATCH_INA226_SHUNT_MILLIOHM) / 1000.0;
double calibration = 0.00512 / (current_lsb_a * shunt_ohms);
if (calibration > 0xFFFF) calibration = 0xFFFF;
uint16_t calibration_value = (uint16_t)calibration;
s_current_lsb_ma = (float)current_lsb_a * 1000.0f;
const uint16_t config_value = INA226_CONFIG_AVG_16 |
INA226_CONFIG_VBUS_1100US |
INA226_CONFIG_VSH_1100US |
INA226_CONFIG_MODE_SHUNT_BUS_CONT;
uint8_t payload_cfg[3] = { INA226_REG_CONFIG, (uint8_t)(config_value >> 8), (uint8_t)(config_value & 0xFF) };
ESP_RETURN_ON_ERROR(i2c_master_write_to_device(CONFIG_WATCH_INA226_I2C_PORT,
s_address, payload_cfg, sizeof(payload_cfg),
pdMS_TO_TICKS(100)),
TAG, "config write failed");
uint8_t payload_cal[3] = { INA226_REG_CALIBRATION, (uint8_t)(calibration_value >> 8),
(uint8_t)(calibration_value & 0xFF) };
ESP_RETURN_ON_ERROR(i2c_master_write_to_device(CONFIG_WATCH_INA226_I2C_PORT,
s_address, payload_cal, sizeof(payload_cal),
pdMS_TO_TICKS(100)),
TAG, "calibration write failed");
s_last_reading = (ina226_reading_t){0};
s_initialized = true;
ESP_LOGI(TAG, "INA226 ініціалізовано (адреса 0x%02X)", s_address);
return ESP_OK;
#endif
}
bool ina226_monitor_ready(void)
{
#if CONFIG_WATCH_INA226_ENABLED
return s_initialized;
#else
return false;
#endif
}
size_t ina226_monitor_channel_count(void)
{
return 1;
}
#if CONFIG_WATCH_INA226_ENABLED
static esp_err_t ina226_read_register(uint8_t reg, uint16_t *out_value)
{
uint8_t value[2];
esp_err_t err = i2c_master_write_read_device(CONFIG_WATCH_INA226_I2C_PORT,
s_address, &reg, 1, value, sizeof(value),
pdMS_TO_TICKS(100));
if (err != ESP_OK) {
return err;
}
*out_value = ((uint16_t)value[0] << 8) | value[1];
return ESP_OK;
}
#endif
esp_err_t ina226_monitor_sample(ina226_reading_t *out_reading)
{
#if !CONFIG_WATCH_INA226_ENABLED
return ESP_ERR_NOT_SUPPORTED;
#else
if (!s_initialized) {
return ESP_ERR_INVALID_STATE;
}
uint16_t bus_raw = 0;
uint16_t current_raw = 0;
ESP_RETURN_ON_ERROR(ina226_read_register(INA226_REG_BUS, &bus_raw), TAG, "bus read failed");
ESP_RETURN_ON_ERROR(ina226_read_register(INA226_REG_CURRENT, &current_raw), TAG, "current read failed");
float voltage_v = (float)bus_raw * 1.25f / 1000.0f;
float current_ma = (int16_t)current_raw * s_current_lsb_ma;
float power_mw = voltage_v * current_ma;
s_last_reading = (ina226_reading_t){
.voltage_v = voltage_v,
.current_ma = current_ma,
.power_mw = power_mw,
};
if (out_reading) {
*out_reading = s_last_reading;
}
return ESP_OK;
#endif
}
const ina226_reading_t *ina226_monitor_get_last(void)
{
#if CONFIG_WATCH_INA226_ENABLED
return &s_last_reading;
#else
return NULL;
#endif
}