Thu, 06 Apr 2023 13:22:03 +0200
Code changes to try to fix the not working ADPS9930 driver.
/** * @file iotbalkon.c * @brief iotbalkon project. */ #include "config.h" static const char *TAG = "iotbalkon"; /* * Main State table. */ typedef enum { State_Init = 0, State_Connect_Wifi, State_Connect_MQTT, State_Working, State_WorkDone, State_Disconnect_MQTT, State_Disconnect_Wifi, State_Wait, State_Measure, State_GoSleep } Main_State; static TaskHandle_t xTaskBMP280 = NULL; static TaskHandle_t xTaskINA219 = NULL; static TaskHandle_t xTaskAPDS9930 = NULL; static TaskHandle_t xTaskMQTT = NULL; static TaskHandle_t xTaskWifi = NULL; static TaskHandle_t xTaskOUT = NULL; #define MAX_LOOPS 32 #define SUB_TIME 1000 float temperature; float pressure; float solarVolts, solarCurrent, solarPower; float batteryVolts, batteryCurrent, batteryPower; int batteryState; float s_Volts[MAX_LOOPS + 1]; float s_Current[MAX_LOOPS + 1]; float b_Volts[MAX_LOOPS + 1]; float b_Current[MAX_LOOPS + 1]; bool m_Valid[MAX_LOOPS + 1]; uint64_t m_Time[MAX_LOOPS + 1]; uint64_t gLastTime; uint64_t gTimeNext; uint8_t loopno = 0; uint8_t loops = 0; uint8_t ST_LOOPS = 6; int DisCounter = 0; 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; extern INA219_State *ina219_state; extern SemaphoreHandle_t xSemaphoreAPDS9930; extern apds9930_t apds9930_dev; extern APDS9930_State *apds9930_state; extern SemaphoreHandle_t xSemaphoreWiFi; extern WIFI_State *wifi_state; ///< WiFi state uint32_t Alarm = 0; /* * Alarm bits */ #define AL_ACCULOW 0x01 #define AL_NOWIFI 0x02 #define ST_INTERVAL 10000 #define MAX_LOOPS 32 /* * Variables in NVS namespace "balkon" */ uint8_t Relay1; uint8_t Relay2; uint8_t Dimmer3; uint8_t Dimmer4; // 0% 10% 20% 30% 40% 50% 60% 70% 80% 90% 100% float Charge_C_5[11] = { 12.40, 12.60, 12.75, 12.95, 13.20, 13.35, 13.55, 13.67, 14.00, 15.25, 15.94 }; float Charge_C_10[11] = { 12.20, 12.38, 12.60, 12.80, 13.05, 13.20, 13.28, 13.39, 13.60, 14.20, 15.25 }; float Charge_C_20[11] = { 12.00, 12.07, 12.42, 12.70, 12.85, 13.02, 13.11, 13.15, 13.25, 13.60, 14.15 }; float Charge_C_40[11] = { 11.55, 11.80, 12.25, 12.57, 12.70, 12.80, 12.90, 12.95, 13.00, 13.20, 13.50 }; float Rest[11] = { 11.50, 11.70, 11.88, 12.10, 12.22, 12.30, 12.40, 12.50, 12.57, 12.64, 12.72 }; float Load_C_20[11] = { 11.45, 11.70, 11.80, 12.05, 12.20, 12.28, 12.37, 12.48, 12.55, 12.57, 12.60 }; float Load_C_10[11] = { 10.98, 11.27, 11.50, 11.65, 11.85, 12.00, 12.10, 12.20, 12.30, 12.40, 12.50 }; float Load_C_5[11] = { 10.20, 10.65, 10.90, 11.15, 11.35, 11.55, 11.63, 11.75, 11.90, 12.00, 12.08 }; /* * Calculate the load state of the battery. */ void BatteryState(float Voltage, float Current) { int i; batteryState = 0; ESP_LOGI(TAG, "Batt %.4fV %.4fmA", Voltage, Current); if (Current < -750) { // Load current > C/5, 1A for (i = 0; i < 10; i++) { if (Load_C_5[i + 1] >= Voltage) { break; } } batteryState = i * 10; ESP_LOGI(TAG, "Load C/5 %d%%", batteryState); } else if (Current < -375) { // Load current > C/10, 500mA for (i = 0; i < 10; i++) { if (Load_C_10[i + 1] >= Voltage) { break; } } batteryState = i * 10; ESP_LOGI(TAG, "Load C/10 %d%%", batteryState); } else if (Current < -125) { // Load current > C/20, 250mA for (i = 0; i < 10; i++) { if (Load_C_20[i + 1] >= Voltage) { break; } } batteryState = i * 10; ESP_LOGI(TAG, "Load C/20 %d%%", batteryState); } else if (Current > 750) { // Charge > C/5, 1A for (i = 0; i < 10; i++) { if (Charge_C_5[i + 1] >= Voltage) { break; } } batteryState = i * 10; ESP_LOGI(TAG, "Charge C/5 %d%%", batteryState); } else if (Current > 375) { // Charge > C/10, 500mA for (i = 0; i < 10; i++) { if (Charge_C_10[i + 1] >= Voltage) { break; } } batteryState = i * 10; ESP_LOGI(TAG, "Charge C/10 %d%%", batteryState); } else if (Current > 187.5) { // Charge > C/20, 250 mA for (i = 0; i < 10; i++) { if (Charge_C_20[i + 1] >= Voltage) { break; } } batteryState = i * 10; ESP_LOGI(TAG, "Charge C/20 %d%%", batteryState); } else if (Current > 62.5) { // Charge > C/40, 125 mA for (i = 0; i < 10; i++) { if (Charge_C_40[i + 1] >= Voltage) { break; } } batteryState = i * 10; ESP_LOGI(TAG, "Charge C/40 %d%%", batteryState); } else { // Rest for (i = 0; i < 10; i++) { if (Rest[i + 1] >= Voltage) { break; } } batteryState = i * 10; ESP_LOGI(TAG, "Rest %d%%", batteryState); } } uint64_t millis(void) { return esp_timer_get_time() / 1000; } /* * Read the temperature and pressure from the BMP280. */ void getTempBaro() { temperature = 20; pressure = 1000; request_bmp280(); while (! ready_bmp280()) { vTaskDelay(10 / portTICK_PERIOD_MS); } if (xSemaphoreTake(xSemaphoreBMP280, 25) == pdTRUE) { temperature = bmp280_state->temperature; pressure = bmp280_state->pressure / 100.0; ESP_LOGI(TAG, "Temperature: %.2f Pressure: %.1f hPa", temperature, pressure); xSemaphoreGive(xSemaphoreBMP280); } else { ESP_LOGE(TAG, "Can't lock xSemaphoreBMP280"); } } /* * Read voltage and current from both INA219 boards. The results * are stored in an array so that we later can average the results. */ void getVoltsCurrent() { request_ina219(); while (! ready_ina219()) { vTaskDelay(10 / portTICK_PERIOD_MS); } if (xSemaphoreTake(xSemaphoreINA219, 25) == pdTRUE) { s_Volts[loopno] = ina219_state->Solar.volts + ina219_state->Solar.shunt; s_Current[loopno] = ina219_state->Solar.current; b_Volts[loopno] = ina219_state->Battery.volts + ina219_state->Battery.shunt; b_Current[loopno] = ina219_state->Battery.current; m_Valid[loopno] = ina219_state->valid; xSemaphoreGive(xSemaphoreINA219); /* * Check for crazy values */ if ((b_Volts[loopno] < 10) || (b_Volts[loopno] > 15.0)) { m_Valid[loopno] = false; } if ((b_Current[loopno] < 0) || (b_Current[loopno] > 2000)) { m_Valid[loopno] = false; } if (s_Volts[loopno] < 0) { s_Volts[loopno] = 0.0; } if (s_Volts[loopno] > 24) { m_Valid[loopno] = false; } if (s_Current[loopno] < 0) { s_Current[loopno] = 0.0; } if (s_Current[loopno] > 2000) { m_Valid[loopno] = false; } } uint64_t ms = millis(); m_Time[loopno] = ms - gLastTime; gLastTime = ms; ESP_LOGI(TAG, "Measure: %d Valid: %s Battery: %.3fV %.1fmA Solar: %.3fV %.1fmA time %llu", loopno, (m_Valid[loopno]) ? "true":"false", b_Volts[loopno], b_Current[loopno], s_Volts[loopno], s_Current[loopno], m_Time[loopno]); if (loopno < (MAX_LOOPS - 1)) loopno++; } void getLightValues() { request_apds9930(); while (! ready_apds9930()) { vTaskDelay(10 / portTICK_PERIOD_MS); } } void app_main(void) { uint64_t totalTime, gTimeInMillis; nvs_handle_t my_handle; #ifdef CONFIG_CODE_PRODUCTION ESP_LOGI(TAG, "Starting production"); #endif #ifdef CONFIG_CODE_TESTING ESP_LOGI(TAG, "Starting testing"); #endif /* * Initialize NVS */ esp_err_t err = nvs_flash_init(); if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); err = nvs_flash_init(); } ESP_ERROR_CHECK(err); err = nvs_open("balkon", NVS_READWRITE, &my_handle); if (err != ESP_OK) { ESP_LOGE(TAG, "Error (%s) opening NVS handle 'balkon'", esp_err_to_name(err)); } else { err = nvs_get_u8(my_handle, (char *)"out1", &Relay1); if (err == ESP_ERR_NVS_NOT_FOUND) { err = nvs_set_u8(my_handle, (char *)"out1", 0); Relay1 = 0; } if (err != ESP_OK) { ESP_LOGE(TAG, "Error (%s) opening NVS handle 'balkon', key 'out1'", esp_err_to_name(err)); } err = nvs_get_u8(my_handle, (char *)"out2", &Relay2); if (err == ESP_ERR_NVS_NOT_FOUND) { err = nvs_set_u8(my_handle, (char *)"out2", 0); Relay2 = 0; } if (err != ESP_OK) { ESP_LOGE(TAG, "Error (%s) opening NVS handle 'balkon', key 'out2'", esp_err_to_name(err)); } err = nvs_get_u8(my_handle, (char *)"out3", &Dimmer3); if (err == ESP_ERR_NVS_NOT_FOUND) { err = nvs_set_u8(my_handle, (char *)"out3", 0); Dimmer3 = 0; } if (err != ESP_OK) { ESP_LOGE(TAG, "Error (%s) opening NVS handle 'balkon', key 'out3'", esp_err_to_name(err)); } err = nvs_get_u8(my_handle, (char *)"out4", &Dimmer4); if (err == ESP_ERR_NVS_NOT_FOUND) { err = nvs_set_u8(my_handle, (char *)"out4", 0); Dimmer4 = 0; } if (err != ESP_OK) { ESP_LOGE(TAG, "Error (%s) opening NVS handle 'balkon', key 'out4'", esp_err_to_name(err)); } nvs_commit(my_handle); nvs_close(my_handle); } gLastTime = millis(); ESP_ERROR_CHECK(i2cdev_init()); 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)); memset(&apds9930_dev, 0, sizeof(apds9930_t)); i2c_dev_t dev = { 0 }; dev.cfg.sda_io_num = CONFIG_I2C_MASTER_SDA; dev.cfg.scl_io_num = CONFIG_I2C_MASTER_SCL; dev.cfg.master.clk_speed = 400000; dev.addr = 0x39; if (i2c_dev_probe(&dev, I2C_DEV_WRITE) == 0) { ESP_ERROR_CHECK(apds9930_init_desc(&apds9930_dev, 0x39, 0, CONFIG_I2C_MASTER_SDA, CONFIG_I2C_MASTER_SCL)); ESP_ERROR_CHECK(apds9930_init(&apds9930_dev)); ESP_LOGI(TAG, "Found APDS-9930 id: 0x%02x", apds9930_dev.id); } 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 @0x40 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 @0x41 Solar"); } dev.addr = 0x76; if (i2c_dev_probe(&dev, I2C_DEV_WRITE) == 0) { 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); } 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); } } /* * Create FreeRTOS tasks */ xSemaphoreBMP280 = xSemaphoreCreateMutex(); xSemaphoreINA219 = xSemaphoreCreateMutex(); xSemaphoreAPDS9930 = xSemaphoreCreateMutex(); xTaskCreate(&task_bmp280, "task_bmp280", 2560, NULL, 8, &xTaskBMP280); xTaskCreate(&task_ina219, "task_ina219", 2560, NULL, 8, &xTaskINA219); xTaskCreate(&task_apds9930, "task_apds9930", 2560, NULL, 8, &xTaskAPDS9930); xTaskCreate(&task_out, "task_out", 2560, NULL, 9, &xTaskOUT); // esp_log_level_set("MQTT_CLIENT", ESP_LOG_ERROR); xTaskCreate(&task_mqtt, "task_mqtt", 4096, NULL, 5, &xTaskMQTT); // esp_log_level_set("wifi", ESP_LOG_ERROR); xTaskCreate(&task_wifi, "task_wifi", 4096, NULL, 3, &xTaskWifi); vTaskDelay(10 / portTICK_PERIOD_MS); /* * Main application loop. */ int State = State_Init; int OldState = State_Init + 1; while (1) { // request_ina219(); request_apds9930(); vTaskDelay(5000 / portTICK_PERIOD_MS); } while (1) { if (OldState != State) { ESP_LOGI(TAG, "Switch to state %d", State); OldState = State; } gTimeInMillis = millis(); switch (State) { case State_Init: getTempBaro(); getLightValues(); getVoltsCurrent(); State = State_Connect_Wifi; DisCounter = 0; request_WiFi(true); break; case State_Connect_Wifi: if (ready_WiFi()) { State = State_Connect_MQTT; /* * Give the network stack a bit more time to setup */ vTaskDelay(250 / portTICK_PERIOD_MS); request_mqtt(true); Alarm &= ~AL_NOWIFI; ESP_LOGI(TAG, "Connected counter WiFi %d", DisCounter); DisCounter = 0; } else { /* * 30 seconds connection try. */ DisCounter++; vTaskDelay(1000 / portTICK_PERIOD_MS); ESP_LOGI(TAG, "* Counter WiFi %d", DisCounter); if (DisCounter > 30) { Alarm |= AL_NOWIFI; request_WiFi(false); State = State_Init; vTaskDelay(4000 / portTICK_PERIOD_MS); } } break; case State_Connect_MQTT: if (ready_mqtt()) { State = State_Working; ESP_LOGI(TAG, "Connected counter MQTT %d", DisCounter); DisCounter = 0; } else { DisCounter++; if (DisCounter > 60) { request_mqtt(false); request_WiFi(false); State = State_Init; vTaskDelay(2000 / portTICK_PERIOD_MS); } vTaskDelay(1000 / portTICK_PERIOD_MS); } break; case State_Working: /* * The final measure power usage. * This one should include the WiFi usage current. */ getVoltsCurrent(); solarVolts = solarCurrent = solarPower = batteryVolts = batteryCurrent = batteryPower = 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); ESP_LOGI(TAG, " Solar Volts: %.4fV Current %.4fmA Power %.4fmW", solarVolts, solarCurrent, solarPower); ESP_LOGI(TAG, "Battery Volts: %.4fV Current %.4fmA Power %.4fmW", batteryVolts, batteryCurrent, batteryPower); /* Check alarm conditions */ // if (batteryState <= 10) { // Alarm |= AL_ACCULOW; // } else { Alarm &= ~AL_ACCULOW; // } } getTempBaro(); publish(); State = State_WorkDone; gTimeNext = millis() + SUB_TIME; break; case State_WorkDone: vTaskDelay(20 / portTICK_PERIOD_MS); if (gTimeInMillis > gTimeNext) { State = State_Disconnect_MQTT; request_mqtt(false); } break; case State_Disconnect_MQTT: wait_mqtt(10000); State = State_Disconnect_Wifi; break; case State_Disconnect_Wifi: request_WiFi(false); // // Reset values for average current measurement. // HaveIP = false; loopno = 0; gLastTime = millis(); //vTaskDelay(10 / portTICK_PERIOD_MS); /* * If any output is on, use 6 10 seconds loops. * If nothing on, do a deep sleep. */ if (Relay1 || Relay2 || Dimmer3 || 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; ESP_LOGI(TAG, "Start sleeploops"); State = State_Wait; // #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; // } break; case State_Wait: if (gTimeInMillis > gTimeNext) { State = State_Measure; } vTaskDelay(10 / portTICK_PERIOD_MS); break; case State_Measure: getVoltsCurrent(); // if (isnan(Temperature)) { // getTempHumi(); // } gTimeNext = millis() + ST_INTERVAL; if (loopno >= ST_LOOPS) { ESP_LOGI(TAG, "Enough loops, do connect"); getLightValues(); State = State_Connect_Wifi; DisCounter = 0; request_WiFi(true); } else { State = State_Wait; } break; case State_GoSleep: ESP_LOGE(TAG, "Entered GoSleep -- not supported"); // 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. }