Compare commits

...

4 Commits

Author SHA1 Message Date
fb-pilot 9683d1bf00
Merge 6d4bc04d63 into 41970f7d62 2024-04-25 19:47:29 +00:00
fb-pilot 6d4bc04d63
Merge branch 'arendst:development' into development 2024-04-25 21:47:26 +02:00
Jason2866 41970f7d62
Disable psram check to avoid "blinking" of GPIO 16/17 at startup (#21282)
* add `DISABLE_PSRAMCHECK`

* remove code before IDF 5

* add no psram env
2024-04-25 20:19:20 +02:00
fb-pilot 6d69b0bb41
Add files via upload
fix display multiple devices and add more options
2024-04-24 12:19:22 +02:00
3 changed files with 336 additions and 233 deletions

View File

@ -20,6 +20,17 @@ build_flags = ${env:tasmota32_base.build_flags}
-DUSE_WIFI_RANGE_EXTENDER_NAPT
-DOTA_URL='""'
; remove PSRAM support -> avoid "blinking" of GPIO 16/17 at boot
[env:tasmota32-nopsram]
extends = env:tasmota32
build_unflags = ${env:tasmota32_base.build_unflags}
-DBOARD_HAS_PSRAM
build_flags = ${env:tasmota32_base.build_flags}
-DFIRMWARE_TASMOTA32
-DDISABLE_PSRAMCHECK
-DCODE_IMAGE_STR='"tasmota32-nopsram"'
-DOTA_URL='""'
[env:tasmota32s3-file]
extends = env:tasmota32_base
board = esp32s3-qio_qspi

View File

@ -40,27 +40,23 @@ const static char kWifiPhyMode[] PROGMEM = "low rate|11b|11g|HT20|HT40|HE20"; //
#endif
// See libraries\ESP32\examples\ResetReason.ino
#if ESP_IDF_VERSION_MAJOR > 3 // IDF 4+
#include "esp_chip_info.h"
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "esp32/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2
#include "esp32s2/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3
#include "esp32s3/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C2 // ESP32-C2
#include "esp32c2/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3
#include "esp32c3/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C6 // ESP32-C6
#include "esp32c6/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32H2 // ESP32-H2
#include "esp32h2/rom/rtc.h"
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
#else // ESP32 Before IDF 4.0
#include "rom/rtc.h"
#include "esp_chip_info.h"
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "esp32/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2
#include "esp32s2/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3
#include "esp32s3/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C2 // ESP32-C2
#include "esp32c2/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3
#include "esp32c3/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C6 // ESP32-C6
#include "esp32c6/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32H2 // ESP32-H2
#include "esp32h2/rom/rtc.h"
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
// Set the Stacksize for Arduino core. Default is 8192, some builds may need a bigger one
@ -204,12 +200,7 @@ void NvsInfo(void) {
// See Esp.cpp
#include "Esp.h"
#if ESP_IDF_VERSION_MAJOR >= 5
// esp_spi_flash.h is deprecated, please use spi_flash_mmap.h instead
#include "spi_flash_mmap.h"
#else
#include "esp_spi_flash.h"
#endif
#include "spi_flash_mmap.h"
#include <memory>
#include <soc/soc.h>
#include <soc/efuse_reg.h>
@ -220,39 +211,34 @@ extern "C" {
}
#include "esp_system.h"
#include "esp_flash.h"
#if ESP_IDF_VERSION_MAJOR > 3 // IDF 4+
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "esp32/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x1000 // Flash offset containing magic flash size and spi mode
#elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2
#include "esp32s2/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x1000 // Flash offset containing magic flash size and spi mode
#elif CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3
#include "esp32s3/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32s3 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32C2 // ESP32-C2
#include "esp32c2/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c2 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3
#include "esp32c3/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c3 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32C6 // ESP32-C6
#include "esp32c6/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c6 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32H2 // ESP32-H2
#include "esp32h2/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32h2 is located at 0x0000
#else
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "esp32/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x1000 // Flash offset containing magic flash size and spi mode
#elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2
#include "esp32s2/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x1000 // Flash offset containing magic flash size and spi mode
#elif CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3
#include "esp32s3/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32s3 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32C2 // ESP32-C2
#include "esp32c2/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c2 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3
#include "esp32c3/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c3 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32C6 // ESP32-C6
#include "esp32c6/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c6 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32H2 // ESP32-H2
#include "esp32h2/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32h2 is located at 0x0000
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
#else // ESP32 Before IDF 4.0
#include "rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x1000
#endif
#if ESP_IDF_VERSION_MAJOR >= 5
#include "bootloader_common.h"
#endif
#include "bootloader_common.h"
uint32_t EspProgramSize(const char *label) {
const esp_partition_t *part = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, label);
if (!part) {
@ -462,7 +448,7 @@ uint32_t ESP_getFlashChipMagicSize(void) {
}
uint32_t ESP_magicFlashChipSize(uint8_t spi_size) {
/*
/*
switch(spi_size & 0x0F) {
case 0x0: // 8 MBit (1MB)
return 1048576;
@ -477,7 +463,7 @@ uint32_t ESP_magicFlashChipSize(uint8_t spi_size) {
case 0x5: // 256 MBit (32MB)
return 33554432;
default: // fail so return (1KB)
return 1024;
return 1024;
}
*/
// When spi_size is bigger than 11 will return 0 (0x100000000 = 0x00000000)
@ -590,14 +576,10 @@ extern "C" {
// `psramFound()` can return true even if no PSRAM is actually installed
// This new version also checks `esp_spiram_is_initialized` to know if the PSRAM is initialized
bool FoundPSRAM(void) {
#if CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6
#if CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 || DISABLE_PSRAMCHECK
return psramFound();
#else
#if ESP_IDF_VERSION_MAJOR >= 5
return psramFound() && esp_psram_is_initialized();
#else
return psramFound() && esp_spiram_is_initialized();
#endif
return psramFound() && esp_psram_is_initialized();
#endif
}
@ -774,9 +756,6 @@ typedef struct {
- Peripherals include capacitive touch sensors, Hall sensor, SD card interface, Ethernet, high-speed SPI, UART, I2S and I2C
*/
#ifdef CONFIG_IDF_TARGET_ESP32
#if (ESP_IDF_VERSION_MAJOR < 5)
pkg_version = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_PKG) & 0x7;
#endif
// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HDW: ESP32 Model %d, Revision %d, Core %d"), chip_info.model, chip_revision, chip_info.cores);
@ -816,9 +795,6 @@ typedef struct {
- Availability of common cloud connectivity agents and common product features shortens the time to market
*/
#ifdef CONFIG_IDF_TARGET_ESP32S2
#if (ESP_IDF_VERSION_MAJOR < 5)
pkg_version = REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_3_REG, EFUSE_FLASH_VERSION) & 0xF;
#endif
uint32_t psram_ver = REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_3_REG, EFUSE_PSRAM_VERSION);
pkg_version += ((psram_ver & 0xF) * 100);
@ -846,9 +822,6 @@ typedef struct {
- Rich set of peripheral interfaces and GPIOs, ideal for various scenarios and complex applications
*/
#ifdef CONFIG_IDF_TARGET_ESP32C3
#if (ESP_IDF_VERSION_MAJOR < 5)
pkg_version = REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_3_REG, EFUSE_PKG_VERSION) & 0x7;
#endif
switch (pkg_version) {
case 0: return F("ESP32-C3"); // Max 160MHz, Single core, QFN 5*5, ESP32-C3-WROOM-02, ESP32-C3-DevKitC-02
// case 1: return F("ESP32-C3FH4"); // Max 160MHz, Single core, QFN 5*5, 4MB embedded flash, ESP32-C3-MINI-1, ESP32-C3-DevKitM-1
@ -872,12 +845,10 @@ typedef struct {
- Reliable security features ensured by RSA-based secure boot, AES-XTS-based flash encryption, the innovative digital signature and the HMAC peripheral, World Controller
*/
#ifdef CONFIG_IDF_TARGET_ESP32S3
#if (ESP_IDF_VERSION_MAJOR >= 5)
switch (pkg_version) {
case 0: return F("ESP32-S3"); // QFN56
case 1: return F("ESP32-S3-PICO-1"); // LGA56
}
#endif
#endif // CONFIG_IDF_TARGET_ESP32S3
return F("ESP32-S3"); // Max 240MHz, Dual core, QFN 7*7, ESP32-S3-WROOM-1, ESP32-S3-DevKitC-1
}
@ -989,29 +960,13 @@ String WifiGetPhyMode(void) {
* Thanks to DigitalAlchemist
\*********************************************************************************************/
#if ESP_IDF_VERSION_MAJOR >= 5
#include <esp_random.h>
#endif
// Based on code from https://raw.githubusercontent.com/espressif/esp-idf/master/components/esp32/hw_random.c
uint32_t HwRandom(void) {
#if ESP_IDF_VERSION_MAJOR >= 5
// See for more info on the HW RNG:
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/system/random.html
return esp_random();
#else
#define _RAND_ADDR 0x3FF75144UL
static uint32_t last_ccount = 0;
uint32_t ccount;
uint32_t result = 0;
do {
ccount = ESP.getCycleCount();
result ^= *(volatile uint32_t *)_RAND_ADDR;
} while (ccount - last_ccount < 64);
last_ccount = ccount;
return result ^ *(volatile uint32_t *)_RAND_ADDR;
#undef _RAND_ADDR
#endif // ESP_IDF_VERSION_MAJOR >= 5
}
/********************************************************************************************/

View File

@ -1,7 +1,7 @@
/*
xsns_100_ina3221.ino - INA3221 3-channels Current Sensor support for Tasmota
Copyright (C) 2021 Barbudor and Theo Arends + fb-pilot 2024-3-7
Copyright (C) 2021 Barbudor and Theo Arends + fb-pilot 2024-4-4, 2024-4-20
Based on Barbudor's CircuitPython_INA3221
This program is free software: you can redistribute it and/or modify
@ -38,32 +38,57 @@
* #define INA3221_ADDRESS1 0x41
* That would leave 0x40 and 0x43 for other devices
* By defining INA3221_CALC_CHARGE_AH INA3221_CALC_ENERGY_WH the driver adds a estimation of
* energies in Ah and Wh to the output, To reset the energie calculation disable tge
* according INA3221 chanel by setting the shunt to 0.0 and enable it again
* For example :
* sensor100 1,0.0,0.1,0.2 and sensor100 1,0.1,0.1,0.2 will reset channel 1
* Nevertheless, hte driver tries to identifiy if the chip as an address is a IN3221
* energies in Ah and Wh to the output, To reset the energie calculation disable tge
* according INA3221 chanel by setting the shunt to 0.0 and enable it again
* For example :
* sensor100 1,0,0,0 (or sensor100 1,,,) and sensor100 1,0.1,0.1,0.2 will reset channel 1
* By defining INA3221_SUPPLY_SIDE the driver adds the measured Shunt Voltage to the Bus Voltage
* for the cannel with a negativ shunt (shunt <0) thus showing the values of the supply side (IN+)
* otherwise (shunt >0) the load-side (IN-) is shown.
* e.g. sensor100 1,-0.1,0.1,-0.2
* additionaly the bits set (bit 0,1,2) enable the scanning of the voltage in the according channel
* e.g. INA3221_SUPPLY_SIDE = 0x0005 enables enables voltage measurment of channel 1 and 3 for the device 1
* By defining INA3221_CYCLE_COUNT xx ... the device is scanned all xx * 250ms range 1 .. 127
* By defining USE_DEEPSLEEP the device is configured to Power down (reduces the supply current 1mA --> 15µA )
* Nevertheless, the driver tries to identifiy if the chip as an address is a IN3221
\*********************************************************************************************/
// setup of INA3221 config
#define XSNS_100 100
#define XI2C_72 72 // See I2CDEVICES.md
// setup of INA3221 config
#ifndef INA3221_CONFIG_INIT
#define INA3221_CONFIG_INIT INA3221_AVERAGING_16_SAMPLES |\
INA3221_VBUS_CONV_TIME_1MS |\
INA3221_SHUNT_CONV_TIME_1MS
// that results in a complete conversions sequence in 6,6 ms and a slope time of 105,6 ms
#endif // end of setup of INA3221 config
#if !defined(INA3221_MAX_COUNT)
#define INA3221_MAX_COUNT 4
#elif (INA3221_MAX_COUNT > 4)
#undef INA3221_MAX_COUNT
#define INA3221_MAX_COUNT 4
#warning **** INA3221 bad INA3221_MAX_COUNT ****
#warning **** has to be 1 to 4 ... set to 4 ****
#elif (INA3221_MAX_COUNT < 1)
#undef INA3221_MAX_COUNT
#define INA3221_MAX_COUNT 1
#warning **** INA3221 bad INA3221_MAX_COUNT ****
#warning **** has to be 1 to 4 ... set to 1 ****
#endif
// end of setup of INA3221 config
#define XSNS_100 100
#define XI2C_72 72 // See I2CDEVICES.md
#ifndef INA3221_MAX_COUNT
#define INA3221_MAX_COUNT 4
#endif
#if (INA3221_MAX_COUNT > 4)
#error "**** INA3221_MAX_COUNT can't be greater than 4 ****"
#if !defined(INA3221_CYCLE_COUNT)
#define INA3221_CYCLE_COUNT 1
#elif (INA3221_CYCLE_COUNT < 1)
#undef INA3221_CYCLE_COUNT
#define INA3221_CYCLE_COUNT 1
#warning **** INA3221 bad INA3221_CYCLE_COUNT ****
#warning **** must be 1 to 127 ... set to 1 ****
#elif (INA3221_CYCLE_COUNT > 127)
#undef INA3221_CYCLE_COUNT
#define INA3221_CYCLE_COUNT 127
#warning **** INA3221 bad INA3221_CYCLE_COUNT ****
#warning **** must be 1 to 127 ... set to 127 ****
#endif
#ifndef INA3221_ADDRESS1
@ -77,10 +102,10 @@
#error "**** INA3221 bad combination for ADDRESS1 and MAX_COUNT ****"
#endif
#define INA3221_NB_CHAN (3)
#if (INA3221_NB_CHAN>4)
#error "**** INA3221 too manny channels ****"
#endif
// Config register - ch : 0..2
#define INA3221_REG_CONFIG (0x00)
@ -128,6 +153,7 @@
#define INA3221_MODE_BUS_VOLTAGE_CONTINUOUS (0x0006)
#define INA3221_MODE_SHUNT_AND_BUS_CONTINOUS (0x0007) // default
// Other registers - ch = 0..2
#define INA3221_REG_SHUNT_VOLTAGE_CH(ch) (0x01+((ch)<<1))
#define INA3221_REG_BUS_VOLTAGE_CH(ch) (0x02+((ch)<<1))
@ -146,7 +172,8 @@
#define INA3221_WARNING_FLAG_CH(ch) (0x0020>>((ch)-1))
#define INA3221_POWER_ALERT_FLAG (0x0004)
#define INA3221_TIMING_ALERT_FLAG (0x0002)
#define INA3221_CONV_READY_FLAG (0x0001)
#define INA3221_CONV_READY_FLAG (0x0001) // The conversion bit is set after all conversions are
// complete. Conversion ready clearsReading the Mask/Enable register.
// Other registers
#define INA3221_REG_POWER_VALID_UPPER_LIMIT (0x10)
@ -159,11 +186,42 @@
#define INA3221_DIE_ID (0x3220)
// General constants
#define INA3221C_BUS_ADC_LSB (0.008) // VBus ADC LSB is 8mV
#define INA3221C_SHUNT_ADC_LSB (0.00004) // VShunt ADC LSB is 40µV
#define INA3221_DEFAULT_SHUNT_RESISTOR (0.1)
#define INA3221C_BUS_ADC_LSB (0.008f) // VBus ADC LSB is 8mV
#define INA3221C_SHUNT_ADC_LSB (0.00004f) // VShunt ADC LSB is 40µV
#define INA3221_DEFAULT_SHUNT_RESISTOR (0.1f)
#define INA3221_ENERGY_FACTOR (1.0/(3600.0*1000.0)) // reading values all xx ms
#define INA3221_ENERGY_FACTOR (1.0f/(3600.0f*1000.0f)) // reading values all xx ms
#ifdef INA3221_SUPPLY_SIDE
#define INA3221_ENABLE_CHAN(device) (((INA3221_SUPPLY_SIDE >> ((device)*4)) & (0xF >> (4-INA3221_NB_CHAN))) << 4)
#endif
/* show final Defines :
#ifdef INA3221_CALC_CHARGE_AH
#define BF_DEF_CHARGE_AH defined
#else
#define BF_DEF_CHARGE_AH not defined
#endif
#ifdef INA3221_CALC_ENERGY_WH
#define BF_DEF_ENERGY_WH defined
#else
#define BF_DEF_ENERGY_WH not defined
#endif
#ifdef INA3221_SUPPLY_SIDE
#define BF_DEF_SUPPLY_SIDE defined ... INA3221_SUPPLY_SIDE
#else
#define BF_DEF_SUPPLY_SIDE not defined
#endif
#pragma message ("\n=========================>"\
"\n Sensor XSNS_ = " BF_TEXT(XSNS_100)" ... I2C-driver = " BF_TEXT(XI2C_72) \
"\n first Address = " BF_TEXT(INA3221_ADDRESS1)\
"\n init-config = " BF_TEXT(INA3221_CONFIG_INIT)\
"\n INA3221_MAX_COUNT = " BF_TEXT(INA3221_MAX_COUNT)\
"\n INA3221_CYCLE_COUNT = " BF_TEXT(INA3221_CYCLE_COUNT)\
"\n CHARGE_AH = " BF_TEXT(BF_DEF_CHARGE_AH)" ... ENERGY_WH = " BF_TEXT(BF_DEF_ENERGY_WH)\
"\n SUPPLY_SIDE = " BF_TEXT(BF_DEF_SUPPLY_SIDE) \
"\n=========================>\n\n")
// end show final Defines */
#ifdef DEBUG_TASMOTA_SENSOR
// temporary strings for floating point in debug messages
@ -199,11 +257,52 @@ struct INA3221_Data {
struct INA3221_Data *Ina3221Data = nullptr;
uint8_t Ina3221count = 0;
static uint8_t _ina3221_current_device = 0;
static int8_t _ina3221_current_device = 0;
#define D_INA3221 "INA3221"
const char INA3221_TYPE[] = D_INA3221;
#ifdef INA3221_SUPPLY_SIDE
bool Ina3221WriteConfig(uint8_t device)
{
uint16_t config = 0x8000;
for (uint32_t ch = 0; ch < INA3221_NB_CHAN; ch++) {
if ((Ina3221Data[device].enabled_chan & (1 << ch)) || (Ina3221Data[device].enabled_chan & (1 << (ch+4)))){
config |= INA3221_ENABLE_CH(ch);
}
}
config |= INA3221_CONFIG_INIT;
// bf.. INA3221_MODE_SHUNT_AND_BUS_CONTINOUS ableiten aus Ina3221Data[device].enabled_chan device !!
if (Ina3221Data[device].enabled_chan < 0x0F){
config |= INA3221_MODE_SHUNT_VOLTAGE_CONTINUOUS;
}
else if (Ina3221Data[device].enabled_chan == (Ina3221Data[device].enabled_chan & 0xF0) ){
config |= INA3221_MODE_BUS_VOLTAGE_CONTINUOUS;
}
else{
config |= INA3221_MODE_SHUNT_AND_BUS_CONTINOUS;
}
/*
#define INA3221_ENABLE_MASK (0x7000)
#define INA3221_ENABLE_CH(ch) (0x4000>>(ch)) // default: set
#define INA3221_MODE_SHUNT_VOLTAGE_CONTINUOUS (0x0005)
#define INA3221_MODE_BUS_VOLTAGE_CONTINUOUS (0x0006)
#define INA3221_MODE_SHUNT_AND_BUS_CONTINOUS (0x0007) // default
*/
#ifdef DEBUG_TASMOTA_SENSOR
DEBUG_SENSOR_LOG(PSTR(D_INA3221 ":WriteConfig: device=%d, addr:0x%02X, onfiguration register=0x%04X"),device+1, Ina3221Data[device].i2caddr, config);
#endif
// Set Config register
if (!I2cWrite16(Ina3221Data[device].i2caddr, INA3221_REG_CONFIG, config))
return false;
// AddLog(LOG_LEVEL_DEBUG, PSTR(D_INA3221 ":WriteConfig: device=%d, addr:0x%02X, onfiguration register=0x%04X"),device+1, Ina3221Data[device].i2caddr, config);
return true;
}
#endif
bool Ina3221SetConfig(uint8_t addr)
{
// check if device is a INA3221
@ -216,8 +315,7 @@ bool Ina3221SetConfig(uint8_t addr)
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_INA3221 ":SetConfig: manId=0x%04X, dieId=0x%04X"), manufacturer_id, die_id);
return false;
}
// write default configuration
#ifndef INA3221_SUPPLY_SIDE
uint16_t config = INA3221_ENABLE_MASK |
INA3221_CONFIG_INIT |
INA3221_MODE_SHUNT_AND_BUS_CONTINOUS;
@ -225,6 +323,7 @@ bool Ina3221SetConfig(uint8_t addr)
// Set Config register
if (!I2cWrite16(addr, INA3221_REG_CONFIG, config))
return false;
#endif
return true;
}
@ -247,56 +346,105 @@ bool Ina3221PowerDown(uint8_t device)
void Ina3221SetShunt(uint8_t device, uint8_t channel, float shunt)
{
Ina3221Data[device].chan[channel].shunt = shunt;
if (shunt > 0.0){
#ifdef INA3221_SUPPLY_SIDE
if (fabs(shunt) > 0.0f){
#else
if (shunt > 0.0f){
#endif
#if (defined(INA3221_CALC_CHARGE_AH) || defined(INA3221_CALC_ENERGY_WH))
if (!(Ina3221Data[device].enabled_chan & (1<<channel))){
#ifdef INA3221_CALC_CHARGE_AH
Ina3221Data[device].chan[channel].charge_ah = 0.0f;
#endif
#ifdef INA3221_CALC_ENERGY_WH
if ((Ina3221Data[device].enabled_chan & (1<<(channel+4)))){
Ina3221Data[device].chan[channel].energy_wh = 0.0f;
}
#endif
}
#endif
Ina3221Data[device].enabled_chan |= (1<<channel);
}else{
}
else{
Ina3221Data[device].enabled_chan &= ~(1<<channel);
Ina3221Data[device].chan[channel].current = NAN;
#ifdef INA3221_CALC_CHARGE_AH
Ina3221Data[device].chan[channel].charge_ah =0.0;
Ina3221Data[device].chan[channel].charge_ah = NAN;
#endif
#ifdef INA3221_CALC_ENERGY_WH
Ina3221Data[device].chan[channel].energy_wh =0.0;
Ina3221Data[device].chan[channel].energy_wh = NAN;
#endif
}
// AddLog(LOG_LEVEL_DEBUG, PSTR(D_INA3221 ":enabled: device/channel=%d/%d,shunt=%-3_f enabled=0x%02X"), device+1,channel,&shunt,Ina3221Data[device].enabled_chan);
}
bool Ina3221Read(uint8_t device, uint8_t channel)
{
// if (channel>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];
bus_voltage = I2cReadS16(addr, INA3221_REG_BUS_VOLTAGE_CH(channel));
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);
// Convert to VBus voltage in V
pChannel->voltage = INA3221C_BUS_ADC_LSB * (float)(bus_voltage >> 3);
if (pChannel->shunt > 0.0) {
shunt_voltage = I2cReadS16(addr, INA3221_REG_SHUNT_VOLTAGE_CH(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;
}
else {
pChannel->current = INFINITY;
#ifdef INA3221_CALC_CHARGE_AH
pChannel->charge_ah = INFINITY;
#endif
#ifdef INA3221_CALC_ENERGY_WH
pChannel->energy_wh = INFINITY;
#endif
#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;
}
@ -308,28 +456,32 @@ bool Ina3221Read(uint8_t device, uint8_t channel)
bool Ina3221CmndSensor(void)
{
int argc = ArgC();
if(argc != 1 && argc != 4) {
AddLog(LOG_LEVEL_DEBUG, PSTR(D_INA3221 ": Not enough arguments (1 or 4)"));
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) {
DEBUG_SENSOR_LOG(D_INA3221 ":Sensor: invalid device %d", device+1);
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);
}
if (!Ina3221WriteConfig(device)){
#ifdef DEBUG_TASMOTA_SENSOR
DEBUG_SENSOR_LOG(D_INA3221 "error write configuration %d", device+1);
#endif
return false;
}
}
Response_P(INA3221_SENSORCMND_START, XSNS_100, device +1, Ina3221Data[device].i2caddr);
for (int channel = 0 ; channel < INA3221_NB_CHAN ; channel++ ) {
dtostrfd(Ina3221Data[device].chan[channel].shunt,5,argument);
ResponseAppend_P(PSTR("%s%c"), argument, ((channel < (INA3221_NB_CHAN-1))?',':'\0'));
ResponseAppend_P(PSTR("%5_f%c"),&Ina3221Data[device].chan[channel].shunt , ((channel < (INA3221_NB_CHAN-1))?',':'\0'));
}
ResponseAppend_P(INA3221_SENSORCMND_END);
@ -342,23 +494,40 @@ 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) {
Ina3221Data = (struct INA3221_Data*)calloc(INA3221_MAX_COUNT,sizeof(struct INA3221_Data));
// 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;
}
}
if (Ina3221SetConfig(addr)) {
// 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;
Ina3221Data[Ina3221count].chan[0].shunt = \
Ina3221Data[Ina3221count].chan[1].shunt = \
Ina3221Data[Ina3221count].chan[2].shunt = 0.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++;
}
}
@ -373,27 +542,35 @@ void Ina3221Detect(void)
void Ina3221Every250ms(void)
{
DEBUG_SENSOR_LOG(PSTR(D_INA3221 ": cur:%d, en:%d"), _ina3221_current_device, Ina3221Data[_ina3221_current_device].enabled_chan);
uint8_t enabled_chan = Ina3221Data[_ina3221_current_device].enabled_chan;
for (int chan = 0 ; enabled_chan ; chan++, enabled_chan>>=1) {
if (0x01 & enabled_chan)
Ina3221Read(_ina3221_current_device, chan);
}
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 = 0;
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} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
#define INA3221_AL "<td style='text-align:right'>"
const char HTTP_SNS_INA3221_HEADER[] PROGMEM =
// "{s}" D_INA3221 "&nbsp;&nbsp;&nbsp;&nbsp;</th><td>&nbsp;</td><td style='text-align:right'>" D_VOLTAGE " </td><td>&nbsp;</td><td style='text-align:right'>" D_CURRENT " </td><td>&nbsp;</td><td style='text-align:right'>" D_POWERUSAGE
"{s}" D_INA3221 "&nbsp;&nbsp;&nbsp;&nbsp;</th>" INA3221_AL D_VOLTAGE " </td><td>&nbsp;</td>" INA3221_AL D_CURRENT " </td><td>&nbsp;</td>" INA3221_AL D_POWERUSAGE
#ifdef INA3221_CALC_CHARGE_AH
"</td><td>&nbsp;</td>" INA3221_AL D_CHARGE
@ -404,31 +581,31 @@ const char HTTP_SNS_INA3221_HEADER[] PROGMEM =
"{e}";
const char HTTP_SNS_INA3221_DATA[] PROGMEM =
// "{s}%s </th></th><td>&nbsp;</td><td style='text-align:right'>%s " D_UNIT_VOLT " </td><td>&nbsp;</td><td style='text-align:right'>%s " D_UNIT_AMPERE " </td><td>&nbsp;</td><td style='text-align:right'>%s " D_UNIT_WATT
"{s}%s </th></th>" INA3221_AL " %s " D_UNIT_VOLT " </td><td>&nbsp;</td>" INA3221_AL " %s " D_UNIT_AMPERE " </td><td>&nbsp;</td>" INA3221_AL " %s " D_UNIT_WATT
"{s}%s </th>" INA3221_AL " %*_f " D_UNIT_VOLT " </td><td>&nbsp;</td>" INA3221_AL " %*_f " D_UNIT_AMPERE " </td><td>&nbsp;</td>" INA3221_AL " %*_f " D_UNIT_WATT
#ifdef INA3221_CALC_CHARGE_AH
"</td><td>&nbsp;</td>" INA3221_AL " %s " D_UNIT_CHARGE
"</td><td>&nbsp;</td>" INA3221_AL " %*_f " D_UNIT_CHARGE
#endif
#ifdef INA3221_CALC_ENERGY_WH
"</td><td>&nbsp;</td>" INA3221_AL " %s " D_UNIT_WATTHOUR
"</td><td>&nbsp;</td>" INA3221_AL " %*_f " D_UNIT_WATTHOUR
#endif
"{e}";
#endif // USE_WEBSERVER
#endif // USE_WEBSERVER
void Ina3221Show(bool json)
{
char name[FLOATSZ];
char temp[FLOATSZ];
char voltage[3*FLOATSZ+3];
char current[3*FLOATSZ+3];
char power[3*FLOATSZ+3];
// 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[3*FLOATSZ+3];
char charge_ah[INA3221_NB_CHAN*FLOATSZ+3];
#endif
#ifdef INA3221_CALC_ENERGY_WH
char energy_wh[3*FLOATSZ+3];
char energy_wh[INA3221_NB_CHAN*FLOATSZ+3];
#endif
float pw = 0.0f;
if (json) {
// data
for (int device=0 ; device < Ina3221count ; device++) {
@ -440,56 +617,23 @@ void Ina3221Show(bool json)
}
else{
snprintf_P(name, sizeof(name), PSTR("%s"), INA3221_TYPE);
voltage[0] = current[0] = power[0] =
#ifdef INA3221_CALC_CHARGE_AH
charge_ah[0] =
#endif
#ifdef INA3221_CALC_ENERGY_WH
energy_wh[0] =
#endif
'\0';
}
for (int chan=0 ; enabled_chan ; chan++, enabled_chan>>=1) {
if (0x01 & enabled_chan) {
dtostrfd(Ina3221Data[device].chan[chan].voltage, Settings->flag2.voltage_resolution, temp);
strncat(voltage, temp, sizeof(voltage));
dtostrfd(Ina3221Data[device].chan[chan].current, Settings->flag2.current_resolution, temp);
strncat(current, temp, sizeof(current));
dtostrfd(Ina3221Data[device].chan[chan].voltage * Ina3221Data[device].chan[chan].current, Settings->flag2.wattage_resolution, temp);
strncat(power, temp, sizeof(power));
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(&current[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
dtostrfd(Ina3221Data[device].chan[chan].charge_ah, Settings->flag2.energy_resolution, temp);
strncat(charge_ah, temp, sizeof(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
dtostrfd(Ina3221Data[device].chan[chan].energy_wh, Settings->flag2.energy_resolution, temp);
strncat(energy_wh, temp, sizeof(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
else {
strncat(voltage, "null", sizeof(voltage));
strncat(current, "null", sizeof(current));
strncat(power, "null", sizeof(power));
#ifdef INA3221_CALC_CHARGE_AH
strncat(charge_ah, "null", sizeof(charge_ah));
#endif
#ifdef INA3221_CALC_ENERGY_WH
strncat(energy_wh, "null", sizeof(energy_wh));
#endif
}
if (0xFE & enabled_chan) {
strncat(voltage, ",", sizeof(voltage));
strncat(current, ",", sizeof(current));
strncat(power, ",", sizeof(power));
#ifdef INA3221_CALC_CHARGE_AH
strncat(charge_ah, ",", sizeof(charge_ah));
#endif
#ifdef INA3221_CALC_ENERGY_WH
strncat(energy_wh, ",", sizeof(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]"
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
@ -519,30 +663,23 @@ void Ina3221Show(bool json)
// 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) {
if (0x01 & 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{
}
else{
snprintf_P(name, sizeof(name), PSTR("%s:%d"), INA3221_TYPE, chan);
dtostrfd(Ina3221Data[device].chan[chan].voltage, Settings->flag2.voltage_resolution, voltage);
dtostrfd(Ina3221Data[device].chan[chan].current, Settings->flag2.current_resolution, current);
dtostrfd(Ina3221Data[device].chan[chan].voltage * Ina3221Data[device].chan[chan].current, Settings->flag2.wattage_resolution, power);
#ifdef INA3221_CALC_CHARGE_AH
dtostrfd(Ina3221Data[device].chan[chan].charge_ah, Settings->flag2.energy_resolution, charge_ah);
#endif
#ifdef INA3221_CALC_ENERGY_WH
dtostrfd(Ina3221Data[device].chan[chan].energy_wh, Settings->flag2.energy_resolution, energy_wh);
#endif
WSContentSend_PD(HTTP_SNS_INA3221_DATA, name, voltage, current, power
#ifdef INA3221_CALC_CHARGE_AH
, charge_ah
#endif
#ifdef INA3221_CALC_ENERGY_WH
, energy_wh
#endif
);
}
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