Added volts/current loop calculations. Added millis() timer running on the hardware clock. Completed most of the main state loop. Added MQTT wait for disconnect. MQTT disconnect in two passes, disconnect and stop.

Fri, 31 Mar 2023 20:31:12 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Fri, 31 Mar 2023 20:31:12 +0200
changeset 7
2b337dd92f25
parent 6
bad3414f7bc4
child 8
115e93bf8796

Added volts/current loop calculations. Added millis() timer running on the hardware clock. Completed most of the main state loop. Added MQTT wait for disconnect. MQTT disconnect in two passes, disconnect and stop.

main/config.h file | annotate | diff | comparison | revisions
main/iotbalkon.c file | annotate | diff | comparison | revisions
main/task_mqtt.c file | annotate | diff | comparison | revisions
main/task_mqtt.h file | annotate | diff | comparison | revisions
main/task_wifi.c file | annotate | diff | comparison | revisions
--- a/main/config.h	Thu Mar 30 21:55:24 2023 +0200
+++ b/main/config.h	Fri Mar 31 20:31:12 2023 +0200
@@ -25,11 +25,9 @@
 #include "freertos/queue.h"
 #include "driver/gpio.h"
 #include "driver/i2c.h"
-//#include "driver/rtc_io.h"
-//#include "soc/sens_periph.h"
-//#include "soc/rtc.h"
 #include "esp_log.h"
 #include "esp_system.h"
+#include "esp_timer.h"
 #include "esp_app_desc.h"
 #include "esp_wifi.h"
 #include "esp_event.h"
--- a/main/iotbalkon.c	Thu Mar 30 21:55:24 2023 +0200
+++ b/main/iotbalkon.c	Fri Mar 31 20:31:12 2023 +0200
@@ -23,23 +23,27 @@
 static TaskHandle_t			xTaskWifi = NULL;
 
 #define	MAX_LOOPS			32
+#define SUB_TIME			1000
 
 float					temperature;
 float					pressure;
-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()
+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;
 
+bool					WorkAgain = false;
 int					DisCounter = 0;
 
 extern BMP280_State			*bmp280_state;			///< I2C state
@@ -56,8 +60,8 @@
 uint32_t				Alarm = 0;
 
 /*
-   Alarm bits
-*/
+ * Alarm bits
+ */
 #define AL_ACCULOW			0x01
 #define AL_NOWIFI			0x02
 
@@ -171,6 +175,12 @@
 
 
 
+uint64_t millis(void) {
+    return esp_timer_get_time() / 1000;
+}
+
+
+
 /*
  * Read the temperature and pressure from the BMP280.
  */
@@ -240,10 +250,11 @@
 	}
     }
 
-   // m_Time[loopno] = millis() - gLastTime;
-  //gLastTime = millis();
+    uint64_t  ms = millis();
+    m_Time[loopno] = ms - gLastTime;
+    gLastTime = ms;
 
-    ESP_LOGI(TAG, "Measure: %d  Valid: %s  Solar: %.4fV %.4fmA  Battery: %.4fV %.4fmA  time %ld",
+    ESP_LOGI(TAG, "Measure: %d  Valid: %s  Solar: %.4fV %.4fmA  Battery: %.4fV %.4fmA  time %llu",
 		  loopno, (m_Valid[loopno]) ? "true":"false", s_Volts[loopno], s_Current[loopno], b_Volts[loopno], b_Current[loopno], m_Time[loopno]);
 
     if (loopno < (MAX_LOOPS - 1))
@@ -254,6 +265,8 @@
 
 void app_main(void)
 {
+    uint64_t	totalTime, gTimeInMillis;
+
 #ifdef CONFIG_CODE_PRODUCTION
     ESP_LOGI(TAG, "Starting production");
 #endif
@@ -261,6 +274,7 @@
     ESP_LOGI(TAG, "Starting testing");
 #endif
 
+    gLastTime = millis();
     ESP_ERROR_CHECK(i2cdev_init());
 
     bmp280_init_default_params(&bmp280_params);
@@ -330,6 +344,9 @@
 	    ESP_LOGI(TAG, "Switch to state %d", State);
 	    OldState = State;
 	}
+
+	gTimeInMillis = millis();
+
 	switch (State) {
 	    case State_Init:		getTempBaro();
 					// getLightValues();
@@ -342,6 +359,7 @@
 	    case State_Connect:		if (ready_WiFi() && ready_mqtt()) {
 					    State = State_Working;
 					    Alarm &= ~AL_NOWIFI;
+					    ESP_LOGI(TAG, "Connected counter %d", DisCounter);
 					    DisCounter = 0;
 					} else {
 					    DisCounter++;
@@ -354,14 +372,14 @@
 					}
 					break;
 
-	    case State_Working:		// WorkAgain = false;
+	    case State_Working:		WorkAgain = false;
 
       					// Measure
 					getVoltsCurrent();
 					solarVolts = solarCurrent = solarPower = batteryVolts = batteryCurrent = batteryPower = 0;
-      					// loops = 0;
-      					// totalTime = 0;
-      					// for (int i = 0; i < loopno; i++) {
+					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:
@@ -375,19 +393,19 @@
     					     *   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 (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;
@@ -396,49 +414,48 @@
       					// }
 
       					// 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 (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);
+					    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;
-					}
-      					// }
+        				    /*   Check alarm conditions */
+					    if (batteryState <= 10) {
+					    	Alarm |= AL_ACCULOW;
+					    } else {
+					    	Alarm &= ~AL_ACCULOW;
+					    }
+      					}
 					getTempBaro();
-      					// Publish();
-      					// State = State_WorkDone;
-      					// gTimeNext = millis() + SUB_TIME;
+      					publish();
+      					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;
-      					// }
+	    case State_WorkDone:	vTaskDelay(2 / portTICK_PERIOD_MS);
+					// Hang around for a while to process the subscriptions.
+      					if (WorkAgain) {
+					    // Some command was executed.
+					    State = State_Working;
+					}
+					if (gTimeInMillis > gTimeNext) {
+					    State = State_Stop;
+					}
 					break;
 
 	    case State_Stop:		request_WiFi(false);
       					// // Reset values for average current measurement.
       					// HaveIP = false;
 					loopno = 0;
-      					// gLastTime = millis();
-      					// delay(10);
+					gLastTime = millis();
+					vTaskDelay(10 / portTICK_PERIOD_MS);
 					// #if defined(ARDUINO_ESP8266_WEMOS_D1MINI)
       					// WiFi.forceSleepBegin(0);  // 0 == forever
 					// #endif
@@ -454,8 +471,10 @@
         				//    }
 
         				//    // Active mode, 60 seconds loop
-        				//    ST_LOOPS = 6;
-        				//    gTimeNext = millis() + ST_INTERVAL;
+					ST_LOOPS = 6;
+					gTimeNext = millis() + ST_INTERVAL;
+					ESP_LOGI(TAG, "Start sleeploops");
+					State = State_Wait;
 					//    #if Debug == true
         				//    Serial.println(F("Start sleeploops"));
 					//    #endif
@@ -490,25 +509,30 @@
 					// #endif
 					break;
 
-	    case State_Wait:		// if (gTimeInMillis > gTimeNext) {
-      					//   State = State_Measure;
-      					// }
+	    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) {
+					gTimeNext = millis() + ST_INTERVAL;
+					if (loopno >= ST_LOOPS) {
+					    ESP_LOGI(TAG, "Enough loops, do connect");
         				//    getLightValues();
-        				//    State = State_Connect;
-      					// } else {
-        				//    State = State_Wait;
-      					// }
+					    State = State_Connect;
+					    DisCounter = 0;
+					    request_WiFi(true);
+					} else {
+					    State = State_Wait;
+					}
 					break;
 
-	    case State_GoSleep:		// ds_time = EEPROM.read(EM_DS_Time);
+	    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
--- a/main/task_mqtt.c	Thu Mar 30 21:55:24 2023 +0200
+++ b/main/task_mqtt.c	Fri Mar 31 20:31:12 2023 +0200
@@ -17,6 +17,7 @@
 const int TASK_MQTT_CONNECT = BIT0;			///< Request MQTT connection
 const int TASK_MQTT_DISCONNECT = BIT1;			///< Request MQTT disconnect
 const int TASK_MQTT_CONNECTED = BIT2;			///< MQTT is connected
+const int TASK_MQTT_DISCONNECTED = BIT3;
 
 const char			*sensState[] = { "OK", "ERROR" };	///< Sensor state strings
 const char			*unitMode[] = { "OFF", "ON" };		///< Units state strings
@@ -58,6 +59,21 @@
 
 
 
+void wait_mqtt(int time)
+{
+    EventBits_t uxBits;
+
+    ESP_LOGI(TAG, "wait_mqtt(%d) 1", time);
+    if (xEventGroupGetBits(xEventGroupMQTT) & TASK_MQTT_CONNECTED) {
+	uxBits = xEventGroupWaitBits(xEventGroupMQTT, TASK_MQTT_DISCONNECTED, pdTRUE, pdFALSE, time / portTICK_PERIOD_MS);
+	ESP_LOGI(TAG, "wait_mqtt(%d) 2 %lu", time, uxBits & TASK_MQTT_DISCONNECTED);
+    } else {
+	ESP_LOGI(TAG, "wait_mqtt(%d) 3 not connected", time);
+    }
+}
+
+
+
 /**
  * @brief Generate the mqtt payload header.
  * @return Allocated character string with the header.
@@ -119,7 +135,8 @@
 
 void publish(void)
 {
-    char        *topic = NULL, *payload = NULL, buf[64];
+    char        		*topic = NULL, *payload = NULL, buf[64];
+    const esp_app_desc_t	*app_desc = esp_app_get_description();
 
     // {"system":{"battery":70,"alarm":0,"version":"0.2.6","rssi":-56,"wifi":88,"light":{"lux":12.34,"gain":2}},"solar":{"voltage":13.98,"current":234.1,"power":3.272718},"battery":{"voltage":13.21,"current":4.942289,"power":0.065288},"real":{"current":229.1577},"TH":{"temperature":20.2,"humidity":48.3},"output":{"relay1":0,"relay2":0,"dimmer3":0,"dimmer4":0}}
     //
@@ -132,8 +149,12 @@
     payload = xstrcat(payload, buf);
     payload = xstrcat(payload, (char *)",\"version\":\"");
     payload = xstrcat(payload, (char *)app_desc->version);
-    payload = xstrcat(payload, (char *)",\"rssi\":");
-
+    payload = xstrcat(payload, (char *)"\",\"rssi\":");
+    if (xSemaphoreTake(xSemaphoreWiFi, 25) == pdTRUE) {
+	sprintf(buf, "%d", wifi_state->STA_rssi);
+	payload = xstrcat(payload, buf);
+	xSemaphoreGive(xSemaphoreWiFi);
+    }
     payload = xstrcat(payload, (char *)",\"light\":{\"lux\":");
 
     payload = xstrcat(payload, (char *)",\"gain\":");
@@ -157,6 +178,8 @@
     sprintf(buf, "%.3f", batteryPower / 1000.0);
     payload = xstrcat(payload, buf);
     payload = xstrcat(payload, (char *)"},\"real\":{\"current\":");
+    sprintf(buf, "%.1f", (0 - batteryCurrent) + solarCurrent);
+    payload = xstrcat(payload, buf);
     payload = xstrcat(payload, (char *)"},\"TB\":{\"temperature\":");
     if (xSemaphoreTake(xSemaphoreBMP280, 25) == pdTRUE) {
 	sprintf(buf, "%.2f", bmp280_state->temperature);
@@ -177,7 +200,8 @@
     payload = xstrcat(payload, (char *)"}}");
     topic = topic_base();
     topic = xstrcat(topic, (char *)"status");
-    publisher(topic, payload);
+    ESP_LOGI(TAG, "%s %s", topic, payload);
+//    publisher(topic, payload);
     free(topic);
     topic = NULL;
     free(payload);
@@ -197,11 +221,13 @@
             ESP_LOGI(TAG, "Subscribe `%s' id %d", topic, esp_mqtt_client_subscribe(client, topic, 0));
             free(topic);
 	    xEventGroupSetBits(xEventGroupMQTT, TASK_MQTT_CONNECTED);
+	    xEventGroupClearBits(xEventGroupMQTT, TASK_MQTT_DISCONNECTED);
             break;
 
         case MQTT_EVENT_DISCONNECTED:
             ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
 	    xEventGroupClearBits(xEventGroupMQTT, TASK_MQTT_CONNECTED);
+	    xEventGroupSetBits(xEventGroupMQTT, TASK_MQTT_DISCONNECTED);
             break;
 
         case MQTT_EVENT_SUBSCRIBED:
@@ -268,6 +294,7 @@
     /* event handler and event group for the wifi driver */
     xEventGroupMQTT = xEventGroupCreate();
     EventBits_t uxBits;
+    xEventGroupSetBits(xEventGroupMQTT, TASK_MQTT_DISCONNECTED);
 
     esp_mqtt_client_config_t mqtt_cfg = {
         .broker.address.uri = "mqtt://localhost",
@@ -283,6 +310,9 @@
 	uxBits = xEventGroupWaitBits(xEventGroupMQTT, TASK_MQTT_CONNECT | TASK_MQTT_DISCONNECT, pdFALSE, pdFALSE, portMAX_DELAY );
 
 	if (uxBits & TASK_MQTT_CONNECT) {
+	    /*
+	     * First build the connect uri.
+	     */
 	    uri = xstrcpy((char *)"mqtt://");
 	    if (strlen(CONFIG_MQTT_USER) && strlen(CONFIG_MQTT_PASS)) {
 		uri = xstrcat(uri, CONFIG_MQTT_USER);
@@ -296,8 +326,11 @@
 		sprintf(port, "%d", CONFIG_MQTT_PORT);
 		uri = xstrcat(uri, port);
 	    }
+	    ESP_LOGI(TAG, "Request MQTT connect %s", uri);
 
-	    ESP_LOGI(TAG, "Request MQTT connect %s", uri);
+	    /*
+	     * Connect to the broker.
+	     */
 	    err = esp_mqtt_client_set_uri(client, uri);
             if (err != ESP_OK)
                 ESP_LOGE(TAG, "Set uri %s", esp_err_to_name(err));
@@ -309,15 +342,39 @@
 
 	} else if (uxBits & TASK_MQTT_DISCONNECT) {
 	    ESP_LOGI(TAG, "Request MQTT disconnect");
+	    /*
+	     * Unsubscribe if connected
+	     */
 	    if (ready_mqtt()) {
 	    	char *topic = topic_base();
             	topic = xstrcat(topic, (char *)"output/set/#");
             	esp_mqtt_client_unsubscribe(client, topic);
             	free(topic);
 	    }
-	    esp_mqtt_client_stop(client);
+
+	    /*
+	     * Disconnect from broker and wait until confirmed.
+	     */
+	    err = esp_mqtt_client_disconnect(client);
+	    if (err != ESP_OK) {
+                ESP_LOGE(TAG, "Result %s", esp_err_to_name(err));
+	    }
+	    xEventGroupWaitBits(xEventGroupMQTT, TASK_MQTT_DISCONNECTED, pdTRUE, pdFALSE, 500 / portTICK_PERIOD_MS);
+	    ESP_LOGI(TAG, "disconnect confirmed");
+
+	    /*
+	     * Finally stop the client because new connections start
+	     * with a 'esp_mqtt_client_start()' command.
+	     * This will take about 5 seconds, but we don't need the network.
+	     */
+	    err = esp_mqtt_client_stop(client);
+            if (err != ESP_OK) {
+                ESP_LOGE(TAG, "Result %s", esp_err_to_name(err));
+            } else {
+                ESP_LOGI(TAG, "stopped");
+            }
+
 	    xEventGroupClearBits(xEventGroupMQTT, TASK_MQTT_DISCONNECT);
-	    xEventGroupClearBits(xEventGroupMQTT, TASK_MQTT_CONNECTED);
 	}
     }
 }
--- a/main/task_mqtt.h	Thu Mar 30 21:55:24 2023 +0200
+++ b/main/task_mqtt.h	Fri Mar 31 20:31:12 2023 +0200
@@ -23,6 +23,13 @@
 
 
 /**
+ * @brief Wait for disconnect with timeout.
+ * @param time Timeout in milliseconds.
+ */
+void wait_mqtt(int time);
+
+
+/**
  * @brief Publish
  */
 void publish(void);
--- a/main/task_wifi.c	Thu Mar 30 21:55:24 2023 +0200
+++ b/main/task_wifi.c	Fri Mar 31 20:31:12 2023 +0200
@@ -104,7 +104,8 @@
                 } else {
 		    ESP_LOGE(TAG, "wifi_event_handler() lock error WIFI_EVENT_STA_DISCONNECTED");
 		}
-		connect_mqtt(false);
+		if (ready_mqtt())
+		    connect_mqtt(false);
                 xEventGroupClearBits(xEventGroupWifi, TASK_WIFI_STA_CONNECTED);
                 xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_STA_DISCONNECTED);
 		break;
@@ -227,8 +228,9 @@
 	    /*
 	     * user requested a disconnect, this will in effect disconnect the wifi
 	     */
+	    ESP_LOGI(TAG, "Request STA disconnect");
 	    connect_mqtt(false);
-	    ESP_LOGI(TAG, "Request STA disconnect");
+	    wait_mqtt(10000);
 	    ESP_ERROR_CHECK(esp_wifi_disconnect());
 	    xEventGroupWaitBits(xEventGroupWifi, TASK_WIFI_STA_DISCONNECTED, pdFALSE, pdTRUE, portMAX_DELAY );
 

mercurial