1

I'm trying to read data being transferred via Infrared from my Smartmeter on my ESP8266 using the following sketch:

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>
#include <Ticker.h>
#include <AsyncMqttClient.h>

#ifndef STASSID
#define STASSID "mySSID"
#define STAPSK  "myPassword"
#endif

#ifndef MQTT_HOST
#define MQTT_HOST IPAddress(192, 168, 0, 100)
#define MQTT_PORT 1883
#endif

#ifndef IR
#define SerialDebug Serial1
#define IR Serial
#endif

AsyncMqttClient mqttClient;
Ticker mqttReconnectTimer;

WiFiEventHandler wifiConnectHandler;
WiFiEventHandler wifiDisconnectHandler;
Ticker wifiReconnectTimer;

const char* host = "esp8266-webupdate";

const byte firstByte = 0x7E;
const byte lastByte = 0x7E;

uint8_t i;

ESP8266WebServer httpServer(80);
ESP8266HTTPUpdateServer httpUpdater;

void connectToWifi() {
  SerialDebug.println("Connecting to WiFi ...");
  WiFi.begin(STASSID, STAPSK);
}

void connectToMqtt() {
  SerialDebug.println("Connecting to MQTT ...");
  mqttClient.connect();
}

void onWifiConnect(const WiFiEventStationModeGotIP& event) {
  SerialDebug.println("Connected to Wi-Fi.");
  connectToMqtt();
}

void onWifiDisconnect(const WiFiEventStationModeDisconnected& event) {
  SerialDebug.println("Disconnected from Wi-Fi.");
  mqttReconnectTimer.detach();
  wifiReconnectTimer.once(2, connectToWifi);
}

void onMqttConnect(bool sessionPresent) {
  SerialDebug.println("Connected to MQTT.");
  SerialDebug.print("Session present: ");
  SerialDebug.println(sessionPresent);
}

void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
  SerialDebug.println("Disconnected from MQTT.");

  if (WiFi.isConnected()) {
    mqttReconnectTimer.once(2, connectToMqtt);
  }
}

void array_to_string(byte array[], unsigned int len, char buffer[]) {
  for (unsigned int i = 0; i < len; i++) {
    byte nib1 = (array[i] >> 4) & 0x0F;
    byte nib2 = (array[i] >> 0) & 0x0F;
    buffer[i*2+0] = nib1  < 0xA ? '0' + nib1  : 'A' + nib1  - 0xA;
    buffer[i*2+1] = nib2  < 0xA ? '0' + nib2  : 'A' + nib2  - 0xA;
  }
  buffer[len*2] = '\0';
}

void setup() {

  SerialDebug.begin(115200);
  IR.begin(9600);
  SerialDebug.println();
  SerialDebug.println("Booting Sketch...");
  
  wifiConnectHandler = WiFi.onStationModeGotIP(onWifiConnect);
  wifiDisconnectHandler = WiFi.onStationModeDisconnected(onWifiDisconnect);

  mqttClient.onConnect(onMqttConnect);
  mqttClient.onDisconnect(onMqttDisconnect);
  mqttClient.setServer(MQTT_HOST, MQTT_PORT);

  connectToWifi();

  httpUpdater.setup(&httpServer);
  httpServer.begin();

  mqttClient.setWill("smartmeter/online", 0, true, "no");
}

void loop() {
  httpServer.handleClient();
  
  uint32_t timeout = 2000;
  
  int readCnt = 0;
  uint32_t start_time = millis();
  byte tempChar = 0;
  byte *readMessage;
  uint32_t messageLen = 0;
  
  while ((tempChar != 0x7E) && (millis() - start_time < timeout)) {
    if (IR.available()) {
      tempChar = IR.read();
    }
  }
  
  start_time = millis();
  timeout = 1000;
  bool done = false;
  
  if (tempChar == firstByte) { // if first byte == 0x7E
    while ((millis() - start_time) < timeout && !done) {
      if (IR.available()) {
        tempChar = IR.read(); // second byte must be 0xA0
        if(tempChar == 0xA0) {
          while ((millis() - start_time) < timeout && !done) {
            if (IR.available()) {
              tempChar = IR.read(); // 3rd byte tells the legth of the message
    
              readMessage = new byte[tempChar+2];
              memset(readMessage, 0, tempChar+2);
              readMessage[0] = firstByte;
              readMessage[1] = 0xA0;
              readMessage[2] = tempChar;
              messageLen = ((uint32_t)(tempChar))+2;
              readCnt = 3;
      
              while ( readCnt < messageLen && ((millis() - start_time) < timeout)) { // minimum len 120 chars for 0x7E format
                if (IR.available()) {
                  readMessage[readCnt] = IR.read(); 
                  readCnt++; 
        
                  if(readCnt == tempChar+2 && readMessage[readCnt-1] != lastByte) { // correct len but last byte not 0x7E
                    done = true;
                    SerialDebug.printf("Wrong end byte found - %d\n", readMessage[readCnt-1]);
                  } else if(readCnt == tempChar+2) { // correct len and correct last byte
                    done = true;
                    char str[251] = "";
                    array_to_string(readMessage, readCnt, str);
                    mqttClient.publish("smartmeter/raw", 0, false, str);
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  mqttClient.publish("smartmeter/online", 0, true, "yes");
  char heap[6] = "";
  itoa(ESP.getFreeHeap(), heap, 10);
  mqttClient.publish("smartmeter/freeHeap", 0, false, heap);
}

I'm getting the encoded HEX-data published on my MQTT Topic as desired, the outputs length is 250 characters long (= 125 Bytes) which is correct:

7ea07bcf000200231362b1e6e700db08534d536770075f626120000e32e62addedaede38e64c8c3173035a0a853851a28efc57f7fd76a9df59dbb152f796939328ff0f28df7f257d20b5cb2a458c4eb9188e2c1c251701c891244d859ed9c159714bb4451c090d9b1ed3bbb1fc89785ebafbf59ec4f9d540eb4c90d47e

As I found out, the ESP's heap is decreasing by 100-300 by each iteration of the loop (data is being transmitted every second via Infrared from the power-grid Smartmeter) until the ESP will eventually be rebooted by the watchdog after like 5-8 minutes.

I explicitly did not use String variables in the sketch but I legit cannot find the reason for the memory leak. Tried turning off wifi, webserver and mqtt one after each other which didn't actually made a difference.

The heap-size stays at a constant level though when there is no data being received via the Infrared serial port, so I imagine something is fishy when storing the data in the byte array "readMessage".

Anything obvious that immediately pops into someones eye that I missed?

1
  • the 'problem with Strings' is a problem with new hidden in String class. your use of new is not hidden
    – Juraj
    Commented Aug 26, 2022 at 5:27

1 Answer 1

2

You do a new inside the loop

 readMessage = new byte[tempChar+2];

which causes the heap to be filled with tempChar+2 bytes, but you never delete this.

Try reusing this memory (making it a local, or make it 'dirty' global).

Probably (not tested) the above line can be rewritten as:

byte readMessage[tempChar+2];
3
  • 1
    By now I was already convinced it's something so obvious sigh A simple delete[] readMessage; after the MQTT publish of the "str" variable did the trick! Even if I went a different path so actually solve the problem, you gave the actual hint that guided me there, so I'll mark your answer as accepted. Thank you very much!
    – 1_am_r00t
    Commented Aug 25, 2022 at 23:44
  • 3
    You are welcome ... however, take in mind that a new and delete is not needed in this case, as the data only lives within one function. Typically new / delete is used when the life span of data is higher than one function, or when the amount of data is not known beforehand/varies. Commented Aug 26, 2022 at 0:00
  • True that - I will definitely take it into account when I will be implementing parts of this sketch to the final code!
    – 1_am_r00t
    Commented Aug 26, 2022 at 0:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.