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..68f0bfa --- /dev/null +++ b/examples/knx-pzem004/pzem-004t-v30.ino @@ -0,0 +1,209 @@ +#include +#include +#include "wiring_private.h" // pinPeripheral() function + + +//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(); +} + +//Blinking live LED +const uint8_t ledPin = LED_BUILTIN;// the number of the LED pin +bool ledState = LOW; // ledState used to set the LED +unsigned long previousMillis = 0; // will store last time LED was updated +const long interval = 2000; // interval at which to blink (milliseconds) + +//PZEM stuff +#define PZEM004_NO_SWSERIAL +#define PZEM_DEFAULT_ADDR 0xF8 + +const uint8_t physicalCount = 6; // voltage,current,power_factor,power,energy,frequency + +//knx stuff +#define goReset knx.getGroupObject(1) +#define goDateTime knx.getGroupObject(2) + +const uint8_t ets_startupTimeout[7] = {0, 1, 2, 3, 4, 5, 6}; +const int ets_timePeriod[7] = {0, 1, 5, 15, 1 * 60, 5 * 60, 15 * 60}; +const uint8_t ets_percentCycle[6] = {0, 5, 10, 15, 20, 30}; //need knxprod update... ? + +int percentCycle = 0; // better to define a global or read knx.paramByte each time... ? +unsigned long timePeriod = 0; // same here, +uint8_t resetFlag = 0; // and here... + +// 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(float value){ + unsigned long currentMillis = millis(); + + // Delta Change update as defined in ETS + + int deltaPercent = ( 100 * ( value - lastValue ) / value ); + if ( percentCycle != 0 && abs(deltaPercent) >= percentCycle ) + { + trigger = true; + } + + // Refresh groupAddress value as defined in ETS since last update + if ( timePeriod != 0 && currentMillis - lastUpdate >= 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); + lastUpdate = millis(); + trigger = false; + }else{ + knx.getGroupObject(_GOaddr).valueNoSend(value, _dpt); + } + lastValue = value; + } + + private: + uint8_t _GOaddr; + Dpt _dpt; + bool trigger = false; +// bool isUpdated = false; + + public: + float lastValue = 0; + unsigned long lastUpdate = 0; + +} Physical[physicalCount]; + + +// callback from reset-GO +void resetCallback(GroupObject& go) +{ + if (go.value()) + { + pzem.resetEnergy(); + 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(); + + 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; + + resetFlag = knx.paramByte(3); + + goReset.callback(resetCallback); + goReset.dataPointType(DPT_Trigger); + goDateTime.dataPointType(DPT_DateTime); + + 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); + } + + // 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()) { + return; + } + unsigned long currentMillis = millis(); + + if (currentMillis - previousMillis >= interval) { // I love blinking LED state... + previousMillis = currentMillis; + if (ledState == LOW) { + ledState = HIGH; + } else { + ledState = LOW; + } + digitalWrite(ledPin, ledState); + } + for (uint8_t i=0; i< physicalCount; i++) + { + if (currentMillis - Physical[i].lastUpdate >= interval) + { + float isanValue = refreshValue(i); + if(!isnan(isanValue)) + { + Physical[i].loop(isanValue); + } + } + } +} + +float refreshValue(uint8_t physicalNumber){ + float valueTemp; + switch (physicalNumber) { //maybe a pointer or reference could be nicer... + case 0: + valueTemp = pzem.voltage(); + return valueTemp; + case 1: + valueTemp = pzem.current(); + return valueTemp; + case 2: + valueTemp = pzem.pf(); + return valueTemp; + case 3: + valueTemp = pzem.power(); + return valueTemp; + case 4: + valueTemp = pzem.energy(); + return valueTemp; + case 5: + valueTemp = pzem.frequency(); + return valueTemp; + default: + break; + } +} + +void resetEnergy(){ + pzem.resetEnergy(); +} \ No newline at end of file