main/iotbalkon.c

Thu, 30 Mar 2023 17:05:05 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Thu, 30 Mar 2023 17:05:05 +0200
changeset 5
b1f38105ca7e
parent 4
d0155c16e992
child 6
bad3414f7bc4
permissions
-rw-r--r--

Added task MQTT and some utilities. Added more power measurement variables and code. INA219 measurements are saved in the State record.

/**
 * @file iotbalkon.c
 * @brief iotbalkon project.
 */

#include "config.h"

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;
static TaskHandle_t			xTaskMQTT = NULL;
static TaskHandle_t			xTaskWifi = NULL;

#define	MAX_LOOPS			32

RTC_DATA_ATTR float			solarVolts, solarCurrent, solarPower;
RTC_DATA_ATTR float			batteryVolts, batteryCurrent, batteryPower;
RTC_DATA_ATTR int			batteryState;
RTC_DATA_ATTR float			s_Volts[MAX_LOOPS + 1];
RTC_DATA_ATTR float			s_Current[MAX_LOOPS + 1];
RTC_DATA_ATTR float			b_Volts[MAX_LOOPS + 1];
RTC_DATA_ATTR float			b_Current[MAX_LOOPS + 1];
RTC_DATA_ATTR bool			m_Valid[MAX_LOOPS + 1];
RTC_DATA_ATTR unsigned long		m_Time[MAX_LOOPS + 1];
RTC_DATA_ATTR unsigned long		gLastTime;	// millis()
RTC_DATA_ATTR uint8_t			loopno;
RTC_DATA_ATTR uint8_t			loops;

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 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



//                              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);
  }
}



void app_main(void)
{
#ifdef CONFIG_CODE_PRODUCTION
    ESP_LOGI(TAG, "Starting production");
#endif
#ifdef CONFIG_CODE_TESTING
    ESP_LOGI(TAG, "Starting testing");
#endif

    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));

    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 = 1000000;

    dev.addr = 0x39;
    if (i2c_dev_probe(&dev, I2C_DEV_WRITE) == 0) {
        ESP_LOGI(TAG, "Found ADPS-9930");
    }
    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;
    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();


    xTaskCreate(&task_bmp280,  "task_bmp280",      2560, NULL, 8, &xTaskBMP280);
    xTaskCreate(&task_ina219,  "task_ina219",      2560, NULL, 8, &xTaskINA219);
    // 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) {
	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;
					request_WiFi(true);
		    			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 = 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;
					}
      					// }
      					// 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.
}

mercurial