mirror of
				https://github.com/thelsing/knx.git
				synced 2025-10-26 10:26:25 +01:00 
			
		
		
		
	* knx-pzem004v30 Add first draft of a pzem-004t power/current/tension/etc.. on your main line (BE CAREFUL WITH VOLTAGE!) PZEM-004t-v30 wired on a samd21 with the use of Sercom2 Use with slight modification of: https://github.com/mandulaj/PZEM-004T-v30 View issue: https://github.com/mandulaj/PZEM-004T-v30/issues/43 * Rename examples/knx-pzem0004t.xml to examples/knx-pzem004/knx-pzem0004t.xml * Delete knx-pzem004t.knxprod * Delete pzem-004t-v30.ino * Update: 'just move to right directory' * Update knx-pzem0004t.xml * Add: ProgMode * Fix/Update/Add Add: ResetEnergy function with datetime Update: led Class Fix: many typo, errors * Cleaning Some strange bug/feature... Information is transmitted only when the device got time from KNX, I have put if (timeStatus() == timeSet && resetPeriod != 0) { resetEnergyLoop(); } But don't know if it's enough... * Create knx-433Dio.ino * Add Files: README.md .xml and knxprod files * Delete pzem-004t-v30.ino * Update pzem-004t-v30.ino
		
			
				
	
	
		
			369 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			369 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include <knx.h>
 | |
| //#include <DiOremote.h>
 | |
| 
 | |
| //#define DEBUGSERIAL 1
 | |
| #ifdef DEBUGSERIAL
 | |
|   #define DPRINT(...)    SerialUSB.print(__VA_ARGS__)
 | |
|   #define DPRINTLN(...)  SerialUSB.println(__VA_ARGS__)
 | |
|   #include <MemoryFree.h>
 | |
|   #include <pgmStrToRAM.h>
 | |
| #else
 | |
|   #define DPRINT(...)     //now defines a blank line
 | |
|   #define DPRINTLN(...)   //now defines a blank line
 | |
| #endif
 | |
| 
 | |
| #define goButton1 knx.getGroupObject(1)
 | |
| #define goButton2 knx.getGroupObject(2)
 | |
| #define goButton3 knx.getGroupObject(3)
 | |
| #define goButtonAll knx.getGroupObject(4)
 | |
| #define goProgMode knx.getGroupObject(5)
 | |
| 
 | |
| //DiOremote myRemote = DiOremote(6);
 | |
| 
 | |
| //void function2();
 | |
| //void function1(void (*)());
 | |
| //void loop() {
 | |
| //    function1(function2);
 | |
| //}
 | |
| 
 | |
| //Global Const
 | |
| const uint8_t ets_startupTimeout[7] = {0, 1, 2, 3, 4, 5, 6};
 | |
| const uint16_t ets_timePeriod[7] = {0, 1, 5, 15, 1 * 60, 5 * 60, 15 * 60};
 | |
| const uint8_t ets_progMode[7] = {0, 1, 2 * 60, 3 * 60, 4 * 60, 5 * 60, 10 * 60}; //need knxprod update... ?
 | |
| const uint8_t ledPin =  LED_BUILTIN;
 | |
| const uint8_t rfPin = 6;
 | |
| 
 | |
| //  //Protocol timing (in us)
 | |
| //  #define DiOremote_START_FRAME_1 220
 | |
| //  #define DiOremote_START_FRAME_0 2675
 | |
| //  #define DiOremote_THIGH 220
 | |
| //  #define DiOremote_TLOW_0 350 short
 | |
| //  #define DiOremote_TLOW_1 1400 long
 | |
| //  #define DiOremote_END_FRAME_1 220
 | |
| //  #define DiOremote_END_FRAME_0 10600
 | |
| 
 | |
| // Global Variable
 | |
| bool progMode = true;
 | |
| // bool codeSendindBlock = false;
 | |
| 
 | |
| uint8_t percentCycle = 0; // better to define a global or read knx.paramByte each time... ?
 | |
| uint32_t timePeriod = 0; // same here,
 | |
| uint32_t timerProgMode = 0; // same here,
 | |
| uint32_t ch1_on, ch1_off, ch2_on, ch2_off, ch3_on, ch3_off, chall_on, chall_off;
 | |
| 
 | |
| class RfCode {
 | |
|   private: 
 | |
|     uint8_t _rfPin;
 | |
|     uint32_t _codeValueOn;
 | |
|     uint32_t _codeValueOff;  
 | |
|     uint8_t _GOaddress;
 | |
|     const uint16_t  THIGH = 220, TSTART = 2675, TSHORT = 220, TLONG = 1400, TEND = 10600;
 | |
|     uint8_t _loopCount = 0;  // fixed ==5
 | |
|     bool lastState = false;
 | |
|     uint32_t _codePending = 0;
 | |
|     uint32_t _codePendingMemory = _codePending;
 | |
|     uint8_t _loopPending = 0; // fixed ==32
 | |
|     bool _sendMsgPending= false;
 | |
| 
 | |
|     enum PulseStates {
 | |
|         PULSE_INIT,
 | |
|         PULSE_KEY1, //32 times loop
 | |
|         PULSE_KEY2, //32 times loop
 | |
|         PULSE_END   
 | |
|     };
 | |
|     PulseStates pulseStates = PULSE_INIT;
 | |
|     
 | |
|     bool pulseSend(const uint16_t delayHigh, const uint16_t delayLow){ //alike state machine?
 | |
| 
 | |
|         static bool initPulse = false;
 | |
|         static bool highDone = false;
 | |
|         static uint32_t pulseLastTime = 0;
 | |
|       
 | |
|         uint32_t currentTime = micros();
 | |
|         
 | |
|         if (!initPulse){
 | |
|           initPulse = true;
 | |
|           digitalWrite(_rfPin, HIGH);
 | |
|           pulseLastTime = currentTime;
 | |
|         }
 | |
|         else if (currentTime - pulseLastTime >= delayHigh && !highDone)
 | |
|         {
 | |
|           digitalWrite(_rfPin, LOW);
 | |
|           pulseLastTime = currentTime;
 | |
|           highDone = true;
 | |
|         }
 | |
|         else if (currentTime - pulseLastTime >=  delayLow && highDone)
 | |
|         {
 | |
|           initPulse = false;
 | |
|           highDone = false;
 | |
|         }
 | |
|         return !initPulse;
 | |
|     }
 | |
| 
 | |
|     uint16_t TTime(bool invert){
 | |
|         uint16_t TTIME;
 | |
|         
 | |
|         if (_codePending & 0x80000000L)// future bug if uint32_t _codePengin > 2^32 / 2 ?
 | |
|         {
 | |
|             TTIME = invert ? TSHORT : TLONG;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             TTIME = invert ? TLONG : TSHORT;
 | |
|         }
 | |
|         return TTIME;
 | |
|     }
 | |
|     
 | |
|   public:
 | |
|       RfCode(uint8_t rfPin){
 | |
|         _rfPin = rfPin;
 | |
|         pinMode(_rfPin, OUTPUT);
 | |
|       }
 | |
|       
 | |
| //      void init(uint8_t addr, uint32_t codeValueOn, uint32_t codeValueOff){   //GroupObject &device, long timeOn){
 | |
| //          _codeValueOn = codeValueOn;
 | |
| //          _codeValueOff = codeValueOff;
 | |
| //          _GOaddress = addr;
 | |
| //      }
 | |
| 
 | |
|       void setState(bool modeOnOff, uint32_t codeValueOn, uint32_t codeValueOff){
 | |
|         if (!_sendMsgPending)
 | |
|         {
 | |
|             _sendMsgPending = true;
 | |
|            _codeValueOn = codeValueOn;
 | |
|            _codeValueOff = codeValueOff;
 | |
| //           _GOaddress = addr;           
 | |
|             
 | |
|             if (modeOnOff){
 | |
|                 _codePending = _codeValueOn;
 | |
| //                SerialUSB.println(_codePending);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 _codePending = _codeValueOff;
 | |
| //                SerialUSB.println(_codePending);
 | |
|             }
 | |
|             _codePendingMemory = _codePending;
 | |
|         }
 | |
|       }
 | |
|       
 | |
| //      bool getMsgPendingState(){
 | |
| //          return _sendMsgPending;
 | |
| //      }
 | |
|       
 | |
|       void loop(){
 | |
|           if (_sendMsgPending)
 | |
|           {
 | |
|               // needed to block setState of another Class, yes FIFO is better...
 | |
| //              codeSendindBlock = true;
 | |
|               if (_loopCount < 5)
 | |
|               {
 | |
|                   switch (pulseStates){
 | |
|                       case PULSE_INIT:
 | |
|                           if (pulseSend(THIGH, TSTART)){
 | |
|                               pulseStates = PULSE_KEY1;
 | |
|                           }
 | |
|                           break;
 | |
|                       case PULSE_KEY1:              
 | |
|                           if (pulseSend(THIGH, TTime(false)))
 | |
|                           {
 | |
|                               pulseStates = PULSE_KEY2;
 | |
|                           }
 | |
|                           break;
 | |
|                       case PULSE_KEY2:
 | |
|                           if (pulseSend(THIGH, TTime(true)))
 | |
|                           {
 | |
|                               if (_loopPending < 32){
 | |
|                                   _codePending <<= 1;
 | |
|                                   _loopPending++;
 | |
|                                   pulseStates = PULSE_KEY1; // next loop !
 | |
|                               }
 | |
|                               else{ // finish!
 | |
|                                   pulseStates = PULSE_END;
 | |
|                               }
 | |
|                           }
 | |
|                           break;
 | |
|                       case PULSE_END:
 | |
|                           if (pulseSend(THIGH, TEND)){
 | |
|                               _loopCount++;
 | |
|                               _codePending = _codePendingMemory;
 | |
|                               _loopPending = 0;
 | |
|                               pulseStates = PULSE_INIT;
 | |
|                           }
 | |
|                           break;
 | |
|                       default:
 | |
|                           break;
 | |
|                   } 
 | |
|               }
 | |
|               else
 | |
|               {
 | |
|                   _loopCount = 0;
 | |
|                   _sendMsgPending = false;
 | |
| //                  codeSendindBlock = false;
 | |
|               }
 | |
|           }        
 | |
|       }
 | |
| };
 | |
| 
 | |
| 
 | |
| 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);
 | |
|         }
 | |
|     }
 | |
| };
 | |
| 
 | |
| RfCode button = RfCode(5);
 | |
| 
 | |
| Blinker led = Blinker(ledPin);
 | |
| 
 | |
| 
 | |
| void callBackButton1(GroupObject& go){
 | |
|     button.setState((bool)go.value(), ch1_on, ch1_off);    
 | |
| }
 | |
| void callBackButton2(GroupObject& go){
 | |
|     button.setState((bool)go.value(), ch2_on, ch2_off);    
 | |
| }
 | |
| void callBackButton3(GroupObject& go){
 | |
|     button.setState((bool)go.value(), ch3_on, ch3_off);    
 | |
| }
 | |
| void callBackButtonAll(GroupObject& go){
 | |
|     button.setState((bool)go.value(), chall_on, ch3_off);    
 | |
| }
 | |
| void callBackProgMode(GroupObject& go){ 
 | |
|     progMode = (bool)go.value();
 | |
| }
 | |
| 
 | |
| void setup() {
 | |
| 
 | |
| //    #ifdef DEBUGSERIAL
 | |
| //    SerialUSB.begin(9600);
 | |
| //    while (!SerialUSB) { //wait for DEBUGING
 | |
| //        ; // wait for serial port to connect. Needed for native USB port only
 | |
| //    }
 | |
| //    ArduinoPlatform::SerialDebug = &SerialUSB;
 | |
| //    #endif
 | |
| 
 | |
|     randomSeed(millis());
 | |
|     // knx.bau().deviceObject().individualAddress(1);
 | |
|     knx.readMemory();
 | |
| 
 | |
|     if (knx.configured())
 | |
|     {
 | |
|         DPRINT("Setup: KNX Configuration...");
 | |
| 
 | |
|         progMode = false; // don't need to put device in progMode.
 | |
|         int confStartupTime = ets_startupTimeout[knx.paramByte(0)] * 1000;
 | |
|         delay(confStartupTime); // the only delay used, why make a withoutDelay function for that?
 | |
| 
 | |
|         timePeriod = ets_timePeriod[knx.paramByte(1)] * 1000;
 | |
| //        timerProgMode = ets_progMode[knx.paramByte(34)] * 1000;
 | |
| 
 | |
|         ch1_on =  knx.paramInt(2);
 | |
|         ch1_off =  knx.paramInt(6);
 | |
|         goButton1.callback(callBackButton1);
 | |
|         goButton1.dataPointType(DPT_Switch);
 | |
| 
 | |
|         ch2_on = knx.paramInt(10);
 | |
|         ch2_off = knx.paramInt(14);
 | |
|         goButton2.callback(callBackButton2);
 | |
|         goButton2.dataPointType(DPT_Switch);
 | |
| 
 | |
|         ch3_on = knx.paramInt(18);
 | |
|         ch3_off = knx.paramInt(22);
 | |
|         goButton3.callback(callBackButton3);
 | |
|         goButton3.dataPointType(DPT_Switch);
 | |
| 
 | |
|         chall_on =  knx.paramByte(26);
 | |
|         chall_off = knx.paramByte(30);
 | |
|         goButtonAll.callback(callBackButtonAll);
 | |
|         goButtonAll.dataPointType(DPT_Switch);
 | |
|         
 | |
|         goProgMode.callback(callBackProgMode);
 | |
|         goProgMode.dataPointType(DPT_Trigger);
 | |
| 
 | |
|         DPRINTLN("Finished");
 | |
|     }
 | |
| 
 | |
|     knx.ledPin(5);
 | |
|     knx.ledPinActiveOn(HIGH);
 | |
|     knx.buttonPin(9);
 | |
| 
 | |
|     knx.start();
 | |
|     led.set(2000, 2000);
 | |
| }
 | |
| 
 | |
| void loop()
 | |
| {
 | |
|     knx.loop();
 | |
|     led.loop();
 | |
|     if (knx.configured() && !progMode)
 | |
|     {
 | |
|        button.loop();
 | |
|     }
 | |
|     else if (progMode)
 | |
|     {
 | |
|         prodModeLoop();
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| void prodModeLoop(){ // run Only if progMode triggered ( at start or callback)
 | |
| 
 | |
|     const uint32_t timerProgMode = ( 15 * 60 * 1000 ) ; // 15min
 | |
|     static uint32_t timerProgPrevMillis = 0;
 | |
|     
 | |
|     if (!knx.progMode())
 | |
|     {
 | |
|         knx.progMode(true);
 | |
|         led.set(500, 500);
 | |
|         timerProgPrevMillis = millis();
 | |
|         DPRINTLN("progModeLoop Start");
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         if (millis() - timerProgPrevMillis > timerProgMode) {
 | |
|             knx.progMode(false);
 | |
|             goProgMode.value(false);
 | |
|             progMode = 0;
 | |
|             led.set(100, 100); // panic!
 | |
|             DPRINTLN("progModeLoop Stop");
 | |
|         }
 | |
|     }
 | |
| }
 |