"), Settings.TaskDevicePluginConfig[event->TaskIndex][0]);
- string += tmpString;
+ // Let's prevent divison by zero
+ if(Settings.TaskDevicePluginConfig[event->TaskIndex][IndexPulsesPerkWh]==0) {
+ Settings.TaskDevicePluginConfig[event->TaskIndex][IndexPulsesPerkWh]=1000; // if not configged correctly prevent crashes and set it to 1000 as default value.
+ }
+ addFormNumericBox(F("Pulses per kWh"), F("p112_pulsesperkwh")
+ , Settings.TaskDevicePluginConfig[event->TaskIndex][IndexPulsesPerkWh]);
- sprintf_P(tmpString, PSTR("
"));
+
+ // Let's prevent divison by zero
+ if(Settings.TaskDevicePluginConfig[event->TaskIndex][IndexPulsesPerkWh]==0) {
+ Settings.TaskDevicePluginConfig[event->TaskIndex][IndexPulsesPerkWh]=1000; // if not configged correctly prevent crashes and set it to 1000 as default value.
}
+ addHtml(String(Plugin_112_pulseTotalCounter[event->TaskIndex] * 1000 / Settings.TaskDevicePluginConfig[event->TaskIndex][IndexPulsesPerkWh]));
+ addHtml(F("
"));
+
+ success = true;
+ break;
+ }
case PLUGIN_INIT:
- {
- String log = F("INIT : Power Counter ");
- log += Settings.TaskDevicePin1[event->TaskIndex];
- addLog(LOG_LEVEL_INFO,log);
- pinMode(Settings.TaskDevicePin1[event->TaskIndex], INPUT_PULLUP);
- Plugin_112_pulseinit(Settings.TaskDevicePin1[event->TaskIndex], event->TaskIndex);
- success = true;
- break;
- }
+ {
+ // Restore any value that may have been read from the RTC.
+ Plugin_112_pulseUsage[event->TaskIndex] = UserVar[event->BaseVarIndex];
+ // String log = F("INIT : Load ");
+ // log += LoadCustomTaskSettings(event->TaskIndex, (byte*)&Plugin_112_pulseUsage[event->TaskIndex], sizeof(Plugin_112_pulseUsage[event->TaskIndex]));
+ // addLog(LOG_LEVEL_INFO, log);
+ Plugin_112_pulseTime[event->TaskIndex] = UserVar[event->BaseVarIndex + 2];
+
+ // Restore the total counter from the unused 4th UserVar value.
+ // It may be using a formula to generate the output, which makes it impossible to restore
+ // the true internal state.
+ Plugin_112_pulseTotalCounter[event->TaskIndex] = UserVar[event->BaseVarIndex + 3];
+
+ String log = F("INIT : Power ");
+ log += Settings.TaskDevicePin1[event->TaskIndex];
+ addLog(LOG_LEVEL_INFO, log);
+ pinMode(Settings.TaskDevicePin1[event->TaskIndex], INPUT_PULLUP);
+ success = Plugin_112_pulseinit(Settings.TaskDevicePin1[event->TaskIndex], event->TaskIndex);
+
+ break;
+ }
case PLUGIN_READ:
- {
- Plugin_112_idleusage(event->TaskIndex);
- UserVar[event->BaseVarIndex] = Plugin_112_pulseUsage[event->TaskIndex];
- UserVar[event->BaseVarIndex+1] = Plugin_112_pulseTotalCounter[event->TaskIndex];
- Plugin_112_pulseCounter[event->TaskIndex] = 0;
- success = true;
- break;
- }
+ {
+ Plugin_112_idleusage(event->TaskIndex);
+
+ UserVar[event->BaseVarIndex] = floor(Plugin_112_pulseUsage[event->TaskIndex] + 0.5);
+ UserVar[event->BaseVarIndex + 1] = Plugin_112_pulseTotalCounter[event->TaskIndex] * 1000 /
+ Settings.TaskDevicePluginConfig[event->TaskIndex][IndexPulsesPerkWh];
+ UserVar[event->BaseVarIndex + 2] = Plugin_112_pulseTime[event->TaskIndex];
+
+ // Store the raw value in the unused 4th position.
+ // This is needed to restore the value from RTC as it may be converted into another output value using a formula.
+ UserVar[event->BaseVarIndex + 3] = Plugin_112_pulseTotalCounter[event->TaskIndex];
+ event->sensorType = SENSOR_TYPE_DUAL;
+
+ // String log = F("READ : Save ");
+ // log += SaveCustomTaskSettings(event->TaskIndex, (byte*)&Plugin_112_pulseUsage[event->TaskIndex], sizeof(Plugin_112_pulseUsage[event->TaskIndex]));
+ // addLog(LOG_LEVEL_INFO, log);
+
+ success = true;
+ break;
+ }
}
+
return success;
}
@@ -120,115 +182,128 @@ boolean Plugin_112(byte function, struct EventStruct *event, String& string)
\*********************************************************************************************/
void Plugin_112_idleusage(byte Index)
{
- unsigned long PulseTime=millis() - Plugin_112_pulseTimePrevious[Index];
- if(PulseTime > (Settings.TaskDeviceTimer[Index] * 1000) && //More than $device_delay passed since last pulse
- PulseTime > Plugin_112_pulseTime[Index] ) { //More than last pulse interval
-
- // Let's prevent divison by zero
- if(Settings.TaskDevicePluginConfig[Index][1]==0) {
- Settings.TaskDevicePluginConfig[Index][1]=1000; // if not configged correctly prevent crashes and set it to 1000 as default value.
- }
- // WH = =3600000/[pulses per kwh]/[time since last pulse (ms)]
- Plugin_112_pulseUsage[Index] = (3600000000./Settings.TaskDevicePluginConfig[Index][1])/PulseTime;
+ unsigned long PulseTime = millis() - Plugin_112_pulseTimePrevious[Index];
+ if(PulseTime > (Settings.TaskDeviceTimer[Index] * 1000) && // More than $device_delay passed since last pulse
+ Plugin_112_pulseTimePrevious[Index] > 0 && // Must have at least one pulse received
+ PulseTime > Plugin_112_pulseTime[Index] ) { // More than last pulse interval
+ // WH = 3600000 / [pulses per kwh] / [time since last pulse (ms)]
+ Plugin_112_pulseUsage[Index] = (3600000000. / Settings.TaskDevicePluginConfig[Index][IndexPulsesPerkWh]) / PulseTime;
}
}
/*********************************************************************************************\
- * Check Pulse Counters (called from irq handler)
+* Check Pulse Counters (called from irq handler)
\*********************************************************************************************/
void Plugin_112_pulsecheck(byte Index)
{
- unsigned long PulseTime=millis() - Plugin_112_pulseTimePrevious[Index];
- if(PulseTime > Settings.TaskDevicePluginConfig[Index][0]) // check with debounce time for this task
- {
- Plugin_112_pulseTimePrevious[Index]=millis(); // moved this up as the operations below might take some time
- Plugin_112_pulseCounter[Index]++;
- Plugin_112_pulseTotalCounter[Index]++;
- Plugin_112_pulseTime[Index] = PulseTime;
+ noInterrupts(); // s0170071: avoid nested interrups due to bouncing.
+ // s0170071: the following gives a glitch if millis() rolls over (every 50 days) and there is a bouncing to be avoided at the exact same
+ // time. Very rare.
+ // Alternatively there is timePassedSince(Plugin_112_bounceTimePrevious[Index]); but this is not in IRAM at this time, so do not use in a
+ // ISR!
+ const unsigned long now = millis(); // remember time here
- // Let's prevent divison by zero
- if(Settings.TaskDevicePluginConfig[Index][1]==0) {
- Settings.TaskDevicePluginConfig[Index][1]=1000; // if not configged correctly prevent crashes and set it to 1000 as default value.
- }
+ // Debounce on both edges:
+ if ((now - Plugin_112_bounceTimePrevious[Index]) > (unsigned long)Settings.TaskDevicePluginConfig[Index][IndexDebounce])
+ {
+ Plugin_112_bounceTimePrevious[Index] = now;
- // WH = =3600000/[pulses per kwh]/[time since last pulse (ms)]
- Plugin_112_pulseUsage[Index] = (3600000000./Settings.TaskDevicePluginConfig[Index][1])/PulseTime;
+ if (!digitalRead(Settings.TaskDevicePin1[Index])) {
+ // only update readings on falling edge
+ const unsigned long PulseTime = now - Plugin_112_pulseTimePrevious[Index];
+ Plugin_112_pulseTimePrevious[Index] = now;
+ Plugin_112_pulseTime[Index] = PulseTime;
+ Plugin_112_pulseTotalCounter[Index]++;
+ // WH = 3600000 / [pulses per kwh] / [time since last pulse (ms)]
+ Plugin_112_pulseUsage[Index] = (3600000000. / Settings.TaskDevicePluginConfig[Index][IndexPulsesPerkWh]) / PulseTime;
}
+ }
+ interrupts(); // enable interrupts again.
}
-
/*********************************************************************************************\
- * Pulse Counter IRQ handlers
+* Pulse Counter IRQ handlers
\*********************************************************************************************/
void Plugin_112_pulse_interrupt1()
{
Plugin_112_pulsecheck(0);
}
+
void Plugin_112_pulse_interrupt2()
{
Plugin_112_pulsecheck(1);
}
+
void Plugin_112_pulse_interrupt3()
{
Plugin_112_pulsecheck(2);
}
+
void Plugin_112_pulse_interrupt4()
{
Plugin_112_pulsecheck(3);
}
+
void Plugin_112_pulse_interrupt5()
{
Plugin_112_pulsecheck(4);
}
+
void Plugin_112_pulse_interrupt6()
{
Plugin_112_pulsecheck(5);
}
+
void Plugin_112_pulse_interrupt7()
{
Plugin_112_pulsecheck(6);
}
+
void Plugin_112_pulse_interrupt8()
{
Plugin_112_pulsecheck(7);
}
-
/*********************************************************************************************\
- * Init Pulse Counters
+* Init Pulse Counters
\*********************************************************************************************/
-void Plugin_112_pulseinit(byte Par1, byte Index)
+bool Plugin_112_pulseinit(byte Par1, byte Index)
{
- // Init IO pins
- String log = F("Power Counter: Init");
- addLog(LOG_LEVEL_INFO,log);
-
switch (Index)
{
+ // use CHANGE and not FALLING for correct debouncing and divide result by 2. See also https://github.com/letscontrolit/ESPEasy/issues/2366
case 0:
- attachInterrupt(Par1, Plugin_112_pulse_interrupt1, FALLING);
+ attachInterrupt(Par1, Plugin_112_pulse_interrupt1, CHANGE);
break;
case 1:
- attachInterrupt(Par1, Plugin_112_pulse_interrupt2, FALLING);
+ attachInterrupt(Par1, Plugin_112_pulse_interrupt2, CHANGE);
break;
case 2:
- attachInterrupt(Par1, Plugin_112_pulse_interrupt3, FALLING);
+ attachInterrupt(Par1, Plugin_112_pulse_interrupt3, CHANGE);
break;
case 3:
- attachInterrupt(Par1, Plugin_112_pulse_interrupt4, FALLING);
- break;
- case 4:
- attachInterrupt(Par1, Plugin_112_pulse_interrupt5, FALLING);
- break;
- case 5:
- attachInterrupt(Par1, Plugin_112_pulse_interrupt6, FALLING);
- break;
- case 6:
- attachInterrupt(Par1, Plugin_112_pulse_interrupt7, FALLING);
- break;
- case 7:
- attachInterrupt(Par1, Plugin_112_pulse_interrupt8, FALLING);
+ attachInterrupt(Par1, Plugin_112_pulse_interrupt4, CHANGE);
break;
+
+ // case 4:
+ // attachInterrupt(Par1, Plugin_112_pulse_interrupt5, Mode);
+ // break;
+ // case 5:
+ // attachInterrupt(Par1, Plugin_112_pulse_interrupt6, Mode);
+ // break;
+ // case 6:
+ // attachInterrupt(Par1, Plugin_112_pulse_interrupt7, Mode);
+ // break;
+ // case 7:
+ // attachInterrupt(Par1, Plugin_112_pulse_interrupt8, Mode);
+ // break;
+ default:
+ addLog(LOG_LEVEL_ERROR, F("POWER: Error, only the first 4 tasks can be pulse counters."));
+ return false;
}
+
+ return true;
}
+
+#endif // USES_P112