Issue with understanding code example

I have a simple question running on a ESP32 I dont see any MQTT unless I change LOCAL_MEASUREMENTS to 1. After I change to 1 I see MQTT message topic every 1 sec. Can some one explain the code in bold to see if I can figure out why its not sending MQTT topics once it has 30? The code am followed is GitHub - Savjee/home-energy-monitor: ESP32-based Home Energy Monitor. My CT clamp and OLED / ESP 32 is working on a Lillygo ESP but trying to get the MQTT side working or at least understand it. Sorry relativity new to Arduino.

#define LOCAL_MEASUREMENTS 30
void sendEnergyToHA(void * parameter){
        if(!HA_mqtt.connected()){
        serial_println("[MQTT] Can't send to HA without MQTT. Abort.");
        vTaskDelete(NULL);
        }

        [b]char msg[30];
        strcpy(msg, "{\"power\":");
            strcat(msg, String(measurements[LOCAL_MEASUREMENTS-1]).c_str());
        strcat(msg, "}");

        serial_print("[MQTT] HA publish: ");
        serial_println(msg);

        HA_mqtt.publish("homeassistant/sensor/" DEVICE_NAME "/state", msg);[/b]

        // Task is done!
        vTaskDelete(NULL);
    }

#define LOCAL_MEASUREMENTS 30This defines which value from the 'Array' "measurements" is added to char msg[30]; which is a c-string with a defined maximum length.
first are copied these 9 characters into it. strcpy(msg, "{\"power\":");
then the 'Array' "value" get converted to a "String" and then to a c-string (there must be another way, but that is not so relevant here) strcat(msg, String(measurements[LOCAL_MEASUREMENTS-1]).c_str());
if "LOCAL_MEASUREMENTS" is '0' or less the index of the "array" is negative and should therefore not result in a valid value, probably the same for anything over 29, though you have not provided the declaration of "measurements"

Thanks so

if "LOCAL_MEASUREMENTS" is '0' or less the index of the "array" is negative and should therefore not result in a valid value, probably the same for anything over 29, though you have not provided the declaration of "measurements"

strcat(msg, String(measurements[LOCAL_MEASUREMENTS-1]).c_str());

So LOCAL_MEASUREMENTS-1 means 29 this is what I dont get. While does the value 1 work has its -1 meaning zero and gives me mqtt messages every second and if set to 30 meaning 29 it doesnt? Any number above 1 does not produce a mqtt topic or am I looking at this wrong does 30 mean I need 29 measurements until it produces a mqtt topic?

you have still not provided the declaration of the variable measurements so i can not give you a real meaningful answer about that, other that the minimum size of the array is [1]
Why don't you post the full sketch ?

Any number above 1 does not produce a mqtt topic or am I looking at this wrong does 30 mean I need 29 measurements until it produces a mqtt topic?

measurements[LOCAL_MEASUREMENTS-1]

means the LOCAL_MEASUREMENTS-1 entry in the char-array, if the array size is less then the entry requested the answer could be anything, since you will be reading from memory not reserved for the variable. I can not see where and what you write into the array, and what size it is. Do you understand what i mean ?

Thanks Deva_Rishi. The code I have been following is made under platformio so its different to the arduino format.

But this is the core of the code. I have build up a LillyGo TTGO ESP32 slightly different display but its working and measuring Watts via CT clamp. I have it connected to Home Assistant via MQTT and have not enabled AWS.

config.h

#define LOCAL_MEASUREMENTS 30
#define HA_ENABLED true
#define HA_ADDRESS "x.x.x.x"
#define HA_PORT 1883
#define HA_USER "username"
#define HA_PASSWORD "password"

main.cpp this runs a task called measureElectricity

unsigned short measurements[LOCAL_MEASUREMENTS];
unsigned char measureIndex = 0;

 xTaskCreate(
    measureElectricity,
    "Measure electricity",  // Task name
    5000,                  // Stack size (bytes)
    NULL,                   // Parameter
    4,                      // Task priority
    NULL                    // Task handle
  );

If set to true Home Assistant enabled

 #if HA_ENABLED == true
    xTaskCreate(
      HADiscovery,
      "MQTT-HA Discovery",  // Task name
      5000,                // Stack size (bytes)
      NULL,                 // Parameter
      5,                    // Task priority
      NULL                  // Task handle
    );

    xTaskCreate(
      keepHAConnectionAlive,
      "MQTT-HA Connect",
      5000,
      NULL,
      4,
      NULL
    );
  #endif

measure-electricty.h task sendEnergyToHA

#ifndef TASK_MEASURE_ELECTRICITY
#define TASK_MEASURE_ELECTRICITY

#include <Arduino.h>
#include "EmonLib.h"

#include "../config/config.h"
#include "../config/enums.h"
#include "mqtt-aws.h"
#include "mqtt-home-assistant.h"

extern DisplayValues gDisplayValues;
extern EnergyMonitor emon1;
extern unsigned short measurements[];
extern unsigned char measureIndex;

void measureElectricity(void * parameter)
{
    for(;;){
      serial_println("[ENERGY] Measuring...");
      long start = millis();

      double amps = emon1.calcIrms(1480);
      double watts = amps * HOME_VOLTAGE;

      gDisplayValues.amps = amps;
      gDisplayValues.watt = watts;

      measurements[measureIndex] = watts;
      measureIndex++;

      if(measureIndex == LOCAL_MEASUREMENTS){
          #if AWS_ENABLED == true
            xTaskCreate(
              uploadMeasurementsToAWS,
              "Upload measurements to AWS",
              10000,             // Stack size (bytes)
              NULL,             // Parameter
              5,                // Task priority
              NULL              // Task handle
            );
          #endif

          #if HA_ENABLED == true
            xTaskCreate(
              sendEnergyToHA,
              "HA-MQTT Upload",
              10000,             // Stack size (bytes)
              NULL,             // Parameter
              5,                // Task priority
              NULL              // Task handle
            );
          #endif
      }

      measureIndex = 0;
      long end = millis();

      // Schedule the task to run again in 1 second (while
      // taking into account how long measurement took)
      vTaskDelay((1000-(end-start)) / portTICK_PERIOD_MS);
    }    
}

#endif

this calls mqtt-home-assistant.h sendEnergyToHA this is where my question starts from

#ifndef TASK_HOME_ASSISTANT
#define TASK_HOME_ASSISTANT

#if HA_ENABLED == true

    #include <Arduino.h>
    //#include <WiFiClientSecure.h>
    #include <WiFiClient.h>
    #include <MQTTClient.h>
    #include "../config/config.h"

    //WiFiClientSecure HA_net;
    WiFiClient HA_net;
    MQTTClient HA_mqtt(1024);

    extern unsigned short measurements[];

    const char* PROGMEM HA_discovery_msg = "{"
            "\"name\":\"" DEVICE_NAME "\","
            "\"device_class\":\"power\","
            "\"unit_of_measurement\":\"W\","
            "\"icon\":\"mdi:transmission-tower\","
            "\"state_topic\":\"homeassistant/sensor/" DEVICE_NAME "/state\","
            "\"value_template\":\"{{ value_json.power}}\","
            "\"device\": {"
                "\"name\":\"" DEVICE_NAME "\","
                "\"sw_version\":\"2.0\","
                "\"model\":\"HW V2\","
                "\"manufacturer\":\"Xavier Decuyper\","
                "\"identifiers\":[\"" DEVICE_NAME "\"]"
            "}"
        "}";

    /**
     * Established a connection to Home Assistant MQTT broker.
     * 
     * This task should run continously. It will check if an
     * MQTT connection is active and if so, will sleep for 1
     * minute. If not, a new connection will be established.
     */
    void keepHAConnectionAlive(void * parameter){
        for(;;){
            // When we are connected, loop the MQTT client and sleep for 0,5s
            if(HA_mqtt.connected()){
                HA_mqtt.loop();
                vTaskDelay(250 / portTICK_PERIOD_MS);
                continue;
            }

            if(!WiFi.isConnected()){
                vTaskDelay(1000 / portTICK_PERIOD_MS);
                continue;
            }

            serial_println(F("[MQTT] Connecting to HA..."));
            HA_mqtt.begin(HA_ADDRESS, HA_PORT, HA_net);

            long startAttemptTime = millis();
        
            while (!HA_mqtt.connect(DEVICE_NAME, HA_USER, HA_PASSWORD) &&
                    millis() - startAttemptTime < MQTT_CONNECT_TIMEOUT)
            {
                vTaskDelay(MQTT_CONNECT_DELAY / portTICK_PERIOD_MS);
            }

            if(!HA_mqtt.connected()){
                serial_println(F("[MQTT] HA connection failed. Waiting 30s.."));
                vTaskDelay(30000 / portTICK_PERIOD_MS);
            }

            serial_println(F("[MQTT] HA Connected!"));
        }
    }

    /**
     * TASK: Every 15 minutes we send Home Assistant a discovery message
     *       so that the energy monitor shows up in the device registry.
     */
    void HADiscovery(void * parameter){
        for(;;){
            if(!HA_mqtt.connected()){
                serial_println("[MQTT] HA: no MQTT connection.");
                vTaskDelay(30 * 1000 / portTICK_PERIOD_MS);
                continue;
            }

            serial_println("[MQTT] HA sending auto discovery");
            HA_mqtt.publish("homeassistant/sensor/" DEVICE_NAME "/config", HA_discovery_msg);
            vTaskDelay(15 * 60 * 1000 / portTICK_PERIOD_MS);
        }
    }

    void sendEnergyToHA(void * parameter){
        if(!HA_mqtt.connected()){
        serial_println("[MQTT] Can't send to HA without MQTT. Abort.");
        vTaskDelete(NULL);
        }

        char msg[30];
        strcpy(msg, "{\"power\":");
            strcat(msg, String(measurements[LOCAL_MEASUREMENTS-1]).c_str());
        strcat(msg, "}");

        serial_print("[MQTT] HA publish: ");
        serial_println(msg);

        HA_mqtt.publish("homeassistant/sensor/" DEVICE_NAME "/state", msg);

        // Task is done!
        vTaskDelete(NULL);
    }
#endif
#endif

It took some looking at, this main loop takes 1 measurement per second, and once the 'measureIndex == LOCAL_MEASUREMENTS'
it does it's sending thing. But since it get's reset at the end of the loop the sending only happens when LOCAL_MEASUREMENTS = 1.
You could move the reset of the reset of the the 'measureIndex' though that would mean that now you would send every so many seconds instead (whatever value you decided upon for LOCAL_MEASUREMENTS)
and then in the other part of the code, unfortunately, you would only send the last measurement.
String(measurements[LOCAL_MEASUREMENTS-1]) instead of all the measurements, which is probably what you want.

for(;;){        // this is the main loop where the measuring takes place.
      serial_println("[ENERGY] Measuring...");
      long start = millis();

      double amps = emon1.calcIrms(1480);
      double watts = amps * HOME_VOLTAGE;

      gDisplayValues.amps = amps;
      gDisplayValues.watt = watts;

      measurements[measureIndex] = watts;
      measureIndex++;                                  // here the index gets upped 1

      if(measureIndex == LOCAL_MEASUREMENTS){
          (...............)     // here is where the mqqt sending happens.
      }

      measureIndex = 0;                 // and here it gets reset to 0 
                                                  // this line is in the wrong place if you want the above statement ever
                                                  // to be true
      long end = millis();

      // Schedule the task to run again in 1 second (while
      // taking into account how long measurement took)
      vTaskDelay((1000-(end-start)) / portTICK_PERIOD_MS);   // this delays for 1 second per measurement.
    }

So with all the code i can explain the behavior, but since you haven't explained exactly how you want it to be, there is no immediate fix. There are some things in the code which are a bit warped, why this #define LOCAL_MEASUREMENTS is in there when really the code only works when the value is 1 ? is a mystery (not that the code only works when it's 1, that is clear) and the conversion, from 'short' to String, and to c-string, just to get the 'numeric representation' of a single byte is not very elegant,

The original author was sending measurements to AWS and then his v2 incorporated home assistant integration is where I became interested. I have had a steep learning curve to get to this point where I dont quite understand the readings. Home Assistant has a historic card that allows you to store in a database so sending every second is fine from what I understand. So my goal is to send power readings to home assistant. At the moment it if LOCAL_MEASUREMENTS 1 it sends every second which appears to be loading Home Assistant. It would be good to send every 30 seconds.

Thank you for taken the time to explain as it is helping me get to my end point.

Your code in the thread above sorry could you confirm where/what file I add it to? Assuming its the measure-electricity.h?

I didn't tell you to add code anywhere. I just explained why withLOCAL_MEASUREMENTShaving any other value than '1' , the code in 'measure-electricity.h' doesn't work.
The condition that triggers the execution of the sending never becomes true. If your data-base is structured the way you say it is, then that shouldn't matter though. You are sending 1 reading every second. Do you want to first collect multiple reading, and then send them all ?

Thanks if I remark //measureIndex = 0; then it send a MQTT measure every x reading but then I get a Guru Meditation Error: Core 1 panic'ed.

So can I assume it stores 30 reading then sends a MQTT topic? Seems to over write the message prior now to a core/panic/reboot.

measurements[measureIndex] = watts;
      measureIndex++;                                  // here the index gets upped 1

      if(measureIndex == LOCAL_MEASUREMENTS){

It takes a measurement, stores it in 'measurements',
ups the index of measurements, and once this index is equal to LOCAL_MEASUREMENTS executes the sending part of the code.
If LOCAL_MEASUREMENTS is defined as '1' that condition becomes true and it sends straight away. Then after sending the index gets reset to '0'. Why is it set up that way ? i don't know, i didn't write the code ! neither did you. There doesn't seem to be any need for the index at all, but maybe there was some plan at some point that didn't get completed. Does the code work for you, when LOCAL_MEASUREMENTS is defined as '1' ?
All you asked is 'why if i define LOCAL_MEASUREMENTS to anything else than 1 does the code not work ?'
I think i've answered that, but go over 'measure-electricity.h' again to make sure that you understand.
Do you want the code do anything different than what it does ? If so, what ?

Thanks Deva_Rishi for you assistance. I left the ESP running over night and it appears to be working with LOCAL_MEASUREMENTS = 1.

The reason it stores

It takes a measurement, stores it in 'measurements',
ups the index of measurements, and once this index is equal to LOCAL_MEASUREMENTS executes the sending part of the code

is due to the AWS side. It sends in batched to keep the cost down see the Dev's comment below.

  1. I'm not inserting multiple rows at once. The sensor basically sends 30 measurements as 1 batch to AWS IoT. This batch is then written as 1 row to DynamoDB. The primary reason for this is the cost. Sending raw measurements each second would create a ton of rows in DynamoDB (which is slower to query) and would increase my IoT Rule bill x30.

At this stage I wanted to bed in the ESP/Code/Home Assistant and then build out to the cloud/AWS long term. I wanted to check pre building the AWS/IOT side to make sure it was working and worth while hence in the dev's code it had AWS = True or False, if false no AWS support. It appears that Home Assistant side stops if you disabled the AWS code and that's where you have assisted me.

I would like to have the ESP send to Home Assistant via MQTT batches rather than in real time as Home Assistant runs on a Rasberry PI and I like to keep resources available. My concern with the LOCAL_MEASUREMENTS = 1 was it appeared to be hammering the Rasberry PI.

As I learn more about ESP/Arduino its getting easier due to your help so thanks to date!

It takes a measurement, stores it in 'measurements',
ups the index of measurements, and once this index is equal to LOCAL_MEASUREMENTS executes the sending part of the code

If that is what it is supposed to do, then the code would need to be modified. If you want to send 30 measurements taken 1 second apart, every 30 seconds it is not difficult, but that is not what the code can do right now. If the dev has created code to do that, this is an earlier version in which that has not been fully implemented. Mind you it really is only a few small modifications that would need to be done.