INA3221_NB_CHAN)
// return false;
uint8_t addr = Ina3221Data[device].i2caddr;
int16_t bus_voltage, shunt_voltage;
struct INA3221_Channel_Data *pChannel = &Ina3221Data[device].chan[channel];
#ifdef INA3221_SUPPLY_SIDE
if (Ina3221Data[device].enabled_chan & (0x01 << (channel+4))){
#endif
bus_voltage = I2cReadS16(addr, INA3221_REG_BUS_VOLTAGE_CH(channel));
#ifdef DEBUG_TASMOTA_SENSOR
DEBUG_SENSOR_LOG(D_INA3221 ":GetBusVoltage: RegVBus[%d:%d](0x%02X) = 0x%04X = %d", device, channel, INA3221_REG_BUS_VOLTAGE_CH(channel), bus_voltage, bus_voltage);
#endif
// Convert to VBus voltage in V
pChannel->voltage = INA3221C_BUS_ADC_LSB * (float)(bus_voltage >> 3);
#ifdef INA3221_SUPPLY_SIDE
// AddLog(LOG_LEVEL_DEBUG, PSTR(D_INA3221 ":GetBusVoltage: RegVBus[0x%02X:%d:%d](0x%02X) = 0x%04X = %d voltage=%5_f"),addr ,device, channel, INA3221_REG_BUS_VOLTAGE_CH(channel), bus_voltage, bus_voltage, &pChannel->voltage);
}
else{
pChannel->voltage = NAN;
}
if ((fabs(pChannel->shunt)) > 0.0f) {
shunt_voltage = I2cReadS16(addr, INA3221_REG_SHUNT_VOLTAGE_CH(channel));
#ifdef DEBUG_TASMOTA_SENSOR
DEBUG_SENSOR_LOG(D_INA3221 ":GetShuntVoltage: RegSh[%d:%d](0x%02X) = 0x%04X = %d", device, channel, INA3221_REG_SHUNT_VOLTAGE_CH(channel), shunt_voltage, shunt_voltage);
#endif
// convert to shunt voltage in V
if (pChannel->shunt < 0){
pChannel->voltage += INA3221C_SHUNT_ADC_LSB * (float)(shunt_voltage >> 3);
}
pChannel->current = INA3221C_SHUNT_ADC_LSB * (float)(shunt_voltage >> 3) / (fabs(pChannel->shunt));
// AddLog(LOG_LEVEL_DEBUG, PSTR(D_INA3221 ":GetShuntVoltage: RegSh[%d:%d](0x%02X) = 0x%04X = %d current=%5_f"),device, channel, INA3221_REG_SHUNT_VOLTAGE_CH(channel), shunt_voltage, shunt_voltage, &pChannel->current);
#else
if (pChannel->shunt > 0.0f) {
shunt_voltage = I2cReadS16(addr, INA3221_REG_SHUNT_VOLTAGE_CH(channel));
#ifdef DEBUG_TASMOTA_SENSOR
DEBUG_SENSOR_LOG(D_INA3221 ":GetShuntVoltage: RegSh[%d:%d](0x%02X) = 0x%04X = %d", device, channel, INA3221_REG_SHUNT_VOLTAGE_CH(channel), shunt_voltage, shunt_voltage);
#endif
// convert to shunt voltage in V
pChannel->current = INA3221C_SHUNT_ADC_LSB * (float)(shunt_voltage >> 3) / pChannel->shunt;
#endif
#ifdef INA3221_CALC_CHARGE_AH
pChannel->charge_ah += (pChannel->current * (float)INA3221_delta_ms * INA3221_ENERGY_FACTOR);
#endif
#ifdef INA3221_CALC_ENERGY_WH
pChannel->energy_wh += (pChannel->current * pChannel->voltage * (float)INA3221_delta_ms * INA3221_ENERGY_FACTOR);
#endif
}
else {
pChannel->current = INFINITY;
#ifdef INA3221_CALC_CHARGE_AH
pChannel->charge_ah = INFINITY;
#endif
#ifdef INA3221_CALC_ENERGY_WH
pChannel->energy_wh = INFINITY;
#endif
}
#ifdef DEBUG_TASMOTA_SENSOR
dtostrfd(pChannel->voltage,5,_ina3221_dbg1);
dtostrfd(pChannel->current,5,_ina3221_dbg2);
#ifdef DEBUG_TASMOTA_SENSOR
DEBUG_SENSOR_LOG(D_INA3221 ":Read[%d:%d]: V=%sV, I=%sA", device, channel, _ina3221_dbg1, _ina3221_dbg2);
#endif
#endif
return true;
}
/*********************************************************************************************\
* Command Sensor
\*********************************************************************************************/
bool Ina3221CmndSensor(void)
{
int argc = ArgC();
if(argc != 1 && argc != (INA3221_NB_CHAN+1)) {
AddLog(LOG_LEVEL_INFO, PSTR(D_INA3221 ": Not enough arguments (1 or %d)"),(INA3221_NB_CHAN+1));
return false;
}
char argument[XdrvMailbox.data_len+FLOATSZ];
uint32_t device = atoi(ArgV(argument,1)) -1;
if (device >= INA3221_MAX_COUNT || !Ina3221Data[device].i2caddr) {
AddLog(LOG_LEVEL_INFO, PSTR(D_INA3221 ":invalid device %d"),device+1);
return false;
}
if (argc > 1) {
for (int channel = 0 ; channel < INA3221_NB_CHAN ; channel++) {
float shunt = CharToFloat(ArgV(argument,2+channel));
Ina3221SetShunt(device, channel, shunt);
}
#ifdef INA3221_SUPPLY_SIDE
if (!Ina3221WriteConfig(device)){
#ifdef DEBUG_TASMOTA_SENSOR
DEBUG_SENSOR_LOG(D_INA3221 "error write configuration %d", device+1);
#endif
return false;
}
#endif
}
Response_P(INA3221_SENSORCMND_START, XSNS_100, device +1, Ina3221Data[device].i2caddr);
for (int channel = 0 ; channel < INA3221_NB_CHAN ; channel++ ) {
ResponseAppend_P(PSTR("%5_f%c"),&Ina3221Data[device].chan[channel].shunt , ((channel < (INA3221_NB_CHAN-1))?',':'\0'));
}
ResponseAppend_P(INA3221_SENSORCMND_END);
return true;
}
/********************************************************************************************/
void Ina3221Detect(void)
{
_ina3221_current_device = 0;
Ina3221count = 0;
// ????
for (uint32_t i = 0; i < INA3221_MAX_COUNT; i++) {
uint16_t addr = INA3221_ADDRESS(i);
if (!I2cSetDevice(addr)) { continue; }
if (!Ina3221Data) {
// bf ... calloc(INA3221_MAX_COUNT ... ??
Ina3221Data = (struct INA3221_Data*)calloc(INA3221_MAX_COUNT,sizeof(struct INA3221_Data));
if (!Ina3221Data) {
AddLog(LOG_LEVEL_ERROR,PSTR(D_INA3221 ": Mem allocation error"));
return;
}
}
// bf.. Ina3221SetConfig(addr)) erweitern om device !!
if (Ina3221SetConfig(addr)) {
I2cSetActiveFound(addr, INA3221_TYPE);
Ina3221Data[Ina3221count].i2caddr = addr;
#ifdef INA3221_SUPPLY_SIDE
Ina3221Data[Ina3221count].enabled_chan = INA3221_ENABLE_CHAN(i);
if (!Ina3221WriteConfig(Ina3221count)){
Ina3221count++;
continue;
}
#else
Ina3221Data[Ina3221count].enabled_chan = 0;
#endif
for (uint32_t j = 0; j < INA3221_NB_CHAN; j++) {
Ina3221Data[Ina3221count].chan[j].shunt = 0.0f;
#ifdef INA3221_CALC_CHARGE_AH
Ina3221Data[Ina3221count].chan[j].charge_ah = 0.0f;
#endif
#ifdef INA3221_CALC_ENERGY_WH
Ina3221Data[Ina3221count].chan[j].energy_wh = 0.0f;
#endif
}
Ina3221count++;
}
}
if (!Ina3221count && Ina3221Data) {
free(Ina3221Data);
Ina3221Data = nullptr;
}
#if defined(INA3221_CALC_CHARGE_AH) || defined(INA3221_CALC_ENERGY_WH)
INA3221_last_millis = millis();
#endif
}
void Ina3221Every250ms(void)
{
if (++_ina3221_current_device < 0 ){
return;
}
// Conversion-ready flag. CVRF ?? ==> INA3221_CONV_READY_FLAG
#ifdef DEBUG_TASMOTA_SENSOR
DEBUG_SENSOR_LOG(PSTR(D_INA3221 ": cur:%d, en:%d"), _ina3221_current_device, Ina3221Data[(uint8_t) _ina3221_current_device].enabled_chan);
#endif
uint8_t enabled_chan = Ina3221Data[(uint8_t)(_ina3221_current_device)].enabled_chan;
if (_ina3221_current_device >= INA3221_MAX_COUNT){
_ina3221_current_device = (-INA3221_CYCLE_COUNT);
#if defined(INA3221_CALC_CHARGE_AH) || defined(INA3221_CALC_ENERGY_WH)
INA3221_delta_ms = millis()-INA3221_last_millis;
INA3221_last_millis = millis();
#endif
}
else{
for (int chan = 0 ; enabled_chan ; chan++, enabled_chan>>=1, enabled_chan &= 0xF7) {
if (0x11 & enabled_chan)
Ina3221Read((uint8_t)(_ina3221_current_device), chan);
}
}
}
#ifdef USE_WEBSERVER
// {s} = , {m} = | , {e} = |
#define INA3221_AL ""
const char HTTP_SNS_INA3221_HEADER[] PROGMEM =
"{s}" D_INA3221 " " INA3221_AL D_VOLTAGE " | | " INA3221_AL D_CURRENT " | " INA3221_AL D_POWERUSAGE
#ifdef INA3221_CALC_CHARGE_AH
" | " INA3221_AL D_CHARGE
#endif
#ifdef INA3221_CALC_ENERGY_WH
" | " INA3221_AL D_ENERGY
#endif
"{e}";
const char HTTP_SNS_INA3221_DATA[] PROGMEM =
"{s}%s " INA3221_AL " %*_f " D_UNIT_VOLT " | " INA3221_AL " %*_f " D_UNIT_AMPERE " | " INA3221_AL " %*_f " D_UNIT_WATT
#ifdef INA3221_CALC_CHARGE_AH
" | " INA3221_AL " %*_f " D_UNIT_CHARGE
#endif
#ifdef INA3221_CALC_ENERGY_WH
" | " INA3221_AL " %*_f " D_UNIT_WATTHOUR
#endif
"{e}";
#endif // USE_WEBSERVER
void Ina3221Show(bool json)
{
char name[FLOATSZ];
// char temp[FLOATSZ];
char voltage[INA3221_NB_CHAN*FLOATSZ+3];
char current[INA3221_NB_CHAN*FLOATSZ+3];
char power[INA3221_NB_CHAN*FLOATSZ+3];
#ifdef INA3221_CALC_CHARGE_AH
char charge_ah[INA3221_NB_CHAN*FLOATSZ+3];
#endif
#ifdef INA3221_CALC_ENERGY_WH
char energy_wh[INA3221_NB_CHAN*FLOATSZ+3];
#endif
float pw = 0.0f;
if (json) {
// data
for (int device=0 ; device < Ina3221count ; device++) {
uint8_t enabled_chan = Ina3221Data[device].enabled_chan;
if (!enabled_chan) continue;
if (Ina3221count > 1){
snprintf_P(name, sizeof(name), PSTR("%s%c%d"), INA3221_TYPE, IndexSeparator(), device +1);
}
else{
snprintf_P(name, sizeof(name), PSTR("%s"), INA3221_TYPE);
}
int32_t count_v = 0, count_i = 0, count_p = 0, count_ah = 0, count_wh = 0;
for (int32_t chan=0 ; enabled_chan; chan++, enabled_chan>>=1, enabled_chan &= 0xF7) {
// if (0x11 & enabled_chan) {
count_v += ext_snprintf_P(&voltage[count_v], sizeof(voltage) - count_v, PSTR("%s%*_f"), (chan>0 ? ",":""), Settings->flag2.voltage_resolution, &Ina3221Data[device].chan[chan].voltage);
count_i += ext_snprintf_P(¤t[count_i], sizeof(current) - count_i, PSTR("%s%*_f"), (chan>0 ? ",":""), Settings->flag2.current_resolution, &Ina3221Data[device].chan[chan].current);
pw = Ina3221Data[device].chan[chan].voltage * Ina3221Data[device].chan[chan].current;
count_p += ext_snprintf_P(&power[count_p], sizeof(power) - count_p, PSTR("%s%*_f"), (chan>0 ? ",":""), Settings->flag2.wattage_resolution, &pw);
#ifdef INA3221_CALC_CHARGE_AH
count_ah += ext_snprintf_P(&charge_ah[count_ah], sizeof(charge_ah) - count_ah, PSTR("%s%*_f"), (chan>0 ? ",":""), Settings->flag2.energy_resolution, &Ina3221Data[device].chan[chan].charge_ah);
#endif
#ifdef INA3221_CALC_ENERGY_WH
count_wh += ext_snprintf_P(&energy_wh[count_wh], sizeof(energy_wh) - count_wh, PSTR("%s%*_f"), (chan>0 ? ",":""), Settings->flag2.energy_resolution, &Ina3221Data[device].chan[chan].energy_wh);
#endif
// } //if enabled
} // for channel
ResponseAppend_P(PSTR(",\"%s\":{\"Id\":\"0x%02x\",\"" D_JSON_VOLTAGE "\":[%s],\"" D_JSON_CURRENT "\":[%s],\"" D_JSON_POWERUSAGE "\":[%s]"
#ifdef INA3221_CALC_CHARGE_AH
",\"" D_JSON_CHARGE "\":[%s]"
#endif
#ifdef INA3221_CALC_ENERGY_WH
",\"" D_JSON_ENERGY "\":[%s]"
#endif
"}"),name, Ina3221Data[device].i2caddr, voltage, current, power
#ifdef INA3221_CALC_CHARGE_AH
, charge_ah
#endif
#ifdef INA3221_CALC_ENERGY_WH
, energy_wh
#endif
);
#ifdef USE_DOMOTICZ
if (0 == TasmotaGlobal.tele_period) {
DomoticzSensor(DZ_VOLTAGE, voltage);
DomoticzSensor(DZ_CURRENT, current);
}
#endif // USE_DOMOTICZ
} // for device
} // if json
#ifdef USE_WEBSERVER
else {
// header
WSContentSend_PD(HTTP_SNS_INA3221_HEADER);
// data
for (int device=0 ; device < Ina3221count ; device++) {
uint8_t enabled_chan = Ina3221Data[device].enabled_chan;
for (int chan=0 ; enabled_chan ; chan++, enabled_chan>>=1, enabled_chan &= 0xF7) {
if (0x11 & enabled_chan) {
if (Ina3221count > 1){
snprintf_P(name, sizeof(name), PSTR("%s%c%d:%d"), INA3221_TYPE, IndexSeparator(), device +1, chan);
}
else{
snprintf_P(name, sizeof(name), PSTR("%s:%d"), INA3221_TYPE, chan);
}
pw = Ina3221Data[device].chan[chan].voltage * Ina3221Data[device].chan[chan].current;
WSContentSend_PD(HTTP_SNS_INA3221_DATA, name, Settings->flag2.voltage_resolution, &Ina3221Data[device].chan[chan].voltage, Settings->flag2.current_resolution, &Ina3221Data[device].chan[chan].current, Settings->flag2.wattage_resolution, &pw
#ifdef INA3221_CALC_CHARGE_AH
, Settings->flag2.energy_resolution, &Ina3221Data[device].chan[chan].charge_ah
#endif
#ifdef INA3221_CALC_ENERGY_WH
, Settings->flag2.energy_resolution, &Ina3221Data[device].chan[chan].energy_wh
#endif
);
} // if active
} // for channel
} // for device
}
#endif
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xsns100(uint32_t function)
{
if (!I2cEnabled(XI2C_72)) { return false; }
bool result = false;
if (FUNC_INIT == function) {
Ina3221Detect();
}
else if (Ina3221Data) {
switch (function) {
case FUNC_COMMAND_SENSOR:
if (XSNS_100 == XdrvMailbox.index) {
result = Ina3221CmndSensor();
}
break;
case FUNC_EVERY_250_MSECOND:
Ina3221Every250ms();
break;
case FUNC_JSON_APPEND:
Ina3221Show(1);
break;
#ifdef USE_WEBSERVER
case FUNC_WEB_SENSOR:
Ina3221Show(0);
break;
#endif // USE_WEBSERVER
#ifdef USE_DEEPSLEEP
case FUNC_SAVE_BEFORE_RESTART:
for (uint8_t device; device < Ina3221count ; device++)
Ina3221PowerDown(device);
break;
#endif // USE_DEEPSLEEP }
}
}
return result;
}
#endif // USE_INA3221
#endif // USE_I2C