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

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

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

main/CMakeLists.txt file | annotate | diff | comparison | revisions
main/Kconfig.projbuild 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
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
main/xutil.c file | annotate | diff | comparison | revisions
main/xutil.h file | annotate | diff | comparison | revisions
--- a/main/CMakeLists.txt	Wed Mar 29 21:39:07 2023 +0200
+++ b/main/CMakeLists.txt	Thu Mar 30 17:05:05 2023 +0200
@@ -1,2 +1,2 @@
-idf_component_register(SRCS config.c iotbalkon.c task_bmp280.c task_ina219.c task_wifi.c
+idf_component_register(SRCS config.c iotbalkon.c task_bmp280.c task_ina219.c task_wifi.c task_mqtt.c xutil.c
                     INCLUDE_DIRS ".")
--- a/main/Kconfig.projbuild	Wed Mar 29 21:39:07 2023 +0200
+++ b/main/Kconfig.projbuild	Thu Mar 30 17:05:05 2023 +0200
@@ -40,17 +40,48 @@
 
     endmenu
 
-    config ESP_WIFI_SSID
-        string "WiFi SSID"
-        default "myssid"
-        help
-            SSID (network name) to connect to.
+    menu "WiFi settings"
+
+    	config ESP_WIFI_SSID
+            string "WiFi SSID"
+            default "myssid"
+            help
+            	SSID (network name) to connect to.
+
+        config ESP_WIFI_PASSWORD
+            string "WiFi Password"
+            default "mypassword"
+            help
+            	WiFi password (WPA or WPA2) to use.
+
+    endmenu
+
+    menu "MQTT server"
 
-    config ESP_WIFI_PASSWORD
-        string "WiFi Password"
-        default "mypassword"
-        help
-            WiFi password (WPA or WPA2) to use.
+    config MQTT_SERVER
+	string "MQTT server"
+	default "mqtt.eclipseprojects.io"
+	help
+	    The MQTT server to connect to
+
+    config MQTT_PORT
+	int "MQTT port number"
+	default 1883
+	help
+	    The MQTT server port
 
+    config MQTT_USER
+	string "MQTT username"
+	default ""
+	help
+	    The optional MQTT username.
+
+    config MQTT_PASS
+	string "MQTT password"
+	default "letmein"
+	help
+	    The optional MQTT password for the username.
+
+    endmenu
 
 endmenu
--- a/main/config.h	Wed Mar 29 21:39:07 2023 +0200
+++ b/main/config.h	Thu Mar 30 17:05:05 2023 +0200
@@ -22,27 +22,22 @@
 #include "freertos/task.h"
 #include "freertos/semphr.h"
 #include "freertos/event_groups.h"
-//#include "freertos/queue.h"
+#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_spiffs.h"
-//#include "esp_vfs.h"
 #include "esp_system.h"
+#include "esp_app_desc.h"
 #include "esp_wifi.h"
-//#include "esp_wifi_types.h"
-//#include "esp_wpa2.h"
 #include "esp_event.h"
+#include "esp_netif.h"
 #include "nvs_flash.h"
 #include "lwip/err.h"
 #include "lwip/sys.h"
-//#include "lwip/sockets.h"
-//#include "lwip/dns.h"
-//#include "lwip/netdb.h"
-//#include "mqtt_client.h"
+#include "mqtt_client.h"
 
 /*
  * esp-idf-lib
@@ -57,10 +52,8 @@
 #include "task_bmp280.h"
 #include "task_ina219.h"
 #include "task_wifi.h"
-//#include "task_mqtt.h"
-//#include "task_user.h"
-//#include "xutil.h"
-
+#include "task_mqtt.h"
+#include "xutil.h"
 
 
 
--- a/main/iotbalkon.c	Wed Mar 29 21:39:07 2023 +0200
+++ b/main/iotbalkon.c	Thu Mar 30 17:05:05 2023 +0200
@@ -19,8 +19,24 @@
 
 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;
@@ -31,6 +47,122 @@
 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)
@@ -93,6 +225,9 @@
 
     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);
@@ -137,7 +272,7 @@
 
       					// // Measure
       					// getVoltsCurrent();
-      					// solarVolts = solarCurrent = batteryVolts = batteryCurrent = 0;
+					solarVolts = solarCurrent = solarPower = batteryVolts = batteryCurrent = batteryPower = 0;
       					// loops = 0;
       					// totalTime = 0;
       					// for (int i = 0; i < loopno; i++) {
@@ -182,34 +317,17 @@
        					//    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"));
+					BatteryState(batteryVolts, (0 - batteryCurrent) + solarCurrent);
 
-					//    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
+					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;
-        				//   }
+					if (batteryState <= 10) {
+					    Alarm |= AL_ACCULOW;
+					} else {
+					    Alarm &= ~AL_ACCULOW;
+					}
       					// }
       					// getTempHumi();
       					// Publish();
--- a/main/task_ina219.c	Wed Mar 29 21:39:07 2023 +0200
+++ b/main/task_ina219.c	Thu Mar 30 17:05:05 2023 +0200
@@ -15,6 +15,11 @@
 
 extern ina219_t			ina219_b_dev;
 extern ina219_t			ina219_s_dev;
+extern float			s_Volts[I_MAX_LOOPS + 1];
+extern float			s_Current[I_MAX_LOOPS + 1];
+extern float			b_Volts[I_MAX_LOOPS + 1];
+extern float			b_Current[I_MAX_LOOPS + 1];
+extern uint8_t			loopno;
 
 const int TASK_INA219_REQUEST_DONE = BIT0;			///< All requests are done.
 const int TASK_INA219_REQUEST_POWER = BIT1;			///< Request power readings
@@ -43,7 +48,6 @@
  */
 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);
@@ -104,6 +108,25 @@
 		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 (xSemaphoreTake(xSemaphoreINA219, 25) == pdTRUE) {
+		if (ina219_state->Battery.fake) {
+		    ina219_state->Battery.volts = 13.21;
+		    if (ready_WiFi()) {
+			ina219_state->Battery.shunt = 0.00785;
+			ina219_state->Battery.current = 78.5;
+		    } else {
+			ina219_state->Battery.shunt = 0.00182;
+			ina219_state->Battery.current = 18.2;
+		    }
+		} else {
+		    ina219_state->Battery.volts = bus_voltage;
+		    ina219_state->Battery.shunt = shunt_voltage;
+		    ina219_state->Battery.current = current;
+		}
+		ina219_state->Battery.valid = true;
+		xSemaphoreGive(xSemaphoreINA219);
+	    }
+
 	    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));
@@ -112,42 +135,26 @@
                 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);
+	    if (xSemaphoreTake(xSemaphoreINA219, 25) == pdTRUE) {
+		if (! ina219_state->Solar.fake && ! ina219_state->Battery.fake) {
+		    ina219_state->Solar.volts = bus_voltage;
+                    ina219_state->Solar.shunt = shunt_voltage;
+                    ina219_state->Solar.current = current;
+		} else if (ina219_state->Solar.fake && ! ina219_state->Battery.fake) {
+		    ina219_state->Solar.volts = ina219_state->Battery.volts + 0.78;
+		    ina219_state->Solar.shunt = 0.02341;
+		    ina219_state->Solar.current = 234.1;
+		} else {
+		    ina219_state->Solar.volts = 13.98;
+		    ina219_state->Solar.shunt = 0.02341;
+		    ina219_state->Solar.current = 234.1;
 		}
-	    } 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);
-		}
+		ina219_state->Solar.valid = true;
+		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
 	}
     }
 }
--- a/main/task_ina219.h	Wed Mar 29 21:39:07 2023 +0200
+++ b/main/task_ina219.h	Thu Mar 30 17:05:05 2023 +0200
@@ -38,15 +38,6 @@
     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;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/task_mqtt.c	Thu Mar 30 17:05:05 2023 +0200
@@ -0,0 +1,313 @@
+/**
+ * @file task_mqtt.c
+ * @brief The FreeRTOS task to maintain MQTT connections.
+ */
+
+
+#include "config.h"
+
+
+static const char		*TAG = "task_mqtt";
+
+EventGroupHandle_t		xEventGroupMQTT;	///< Events MQTT task
+SemaphoreHandle_t		xSemaphorePcounter;	///< Publish counter semaphore.
+int				count_pub = 0;		///< Outstanding published messages.
+esp_mqtt_client_handle_t	client;			///< MQTT client handle
+
+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 char			*sensState[] = { "OK", "ERROR" };	///< Sensor state strings
+const char			*unitMode[] = { "OFF", "ON" };		///< Units state strings
+
+extern BMP280_State		*bmp280_state;		///< BMP280 state
+extern SemaphoreHandle_t        xSemaphoreBMP280;	///< BMP280 lock semaphore
+extern INA219_State		*ina219_state;		///< INA219 state
+extern SemaphoreHandle_t	xSemaphoreINA219;	///< INA219 lock semaphore
+extern WIFI_State		*wifi_state;		///< WiFi state
+extern SemaphoreHandle_t	xSemaphoreWiFi;		///< WiFi lock semaphore
+extern const esp_app_desc_t	*app_desc;
+
+extern uint32_t			Alarm;
+extern float			batteryState;
+extern float			batteryVolts;
+extern float			batteryCurrent;
+extern float			batteryPower;
+extern float			solarVolts;
+extern float			solarCurrent;
+extern float			solarPower;
+
+
+void connect_mqtt(bool state)
+{
+    if (state)
+    	xEventGroupSetBits(xEventGroupMQTT, TASK_MQTT_CONNECT);
+    else
+    	xEventGroupSetBits(xEventGroupMQTT, TASK_MQTT_DISCONNECT);
+}
+
+
+
+bool ready_mqtt(void)
+{
+    if (xEventGroupGetBits(xEventGroupMQTT) & TASK_MQTT_CONNECTED)
+        return true;
+    return false;
+}
+
+
+
+/**
+ * @brief Generate the mqtt payload header.
+ * @return Allocated character string with the header.
+ */
+char *payload_header(void)
+{
+    char        *tmp;
+
+    tmp = xstrcpy((char *)"{\"metric\":");
+    return tmp;
+}
+
+
+
+/**
+ * @brief Generate the mqtt topic base part.
+ * @return The topic string allocated in memory.
+ */
+char *topic_base(void)
+{
+    char        *tmp;
+
+#ifdef CONFIG_CODE_PRODUCTION
+    tmp = xstrcpy((char *)"balkon/");
+#endif
+#ifdef CONFIG_CODE_TESTING
+    tmp = xstrcpy((char *)"wemos/");
+#endif
+
+    return tmp;
+}
+
+
+
+/**
+ * @brief The mqtt generic publish function.
+ * @param topic The topic of the mqtt message.
+ * @param payload The payload of the mqtt message.
+ */
+void publisher(char *topic, char *payload)
+{
+    /*
+     * First count, then sent the data.
+     */
+    if (xSemaphoreTake(xSemaphorePcounter, 10) == pdTRUE) {
+        count_pub++;
+        xSemaphoreGive(xSemaphorePcounter);
+    } else {
+        ESP_LOGE(TAG, "publisher() counter lock");
+    }
+
+    if (payload)
+        esp_mqtt_client_publish(client, topic, payload, strlen(payload), 1, 0);
+    else
+	esp_mqtt_client_publish(client, topic, NULL, 0, 1, 0);
+}
+
+
+
+void publish(void)
+{
+    char        *topic = NULL, *payload = NULL, buf[64];
+
+    // {"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}}
+    //
+    payload = payload_header();
+    payload = xstrcat(payload, (char *)"{\"system\":{\"battery\":");
+    sprintf(buf, "%.0f", batteryState);
+    payload = xstrcat(payload, buf);
+    payload = xstrcat(payload, (char *)",\"alarm\":");
+    sprintf(buf, "%ld", Alarm);
+    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 *)",\"light\":{\"lux\":");
+
+    payload = xstrcat(payload, (char *)",\"gain\":");
+
+    payload = xstrcat(payload, (char *)"}},\"solar\":{\"voltage\":");
+    sprintf(buf, "%.2f", solarVolts);
+    payload = xstrcat(payload, buf);
+    payload = xstrcat(payload, (char *)",\"current\":");
+    sprintf(buf, "%.1f", solarCurrent);
+    payload = xstrcat(payload, buf);
+    payload = xstrcat(payload, (char *)",\"power\":");
+    sprintf(buf, "%.3f", solarPower / 1000.0);
+    payload = xstrcat(payload, buf);
+    payload = xstrcat(payload, (char *)"},\"battery\":{\"voltage\":");
+    sprintf(buf, "%.2f", batteryVolts);
+    payload = xstrcat(payload, buf);
+    payload = xstrcat(payload, (char *)",\"current\":");
+    sprintf(buf, "%.1f", batteryCurrent);
+    payload = xstrcat(payload, buf);
+    payload = xstrcat(payload, (char *)",\"power\":");
+    sprintf(buf, "%.3f", batteryPower / 1000.0);
+    payload = xstrcat(payload, buf);
+    payload = xstrcat(payload, (char *)"},\"real\":{\"current\":");
+    payload = xstrcat(payload, (char *)"},\"TB\":{\"temperature\":");
+    if (xSemaphoreTake(xSemaphoreBMP280, 25) == pdTRUE) {
+	sprintf(buf, "%.2f", bmp280_state->temperature);
+	payload = xstrcat(payload, buf);
+	payload = xstrcat(payload, (char *)",\"pressure\":");
+	sprintf(buf, "%.1f", bmp280_state->pressure / 100.0);
+	payload = xstrcat(payload, buf);
+	xSemaphoreGive(xSemaphoreBMP280);
+    }
+    payload = xstrcat(payload, (char *)"},\"output\":{\"relay1\":");
+
+    payload = xstrcat(payload, (char *)",\"relay2\":");
+
+    payload = xstrcat(payload, (char *)",\"dimmer3\":");
+
+    payload = xstrcat(payload, (char *)",\"dimmer4\":");
+
+    payload = xstrcat(payload, (char *)"}}");
+    topic = topic_base();
+    topic = xstrcat(topic, (char *)"status");
+    publisher(topic, payload);
+    free(topic);
+    topic = NULL;
+    free(payload);
+    payload = NULL;
+}
+
+
+
+static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
+{
+    switch (event->event_id) {
+
+        case MQTT_EVENT_CONNECTED:
+            ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
+	    xEventGroupSetBits(xEventGroupMQTT, TASK_MQTT_CONNECTED);
+            break;
+
+        case MQTT_EVENT_DISCONNECTED:
+            ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
+	    xEventGroupClearBits(xEventGroupMQTT, TASK_MQTT_CONNECTED);
+            break;
+
+        case MQTT_EVENT_SUBSCRIBED:
+            ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
+            break;
+
+        case MQTT_EVENT_UNSUBSCRIBED:
+            ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
+            break;
+
+        case MQTT_EVENT_PUBLISHED:
+            ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
+	    if (xSemaphoreTake(xSemaphorePcounter, 10) == pdTRUE) {
+	    	if (count_pub) {
+		    count_pub--;
+	    	}
+		xSemaphoreGive(xSemaphorePcounter);
+	    } else {
+        	ESP_LOGE(TAG, "mqtt_event_handler_cb(() lock error event");
+	    }
+            break;
+
+        case MQTT_EVENT_DATA:
+            ESP_LOGI(TAG, "MQTT_EVENT_DATA");
+            printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
+            printf("DATA=%.*s\r\n", event->data_len, event->data);
+            break;
+
+        case MQTT_EVENT_ERROR:
+            ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
+            break;
+
+	case MQTT_EVENT_BEFORE_CONNECT:
+	    ESP_LOGI(TAG, "MQTT_EVENT_BEFORE_CONNECT");
+	    // Configure connection can be here.
+	    break;
+
+        default:
+            ESP_LOGI(TAG, "Other event id:%d", event->event_id);
+            break;
+    }
+    return ESP_OK;
+}
+
+
+
+static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
+    mqtt_event_handler_cb(event_data);
+}
+
+
+
+/*
+ * Task to read temperature sensors on request.
+ */
+void task_mqtt(void *pvParameter)
+{
+    esp_err_t	err;
+    char	*uri = NULL, port[11];
+
+    ESP_LOGI(TAG, "Starting MQTT task");
+    xSemaphorePcounter = xSemaphoreCreateMutex();
+
+    /* event handler and event group for the wifi driver */
+    xEventGroupMQTT = xEventGroupCreate();
+    EventBits_t uxBits;
+
+    esp_mqtt_client_config_t mqtt_cfg = {
+        .broker.address.uri = "mqtt://localhost",
+    };
+    client = esp_mqtt_client_init(&mqtt_cfg);
+    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);
+
+    /*
+     * Task loop forever.
+     */
+    while (1) {
+
+	uxBits = xEventGroupWaitBits(xEventGroupMQTT, TASK_MQTT_CONNECT | TASK_MQTT_DISCONNECT, pdFALSE, pdFALSE, portMAX_DELAY );
+
+	if (uxBits & TASK_MQTT_CONNECT) {
+	    uri = xstrcpy((char *)"mqtt://");
+	    if (strlen(CONFIG_MQTT_USER) && strlen(CONFIG_MQTT_PASS)) {
+		uri = xstrcat(uri, CONFIG_MQTT_USER);
+		uri = xstrcat(uri, (char *)":");
+		uri = xstrcat(uri, CONFIG_MQTT_PASS);
+		uri = xstrcat(uri, (char *)"@");
+	    }
+	    uri = xstrcat(uri, CONFIG_MQTT_SERVER);
+	    if (CONFIG_MQTT_PORT != 1883) {
+		uri = xstrcat(uri, (char *)":");
+		sprintf(port, "%d", CONFIG_MQTT_PORT);
+		uri = xstrcat(uri, port);
+	    }
+
+	    ESP_LOGI(TAG, "Request MQTT connect %s", uri);
+	    err = esp_mqtt_client_set_uri(client, uri);
+            if (err != ESP_OK)
+                ESP_LOGE(TAG, "Set uri %s", esp_err_to_name(err));
+	    err = esp_mqtt_client_start(client);
+	    if (err != ESP_OK)
+	    	ESP_LOGE(TAG, "Result %s", esp_err_to_name(err));
+	    xEventGroupClearBits(xEventGroupMQTT, TASK_MQTT_CONNECT);
+
+	} else if (uxBits & TASK_MQTT_DISCONNECT) {
+	    ESP_LOGI(TAG, "Request MQTT disconnect");
+	    esp_mqtt_client_stop(client);
+	    xEventGroupClearBits(xEventGroupMQTT, TASK_MQTT_DISCONNECT);
+	    xEventGroupClearBits(xEventGroupMQTT, TASK_MQTT_CONNECTED);
+	}
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/task_mqtt.h	Thu Mar 30 17:05:05 2023 +0200
@@ -0,0 +1,38 @@
+/**
+ * @file task_mqtt.h
+ * @brief The FreeRTOS task to maintain MQTT connections.
+ */
+
+#ifndef	_TASK_MQTT_H
+#define	_TASK_MQTT_H
+
+
+
+/**
+ * @brief Request a MQTT connection
+ * @param state Request of disconnect a connection.
+ */
+void connect_mqtt(bool state);
+
+
+/**
+ * @brief Check if MQTT is connected
+ * @return Returns true if MQTT is connected
+ */
+bool ready_mqtt(void);
+
+
+/**
+ * @brief Publish
+ */
+void publish(void);
+
+
+/**
+ * @brief The FreeRTOS task to run MQTT connections.
+ * @param pvParameters Parameters for the task.
+ */
+void task_mqtt(void *pvParameters);
+
+
+#endif
--- a/main/task_wifi.c	Wed Mar 29 21:39:07 2023 +0200
+++ b/main/task_wifi.c	Thu Mar 30 17:05:05 2023 +0200
@@ -104,7 +104,7 @@
                 } else {
 		    ESP_LOGE(TAG, "wifi_event_handler() lock error WIFI_EVENT_STA_DISCONNECTED");
 		}
-//		connect_mqtt(false);
+		connect_mqtt(false);
                 xEventGroupClearBits(xEventGroupWifi, TASK_WIFI_STA_CONNECTED);
                 xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_STA_DISCONNECTED);
 		break;
@@ -135,7 +135,7 @@
 		} else {
 			ESP_LOGE(TAG, "got_ip_event_handler() lock error IP_EVENT_STA_GOT_IP");
 		}
-//		connect_mqtt(true);
+		connect_mqtt(true);
 		break;
 
 	case IP_EVENT_STA_LOST_IP:
@@ -150,7 +150,7 @@
 		} else {
 			ESP_LOGE(TAG, "got_ip_event_handler() lock error IP_EVENT_STA_LOST_IP");
 		}
-//		connect_mqtt(false);
+		connect_mqtt(false);
 		break;
 
 	case IP_EVENT_AP_STAIPASSIGNED:
@@ -227,7 +227,7 @@
 	    /*
 	     * user requested a disconnect, this will in effect disconnect the wifi
 	     */
-//	    connect_mqtt(false);
+	    connect_mqtt(false);
 	    ESP_LOGI(TAG, "Request STA disconnect");
 	    ESP_ERROR_CHECK(esp_wifi_disconnect());
 	    xEventGroupWaitBits(xEventGroupWifi, TASK_WIFI_STA_DISCONNECTED, pdFALSE, pdTRUE, portMAX_DELAY );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/xutil.c	Thu Mar 30 17:05:05 2023 +0200
@@ -0,0 +1,72 @@
+/*****************************************************************************
+ * Copyright (C) 2008-2019
+ *
+ * Michiel Broek <mbroek at mbse dot eu>
+ *
+ * This file is part of the mbsePi-apps
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * mbsePi-apps is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with ThermFerm; see the file COPYING.  If not, write to the Free
+ * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *****************************************************************************/
+
+#include "config.h"
+
+
+char *xmalloc(size_t size)
+{
+    char *tmp;
+
+    tmp = malloc(size);
+    if (!tmp)
+	abort();
+
+    return tmp;
+}
+
+
+
+char *xstrcpy(char *src)
+{
+    char    *tmp;
+
+    if (src == NULL)
+	return(NULL);
+    tmp = xmalloc(strlen(src)+1);
+    strcpy(tmp, src);
+    return tmp;
+}
+
+
+
+char *xstrcat(char *src, char *add)
+{
+    char    *tmp;
+    size_t  size = 0;
+
+    if ((add == NULL) || (strlen(add) == 0))
+	return src;
+    if (src)
+	size = strlen(src);
+    size += strlen(add);
+    tmp = xmalloc(size + 1);
+    *tmp = '\0';
+    if (src) {
+	strcpy(tmp, src);
+	free(src);
+    }
+    strcat(tmp, add);
+    return tmp;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/xutil.h	Thu Mar 30 17:05:05 2023 +0200
@@ -0,0 +1,32 @@
+/**
+ * @file xutil.h
+ * @brief In memory string manipulation.
+ */
+
+
+#ifndef XUTIL_H
+#define	XUTIL_H
+
+/**
+ * @brief Safe memory allocation. Abort if not enough.
+ * @param size The amount of memory to allocate.
+ * @return A pointer to the allocated memory.
+ */
+char *xmalloc(size_t size);
+
+/**
+ * @brief Copy string in memory.
+ * @param src The source string to copy.
+ * @return A pointer to the string with the copied string.
+ */
+char *xstrcpy(char *src);
+
+/**
+ * @brief Add data to a string in memory.
+ * @param src The original string.
+ * @param add The string to append to src.
+ * @return A pointer to the combined string.
+ */
+char *xstrcat(char *src, char *add);
+
+#endif

mercurial