Slightly confused about plugin development

Moderators: grovkillen, Stuntteam, TD-er

Post Reply
Message
Author
janjoh
Normal user
Posts: 12
Joined: 05 Oct 2018, 19:54

Slightly confused about plugin development

#1 Post by janjoh » 17 Oct 2018, 22:03

Sooo, I decided that I need a plugin for AC measurement with ACS712.

So i followed the guides at https://www.letscontrolit.com/wiki/inde ... Platformio, https://www.letscontrolit.com/wiki/inde ... ith_github, https://www.letscontrolit.com/wiki/inde ... platformio I am running on Windows 10 Professional.

That gave me an environment that does a full PIO Build in about 15 minutes, and a normal_ESP8266_4096 (Which i gather would be the proper one for Wemos D1 Mini Pro, right?) in about 15 seconds.
I see a few warnings, about unused/redeclared variables, and I assume those are normal.

Then i copied _Pxxx_PluginTemplate.ino to _P217_ACS712AC.ino (I saw on the playground that I should use unique values between 100-199, but i also saw "used the next available one", which appears to be 217.

Peeking in _P002_ADC.ino i set out to modify my file to do nothing but initialize

Code: Select all

//#include section
//include libraries here. For example:
//#include <LiquidCrystal_I2C.h>

//uncomment one of the following as needed
//#ifdef PLUGIN_BUILD_DEVELOPMENT
//#ifdef PLUGIN_BUILD_TESTING

#define PLUGIN_217
#define PLUGIN_ID_217     217               //plugin id
#define PLUGIN_NAME_217   "Energy Meter ACS712 AC"     //"Plugin Name" is what will be dislpayed in the selection list
#define PLUGIN_VALUENAME1_217 "Ampere (RMS)"     //variable output of the plugin. The label is in quotation marks
//#define PLUGIN_VALUENAME2_xxx "output2"     //multiple outputs are supported
//#define PLUGIN_xxx_DEBUG  false             //set to true for extra log info in the debug

/*
PIN/port configuration is stored in the following:
Settings.TaskDevicePin1[event->TaskIndex] - The first GPIO pin selected within the task
Settings.TaskDevicePin2[event->TaskIndex] - The second GPIO pin selected within the task
Settings.TaskDevicePin3[event->TaskIndex] - The third GPIO pin selected within the task
Settings.TaskDevicePort[event->TaskIndex] - The port in case the device has multiple in/out pins

Custom configuration is stored in the following:
Settings.TaskDevicePluginConfig[event->TaskIndex][x]
x can be between 1 - 8 and can store values between -32767 - 32768 (16 bit)

*/

//A plugin has to implement the following function

boolean Plugin_217(byte function, struct EventStruct *event, String& string)
{
  //function: reason the plugin was called
  //event: ??add description here??
  // string: ??add description here??

  boolean success = false;

  switch (function)
  {
    case PLUGIN_DEVICE_ADD:
    {
        //This case defines the device characteristics, edit appropriately

        Device[++deviceCount].Number = PLUGIN_ID_217;
        Device[deviceCount].Type = DEVICE_TYPE_ANALOG;  //how the device is connected
        Device[deviceCount].VType = SENSOR_TYPE_SINGLE; //type of value the plugin will return, used only for Domoticz
        Device[deviceCount].Ports = 0;
        Device[deviceCount].PullUpOption = false;
        Device[deviceCount].InverseLogicOption = false;
        Device[deviceCount].FormulaOption = true;
        Device[deviceCount].ValueCount = 1;             //number of output variables. The value should match the number of keys PLUGIN_VALUENAME1_xxx
        Device[deviceCount].SendDataOption = true;
        Device[deviceCount].TimerOption = true;
        Device[deviceCount].TimerOptional = true;
        Device[deviceCount].GlobalSyncOption = true;
        Device[deviceCount].DecimalsOnly = true;
        break;
    }

    case PLUGIN_GET_DEVICENAME:
    {
      //return the device name
      string = F(PLUGIN_NAME_217);
      break;
    }

    case PLUGIN_GET_DEVICEVALUENAMES:
    {
      //called when the user opens the module configuration page
      //it allows to add a new row for each output variable of the plugin
      strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_217));
      break;
    }

    case PLUGIN_WEBFORM_LOAD:
    {
      //this case defines what should be displayed on the web form, when this plugin is selected
      //The user's selection will be stored in
      //Settings.TaskDevicePluginConfig[event->TaskIndex][x] (custom configuration)

      // Make sure not to append data to the string variable in this PLUGIN_WEBFORM_LOAD call.
      // This has changed, so now use the appropriate functions to write directly to the Streaming
      // WebServer. This takes much less memory and is faster.
      // There will be an error in the web interface if something is added to the "string" variable.

      //Use any of the following (defined at WebServer.ino):
      //addFormNote(F("not editable text added here"));
      To add some html, which cannot be done in the existing functions, add it in the following way:
      addHtml(F("<TR><TD>Analog Pin:<TD>"));


      For strings, always use the F() macro, which stores the string in flash, not in memory.

      //String dropdown[5] = { F("option1"), F("option2"), F("option3"), F("option4")};
      //addFormSelector(string, F("drop-down menu"), F("plugin_xxx_displtype"), 4, dropdown, NULL, Settings.TaskDevicePluginConfig[event->TaskIndex][0]);

      //number selection (min-value - max-value)
      addFormNumericBox(string, F("description"), F("plugin_xxx_description"), Settings.TaskDevicePluginConfig[event->TaskIndex][1], min-value, max-value);

      //after the form has been loaded, set success and break
      success = true;
      break;
    }

    case PLUGIN_WEBFORM_SAVE:
    {
      //this case defines the code to be executed when the form is submitted
      //the plugin settings should be saved to Settings.TaskDevicePluginConfig[event->TaskIndex][x]
      //ping configuration should be read from Settings.TaskDevicePin1[event->TaskIndex] and stored

      //after the form has been saved successfuly, set success and break
      success = true;
      break;

    }
    case PLUGIN_INIT:
    {
      //this case defines code to be executed when the plugin is initialised

      //after the plugin has been initialised successfuly, set success and break
      success = true;
      break;

    }

    case PLUGIN_READ:
    {
      //code to be executed to read data
      //It is executed according to the delay configured on the device configuration page, only once

      //after the plugin has read data successfuly, set success and break
      success = true;
      break;

    }

    case PLUGIN_WRITE:
    {
      //this case defines code to be executed when the plugin executes an action (command).
      //Commands can be accessed via rules or via http.
      //As an example, http://192.168.1.12//control?cmd=dothis
      //implies that there exists the comamnd "dothis"

      if (plugin_not_initialised)
        break;

      //parse string to extract the command
      String tmpString  = string;
      int argIndex = tmpString.indexOf(',');
      if (argIndex)
        tmpString = tmpString.substring(0, argIndex);

      String tmpStr = string;
      int comma1 = tmpStr.indexOf(',');
      if (tmpString.equalsIgnoreCase(F("dothis"))) {
        //do something
        success = true;     //set to true only if plugin has executed a command successfully
      }

       break;
    }

	case PLUGIN_EXIT:
	{
	  //perform cleanup tasks here. For example, free memory

	  break;

	}

    case PLUGIN_ONCE_A_SECOND:
    {
      //code to be executed once a second. Tasks which do not require fast response can be added here

      success = true;

    }

    case PLUGIN_TEN_PER_SECOND:
    {
      //code to be executed 10 times per second. Tasks which require fast response can be added here
      //be careful on what is added here. Heavy processing will result in slowing the module down!

      success = true;

    }
  }   // switch
  return success;

}     //function

//implement plugin specific procedures and functions here
void p217_do_sth_useful()
{
  //code

}
That, however, fails quite miserably. So, I take it I am missing something quite fundamental.. but what?

Code: Select all

C:/Users/j2/Documents/GitHub/ESPEasy/src/_P217_ACS712AC.ino:197:51: note: in expansion of macro 'F'
 
^
C:/Users/j2/Documents/GitHub/ESPEasy/src/_P217_ACS712AC.ino:289:11: error: 'plugin_not_initialised' was not declared in this scope
C:/Users/j2/Documents/GitHub/ESPEasy/src/_P217_ACS712AC.ino:309:11: warning: unused variable 'comma1' [-Wunused-variable]
*** [.pioenvs\normal_ESP8266_4096\src\ESPEasy.ino.cpp.o] Error 1
 [ERROR] Took 7.91 seconds

Environment normal_ESP8266_4096                 [ERROR]
 [ERROR] Took 7.92 seconds
 
 [SUMMARY]
Environment esp-wrover-kit_test_1M8_partition   [SKIP]
Environment esp32dev                            [SKIP]
Environment esp32test_1M8_partition             [SKIP]
Environment normal_ESP8266_1024                 [SKIP]
Environment normal_ESP8285_1024                 [SKIP]
Environment normal_WROOM02_2048                 [SKIP]
Environment normal_IR_ESP8266_4096              [SKIP]
Environment test_ESP8266_1024                   [SKIP]
Environment test_ESP8285_1024                   [SKIP]
Environment test_WROOM02_2048                   [SKIP]
Environment test_ESP8266_4096                   [SKIP]
Environment test_ESP8266_4096_VCC               [SKIP]
Environment dev_ESP8266_1024                    [SKIP]
Environment dev_ESP8285_1024                    [SKIP]
Environment dev_WROOM02_2048                    [SKIP]
Environment dev_ESP8266_4096                    [SKIP]
Environment dev_ESP8266PUYA_1024                [SKIP]
Environment dev_ESP8266PUYA_1024_VCC            [SKIP]
Environment hard_SONOFF_POW                     [SKIP]
Environment hard_SONOFF_POW_R2                  [SKIP]
Environment hard_Shelly_1                       [SKIP]
Environment hard_Ventus_W266                    [SKIP]
 

janjoh
Normal user
Posts: 12
Joined: 05 Oct 2018, 19:54

Re: Slightly confused about plugin development

#2 Post by janjoh » 20 Oct 2018, 23:54

Okay.. I think i now have a skeleton that at least appears to build. But, I am still not really understanding userVars i guess...

In PLUGIN_READ.. Lets say that i want the device to report my "AmpsRMS"... what is my next step so to say?

Code: Select all

  #define PLUGIN_217
  #define PLUGIN_ID_217     217               //plugin id
  #define PLUGIN_NAME_217   "Energy (AC) - ACS712"     //"Plugin Name" is what will be dislpayed in the selection list
  #define PLUGIN_VALUENAME1_217 "Amperes (RMS)"     //variable output of the plugin. The label is in quotation marks
  #define PLUGIN_217_DEBUG  true             //set to true for extra log info in the debug

  boolean Plugin_217(byte function, struct EventStruct *event, String& string)
  {
    boolean success = false;
    switch (function)
    {
      case PLUGIN_DEVICE_ADD:
      {

          //This case defines the device characteristics, edit appropriately

          Device[++deviceCount].Number = PLUGIN_ID_217;
          Device[deviceCount].Type = DEVICE_TYPE_ANALOG;  //how the device is connected
          Device[deviceCount].VType = SENSOR_TYPE_SINGLE; //type of value the plugin will return, used only for Domoticz
          Device[deviceCount].Ports = 0;
          Device[deviceCount].PullUpOption = false;
          Device[deviceCount].InverseLogicOption = false;
          Device[deviceCount].FormulaOption = false;
          Device[deviceCount].ValueCount = 1;             //number of output variables. The value should match the number of keys PLUGIN_VALUENAME1_xxx
          Device[deviceCount].SendDataOption = true;
          Device[deviceCount].TimerOption = true;
          Device[deviceCount].TimerOptional = true;
          Device[deviceCount].GlobalSyncOption = true;
          Device[deviceCount].DecimalsOnly = true;
          break;
      }
      case PLUGIN_GET_DEVICENAME:
      {
        //return the device name
        string = F(PLUGIN_NAME_217);
        break;
      }
      case PLUGIN_GET_DEVICEVALUENAMES:
      {
        //called when the user opens the module configuration page
        //it allows to add a new row for each output variable of the plugin
        strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_217));
        break;
      }
      case PLUGIN_WEBFORM_LOAD:
      {
        success = true;
        break;
      }
      case PLUGIN_WEBFORM_SAVE:
      {
        //this case defines the code to be executed when the form is submitted
        //the plugin settings should be saved to Settings.TaskDevicePluginConfig[event->TaskIndex][x]
        //ping configuration should be read from Settings.TaskDevicePin1[event->TaskIndex] and stored

        //after the form has been saved successfuly, set success and break
        success = true;
        break;

      }
      case PLUGIN_INIT:
      {
        //this case defines code to be executed when the plugin is initialised

        //after the plugin has been initialised successfuly, set success and break
        success = true;
        break;

      }

      case PLUGIN_READ:
      {

        int mVperAmp = 66; // use 100 for 20A Module and 66 for 30A Module
        double Voltage = 0;
        double VRMS = 0;
        double AmpsRMS = 0;
       Voltage = p217_get_VPP();
        VRMS = (Voltage/2.0) *0.707;
        AmpsRMS = (VRMS * 1000)/mVperAmp;
        success = true;
        break;

      }
      case PLUGIN_EXIT:
    	{
    	  //perform cleanup tasks here. For example, free memory

    	  break;

    	}

      case PLUGIN_ONCE_A_SECOND:
      {
        //code to be executed once a second. Tasks which do not require fast response can be added here
        success = true;
      }
    }
  }

float p217_get_VPP()
  {
    const int sensorIn = A0;
    float result;
    int readValue;             //value read from the sensor
    int maxValue = 0;          // store max value here
    int minValue = 1024;          // store min value here
    uint32_t start_time = millis();
   while((millis()-start_time) < 1000) //sample for 1 Sec
   {
       readValue = analogRead(sensorIn);
       // see if you have a new maxValue
       if (readValue > maxValue)
       {
           /*record the maximum sensor value*/
           maxValue = readValue;
       }
       if (readValue < minValue)
       {
           /*record the maximum sensor value*/
           minValue = readValue;
       }
   }

   // Subtract min from max
   result = ((maxValue - minValue) * 5.0)/1024.0;

   return result;

  }

User avatar
ThomasB
Normal user
Posts: 425
Joined: 17 Jun 2018, 20:41
Location: USA

Re: Slightly confused about plugin development

#3 Post by ThomasB » 26 Oct 2018, 16:47

You would assign PLUGIN_READ's AmpsRMS to the UserVar like this:

Code: Select all

UserVar[event->BaseVarIndex] = (float)AmpsRMS;
- Thomas

janjoh
Normal user
Posts: 12
Joined: 05 Oct 2018, 19:54

Re: Slightly confused about plugin development

#4 Post by janjoh » 26 Oct 2018, 22:16

ThomasB wrote:
26 Oct 2018, 16:47
You would assign PLUGIN_READ's AmpsRMS to the UserVar like this:

Code: Select all

UserVar[event->BaseVarIndex] = (float)AmpsRMS;
- Thomas
Ok. That was, eh, to simple in theory :)

But I still do not get any values in the web UI. I added som logging, and i see the "expected" values there, but i am still missing something... Annoying.

Code: Select all

  #define PLUGIN_217
  #define PLUGIN_ID_217     217               //plugin id
  #define PLUGIN_NAME_217   "Energy (AC) - ACS712"     //"Plugin Name" is what will be dislpayed in the selection list
  #define PLUGIN_VALUENAME1_217 "Amperes (RMS)"     //variable output of the plugin. The label is in quotation marks
  #define PLUGIN_217_DEBUG  true             //set to true for extra log info in the debug

  boolean Plugin_217(byte function, struct EventStruct *event, String& string)
  {
    boolean success = false;
    switch (function)
    {
      case PLUGIN_DEVICE_ADD:
      {

          //This case defines the device characteristics, edit appropriately

          Device[++deviceCount].Number = PLUGIN_ID_217;
          Device[deviceCount].Type = DEVICE_TYPE_ANALOG;  //how the device is connected
          Device[deviceCount].VType = SENSOR_TYPE_SINGLE; //type of value the plugin will return, used only for Domoticz
          Device[deviceCount].Ports = 0;
          Device[deviceCount].PullUpOption = false;
          Device[deviceCount].InverseLogicOption = false;
          Device[deviceCount].FormulaOption = false;
          Device[deviceCount].ValueCount = 1;             //number of output variables. The value should match the number of keys PLUGIN_VALUENAME1_xxx
          Device[deviceCount].SendDataOption = true;
          Device[deviceCount].TimerOption = true;
          Device[deviceCount].TimerOptional = true;
          Device[deviceCount].GlobalSyncOption = true;
          Device[deviceCount].DecimalsOnly = true;
          break;
      }
      case PLUGIN_GET_DEVICENAME:
      {
        //return the device name
        string = F(PLUGIN_NAME_217);
        break;
      }
      case PLUGIN_GET_DEVICEVALUENAMES:
      {
        //called when the user opens the module configuration page
        //it allows to add a new row for each output variable of the plugin
        strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_217));
        break;
      }
      case PLUGIN_WEBFORM_LOAD:
      {
        success = true;
        break;
      }
      case PLUGIN_WEBFORM_SAVE:
      {
        //this case defines the code to be executed when the form is submitted
        //the plugin settings should be saved to Settings.TaskDevicePluginConfig[event->TaskIndex][x]
        //ping configuration should be read from Settings.TaskDevicePin1[event->TaskIndex] and stored

        //after the form has been saved successfuly, set success and break
        success = true;
        break;

      }
      case PLUGIN_INIT:
      {
        //this case defines code to be executed when the plugin is initialised

        //after the plugin has been initialised successfuly, set success and break
        success = true;
        break;

      }

      case PLUGIN_READ:
      {
        addLog(LOG_LEVEL_INFO,"P217PluginRead");
        int mVperAmp = 66; // use 100 for 20A Module and 66 for 30A Module
        double Voltage = 0;
        double VRMS = 0;
        double AmpsRMS = 0;
        Voltage = p217_get_VPP();
        addLog(LOG_LEVEL_INFO,String(Voltage));
        VRMS = (Voltage/2.0) *0.707;
        AmpsRMS = (VRMS * 1000)/mVperAmp;
        addLog(LOG_LEVEL_INFO,String(AmpsRMS));
        UserVar[event->BaseVarIndex] = (float)AmpsRMS;
        success = true;
        break;

      }
      case PLUGIN_EXIT:
    	{
    	  //perform cleanup tasks here. For example, free memory

    	  break;

    	}

      case PLUGIN_ONCE_A_SECOND:
      {
        //code to be executed once a second. Tasks which do not require fast response can be added here
        success = true;
      }
    }
  }

float p217_get_VPP()
  {
    const int sensorIn = A0;
    float result;
    int readValue;             //value read from the sensor
    int maxValue = 0;          // store max value here
    int minValue = 1024;          // store min value here
    uint32_t start_time = millis();
   while((millis()-start_time) < 1000) //sample for 1 Sec
   {
       readValue = analogRead(sensorIn);
       // see if you have a new maxValue
       if (readValue > maxValue)
       {
           /*record the maximum sensor value*/
           maxValue = readValue;
       }
       if (readValue < minValue)
       {
           /*record the maximum sensor value*/
           minValue = readValue;
       }
   }

   // Subtract min from max
   result = ((maxValue - minValue) * 5.0)/1024.0;

   return result;

  }

User avatar
ThomasB
Normal user
Posts: 425
Joined: 17 Jun 2018, 20:41
Location: USA

Re: Slightly confused about plugin development

#5 Post by ThomasB » 27 Oct 2018, 00:40

Then continue to check for mistakes in your code. You can review the other plugins to see working examples.

Unfortunately I don't have time to do a thorough review. But I see a significant error. Your Plugin_075() function is missing the closing return success; that tells the calling function to update the user vars.

- Thomas

janjoh
Normal user
Posts: 12
Joined: 05 Oct 2018, 19:54

Re: Slightly confused about plugin development

#6 Post by janjoh » 27 Oct 2018, 08:56

ThomasB wrote:
27 Oct 2018, 00:40
Then continue to check for mistakes in your code. You can review the other plugins to see working examples.

Unfortunately I don't have time to do a thorough review. But I see a significant error. Your Plugin_075() function is missing the closing return success; that tells the calling function to update the user vars.

- Thomas
That it all it was. Sigh...

I am so very grateful for your help. Truly. Thank you.

Now to re-do it. Clean it up and make it better :)

User avatar
ThomasB
Normal user
Posts: 425
Joined: 17 Jun 2018, 20:41
Location: USA

Re: Slightly confused about plugin development

#7 Post by ThomasB » 27 Oct 2018, 18:42

Glad to hear you got it working.
- Thomas

janjoh
Normal user
Posts: 12
Joined: 05 Oct 2018, 19:54

Re: Slightly confused about plugin development

#8 Post by janjoh » 27 Oct 2018, 21:19

ThomasB wrote:
27 Oct 2018, 18:42
Glad to hear you got it working.
- Thomas
I think i must have accidentally deleted that during all my "incremental approximation" :D

stefannetzer
Normal user
Posts: 1
Joined: 11 Jul 2019, 23:47

Re: Slightly confused about plugin development

#9 Post by stefannetzer » 12 Jul 2019, 00:02

Hi janjoh

I like your Plugin. After some work with the module I did some reasearch to stabilize the Measurements and to shorten the measurement cycle. Before adding anything to GitHub, I would like to get in touch with you first.

Best Regards
Stefan

alexthesmeus
Normal user
Posts: 1
Joined: 12 Nov 2019, 13:28

Re: Slightly confused about plugin development

#10 Post by alexthesmeus » 12 Nov 2019, 13:31

I Stefan,
Can you share your version of the plugin? I'd like to use it.
Thanks!
Alex

Post Reply

Who is online

Users browsing this forum: No registered users and 17 guests