F&F LE-01MW Energy meter

Moderators: grovkillen, Stuntteam, TD-er

Post Reply
Message
Author
janumix
New user
Posts: 2
Joined: 11 Sep 2021, 00:01

F&F LE-01MW Energy meter

#1 Post by janumix » 11 Sep 2021, 00:29

Hi

I've modify _P027_INA219 plugin for energy meter F&F LE-01MW https://www.fif.com.pl/en/usage-electri ... -01mw.html and like to share the code to one who could verify this. It works for me on Wemos D1 PRO board with RS485 converter.

BR

Code: Select all

#ifdef USES_P185

// #######################################################################################################
// ############################# Plugin 185: F&F LE-01MW #################################################
// #######################################################################################################


/*

   Circuit wiring
    GPIO Setting 1 -> RX
    GPIO Setting 2 -> TX
    GPIO Setting 3 -> DE/RE pin for MAX485
    Use 1kOhm in serie on datapins!
 */

#define PLUGIN_185
#define PLUGIN_ID_185 185
#define PLUGIN_NAME_185      "Energy - F&F LE-01MW [TESTING]"
#define PLUGIN_VALUENAME1_185 ""

#define Plugin_DEV_ID         PCONFIG(0)
#define Plugin_DEV_ID_LABEL   PCONFIG_LABEL(0)
#define Plugin_MODEL          PCONFIG(1)
#define Plugin_MODEL_LABEL    PCONFIG_LABEL(1)
#define Plugin_BAUDRATE     PCONFIG(2)
#define Plugin_BAUDRATE_LABEL PCONFIG_LABEL(2)
#define Plugin_QUERY1       PCONFIG(3)
#define Plugin_QUERY2       PCONFIG(4)
#define Plugin_QUERY3       PCONFIG(5)
#define Plugin_QUERY4       PCONFIG(6)
#define Plugin_DEPIN          CONFIG_PIN3

#define Plugin_NR_OUTPUT_VALUES   VARS_PER_TASK
#define Plugin_QUERY1_CONFIG_POS  3

#define Plugin_QUERY_V       0
#define Plugin_QUERY_A       1
#define Plugin_QUERY_W       2
#define Plugin_QUERY_VAR     3
#define Plugin_QUERY_VA      4
#define Plugin_QUERY_Wh      5
#define Plugin_QUERY_varh    6
#define Plugin_QUERY_Pf      7
#define Plugin_QUERY_f       8
#define Plugin_NR_OUTPUT_OPTIONS   9 // Must be the last one

#define Plugin_DEV_ID_DFLT   1       // Modbus communication address
#define Plugin_MODEL_DFLT    0       // 
#define Plugin_BAUDRATE_DFLT 4       // 19200 baud
#define Plugin_QUERY1_DFLT   Plugin_QUERY_V
#define Plugin_QUERY2_DFLT   Plugin_QUERY_A
#define Plugin_QUERY3_DFLT   Plugin_QUERY_W
#define Plugin_QUERY4_DFLT   Plugin_QUERY_Wh

#define Plugin_MEASUREMENT_INTERVAL 60000L

#include <ESPeasySerial.h>
#include "_Plugin_Helper.h"
#include "src/Helpers/Modbus_RTU.h"

struct Plugin_data_struct : public PluginTaskData_base {
  Plugin_data_struct() {}

  ~Plugin_data_struct() {
    reset();
  }

  void reset() {
    modbus.reset();
  }

  bool init(const int16_t serial_rx, const int16_t serial_tx, int8_t dere_pin,
            unsigned int baudrate, uint8_t modbusAddress) {
    return modbus.init(serial_rx, serial_tx, baudrate, modbusAddress, dere_pin);
  }

  bool isInitialized() const {
    return modbus.isInitialized();
  }

  ModbusRTU_struct modbus;
};

unsigned int _plugin_185_last_measurement = 0;

boolean Plugin_185(byte function, struct EventStruct *event, String& string) {
  boolean success = false;

  switch (function) {
    case PLUGIN_DEVICE_ADD: {
      Device[++deviceCount].Number           = PLUGIN_ID_185;
      Device[deviceCount].Type               = DEVICE_TYPE_SERIAL_PLUS1; // connected through 3 datapins
      Device[deviceCount].VType              = SENSOR_TYPE_QUAD;
      Device[deviceCount].Ports              = 0;
      Device[deviceCount].PullUpOption       = false;
      Device[deviceCount].InverseLogicOption = false;
      Device[deviceCount].FormulaOption      = true;
      Device[deviceCount].ValueCount         = Plugin_NR_OUTPUT_VALUES;
      Device[deviceCount].SendDataOption     = true;
      Device[deviceCount].TimerOption        = true;
      Device[deviceCount].GlobalSyncOption   = true;
      break;
    }

    case PLUGIN_GET_DEVICENAME: {
      string = F(PLUGIN_NAME_185);
      break;
    }

    case PLUGIN_GET_DEVICEVALUENAMES: {
      for (byte i = 0; i < VARS_PER_TASK; ++i) {
        if (i < Plugin_NR_OUTPUT_VALUES) {
          const byte pconfigIndex = i + Plugin_QUERY1_CONFIG_POS;
          byte choice             = PCONFIG(pconfigIndex);
          safe_strncpy(
            ExtraTaskSettings.TaskDeviceValueNames[i],
            Plugin_valuename(choice, false),
            sizeof(ExtraTaskSettings.TaskDeviceValueNames[i]));
        } else {
          ZERO_FILL(ExtraTaskSettings.TaskDeviceValueNames[i]);
        }
      }
      break;
    }

    case PLUGIN_GET_DEVICEGPIONAMES: {
      serialHelper_getGpioNames(event);
      event->String3 = formatGpioName_output_optional("DE");
      break;
    }

    case PLUGIN_WEBFORM_SHOW_CONFIG:
    {
      string += serialHelper_getSerialTypeLabel(event);
      success = true;
      break;
    }

    case PLUGIN_SET_DEFAULTS: {
      Plugin_DEV_ID   = Plugin_DEV_ID_DFLT;
      Plugin_MODEL    = Plugin_MODEL_DFLT;
      Plugin_BAUDRATE = Plugin_BAUDRATE_DFLT;
      Plugin_QUERY1   = Plugin_QUERY1_DFLT;
      Plugin_QUERY2   = Plugin_QUERY2_DFLT;
      Plugin_QUERY3   = Plugin_QUERY3_DFLT;
      Plugin_QUERY4   = Plugin_QUERY4_DFLT;

      success = true;
      break;
    }

    case PLUGIN_WRITE: {
      break;
    }

    case PLUGIN_WEBFORM_LOAD: {
      serialHelper_webformLoad(event);

      {
        // Modbus parameters put in scope to make sure the String array will not keep memory occupied.
        String options_baudrate[6];

        for (int i = 0; i < 6; ++i) {
          options_baudrate[i] = String(StorageValueToBaudrate(i));
        }
        addFormNumericBox(F("Modbus Address"), Plugin_DEV_ID_LABEL, Plugin_DEV_ID, 1,
                          247);
        addFormSelector(F("Baud Rate"), Plugin_BAUDRATE_LABEL, 6, options_baudrate,
                        NULL, Plugin_BAUDRATE);
      }

      Plugin_data_struct *Plugin_data =
        static_cast<Plugin_data_struct *>(getPluginTaskData(event->TaskIndex));

      if ((nullptr != Plugin_data) && Plugin_data->isInitialized()) {
        String detectedString = Plugin_data->modbus.detected_device_description;

        if (detectedString.length() > 0) {
          addFormNote(detectedString);
        }
        addRowLabel(F("Checksum (pass/fail/nodata)"));
        uint32_t reads_pass, reads_crc_failed, reads_nodata;
        Plugin_data->modbus.getStatistics(reads_pass, reads_crc_failed, reads_nodata);
        String chksumStats;
        chksumStats  = reads_pass;
        chksumStats += '/';
        chksumStats += reads_crc_failed;
        chksumStats += '/';
        chksumStats += reads_nodata;
        addHtml(chksumStats);

        addFormSubHeader(F("Calibration"));


        addFormSubHeader(F("Logged Values"));
        ShowValueLoadPage(Plugin_QUERY_VAR, event);
        ShowValueLoadPage(Plugin_QUERY_VA,  event);
        ShowValueLoadPage(Plugin_QUERY_Wh, event);
        ShowValueLoadPage(Plugin_QUERY_varh, event);
        ShowValueLoadPage(Plugin_QUERY_Pf,  event);
        ShowValueLoadPage(Plugin_QUERY_f, event);

        // Checkbox is always presented unchecked.
        // Must check and save to clear the stored accumulated values in the sensor.
        addFormCheckBox(F("Clear logged values"), F("Plugin_clear_log"), false);
        addFormNote(F("Will clear all logged values when checked and saved"));
      }

      {
        // In a separate scope to free memory of String array as soon as possible
        sensorTypeHelper_webformLoad_header();
        String options[Plugin_NR_OUTPUT_OPTIONS];

        for (int i = 0; i < Plugin_NR_OUTPUT_OPTIONS; ++i) {
          options[i] = Plugin_valuename(i, true);
        }

        for (byte i = 0; i < Plugin_NR_OUTPUT_VALUES; ++i) {
          const byte pconfigIndex = i + Plugin_QUERY1_CONFIG_POS;
          sensorTypeHelper_loadOutputSelector(event, pconfigIndex, i, Plugin_NR_OUTPUT_OPTIONS, options);
        }
      }
      success = true;
      break;
    }

    case PLUGIN_WEBFORM_SAVE: {
      serialHelper_webformSave(event);

      // Save normal parameters
      for (int i = 0; i < Plugin_QUERY1_CONFIG_POS; ++i) {
        pconfig_webformSave(event, i);
      }

      // Save output selector parameters.
      for (byte i = 0; i < Plugin_NR_OUTPUT_VALUES; ++i) {
        const byte pconfigIndex = i + Plugin_QUERY1_CONFIG_POS;
        const byte choice       = PCONFIG(pconfigIndex);
        sensorTypeHelper_saveOutputSelector(event, pconfigIndex, i, Plugin_valuename(choice, false));
      }
      Plugin_data_struct *Plugin_data =
        static_cast<Plugin_data_struct *>(getPluginTaskData(event->TaskIndex));

      if ((nullptr != Plugin_data) && Plugin_data->isInitialized()) {

        // wstawic zapis do rejestrów jeśli trzeba
        
      }


      success = true;
      break;
    }

    case PLUGIN_INIT: {
      const int16_t serial_rx = CONFIG_PIN1;
      const int16_t serial_tx = CONFIG_PIN2;
      initPluginTaskData(event->TaskIndex, new (std::nothrow) Plugin_data_struct());
      Plugin_data_struct *Plugin_data =
        static_cast<Plugin_data_struct *>(getPluginTaskData(event->TaskIndex));

      if (nullptr == Plugin_data) {
        return success;
      }

      if (Plugin_data->init(serial_rx, serial_tx, Plugin_DEPIN,
                          StorageValueToBaudrate(Plugin_BAUDRATE),
                          Plugin_DEV_ID)) {
        serialHelper_log_GpioDescription(serial_rx, serial_tx);
        success = true;
      } else {
        clearPluginTaskData(event->TaskIndex);
      }
      break;
    }

    case PLUGIN_EXIT: {
      clearPluginTaskData(event->TaskIndex);
      success = true;
      break;
    }

    case PLUGIN_READ: {
      Plugin_data_struct *Plugin_data =
        static_cast<Plugin_data_struct *>(getPluginTaskData(event->TaskIndex));

      if ((nullptr != Plugin_data) && Plugin_data->isInitialized()) {
        for (int i = 0; i < Plugin_NR_OUTPUT_VALUES; ++i) {
          UserVar[event->BaseVarIndex + i] = ReadValue(PCONFIG(i + Plugin_QUERY1_CONFIG_POS), event);
          delay(1);
        }

        success = true;
      }
      break;
    }
  }
  return success;
}

String Plugin_valuename(byte value_nr, bool displayString) {
  switch (value_nr) {
    case Plugin_QUERY_V:      return displayString ? F("Voltage (V)") : F("V");
    case Plugin_QUERY_A:      return displayString ? F("Current (A)") : F("A");
    case Plugin_QUERY_W:      return displayString ? F("Power (W)") : F("W");
    case Plugin_QUERY_VAR: return displayString ? F("Power reactive (var)") : F("VAR");
    case Plugin_QUERY_VA:  return displayString ? F("Power apparent (VA)") : F("VA");
    case Plugin_QUERY_Wh: return displayString ? F("Total active Energy (Wh)") : F("Wh");
    case Plugin_QUERY_varh: return displayString ? F("Total reactive Energy (VARh)") : F("varh");
    case Plugin_QUERY_Pf:     return displayString ? F("Phase coef. ()") : F("Pf");
    case Plugin_QUERY_f:      return displayString ? F("Frequency (Hz)") : F("f");
  }
  return "";
}

int StorageValueToBaudrate(byte baudrate_setting) {
  switch (baudrate_setting) {
    case 0:
      return 1200;
    case 1:
      return 2400;
    case 2:
      return 4800;
    case 3:
      return 9600;
    case 4:
      return 19200;
    case 5:
      return 38500;
  }
  return 19200;
}

float ReadValue(byte query, struct EventStruct *event) {
  Plugin_data_struct *Plugin_data =
    static_cast<Plugin_data_struct *>(getPluginTaskData(event->TaskIndex));
 byte errorcode = 0;
  if ((nullptr != Plugin_data) && Plugin_data->isInitialized()) {
    switch (query) {
      case Plugin_QUERY_V:
        return Plugin_data->modbus.readHoldingRegister(0x0131, errorcode);
      case Plugin_QUERY_A:
        return Plugin_data->modbus.read_32b_HoldingRegister(0x0139);
      case Plugin_QUERY_W:
        return Plugin_data->modbus.read_32b_HoldingRegister(0x0140); // 
      case Plugin_QUERY_VAR:
        return Plugin_data->modbus.read_32b_HoldingRegister(0x0148);     //
      case Plugin_QUERY_VA:
        return Plugin_data->modbus.read_32b_HoldingRegister(0x0150);     // 
      case Plugin_QUERY_Wh:
        return Plugin_data->modbus.read_32b_HoldingRegister(0xA000);     // 
      case Plugin_QUERY_varh:
        return Plugin_data->modbus.read_32b_HoldingRegister(0xA01E); 
      case Plugin_QUERY_Pf:
        return Plugin_data->modbus.readHoldingRegister(0x0158, errorcode) ; // *0.001 in GIU
      case Plugin_QUERY_f:
        return Plugin_data->modbus.readHoldingRegister(0x0130, errorcode) ; // * 0.01 in GUI

    }
  }
  return 0.0;
}

void ShowValueLoadPage(byte query, struct EventStruct *event) {
  addRowLabel(Plugin_valuename(query, true));
  addHtml(String(ReadValue(query, event)));
}


#endif // USES_P185

TD-er
Core team member
Posts: 8643
Joined: 01 Sep 2017, 22:13
Location: the Netherlands
Contact:

Re: F&F LE-01MW Energy meter

#2 Post by TD-er » 11 Sep 2021, 00:46

Can you make a pull request for this on GitHub?
That's way easier to process and things are hard to find later on the forum.

janumix
New user
Posts: 2
Joined: 11 Sep 2021, 00:01

Re: F&F LE-01MW Energy meter

#3 Post by janumix » 11 Sep 2021, 00:53

I could try but havn't been doing so before.
It'll take some time for reading :)

Post Reply

Who is online

Users browsing this forum: No registered users and 38 guests