Tue, 28 Mar 2023 22:13:06 +0200
Added begin of INA219 measurements. Added raw main state table.
main/CMakeLists.txt | file | annotate | diff | comparison | revisions | |
main/config.c | file | annotate | diff | comparison | revisions | |
main/config.h | file | annotate | diff | comparison | revisions | |
main/iotbalkon.c | file | annotate | diff | comparison | revisions | |
main/task_ina219.c | file | annotate | diff | comparison | revisions | |
main/task_ina219.h | file | annotate | diff | comparison | revisions |
--- a/main/CMakeLists.txt Tue Mar 28 11:25:46 2023 +0200 +++ b/main/CMakeLists.txt Tue Mar 28 22:13:06 2023 +0200 @@ -1,2 +1,2 @@ -idf_component_register(SRCS config.c iotbalkon.c task_bmp280.c +idf_component_register(SRCS config.c iotbalkon.c task_bmp280.c task_ina219.c INCLUDE_DIRS ".")
--- a/main/config.c Tue Mar 28 11:25:46 2023 +0200 +++ b/main/config.c Tue Mar 28 22:13:06 2023 +0200 @@ -7,4 +7,6 @@ bmp280_params_t bmp280_params; bmp280_t bmp280_dev; +ina219_t ina219_b_dev; +ina219_t ina219_s_dev;
--- a/main/config.h Tue Mar 28 11:25:46 2023 +0200 +++ b/main/config.h Tue Mar 28 22:13:06 2023 +0200 @@ -48,11 +48,13 @@ */ #include <i2cdev.h> #include <bmp280.h> +#include <ina219.h> /* * Application sources */ #include "task_bmp280.h" +#include "task_ina219.h" //#include "task_wifi.h" //#include "task_mqtt.h" //#include "task_user.h"
--- a/main/iotbalkon.c Tue Mar 28 11:25:46 2023 +0200 +++ b/main/iotbalkon.c Tue Mar 28 22:13:06 2023 +0200 @@ -7,13 +7,27 @@ static const char *TAG = "iotbalkon"; +#define State_Init 0 +#define State_Connect 1 +#define State_Working 2 +#define State_WorkDone 3 +#define State_Stop 4 +#define State_Wait 5 +#define State_Measure 6 +#define State_GoSleep 7 + static TaskHandle_t xTaskBMP280 = NULL; +static TaskHandle_t xTaskINA219 = NULL; + extern BMP280_State *bmp280_state; ///< I2C state extern SemaphoreHandle_t xSemaphoreBMP280; ///< I2C lock semaphore extern bmp280_params_t bmp280_params; extern bmp280_t bmp280_dev; +extern SemaphoreHandle_t xSemaphoreINA219; +extern ina219_t ina219_b_dev; +extern ina219_t ina219_s_dev; void app_main(void) @@ -29,6 +43,8 @@ bmp280_init_default_params(&bmp280_params); memset(&bmp280_dev, 0, sizeof(bmp280_t)); + memset(&ina219_b_dev, 0, sizeof(ina219_t)); + memset(&ina219_s_dev, 0, sizeof(ina219_t)); i2c_dev_t dev = { 0 }; dev.cfg.sda_io_num = CONFIG_I2C_MASTER_SDA; @@ -41,10 +57,14 @@ } dev.addr = 0x40; if (i2c_dev_probe(&dev, I2C_DEV_WRITE) == 0) { + ESP_ERROR_CHECK(ina219_init_desc(&ina219_b_dev, 0x40, 0, CONFIG_I2C_MASTER_SDA, CONFIG_I2C_MASTER_SCL)); + ESP_ERROR_CHECK(ina219_init(&ina219_b_dev)); ESP_LOGI(TAG, "Found INA219 Battery"); } dev.addr = 0x41; if (i2c_dev_probe(&dev, I2C_DEV_WRITE) == 0) { + ESP_ERROR_CHECK(ina219_init_desc(&ina219_s_dev, 0x41, 0, CONFIG_I2C_MASTER_SDA, CONFIG_I2C_MASTER_SCL)); + ESP_ERROR_CHECK(ina219_init(&ina219_s_dev)); ESP_LOGI(TAG, "Found INA219 Solar"); } dev.addr = 0x76; @@ -52,10 +72,13 @@ ESP_ERROR_CHECK(bmp280_init_desc(&bmp280_dev, BMP280_I2C_ADDRESS_0, 0, CONFIG_I2C_MASTER_SDA, CONFIG_I2C_MASTER_SCL)); ESP_ERROR_CHECK(bmp280_init(&bmp280_dev, &bmp280_params)); ESP_LOGI(TAG, "Found BMP280 @ 0x76 id: 0x%02x", bmp280_dev.id); - } - dev.addr = 0x77; - if (i2c_dev_probe(&dev, I2C_DEV_WRITE) == 0) { - ESP_LOGI(TAG, "Found BMP280 @ 0x77"); + } else { + dev.addr = 0x77; + if (i2c_dev_probe(&dev, I2C_DEV_WRITE) == 0) { + ESP_ERROR_CHECK(bmp280_init_desc(&bmp280_dev, BMP280_I2C_ADDRESS_1, 0, CONFIG_I2C_MASTER_SDA, CONFIG_I2C_MASTER_SCL)); + ESP_ERROR_CHECK(bmp280_init(&bmp280_dev, &bmp280_params)); + ESP_LOGI(TAG, "Found BMP280 @ 0x77 id: 0x%02x", bmp280_dev.id); + } } /* @@ -64,13 +87,238 @@ xSemaphoreBMP280 = xSemaphoreCreateMutex(); xTaskCreate(&task_bmp280, "task_bmp280", 2560, NULL, 8, &xTaskBMP280); + xTaskCreate(&task_ina219, "task_ina219", 2560, NULL, 8, &xTaskINA219); /* * Main application loop. */ + int State = State_Init; + int OldState = State_Init + 1; + while (1) { - request_bmp280(); - vTaskDelay(2000 / portTICK_PERIOD_MS); + if (OldState != State) { + ESP_LOGI(TAG, "Switch to state %d", State); + OldState = State; + } + switch (State) { + case State_Init: request_bmp280(); + request_ina219(); + // getTempBaro(); + // getLightValues(); + // getVoltsCurrent(); + State = State_Connect; + break; + + case State_Connect: // Wake WiFi ?? + // if (NetworkConnect()) { + // State = State_Working; + // Alarm &= ~AL_NOWIFI; + // DisCounter = 0; + // } else { + // DisCounter++; + // if (DisCounter > 30) { + // Alarm |= AL_NOWIFI; + // } + // delay(2000); + // } + break; + + case State_Working: // WorkAgain = false; + // client.loop(); + + // // Measure + // getVoltsCurrent(); + // solarVolts = solarCurrent = batteryVolts = batteryCurrent = 0; + // loops = 0; + // totalTime = 0; + // for (int i = 0; i < loopno; i++) { + /* + * If there are only 2 loops, and the flag that we came from deep-sleep is set + * then assume the following: + * 1. No current (or very low for the dc converter) during DS_TIME seconds. + * 2. Low power during 0.5 second (no WiFi yet). + * 3. 5 seconds power usage for the last measurement. + * Calculate the average battery current from this. + * + * If there are more loops and we came from continues running do the following: + * 1. Take the current use from all exept the last one, weight loops * 10 seconds. + * 2. Take the last one, and weight for 5 seconds. + * Calculate the average battery current from this. + */ + // if (m_Valid[i]) { + // solarVolts += s_Volts[i]; + // solarCurrent += s_Current[i]; + // batteryVolts += b_Volts[i]; + // if (i == (loopno - 1)) { + // // Add the extra time + // m_Time[i] += SUB_TIME; + // } + // batteryCurrent += b_Current[i] * m_Time[i]; + // totalTime += m_Time[i]; + // loops++; + // } + // } + + // if (EEPROM.read(EM_DS_Active)) { + // totalTime += EEPROM.read(EM_DS_Time) * 1000; + // batteryCurrent += DS_CURRENT * EEPROM.read(EM_DS_Time) * 1000; + // // Serial.printf("Added %d totalTime %d\n", EEPROM.read(EM_DS_Time) * 1000, totalTime); + // } + + // If valid measurements + // if (loops) { + // solarVolts = solarVolts / loops; + // solarCurrent = solarCurrent / loops; + // solarPower = solarVolts * solarCurrent; + // batteryVolts = batteryVolts / loops; + // batteryCurrent = batteryCurrent / totalTime; + // batteryPower = batteryVolts * batteryCurrent; + // BatteryState(batteryVolts, (0 - batteryCurrent) + solarCurrent); + + // //#if Debug == true + // Serial.print(F(" Solar Volts: ")); + // Serial.print(solarVolts, 4); + // Serial.print(F("V Current: ")); + // Serial.print(solarCurrent, 4); + // Serial.print(F("mA Power: ")); + // Serial.print(solarPower, 4); + // Serial.println(F("mW")); + + // Serial.print(F("Battery Volts: ")); + // Serial.print(batteryVolts, 4); + // Serial.print(F("V Current: ")); + // Serial.print(batteryCurrent, 4); + // Serial.print(F("mA Power: ")); + // Serial.print(batteryPower, 4); + // Serial.print(F("mW Capacity ")); + // Serial.print(batteryState); + // Serial.println(F("%")); + // //#endif + + /* Check alarm conditions */ + // if (batteryState <= 10) { + // Alarm |= AL_ACCULOW; + // } else { + // Alarm &= ~AL_ACCULOW; + // } + // } + // getTempHumi(); + // Publish(); + // Subscribe + // #if Production == true + // client.subscribe("balkon/output/set/#"); + // #else + // client.subscribe("wemos/output/set/#"); + // #endif + // client.loop(); + // State = State_WorkDone; + // gTimeNext = millis() + SUB_TIME; + break; + + case State_WorkDone: // Hang around for a while to process the subscriptions. + // client.loop(); + // delay(1); + // if (WorkAgain) { + // // Some command was executed. + // State = State_Working; + // } + // if (gTimeInMillis > gTimeNext) { + // State = State_Stop; + // } + break; + + case State_Stop: // #if Production == true + // client.unsubscribe("balkon/output/set/#"); + // #else + // client.unsubscribe("wemos/output/set/#"); + // #endif + // delay(1); + // client.loop(); + // client.disconnect(); + // delay(1); + // WiFi.mode( WIFI_OFF ); + // // Reset values for average current measurement. + // HaveIP = false; + // loopno = 0; + // gLastTime = millis(); + // delay(10); + // #if defined(ARDUINO_ESP8266_WEMOS_D1MINI) + // WiFi.forceSleepBegin(0); // 0 == forever + // #endif + + /* + * If any output is on, use 6 10 seconds loops. + * If nothing on, do a deep sleep. + */ + // if (EEPROM.read(EM_Relay1) || EEPROM.read(EM_Relay2) || EEPROM.read(EM_Dimmer3) || EEPROM.read(EM_Dimmer4)) { + // if (EEPROM.read(EM_DS_Active)) { + // EEPROM.write(EM_DS_Active, 0); + // EEPROM.commit(); + // } + + // // Active mode, 60 seconds loop + // ST_LOOPS = 6; + // gTimeNext = millis() + ST_INTERVAL; + // #if Debug == true + // Serial.println(F("Start sleeploops")); + // #endif + // State = State_Wait; + // } else { + // ds_time = DS_TIME; + // if (solarVolts < 6) { + // // At night, increase the deep-sleep time. + // ds_time *= 4; + // } + // if ((! EEPROM.read(EM_DS_Active)) || (EEPROM.read(EM_DS_Time) != ds_time)) { + // EEPROM.write(EM_DS_Active, 1); + // EEPROM.write(EM_DS_Time, ds_time); + // EEPROM.commit(); + // Serial.println("wrote new deep-sleep settings"); + // } + // State = State_GoSleep; + // } + /* + * Update CRC and write rtcData. + */ + // rtcData.crc32 = calculateCRC32((uint8_t*) &rtcData.data, sizeof(rtcData)-4); + // #if defined(ARDUINO_ESP8266_WEMOS_D1MINI) + // if (ESP.rtcUserMemoryWrite(0, (uint32_t*) &rtcData, sizeof(rtcData))) { + // #if Debug == true + // Serial.print("Write: "); + // printMemory(); + // #endif + // } else { + // Serial.println("Write error rtcData"); + // } + // #endif + break; + + case State_Wait: // if (gTimeInMillis > gTimeNext) { + // State = State_Measure; + // } + break; + + case State_Measure: // getVoltsCurrent(); + // if (isnan(Temperature)) { + // getTempHumi(); + // } + // gTimeNext = millis() + ST_INTERVAL; + // if (loopno >= ST_LOOPS) { + // getLightValues(); + // State = State_Connect; + // } else { + // State = State_Wait; + // } + break; + + case State_GoSleep: // ds_time = EEPROM.read(EM_DS_Time); + // #if Debug == true + // Serial.printf("Going to deep-sleep for %d seconds\n", ds_time); + // #endif + // ESP.deepSleep(ds_time * 1e6); + break; + } + vTaskDelay(20 / portTICK_PERIOD_MS); } // Not reached. }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main/task_ina219.c Tue Mar 28 22:13:06 2023 +0200 @@ -0,0 +1,153 @@ +/** + * @file task_ina219.c + * @brief The FreeRTOS task to query the INA219 sensors. + */ + + +#include "config.h" + + +static const char *TAG = "task_ina219"; + +SemaphoreHandle_t xSemaphoreINA219 = NULL; ///< Semaphore INA219 task +EventGroupHandle_t xEventGroupINA219; ///< Events INA219 task +INA219_State *ina219_state; ///< Public state for other tasks + +extern ina219_t ina219_b_dev; +extern ina219_t ina219_s_dev; + +const int TASK_INA219_REQUEST_DONE = BIT0; ///< All requests are done. +const int TASK_INA219_REQUEST_POWER = BIT1; ///< Request power readings + + + +void request_ina219(void) +{ + xEventGroupClearBits(xEventGroupINA219, TASK_INA219_REQUEST_DONE); + xEventGroupSetBits(xEventGroupINA219, TASK_INA219_REQUEST_POWER); +} + + + +bool ready_ina219(void) +{ + if (xEventGroupGetBits(xEventGroupINA219) & TASK_INA219_REQUEST_DONE) + return true; + return false; +} + + + +/* + * Task to read INA219 sensors on request. + */ +void task_ina219(void *pvParameter) +{ + int error = 0; + float bus_voltage, shunt_voltage, current, power; + + ESP_LOGI(TAG, "Starting task INA219 sda=%d scl=%d", CONFIG_I2C_MASTER_SDA, CONFIG_I2C_MASTER_SCL); + ina219_state = malloc(sizeof(INA219_State)); + + ina219_state->Battery.valid = false; + ina219_state->Battery.fake = (ina219_b_dev.i2c_dev.addr == 0) ? true:false; + ina219_state->Battery.address = ina219_b_dev.i2c_dev.addr; + ina219_state->Battery.error = INA219_ERR_NONE; + if (ina219_b_dev.i2c_dev.addr) { + ESP_LOGI(TAG, "Configuring INA219 Battery"); + ESP_ERROR_CHECK(ina219_configure(&ina219_b_dev, INA219_BUS_RANGE_16V, INA219_GAIN_0_125, + INA219_RES_12BIT_1S, INA219_RES_12BIT_1S, INA219_MODE_CONT_SHUNT_BUS)); + ESP_LOGI(TAG, "Calibrating INA219 Battery"); + ESP_ERROR_CHECK(ina219_calibrate(&ina219_b_dev, (float)I_MAX_CURRENT, (float)I_SHUNT_RESISTOR_MILLI_OHM / 1000.0f)); + } + + ina219_state->Solar.valid = false; + ina219_state->Solar.fake = (ina219_s_dev.i2c_dev.addr == 0) ? true:false; + ina219_state->Solar.address = ina219_s_dev.i2c_dev.addr; + ina219_state->Solar.error = INA219_ERR_NONE; + if (ina219_s_dev.i2c_dev.addr) { + ESP_LOGI(TAG, "Configuring INA219 Solar"); + ESP_ERROR_CHECK(ina219_configure(&ina219_s_dev, INA219_BUS_RANGE_16V, INA219_GAIN_0_125, + INA219_RES_12BIT_1S, INA219_RES_12BIT_1S, INA219_MODE_CONT_SHUNT_BUS)); + ESP_LOGI(TAG, "Calibrating INA219 Solar"); + ESP_ERROR_CHECK(ina219_calibrate(&ina219_s_dev, (float)I_MAX_CURRENT, (float)I_SHUNT_RESISTOR_MILLI_OHM / 1000.0f)); + } + + /* event handler and event group for this task */ + xEventGroupINA219 = xEventGroupCreate(); + EventBits_t uxBits; + + /* + * Task loop forever. + */ + ESP_LOGI(TAG, "Starting loop INA219 sensors 0x%02x %d, 0x%02x %d", + ina219_state->Battery.address, ina219_state->Battery.fake, ina219_state->Solar.address, ina219_state->Solar.fake); + while (1) { + + uxBits = xEventGroupWaitBits(xEventGroupINA219, TASK_INA219_REQUEST_POWER, pdFALSE, pdFALSE, portMAX_DELAY ); + + if (uxBits & TASK_INA219_REQUEST_POWER) { + + /* + * Four scenario's: + * 1. Both sensors present, just use them. + * 2. Only battery sensor (test environment). Use it and fake + * the solar chip as if it is charging. + * 3. Only solar sensor. Use scenario 4, but show measured values. + * 4. Fake everything. + */ + if (! ina219_state->Battery.fake) { + ESP_ERROR_CHECK(ina219_get_bus_voltage(&ina219_b_dev, &bus_voltage)); + ESP_ERROR_CHECK(ina219_get_shunt_voltage(&ina219_b_dev, &shunt_voltage)); + ESP_ERROR_CHECK(ina219_get_current(&ina219_b_dev, ¤t)); + ESP_ERROR_CHECK(ina219_get_power(&ina219_b_dev, &power)); + ESP_LOGI(TAG, "Battery VBUS: %.04f V, VSHUNT: %.04f mV, IBUS: %.04f mA, PBUS: %.04f mW", + bus_voltage, shunt_voltage * 1000, current * 1000, power * 1000); + } + if (! ina219_state->Solar.fake) { + ESP_ERROR_CHECK(ina219_get_bus_voltage(&ina219_s_dev, &bus_voltage)); + ESP_ERROR_CHECK(ina219_get_shunt_voltage(&ina219_s_dev, &shunt_voltage)); + ESP_ERROR_CHECK(ina219_get_current(&ina219_s_dev, ¤t)); + ESP_ERROR_CHECK(ina219_get_power(&ina219_s_dev, &power)); + ESP_LOGI(TAG, " Solar VBUS: %.04f V, VSHUNT: %.04f mV, IBUS: %.04f mA, PBUS: %.04f mW", + bus_voltage, shunt_voltage * 1000, current * 1000, power * 1000); + } + + /* + error = ina219_read_float(&ina219_dev, &temperature, &pressure, &humidity); + if (xSemaphoreTake(xSemaphoreINA219, 25) == pdTRUE) { + if (error == ESP_OK) { + ina219_state->error = INA219_ERR_NONE; + ina219_state->valid = true; + ina219_state->temperature = temperature; + ina219_state->pressure = pressure; + ina219_state->humidity = humidity; + } else { + ina219_state->error = INA219_ERR_READ; + ina219_state->valid = false; + ina219_state->temperature = 0; + ina219_state->pressure = 0; + ina219_state->humidity = 0; + } + xSemaphoreGive(xSemaphoreINA219); + } + } else { + if (xSemaphoreTake(xSemaphoreINA219, 25) == pdTRUE) { + ina219_state->error = INA219_ERR_NONE; + ina219_state->valid = true; + ina219_state->temperature = 21.23; + ina219_state->pressure = 101360; + ina219_state->humidity = 0; + xSemaphoreGive(xSemaphoreINA219); + } + } + + */ + xEventGroupClearBits(xEventGroupINA219, TASK_INA219_REQUEST_POWER); + xEventGroupSetBits(xEventGroupINA219, TASK_INA219_REQUEST_DONE); +#if 0 + ESP_LOGI(TAG, " TB: %.3f C, %.1f hPa, error: %d", ina219_state->temperature, ina219_state->pressure / 100, ina219_state->error); +#endif + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main/task_ina219.h Tue Mar 28 22:13:06 2023 +0200 @@ -0,0 +1,77 @@ +/** + * @file task_ina219.h + * @brief The FreeRTOS task to query the INA219 sensors connected to + * the I2C bus. + * The task will update the sensor state structures. + */ + +#ifndef _TASK_INA219_H +#define _TASK_INA219_H + +/* + * Error codes in this task + */ +#define INA219_ERR_NONE 0 ///< No errors +#define INA219_ERR_READ 1 + +#define I_MAX_LOOPS 32 +#define I_MAX_CURRENT 5 ///< Max 5 Ampere +#define I_SHUNT_RESISTOR_MILLI_OHM 100 ///< Shunt value + + +typedef struct { + bool valid; ///< Valid measurement + bool fake; ///< Fake measurement + uint8_t address; ///< Device i2c address + float shunt; ///< Shunt voltage + float volts; ///< Bus voltage + float current; ///< Current mA + int error; ///< Error result +} INA219_tt; + + +/** + * @brief Structure containing the variables for the INA219 task. + */ +typedef struct { + bool valid; ///< Valid measurement + bool fake; ///< Fake measurement + INA219_tt Battery; ///< Battery measurement + INA219_tt Solar; ///< Solar measurement + float s_Volts[I_MAX_LOOPS]; + float s_Current[I_MAX_LOOPS]; + float b_Volts[I_MAX_LOOPS]; + float b_Current[I_MAX_LOOPS]; + bool m_Valid[I_MAX_LOOPS]; + time_t m_Time[I_MAX_LOOPS]; + time_t gLastTime; + uint8_t loopno; + uint8_t loops; + uint8_t tries; + int error; ///< Error result +} INA219_State; + + + +/** + * @brief Request a new measurement from selected sensors. + */ +void request_ina219(void); + + +/** + * @brief Check if results are ready + * @return true of results are ready, else false. + */ +bool ready_ina219(void); + + +/** + * @brief The FreeRTOS task to update the INA219 on request. + * @param pvParameters Parameters for the task. + */ +void task_ina219(void *pvParameters); + + +#endif +