diff --git a/examples/knx-pzem004/knx-pzem0004t.xml b/examples/knx-pzem004/knx-pzem0004t.xml
new file mode 100644
index 0000000..9059c5f
--- /dev/null
+++ b/examples/knx-pzem004/knx-pzem0004t.xml
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/knx-pzem004/knx-pzem004t.knxprod b/examples/knx-pzem004/knx-pzem004t.knxprod
new file mode 100644
index 0000000..4881a26
Binary files /dev/null and b/examples/knx-pzem004/knx-pzem004t.knxprod differ
diff --git a/examples/knx-pzem004/pzem-004t-v30.ino b/examples/knx-pzem004/pzem-004t-v30.ino
new file mode 100644
index 0000000..2bc92ff
--- /dev/null
+++ b/examples/knx-pzem004/pzem-004t-v30.ino
@@ -0,0 +1,352 @@
+#include
+#include
+#include "wiring_private.h" // pinPeripheral() function
+
+#include
+
+//Sercom Stuff
+#define PIN_SERIAL2_RX (34ul) // Pin description number for PIO_SERCOM on D12 (34ul)
+#define PIN_SERIAL2_TX (36ul) // Pin description number for PIO_SERCOM on D10 (36ul)
+#define PAD_SERIAL2_TX (UART_TX_PAD_2) // SERCOM pad 2
+#define PAD_SERIAL2_RX (SERCOM_RX_PAD_3) // SERCOM pad 3
+Uart Serial2(&sercom1, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX); //TX D10, RX D12
+
+void SERCOM1_Handler()
+{
+ Serial2.IrqHandler();
+}
+
+//PZEM stuff
+#define PZEM004_NO_SWSERIAL
+#define PZEM_DEFAULT_ADDR 0xF8
+
+
+//knx stuff
+#define goReset knx.getGroupObject(1)
+#define goDateTime knx.getGroupObject(2)
+#define goProgMode knx.getGroupObject(9)
+
+// Global Const
+const uint16_t ets_timePeriod[7] = {0, 1, 5, 15, 1 * 60, 5 * 60, 15 * 60};
+const uint8_t ets_startupTimeout[7] = {0, 1, 2, 3, 4, 5, 6};
+const uint8_t ets_percentCycle[6] = {0, 5, 10, 15, 20, 30}; //need knxprod update... ?
+
+const uint8_t ledPin = LED_BUILTIN;// the number of the LED pin
+const uint8_t physicalCount = 6; // voltage,current,power_factor,power,energy,frequency
+
+// Global Variable
+uint8_t percentCycle = 0; // better to define a global or read knx.paramByte each time... ?
+uint32_t timePeriod = 0; // same here,
+uint8_t resetPeriod = 0; //same here ...
+uint8_t resetEnergy = 0; // and here... disabled/day/week/month
+
+bool progMode = true;
+
+// Issue on https://github.com/mandulaj/PZEM-004T-v30/issues/43
+PZEM004Tv30 pzem(Serial2, PZEM_DEFAULT_ADDR);
+
+struct Physical {
+ void init(uint8_t GOaddr, Dpt type_dpt){
+ _GOaddr = GOaddr;
+ _dpt = type_dpt;
+ }
+
+ void loop(){
+// unsigned long currentMillis = millis();
+ // Delta Change update as defined in ETS
+ int32_t deltaPercent = ( 100 * ( _value - _lastValue ) / _value );
+ if ( percentCycle != 0 && abs(deltaPercent) >= percentCycle )
+ {
+ _trigger = true;
+ _lastValue = _value;
+ }
+
+ // Refresh groupAddress value as defined in ETS since last update
+ if ( timePeriod != 0 && millis() - _lastMillis >= timePeriod )
+ {
+ _trigger = true;
+ }
+
+ // UpdateGO but send to bus only if triggered by time or value change percentage
+ if (_trigger){
+ knx.getGroupObject(_GOaddr).value(_value, _dpt);
+ _lastMillis = millis();
+ _trigger = false;
+ }else{
+ knx.getGroupObject(_GOaddr).valueNoSend(_value, _dpt);
+ }
+ }
+
+ void setValue(float value){
+ if (value != _value)
+ {
+ _value = value;
+ }
+ }
+
+ private:
+ Dpt _dpt;
+ float _value = 0;
+ float _lastValue = 0;
+ uint32_t _lastMillis = 0;
+ uint8_t _GOaddr;
+ bool _trigger = false;
+
+// bool isUpdated = false;
+
+ public:
+
+} Physical[physicalCount];
+
+
+class Blinker
+{
+ private:
+ uint8_t ledPin_; // the number of the LED pin
+ uint32_t OnTime = 1000; // milliseconds of on-time
+ uint32_t OffTime = 1000; // milliseconds of off-time
+ bool ledState = LOW; // ledState used to set the LED
+ uint32_t previousMillis; // will store last time LED was updated
+
+ void setOutput(bool state_, uint32_t currentMillis_){
+ ledState = state_;
+ previousMillis = currentMillis_;
+ digitalWrite(ledPin_, state_);
+ }
+
+ public:
+ Blinker(uint8_t pin)
+ {
+ ledPin_ = pin;
+ pinMode(ledPin_, OUTPUT);
+ previousMillis = 0;
+ }
+
+ void set(uint32_t on, uint32_t off){
+ OnTime = on;
+ OffTime = off;
+ }
+
+ void loop(){
+ uint32_t currentMillis = millis();
+
+ if((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
+ {
+ setOutput(LOW, currentMillis);
+ }
+ else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime))
+ {
+ setOutput(HIGH, currentMillis);
+ }
+ }
+};
+
+Blinker led = Blinker(ledPin);
+
+
+void callBackProgMode(GroupObject& go){
+ progMode = (bool)go.value();
+}
+
+void callBackDateTime(GroupObject& go){
+ static uint32_t lastUpdate = 0;
+ const uint32_t interval = (1000 * 60 * 60 * 24); // 1day
+
+ struct tm myTime;
+ myTime = go.value();
+ unsigned short tmp_sec = myTime.tm_sec;
+ unsigned short tmp_min = myTime.tm_min;
+ unsigned short tmp_hour = myTime.tm_hour;
+ unsigned short tmp_mday = myTime.tm_mday;
+ unsigned short tmp_month = myTime.tm_mon;
+ unsigned short tmp_year = myTime.tm_year;
+
+ if (millis() - lastUpdate >= interval && !timeStatus() == timeSet)
+ {
+ setTime(tmp_hour, tmp_min, tmp_sec, tmp_mday, tmp_month, tmp_year);
+ lastUpdate = millis();
+ }
+}
+
+void resetCallback(GroupObject& go)
+{
+ if (go.value())
+ {
+ resetEnergy = true;
+ goReset.value(false);
+ }
+}
+
+void setup() {
+ pinPeripheral(PIN_SERIAL2_RX, PIO_SERCOM);
+ pinPeripheral(PIN_SERIAL2_TX, PIO_SERCOM);
+
+ SerialUSB.begin(9600);
+ Serial2.begin(9600);
+
+ ArduinoPlatform::SerialDebug = &SerialUSB;
+
+ randomSeed(millis());
+
+ knx.readMemory();
+// led.set(5000, 5000);
+
+ if (knx.configured())
+ {
+ int confStartupTime = ets_startupTimeout[knx.paramByte(0)] * 1000;
+ delay(confStartupTime); // the only delay used, why make a withoutDelay function for that?
+
+ percentCycle = ets_percentCycle[knx.paramByte(1)];
+ timePeriod = ets_timePeriod[knx.paramByte(2)] * 1000;
+
+ resetPeriod = knx.paramByte(3);
+
+ goReset.callback(resetCallback);
+ goReset.dataPointType(DPT_Trigger);
+
+ goDateTime.dataPointType(DPT_DateTime);
+
+ goProgMode.dataPointType(DPT_Trigger);
+ goProgMode.callback(callBackProgMode);
+
+ uint8_t GOaddr = 3;
+ Physical[0].init(GOaddr, DPT_Value_Electric_Potential); // voltage
+ Physical[1].init(GOaddr += 1, DPT_Value_Electric_Current);
+ Physical[2].init(GOaddr += 1, DPT_Value_Power_Factor);
+ Physical[3].init(GOaddr += 1, DPT_Value_Power);
+ Physical[4].init(GOaddr += 1, DPT_Value_Energy);
+ Physical[5].init(GOaddr += 1, DPT_Value_Frequency);
+ led.set(2000, 1000);
+ }
+
+ // is the led active on HIGH or low? Default is LOW
+ knx.ledPinActiveOn(HIGH);
+ // pin or GPIO programming button is connected to. Default is 0
+ knx.ledPin(5);
+ knx.buttonPin(9);
+
+ knx.start();
+// while (!SerialUSB) { //wait for DEBUGING
+// ; // wait for serial port to connect. Needed for native USB port only
+// }
+}
+
+void loop() {
+
+ knx.loop();
+
+ if (knx.configured() && !progMode)
+ {
+ refreshValueLoop();
+ resetEnergyLoop();
+
+ for (uint8_t i=0; i< physicalCount; i++)
+ {
+ Physical[i].loop();
+ }
+ }
+ else if (progMode)
+ {
+ prodModeLoop();
+ }
+}
+
+void refreshValueLoop(){
+ static const uint16_t pzemInterval = 500; // interval at which to blink (milliseconds)
+ static uint32_t lastPzemUpdate = 0;
+
+ if (millis() - lastPzemUpdate >= pzemInterval)
+ {
+ for (uint8_t i=0; i< physicalCount; i++)
+ {
+ float isaValue;
+ switch (i) { //maybe a pointer or reference could be nicer...
+ case 0:
+ isaValue = pzem.voltage();
+ break;
+ case 1:
+ isaValue = pzem.current();
+ break;
+ case 2:
+ isaValue = pzem.pf();
+ break;
+ case 3:
+ isaValue = pzem.power();
+ break;
+ case 4:
+ isaValue = pzem.energy();
+ break;
+ case 5:
+ isaValue = pzem.frequency();
+ break;
+ default:
+ break;
+ }
+ if(!isnan(isaValue))
+ {
+ Physical[i].setValue(isaValue);
+ }
+ }
+ }
+}
+
+void resetEnergyLoop(){
+ static time_t lastTime;
+ time_t samdTime = now();
+
+ if (timeStatus() == timeSet)
+ {
+ switch (resetPeriod)
+ {
+ case 1: //day
+ if (day(samdTime) != day(lastTime))
+ {
+ lastTime = samdTime;
+ pzem.resetEnergy();
+ }
+ break;
+ case 2: //week
+ if (weekday(samdTime) != weekday(lastTime) && weekday(samdTime) == 2) //monday
+ {
+ lastTime = samdTime;
+ pzem.resetEnergy();
+ }
+ break;
+ case 3: // month
+ if (month(samdTime) != month(lastTime))
+ {
+ lastTime = samdTime;
+ pzem.resetEnergy();
+ }
+ break;
+ case 4: // year
+ if (year(samdTime) != year(lastTime))
+ {
+ lastTime = samdTime;
+ pzem.resetEnergy();
+ }
+ default:
+ break;
+ }
+ }
+}
+
+void prodModeLoop(){ // run Only if progMode triggered ( at start or callback)
+ static uint32_t timerProgPrevMillis = 0;
+ const uint32_t timerProgMode = ( 15 * 60 * 1000 ) ; // 15min
+
+ if (!knx.progMode())
+ {
+ knx.progMode(true);
+ timerProgPrevMillis = millis();
+ led.set(500, 250);
+ }
+ else
+ {
+ if (millis() - timerProgPrevMillis > timerProgMode) {
+ knx.progMode(false);
+ goProgMode.value(false);
+ progMode = 0;
+ }
+ }
+}