diff --git a/examples/knx-433Dio/KNXto433.xml b/examples/knx-433Dio/KNXto433.xml new file mode 100644 index 0000000..888c1e9 --- /dev/null +++ b/examples/knx-433Dio/KNXto433.xml @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/knx-433Dio/README.md b/examples/knx-433Dio/README.md new file mode 100644 index 0000000..31d7ce1 --- /dev/null +++ b/examples/knx-433Dio/README.md @@ -0,0 +1,32 @@ +Control Chacon/DIO 433Mhz plug like that: (not an affiliated link just for info) +https://www.amazon.fr/DiO-Connected-Home-t%C3%A9l%C3%A9command%C3%A9es-t%C3%A9l%C3%A9commande/dp/B005LKMAW0 + + +Hardware: +- Samd21 (gy-samd21) +- FS 1000A (cheap 433 transmitter) +- TpUart KNX +- logic level converter +- Chacon/DIO 433Mhz plug (must work with every dumb 433mhz plug/remote that support the HomeEasy protocol) + +Software +- change the rfPin variable, compile and tranfert into the samd21 (or any microcontroller) +- (maybe) adapt THIGH and TLOW in rfCode class for your case. + +Before configuring ETS, you need to receive the 433mhz code for each button of your remote. +Take an arduino UNO, plug the FS1000a receiver, look on github/google to receive the code rc-switch or https://charleslabs.fr/fr/project-Contr%C3%B4le+de+prises+DiO+avec+Arduino (in french), or https://caron.ws/diy-cartes-microcontroleurs/piloter-vos-prises-chacon-rf433mhz-arduino/ ( in french too) + + +for me it's : 1806947472 and 1806947456 for the channel 1 on/off, etc... + +Now in ETS put the received 433Mhz code into parameters. +-> Full Download in ETS and enjoy. + + + +Feature: +This code is delay() free, blocking code free (ie: no long while or for loop), to call the knx.loop() as fast as possible. + + + + diff --git a/examples/knx-433Dio/knx-433.knxprod b/examples/knx-433Dio/knx-433.knxprod new file mode 100644 index 0000000..b3d72fc Binary files /dev/null and b/examples/knx-433Dio/knx-433.knxprod differ diff --git a/examples/knx-433Dio/knx-433Dio.ino b/examples/knx-433Dio/knx-433Dio.ino new file mode 100644 index 0000000..841dd6a --- /dev/null +++ b/examples/knx-433Dio/knx-433Dio.ino @@ -0,0 +1,368 @@ +#include +//#include + +//#define DEBUGSERIAL 1 +#ifdef DEBUGSERIAL + #define DPRINT(...) SerialUSB.print(__VA_ARGS__) + #define DPRINTLN(...) SerialUSB.println(__VA_ARGS__) + #include + #include +#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"); + } + } +} diff --git a/examples/knx-pzem004/pzem-004t-v30.ino b/examples/knx-pzem004/pzem-004t-v30.ino index 2bc92ff..2bf6527 100644 --- a/examples/knx-pzem004/pzem-004t-v30.ino +++ b/examples/knx-pzem004/pzem-004t-v30.ino @@ -38,7 +38,7 @@ const uint8_t physicalCount = 6; // voltage,current,power_factor,power,energy,fr 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 +//uint8_t resetEnergy = 0; // and here... disabled/day/week/month bool progMode = true; @@ -54,11 +54,10 @@ struct Physical { void loop(){ // unsigned long currentMillis = millis(); // Delta Change update as defined in ETS - int32_t deltaPercent = ( 100 * ( _value - _lastValue ) / _value ); + float 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 @@ -70,6 +69,7 @@ struct Physical { // UpdateGO but send to bus only if triggered by time or value change percentage if (_trigger){ knx.getGroupObject(_GOaddr).value(_value, _dpt); + _lastValue = _value; _lastMillis = millis(); _trigger = false; }else{ @@ -143,7 +143,6 @@ class Blinker Blinker led = Blinker(ledPin); - void callBackProgMode(GroupObject& go){ progMode = (bool)go.value(); } @@ -172,7 +171,7 @@ void resetCallback(GroupObject& go) { if (go.value()) { - resetEnergy = true; + pzem.resetEnergy(); goReset.value(false); } } @@ -181,7 +180,7 @@ void setup() { pinPeripheral(PIN_SERIAL2_RX, PIO_SERCOM); pinPeripheral(PIN_SERIAL2_TX, PIO_SERCOM); - SerialUSB.begin(9600); +// SerialUSB.begin(9600); Serial2.begin(9600); ArduinoPlatform::SerialDebug = &SerialUSB; @@ -238,12 +237,17 @@ void loop() { if (knx.configured() && !progMode) { refreshValueLoop(); - resetEnergyLoop(); for (uint8_t i=0; i< physicalCount; i++) { Physical[i].loop(); } + + if (timeStatus() == timeSet && resetPeriod != 0) + { + resetEnergyLoop(); + } + } else if (progMode) { @@ -257,7 +261,7 @@ void refreshValueLoop(){ if (millis() - lastPzemUpdate >= pzemInterval) { - for (uint8_t i=0; i< physicalCount; i++) + for (uint8_t i=0; i < physicalCount; i++) { float isaValue; switch (i) { //maybe a pointer or reference could be nicer... @@ -282,11 +286,18 @@ void refreshValueLoop(){ default: break; } + if(!isnan(isaValue)) { Physical[i].setValue(isaValue); } + else + { + Physical[i].setValue(-1); + } } + lastPzemUpdate = millis(); + led.set(500, 1000); } } @@ -339,14 +350,14 @@ void prodModeLoop(){ // run Only if progMode triggered ( at start or callback) { knx.progMode(true); timerProgPrevMillis = millis(); - led.set(500, 250); + led.set(50, 100); } else { if (millis() - timerProgPrevMillis > timerProgMode) { knx.progMode(false); goProgMode.value(false); - progMode = 0; + progMode = false; } } }