From 58d37215711b3e214d39cfc426eaede3981e62d0 Mon Sep 17 00:00:00 2001 From: zelogik Date: Thu, 21 Jan 2021 18:41:55 +0100 Subject: [PATCH 1/3] Add: 'Demo example for PZEM004t-V3 (#120) * 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 --- examples/knx-pzem004/knx-pzem0004t.xml | 163 ++++++++++ examples/knx-pzem004/knx-pzem004t.knxprod | Bin 0 -> 36595 bytes examples/knx-pzem004/pzem-004t-v30.ino | 352 ++++++++++++++++++++++ 3 files changed, 515 insertions(+) create mode 100644 examples/knx-pzem004/knx-pzem0004t.xml create mode 100644 examples/knx-pzem004/knx-pzem004t.knxprod create mode 100644 examples/knx-pzem004/pzem-004t-v30.ino 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 0000000000000000000000000000000000000000..4881a2669561f1105cec2177961f1facaa38d741 GIT binary patch literal 36595 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 TVG;n?$zPB2KM$l|`Ly*rQ5}?> literal 0 HcmV?d00001 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; + } + } +} From f169e20f2f3fbd205a95cc45cab1650c5b6af971 Mon Sep 17 00:00:00 2001 From: Dennis Fleurbaaij Date: Fri, 5 Feb 2021 15:57:45 +0100 Subject: [PATCH 2/3] opt-out global KNX, ability to DIY construct knx object, minor cleanups (#121) * Refactored the knx_facade a bit to make the auto-building of the global knx object and the buttonUp() ISR function opt-in. Also added the ability to quite easily attach another serial (in this case tested with Serial2 of an ESP32). * Added visual studio code temp files to .gitignore * Fix for missing build flag for WifiManager in ESP32_ip demo * Added example in which we DIY build the KNX object and use ESP32's second UART to connect so that you can debug with the first one that's connected to USB. * Minor platform cleanups to remove compilation warnings. * ESP32 can't handle the defaulted 8192 byte EPROM. Next to that I needed to begin() the eprom lib in my setup() to get it to work and that requires knowing how much memory KNX will use. This fix makes the default more conservative and moves it's definition to the .h file so that other files can always use it as well. * After comments from thelsing, inverted the logic. The default global knx object is now opt-out by defining KNX_NO_AUTOMATIC_GLOBAL_INSTANCE. See the knx-demo-diy project for an example. Minor syntactic cleanups. * Removed hardwareserial from facade (detail which was not needed) Placed facade constructors together Minor syntactic cleanup * Fixed knx-cc1310 where own button routine was calling now private internal knx facade variable. Renamed files in knx-demo-diy Minor syntactic stuff * Added gitattributes for binary knxprod files --- .gitattributes | 1 + .gitignore | 1 + examples/knx-cc1310/knx_wrapper.cpp | 2 +- examples/knx-demo-diy/.gitignore | 5 + examples/knx-demo-diy/knx-demo-diy-tp.knxprod | Bin 0 -> 41318 bytes examples/knx-demo-diy/knx-demo-diy-tp.xml | 133 +++++++++++++ examples/knx-demo-diy/knx-demo-diy.ino | 138 +++++++++++++ examples/knx-demo-diy/platformio-ci.ini | 24 +++ examples/knx-demo-diy/platformio.ini | 33 ++++ examples/knx-demo/platformio.ini | 1 + src/arduino_platform.cpp | 5 +- src/esp32_platform.cpp | 9 +- src/esp32_platform.h | 2 +- src/esp_platform.cpp | 7 +- src/esp_platform.h | 2 +- src/knx/memory.cpp | 4 - src/knx/memory.h | 4 + src/knx_facade.cpp | 130 ++++++------ src/knx_facade.h | 187 +++++++++++------- 19 files changed, 529 insertions(+), 159 deletions(-) create mode 100644 .gitattributes create mode 100644 examples/knx-demo-diy/.gitignore create mode 100644 examples/knx-demo-diy/knx-demo-diy-tp.knxprod create mode 100644 examples/knx-demo-diy/knx-demo-diy-tp.xml create mode 100644 examples/knx-demo-diy/knx-demo-diy.ino create mode 100644 examples/knx-demo-diy/platformio-ci.ini create mode 100644 examples/knx-demo-diy/platformio.ini diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..163bf16 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.knxprod binary diff --git a/.gitignore b/.gitignore index 3339a25..69f65ed 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ _build # Visual Studio 2015 cache/options directory .vs/ +.vscode/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ diff --git a/examples/knx-cc1310/knx_wrapper.cpp b/examples/knx-cc1310/knx_wrapper.cpp index f19a65a..19b616f 100644 --- a/examples/knx-cc1310/knx_wrapper.cpp +++ b/examples/knx-cc1310/knx_wrapper.cpp @@ -11,7 +11,7 @@ void buttonUp() if (millis() - lastpressed > 200) { KnxFacade &knx = *pKnx; - knx._toogleProgMode = true; + knx.toggleProgMode(); lastpressed = millis(); } } diff --git a/examples/knx-demo-diy/.gitignore b/examples/knx-demo-diy/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/examples/knx-demo-diy/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/examples/knx-demo-diy/knx-demo-diy-tp.knxprod b/examples/knx-demo-diy/knx-demo-diy-tp.knxprod new file mode 100644 index 0000000000000000000000000000000000000000..ccb136353a00735032916bfe4dcfd1f3a3969ad1 GIT binary patch literal 41318 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 z z!i3Dpt~~UX9uEHJ7vR%z{4RdtO)d;%Z9v7`b2wB&-Jo^(s7Zj{s19#JK!%h`4sF{_|x!V_Nc zT#c^2lDFohH0Tpj0vC6I=%tvRH<4{qXVkW58vcbilSsEZHMhuwDX~JjVT3;DlR`b3 zd~r!i{RuB7H$$geNI&n-l)B3g{t>T`Tu3nhZ*^-n3&0gS`0c6k(0{kEE4Uujdp{N{ zP2B&l6%r@RHWQ3M3xh&)CvAk`jkbeKLi6{|$_sopi2&mW$fxBU7R?V%&6nQO_0oZu zPs1JF#A{f(z_^paoySS$$()Ye4(MZ};kl%&WwrruQ|TI*w_@?C}<2@0hD!vO>SKMwdDOwsxjSg2kf z`u|`+$cP*V%Tg7Fey~Rs$%+qR`pS(m#U958QIlKDh6~gLrdMKf@aqMRT7f|kulxQ#$31_EeJz8G{YiwGVWWR#MpR(_F8tB?tcVN5~MW@l)d=E_mpVM8Y# z@3tuG`2$2SP^51~UxC5kYEn9=Y5IlvKQvGn?Hdj3>HkIprEI>@z`4%RaNV2UPjubW zU*fNjs;^0MCs!46h<2S-8=UH3-7U?8^z$noqK#Iba@8%_I&r@tOTZ^Wzz-CiakieNzwsRDPo0Lw0yFnUXm*OMoi}is%-uMKZs702^el8uK;|vROTC| zyBVEHMgf& zZoOZ2BYIhdE{iO#iL940FH@@cYq&b`pW|mYYd&p6-2Juu+7{r7-L`w{;5%l$f`1P9 zteJl{KyLhF!fF}AT(Aoldk2SX5@1i?)1s?=@9lKq;psH;wlH{gv(WKW4$wU*=g5lB zfEs|v2<&$pOk?(TLEOH*Cn{L5( z6!>h*Ki>pdwyF)v&NNGoJ7mZcPOcP%q)!V=cO?@nf7P?MNJ;V^+zzC6(F&@At>CPt z>#K$}qfV}7;G@@9f;b;|G8+qL{A+^=^T9O8*kmxL|Dms`Ke$e${hTfuN9M0se`_T1gp8+HXr5&-Up2%atoLrU+ID zV(`r3e$I-^$?Ylk|1=E9T5ta+4JG96vDCecJMR6P5M)ZtznPv%`DNl~&31VNqCXU2 zXe$XrMbJqWXPgTBGDpu~s)$%O@#11?1ny-Tv~SR`rCTBLkK6^cP3}<<_c%&AL1#Fj zHN8?&Rhqx{sGj8X!qhT-$JaoM8yxWJc(N)O|NAK+Em1BDGPX4p>V<1q+LLhdfWeG) z7>YXiamW5k@p!?=h>aS+FJj_2KrW_-PMn7HJHy@Q71z$z&kad{E?8T(D^wCRv%>TZ zh}mjJYENYb#s^V&I3o_RsV~~qIxM`Iw#cLjhKo5|PXOq$@;fp=9m^olag)I;}* zU%FW(RBS%+6Hh%YGffobE#zn;@*Sm--~7w zxNN_dlPp@iqj>!f;rbt?dla%qfVqubK+i=)`$krw*h2*U4@T*YvWrG*(NXI$$I43! zN?1_|i?M38yA+ZJwrqy(5x>@j8LKeM7Ywt{do-LYo>X)6?rWv~#ZdzB+WVkF@wlX3 z1z7RI0pLUjly)P8T`_!?w;UJXG+r8d9_#p zb*vb2D^Q?Of2%XCCSw@nyCro*vw(V8Vy)4e*~%2_MOqQxT!TsaV+R>JyRhun%Q?9C zE@ACmso1UvKmMU&Oih?)D&P`Pmt@4-^m*)utrl$hPo9KHgo9Kle#k!c&k<2Lk@tLE z<{`Ey@?a4&w9b`2UwJ%qT7?@u*`HKhb2qH_v9TE>Ee=fk?^YKDKH~p=C-z=o*%8eC zi8a$O6+@!r-N6(!K5Z{gz*0!Gqv;NvwQ_ya4t)e%RL&&W*oRL~b>`fiuFvvkP=p7J z!Ay38N=zjstbLIM+n*oj{r+?z*{nV~N1p$v`dDw~+=8OoCunP7HOf%LN;&jXm+#PI z!MkdT15cp33U07ajb;#KIa;bEvERs)eH%qqbT@r`;?}pB$^&&@CEy#e=w3+9+D{5w z?DN{u7F8+i5L3Y^xwyF*ls=Z9}$TP|J7E-akoDv#uZ<|%5 zR9yQ1vA??i7yF~(=|~W!dun}U=3`GH1g6In2x{S;F$Siv+FdIKHB%@%Vco8sMJ_Oy z9o>&_Cfd(~|MY(n6bL{K$RtUDKQm(sUlRjQuO2_MB4xZZicMs~x!NBurU+{cOWxf? zYfSr>`h}!8->0zd(N|Y#RyOoa36)ZSSn2SGp0xkZv19Gj3K*-Te1L4_E{{9EgIP*- z9S4;BfB{s-WY@?}`!bKXds+lqK+3kHw!nWHJHXX3kN zT8(Ps^)I$_*_&a+3T538Z|uF3Ya?NU)~bm*`gTWch)rXg51|tHhj4Qsp`vbmc31GX zJJ$@~OMX_`m_2IgWTwcCzO5%XkRn?VjX0#fJ18#ske&Ke1ayk29M?;aDG;xWL>Oxj zS5FG1Lblb`#0+(hi+OMXWBDUOacQIe^X8NFSDYflY;}nRaX7(6tmI@T4}5UmzEGbG z5g^Cq>ZP8Lj`LDQ3*xxAbA@pq-^2@|A^_R3tP2W3WO4mGSpc^3Tz|TZQVRctQ>Io* z@t4N7pHjrdocYctB~)^B7dMe}^we7A+vVU3OOwxab_dqN@Y7ql*TT5P)?PV)#iHfv zN%fA!qVK9-wOHrxrh|?2ITO0!vHkUW)9B0>m>+#jzR4(%gW+F8l{23^>>~b>4>N-u zZG~Yz9d&&YGq|h>8nERIJBN-Tv3wVj(Y|2V%h9ubYh+ zbFD2gTsNObc=iz~oL=m=Zm524*WQZGu9a>*BB!dNgR>)`z+G{1lf60*QJslP^G9*pVzx@mP=9f|#?FKCel{YKN{XVg zY34)r!dOIq^TOY$RQm-*?e25zx!D@8rWP9d29BG8TmgdalF`${0}*Z%qL+HuHLLOJ zh|H%OKXwDa)OQsHtiY@uov@B)6?sV}RSos3#&REMK8t4eJ%?2rfU;jm@I2F5R5xNK zpqmnE1VRMf0@h<5X^u#)TWfH+ND^Nqsil#20WXRuvh|<7I8r=4&Q+7Ut(hrS0ij}h zi&6u?%hSerlUtUGiKDG${wt#!F8?SY#k`Yo9yrD4bxUt%uC2kA@_OnE64iZURSRc%xDX${Kd6wp{omZR@^yx zE2U+?1+9K#`Mzn&drsJ1Yi~wXB2Mn5S~b441!#;FXt%Xsf9z0v9_sC7_%CTWN|fc4 z+Ve3)4|xy&9DCABD!m#r6j#-n4m!lEB>t=~ZVZv%QD$Si3Av>&_-9FpL$7P53vTZC z8`K&%B7E2Y6YZ13$LmxcAD-j)xES-J44tkv0u>v&p_&aFRm|{rIX&Q>s&ktsvRdQ9 zoMg~wR1Uu5mhoPEAJTBKwzN9JT15U!rSqH{C+IGrDwEE`*;S>`^kn#EQBb;W$7yBuJ8Eq4W2S0^u*6iv}t&ZeuChPX{zmE?e z&vhIa!4LW;C;O5X8TeX!ou%dF^&$Fq2fKgx%p383eV&wfmX?O7`e@js-+J2hIm;X^ z1+||PELm)3B$HRQlrS)vCmpJ}@^-gdUYLk0^wTO_gd~(3;#Jg|)fl8srmiXiL_H!S zfNma~UhRCPQ`UfW&1?&f=!Ma;TU!bXr1iOrwf8npE;lYqeu)EMfL~ol?8D0NoU%OD zC0xq+^`n&1nu$%y!(KkgS{rY5=ZUOj{p%uEwiZFuDJ_g%_EpR6Fq)>18*imE{I5(e zoeIX4HS4d#?*PgDX)r;V%iW=&oVE5Q=IDiY{u}8d<0O!YNMonr{YC{D_ami$Q^#DE zK!Ag`Vxbs3M$RA_3yI3r1lmWUK#^ULidtVQ0z-A~wA55%gi}*o>&w0%Iy;%{V>Fd$ zT}K-eX=p0{?yh-lZkNnP$+$U&FBy1UsNtlL>HJzQQF#6M>Zol2H+zx&vpADL3n4=5 z?f3I>^?-aD9TwH?d#M8tY8jih#z}RI7(uOL`S<|v(f91L)JbDk&BMF1BK%I0V4x?e zeC!ea+T(&>bz5iCqS|W5-p!SnpPw_SnsL?rOwfB3AZ@YDHjx(WvNjy>>K0MTw1Zho zKGyTh{Pm1f^bN94Se04&Pf~X2PZm^BnM2+8(9k1A9)(j&lLqBmd6rB5Q%b;LJnIgA zFb0}QFvTai<0HqS4rgiN{oDQP6fK;rOj-BuT~ojI-DUim=*y^TyZw4sIjrXg5u;r+ z=c?ZEm<0Cr2KAAYSM(Z3<_{+7E8Yj+&&lGWa_2kpglxbY@!*|6# z?vnZXXpPc*GT?PBDTUq8BR)=by&%@mnlzBBj=Yy0b_s%)} zUUxG^M_8DwR*^&?K2F8gT}%iG0`1JEACr~@wjVPzMiqj1bGz)@MJb| zo&zRL9rc4jih!nN-QW;u;fnN7^z=M31=U^t*=vK04-i{Xxj{kZ}xvk-(KIw|-Dk5u}u zypl3&?a9J^rrND^v4ok>W875WqhzU({B)pyR8<=g&f{aa+2=jd*#OpT*7Ee2wN;J`%LXCjX5ie`ZV8F6rPm|eb4yYR7+yk-Qt6Taz~7^^()1 zszfag8+vQ;ST4!X>BgB$LrODEBAnV> zq36nHicm>owE#XU|@L zDXzgqV%X~{nj?6JvVc^LcmJNW$V3|ZmahcJ_rY4`XU+wRa3Cs zEQTTZ={U0UjtmFYsO#Dg=6c$&M1(ptG!@;pa__TL8?@Ze6=x0PFg`1}-tqj2W7L!c z>lV#kzFE^Y!t9)L_b1i&J+)yBKY5HF87#G-ej?sOW16z0eRRy~{?NNZovTX?ZF!49 zdzt7s4Mpie?5~ITIM3WW>d2*cRJFYdHKS83?WSCtFIB^)MLE6|eo@%N)jlkE$+J4Zy|2gL$uZ_xi2N>BNO46N`#bhp-8}wITT^8Z${(T*Jd3 zBeX2cF;;KgFPWSaP>*KDvOJPL?Jiv>NR4Y~mXnfx54Xh)O@R+S)`3=fLN#_yl5RL} zu)a3U!*A#NC<+Dz8tSzOJNOwIP6}B)HiWm3AR;_<-gkO(@v8@_!Or6pjav-jcB=Ji>R^qAO9Q{dTwp6e?u$wrWEk4bnndT_Jz zSj@s-C(yy?wL7{hpPP*3LBmW~Jihq5eqp<@a6(?y54^F9S zz+%IEEa5PfYUbt*K($O3E)$L?bBpbY(PL__?>_f~l}BkSvV1kic7bq|X>jMFKIh*O zgFf++PPV3GF+Vq}PRO2$E{_0bwKkzFyyeYK9wom$Q)an9%tQLLT!;yk3kQ=;Ncjqp zpcX!U%H~i)e}F@l2TtXx`B4&2Y1CTEs;F`t{8V+NiwZuXFWkL}>YJ$y-LrUx6AMkg z``5X@-%U;4=qUn3ar^Rac|pSZ^>}5LK+9vc@zb%!l@Bj_@cQ6dnX+2ukFO3qj+7WN zhZjfN&V5FrR>Uoj&K-?~9Z;W}_s5&O$I*zzxtbLLwqSs@b6=z6UHDPFoOPgro3>)ulpOOd>)`?C9Ft{CeRl*YTM z+<9p4;N|ndLE=9xhqT@2^V=F@=z{ac9judS@R;qh85MEB0ZpJ@gte>oC7y>so004( z2yrsd)HZCZ`S~d%=UW6+<~)U6B%=krBUpsBIa_rt{6eLIT@;~v)`m@XG9XW#Dh@fX z8NhF#F`@}=K24aR-uQ9IS2Y*696q|$sG~_V!CfxEfmmEgmWdImzjt4I=UBfhNfkBr zxTEK+$>cOtc$DIAZ|}0;szaD!?qc=Xrj+6H{?|G!LWn3Z9lv}LFVbeyvq|i)JNkYVQokF;NF#-5trj2Lgqu% zdgI*Wj{=1r;y72_d05GWvYP>?W^-iK2<$wIp3FE`?s@Bd!RIS@cLYd0PLxXz{YnsP zlS(=l-?e+o@;eyinAQw|d$@V1kbh&LBy!8b%}RV%79zQ>P|}{T=Yq zR^E~T(WHOT;=z|rD^n5)D=B1XWuvSQ({S&(?AcYBue;40vrN{>Y1;EWze{P^xcu|R zmNYqf^Ob{YwdCb`F}=~GIq7Dj!=sTqo_p7PUZApzEm^GN`SvHL2BJ(NaO`tL+F~5% z;-$jfsWlMsL$Aj38GB*{px;_i)_F6Q^xBur%UfRGdw=@iRi{8Wy4T4H4T@%tMZsgf z9n^Nm!Ou)<*hJw+DowlG`r~z# zhZE3OxJPLvXovl@-`yoqFRc<6`Kdj2)k3WchEajC$5RP>R~U<9%_0h$Ec_tK#J_A% zNlNa;@tI!IWh(Q`uC@}NAR}}BdBkvbFY?d9&hEB`dDdORYOmi-7bm~vmiE7>r#Nm` zo(^xc@&pUJTjv^l^l&7Om}+R3<|Mk8k9R?ITmpH{s%G=X$wtbBtTH04Ry(J~xJ|nL z{($nc@>?fu*G^2z#^#`0`>DqA-nWsrhDRMbUX0;&!P*aJYC_&8o8fAv%9)3kwGQvR z2oIdNZ;L&#UOmZ7^M1-V(zu?T6Ma3oGSb2>wU>d$b;J90B~#|T!e@GjkHnIGJYMZd3_Q-d8#gCIpS8A%KD*E#1&3 z2MN8VAn}KvS*ZqC z>~DsirCmTG;I}%xzmORu42xPN^Q^o`dVjWgCf6TEDRh$QFDoN~X+A0BnXa zL(j%FhYsUT&xWy#!MSZq}APD0U!tMCc%R?uhX)~P%1s&}Q=;xxkzpQ^UUtg|i zF?wYAdN`5@z-QI?)anb&z!*U{cSo zkDXXDGscMZ=Ta(ZT(PO3Ewi|IJ#)$#*A)q@|0qf+X1b!P?7Ok`t{5H7U9^m}8D0nx znCg4bQ~7W;FL@7QK4to%=Q)`BBOhD5v_v#FvLpI-dSdF$=bGsLqq1zXwtwt1)yH~c zBeAvI{;x_Y;mzuCjheaa)%?lGg1p_dPnQwW%h0>O2}SGe&Figa2esBDlWh9%MU~xK zUCIdDgYR?S1g`l#YK7P$I9Nm-{8zpCOdPh4)z#Y1V?-{;q^YTXXd-uJ_5(yiI&`aZpkq$>8Sf}<{jhS`?Uv9gQf(H z4&US4T_~Sn32r`OJzF%ZwsJ$XyFb4>whve4c>)Bmj;ww`l(~onKxEIsz~IwPmRv~= zX)QQyPdJ$sG54#EfXJKp!{nhL3%0=W557_p_p;&!_{!z(GQvzysB4_k?2oa7kwS6+ zkJ~mn+oY2(r(s~w*CX>T2etF0;{NROJ*Tz*U%2qf{y;vqv*3%vgUam#+J{zO~C}7omasV$}2$szU zjcNxDzibuNhuO!0yjxmy`xtF`rL|}PPYHFv4p1RO1yOR{LI0;m3vb)$Sd@%~egl8Q zq593`UZ_BIr^~XCFu!Z(^uS23tA;ms7m$yDe$utDs*@k9>Zwjri;P&Vq^|{7wSEzw=3RfO zKCyL{(vLLx6jI8rido~S%KLgPxGR7ThPf!dvP z52i7zcW*!)W53L2%Q}y2nAr^z`-Yfi4jd)p!SGNm6gvE=rwR0pSc1q6(QFLS-sO$K z;phz^Y?3evk-44Nw$H`FT$g3$3a}2z)&yTwPR7Tzl(e10OUt;k&Sdt%dX<_J>BSY} zy~o!Dn0Esvmo=M{PusWIU+y=G{J0HkJKOY=vEvt{|C0YvuJSt(O1&C7hwpQ7WK_T} zFfiy;W)#~U(wp*Ez}a!RcEGa>Q*CKW{pw$L;I#~9`ZO2q%z3??rgbSos?FUp1R@2@ zTo>^(pxfNKyej^}=lK|0|3#Dd6&vXnPtJ-?P*B<2J;8Tc{9)SPtlLk)C!OLbfyx-Z zIAfjfsB__T*`{&y09zYt$enjg%%HQ3r5q`c94UrQkllp!!a6Ub($7Q?!-X(&@xsqq zn%c4%*u5Jf-~3p)_%HFWO4fTO{bql6U|~Jpa3IeVu~8jUnmLvPr<%G=xLg7~p;2Y; z;D^l;ME8|`0!LqV!AG+rF9?eWkJ-Yh6RXZAsL$J;e2Cq&xMasjd`ymM+a8y9oBWGw zkAA3*La~y^8vu4@L4}{t96OI-#(IN+}C7Vu{^d^7InO|f90y3nqPD7gQD9&m9C)rHdatKWTYHl0;J8VVfOC`=J8@M~t} zI42fA(;`ejUjkp+H1)Y2=HkFI3?3EYk`jD&g;2~oGzN#?fM`k_p|{3a9QChp!16E} z>Ym=L9l@T0RhZbnPv<<1C?s>v1EoeE>-2#X7@4x9JhID$8j4L3TZIVc>arU;HB3ha zXTre<1+mDBpUo7TqSMM2He2eCd9@wRrCeIF^Cp*EVa-$;x~RUe8fGTaFIYOcMTje? zS7gV9%9Dx_%K|UZu|EKMR&V66=ai45Nso_*`i-uZufXy}b9KjEESq0e$GH;XL&t}e z^@n2xYwqi-Q=P@_IciMS2*8Sfg&}qorZcfM*kPBwCLOrTE2d)Lgx+(84X?D zG|SV~Qc&enU0XS#`6{N&#Kn}rDFXc?coqU5KPN(R09I^_%~sB zSB}!VVeI|d3efB#sPyo+wh>sM4VYeUXJ+A`7_cg^{M8$lY69OB0V*_YQ0}mBH&m+J z7}EBwF<_;6lK^3)?(0+9nbzEySp7v0z|+PY`oe^&)p`>TBNN%Z^&d}l(5HpNp?m9^ zq#7`IO5H-#rfKlsD(^WVB5joP5^`N!tPnLD7DT7Fn4_i#$ruy2XdVg_oh^RN(*~Gyfk^Q3dfvfsFvXXVX z8OLj9^n$g9a(iVGQv>@O_-=X|=?EBPYdcX}f}{Q4IP!6?>*!}6-_c_1F0Htez|hdx zpzHb#>YLCyH6vas#epDMQPdFF@W;3vt1XQM-T5RF+V~s>7x6PL`hVG+WhL)9!vzn= z+-h=5+@%yR&lJuGJaal68AC~PbUIy9$uSX8-e2OkLGm{2t@ZGC zPTz&qFVeERqa>26rtFuJqLeq9fRDFj$T3;jR>fxBtn~FMYZ-BmIkTIN5qs}@UaVd8 zhd<^2s*khc$wu?koEz}yrgm@6_m^6y2YAP?v^1Mc4UW?wSv+=L-9;xQ zC0bhG@%CnvuX-yMcE{e{!q1wS+MB2TOm02i->%}=SmZyyP%KGaMyRgU$+vrlXJw>s z%sQwv0<(h;imC4uQZXlj1@MNVg5AypHu*yaiRap@5!YPuFPqe3#AGyyS{q02?iQLH z9^RQ+vSJ0ED_pMy{-uAd2TZY-C5@`T8L`j{C`r0A%&x%WMVp`1*9SflzL)nYPQlXl zO1I?g^a~$U;NebWU3R=i^mXdaQ#~BJFIey^=g%llsvib7F5$i9c4ul!YYfn{Pqw-J za`kSZN*cXpn}el2Y>;PfN(x)Z)^@?&KjZ?~iwfvw#H}_x;il+feVF@jog>~Y^b%Jj z^ihuBI43%M#(K_#lXs(kPzSt%$X`~9ORDCf6()~^RX#j=6xQrUT=tI#3wQ*#y~ro$^e1;KW0>BkyO%!`x|-w zTz5I9jkX*UkpK=K{V6&)i*+T#9rA1nEFUi7{|L}Nr{aNYlUiAY5~7y79@Us|$Ot7L zWu&;4z*HlgqLNq$-zTtcq-MFuKZt4Psal2V#PdtEk`+%!%u>KD1RoQ-xxN9f6kS=% z0ZL&%W;hhA*NdMjLfObs>W^`8YHHB?e=ky$6$P8(nk57t%)_vNYUi1lg*pew05gvY zc@RBy#(qZ<&QYKy1t0U7LVa5D%EILBsdXoA2b&7itY3Eoaff`nXCMbbU(3mkQa?!tMf#APce9`Hm#j1NP(Y(u2!&GVTVD%8x{-7 z74)DiGt<)-PO~pc#kv}l3U51llJuSZkgY$%R?7pzI4YANuN;;r%y zOl3Qpt=2)LeVwJAKaHMarse4GQ*QlY#fbzg3KtV#O`W?nAG>6eb#hia9}W(ZrA>@Z zDV|xnd9b^AtIN87ROnjr;XZ9da}h*YX9DXS2y~uI9(XL?JGk1X(CHGyW;?VpHgs*U zC;(IQ&0{O-V_#&^e-|lmdxDRRZI3NtpX(E9Y8=B5YDl>8(k~Nb{3$3)*vY>uKw8yY zxo3LuyKleQU7LyfBhBn0$Lv^jwu$sFBX=V3aimifOQ2HF4h0{ZxH_hhg&_2s03xNF!^idX8A zA&znq?(mJk2Ak8dZnars*ZN5C7CyQ#2e2Pj?+?CJcu6PlK!pFyCjeGkB+~V%5Xp7+ z_+%nSMo)&- zj(Q9(-gEFl9WF|4KoVMxP+Pbq{<1hAY&4Z3epX#DPS(WX?$xE|`@PL%J8e?zObkP} z40|u`$lGNwe@0y^v+n{#@p77#d+_`1^e9D>aB*3qnJQ%~7pKJtShd@B#ABz>42euX z=E-=Y4b@5J6l*VvFtr1*U_ui45LMM@ET<6-WK3*L_y_T?_DYg4te_?{UN!k;nuOlD z?ASt{Wf@=a_3jRV0{SuG5DOw?eNjML&zUlkLi%+`mMZy^77mKi~=yCuO){`Ij8j2N2I!Fpm&a_payl=5hHjhJmT zC&#dV9xC@jQvdv)#~(-+V{?*C1CB!+(E~%soRB*WPX^}m&%$~~1G%&qG_*}M8CqcW zs~Ib@46=d+aYR2vtG^sEbmRAF+QY=a1JXAST8M%t+D!}E0i9e-s=tC{^jTDel1xt}94WHRotMHQaQ*3@+cs$K?Ks#bR=+x^GFOWJ z29eo2q>Zc83vq7Ft4=N6-s+#>F_oW3`22Nedoz=3su*XO0X8&iwKnGmvEc~tZ+wy* zg%k?g#1Nv945yt_hJ3=RTd-5)hQzX)wxUTk;8J)7U-Nr;cbo1A)Mlf(B*7shack5g z5q+db5Am6g39_Gx3APx?duIoeymjhD*)hkLh%}rdfZL3AwYBrXUTLJ5E}I1X_MbER zYm9VKME)D)6-M+lD&U}t)eiwtN+Oli5w+|cm-7b_zGQHfufndEbsWE7iGidL#tey3 zlyG?Hr<8;*>1zfpiH+i1#Zh3OApTXUQYZ!mX!x<)Y2fAXK8n;jahR+~K*F!A5W|k; z3*1-fe-gXsEXm!Q&TXCCeZYNkmhj^=dsKmaWqJ`&N~$nPDKV70^O3PRFd=-mXsZxjV{9}v6#gd7%<(0bRQWH{qfjN#Y z<^DZ(g8mX8RB!2a;#M^#Kv7>bK;4uILfnW+gc0-m_R9dSDiH| zCJwGj@d85~Ntt?c-owuJ-C*xwKARL*%Lj=ub1(!TWr4 z@*FbGHMZmk-domA@qVQV%#K6|7sSIMq}3y;w1h%{$acMdHvy$3$^aN@Bud3a_?ay8 zOTGv#FWvHL(*8(P{=P6u(McUM+})&jLw~)0^M~3DZ7zU{m&;iF6rH0{ImI-@1k$S= zU12x3D*?h9!l?b=)?PJY-1RON=B7Qj{Sdt5T2|TrkAI(4HVMDBzp#J1c1+lCI}fsj zc#Sx6GeNno7=p?o1?il6@0#_TbetEx0W9n_%&BwZx#@v><7RVu`rY=uRUmQ*BMQkR zT#>U^-#a*IY`i#<2_#XwvL%O=NTw7ie+EX!nPBHuyD&K{>cLJ$j_Mqw*TNs`nu7QI zCyY9nG#bhM(0YG6t48Fs43Th7y5q$Uoy;+ej2cu*lyA3z_&Dv2GY7ldBOzi{??1{@ zqvkj z`?brWiVel*t?T^XltK(yx^%G@6TYZZs&`2T4gF9K{=~Cjwv> zrO%>8sX{5D^Al)}+aA34=H%R^LYYw7*&yYcH!s%~vI-l|yEe+3%Ucej|0dFKwk0L! zFZi1t40WtrV{GNo#C5Yl$~I?Kpw!UCb+SQmn5 z3j3_25pScv5JER5Uu`bhK2T9Nz_EC=KM20(P^E9TNI{!MBnN9PBEDk(#~pe|$C2(3 zb^cFOPKDf{wMg$05n)1|q((I_*#fTYQkwaRESYC7S3WQ5n}t(_!H7Dp>4D!Q)w@&~ zaycNKy!^`+zwEn5AWF?bq)qB+0Y_*(Lu<|hdud#((Hs{oMCxw=_tUyvr*1`F7~eST zy^`j*nU6cIZprwo&N+=tjLRt zCFU+r@eJ4@=e4}^0*afuK(S1LouyJeFNm&9?q60!?2LH2NQFq4EJ+)u#s=w?(5%PF zP`)%=LE2j!z;))bfm$R*gQCs`38*(}WZCH!QC>hNpYVQuqysO6mEH|)R?m_g`IACj zmF{1=au`KUPhfj^&f)1yUL@GUlsufR3yNK(q4Q_QLq;t5ealYz%+ z9il!Q;uO&}(>g3Bd)Azsx;?coU1Vf|SI?#Gb3bex=BIaJXluHE_X=RWsGN&8l(&~9 z?svAHgK_5FhQ^grLV%svza_!%t9E<^&foTr*W+gK=G30zQk;ckjodUBz-5B+?(~Ay z{|`O6-|yZN%Ng5_>EQEa2;dZgs}7qe1UjmqykZS+WuCU^I9%+%uyp^{R?o`SC@5oC zSZ28RB7#Z(io;lBhgS-0>XI}WpOff*UvJV)d`zTO%j#_?aR^cA-f4URFD-wP3?zvA-jTrB(WyuRcR>Ra0#V z(Bq}VSPQH(-hZi2`E<2FSR$4rc0@&G8L~!*AkLznkNhcKYe3E*3qvfRc}~dVD3RDB zhGpY#I8w_BVip2p_g7Q5Sq&Ad4d`yp~%djtK%xLU>c<+494Vdb<+9DB;go)O#J=scjUIl zBljR;wvK;VHd$OaE9A`WUiwP--p9^zr|C1g3@ND@+@lfCws!EB9`(9_u5dgb>}?^N zI+~9Z6}^tgWE&%3Tk4vb;W3OK15jrFflR2cO436cY}+~uR&mUqUCc=>+-@6aOGu)ty(*NnHwVZ$wELyK5!hdlCW zD)CGLLw5?4D^|$TxiE=P+-Qcl?{-+d=@Ubx;g8c$QSxQVP#}AlDP0dMskkGLbniNT zi$&iiJHu;Q>X$>xhsV=L_LhYh^u_a)fI;%L@nh*&F^SZ%+sY@adhS%T+kRp7bLQ2M zMvP3W*MW7mtUPuNn*y2luRlB2*(*G?xl>=ab2dJoUY8}pE{Ow1zp=^Nseaz7U0HCqAUfSfK-^kZ(GKGEPc0lD=fICb^33^T4E4pVjj`(U z#qh8@v|m1E*+;$p+Lz53^&m$iPA_)Fd&DP6{PgG2R86IRyFZSY;X7xIHho^35+lR*IW7(K7V6+UJ!Q=eP)Zf#b{T2pR8)L# z*Vi+_R#4av3wi0%+td(~ zIxJFB3xlSH>)B^WUopfYP!CMDaiu+U!vVQ$MnXc7*!nE+k%78x!g49URIj&-s4}b?JmH8@b%=vPO=ALYSZ0p4adYGKJn7mkZPLxBVbHix3E? zj<6`|XOpmC&CgC6r}wej#MLg-_mb_R*BunSaVkPX2b%^Igq>-WT3(1;T%InrAFyag z6RK8(tDL7I`@X$Ux?cdr9p);SBhH`PqZ;6$Yu06EaG;^Bx89FVPDG|L#B#BDxx3=n z%70!2ZgkA7uVCN%x?pZcg}S zUHMeal8ETqD}ufO6@wL)vUkVu2_6)cr|FK*W6Gccj)}Oe)_TJZGeFc{MU)_`y1lLz zHsAbgz~EzdLE6iQkv_pBAFXS0pefIdM0msxNhYK{r@8N#B9ZL6O|rwsi8t%8IEep( z#H4BCr&;2Q7?0M6UrAmBZ&`ST&%%}2HcJnz47_uj!Do@UVN+221J$#F6ArS^PCzp&s#dY`n9uB`V+Fva^nhwj{W z7N-}mzl@ax9@iKO?+iY_Qh;-(F76{U7)XjrFt}gPc$sW804^W9l{`5u=o+3FkA(+x zL?Ec}D-5BZ>St8cqs4;KJc{;Uoa&qNbz58S3p!qZZ8k_pZZN+8tVb{N>bXj3Tb9C( zEKdBv{d#&{YwuHQHWW`)LrZhAT}q^-LvD+^tht-2X9`)%PaiW?C*n|0RV%+u>Y^lP zE>2BgAq>(;BnRXi?|l-cp}x})awik|erx#} zMF~D)g34lU~OsEoVba=Zz(WJ9k0~%K02h(`Y-|=CtfX=jDZ7_vo@^b zvC{`ZKFR-LojpO4zP~G<3Wohcy`ddlvlD%iP3Zhg?E7^HZk+N;r%G$@MYYpO3jtB_ zW**7~#4KQRKEx?Mw6%DIlQ7{;fIt>S4Wz09bQm=h?Iv?jypc(q9L*sP?rFjIrYXrj zopssgxab;XLQsy5zBu#Za7Fkz-Bsp4t2y^8E8K;U6nXZ@_p0#l5*#VbtesE#78j8k zsBmLA;;W9dVX$3|La%s}7PTDz(u%mvMZFASXfnC3!OaSH!(jpaRz~Tsq?W3%mPb;D zAr&73y=E*By-<`b%+5iQibc{*I$a0TR}i?Z^iil@Cb#zXXy3})Ro$N!l6bY^_y_I466%7~|M5~QQD|Ha4T}z6p z3*iK_#Fgr~QQ{YcwOa%P4|f z;Tz!P<7L{K<|cFKRa0-;To?HTY`Z&2u#jxZP+*x%;yK$d_KmMN<6O=KocC79>77VY zXYj_WUj0$Vf+{1Mn^X2yC+FO|KDdDJF3zU$6cgTM;EaYH9g)Fx=NiykKS5r{tthCZ zpLvjKA~GBqkyEm4`&&f22S?{OJzp=|%4G->(fe!V=9kH~pN=W5z?sx?Qg#>!I}?vS zm8dmMnWhJ@iMG$0<|IUNJuIG6A#j!>LEf^2$htojrQob{R!~^&N_mvLqCN#peu)bu zY*E{})|8VxX9`K=+B$yTDSmI|>E4)o2yEMP?pSSCHEH5!^RrLxC?ea$gYU$U?0L&% zjW@a7W8_B7TbHiY2Xt;>sgYapXS+s+!XhalF^+@H=+6 z$$tx!wu^mXJ`|i(Fk9L~A`ZTBHL)oXvf}}(xZQ|Aqu0(hDn1mM82Geay*qEln#Ayq<>r?_>$tLS*bG787X?+5JOuNvujb&#?fT9y25wBW*F4R@MoX&WRdZczNrCpGSz6gKK$ObmEY%M?khx2Qp^r#QT zbXLB3uM!LF>|!PAo09iB(PPD)o2>JP3{@kY~!LggrP;GFsxlr6;^>J;a|yK8w&Qqc;Htl5f;sYQfSC=X~hga zV6NjJaG>6Q+oImzx>rs^qyO;wau;8|Gv6eu`YXS+khTeD3Erj;t-r)_=(Wus^(#~k5mx*gk++l@}S>c0B=f& z`AiLzeQlH4iv3}oF8*$Nt{B%HH-x8)q~oUQQWfIJ>)Hhf0? zq;J$wm%@ixL7Gd5QpL6I@~gA~n`hDA$SInvUBc=V}zE!^w+ja4+Wtc^9fz<@;e%Ox4Z= z6jeS5(Qsm2Lg;<4ln^(n{fH+dF-$vK9HNM=+v|!6m$6j zGj+VC9$p0v>gcwsR{{@ zkkS6!+`~}4nS?|9oPp9Wj(ncvW%O_Nzv;u7>`D2lN5mXR2D<}E~h3z~$RIp2pr~EA4crDyxpZ;BV zls$PQ7UH*?J!R$YG?QlKI>=1*kUKXm7nwWyAoK%xvIr`f19T4A$G>1@$5Aa>)?|JU z-2g3wFMCT!OL}*sd2I2tx zB@{&~cjfJao%m4P?+f3THWc(e+*uB4BCr@Cu?GL}37<|2j>hT%a9}7zeXCBBlVc7Q z?jXo(L(>3wP5l^j&dPr}Uc_LLL?(B0pz|BYUUvWd!&*nIomIS=4VWM4p3Dg2Dw^Pu zK$~Q3#1rCTdZ;c~zaGGN(W`Z_E^@PKzteRZFZEjU%4$!=s4Y{XE{jLkMm$5wh7Zg8 z=CKpKe)A!y71)pLSIsm}Apm)wC&R)wf_x6wg^w!KxvDhm7fGe~4ulxwBwrf998bJ|DvlZznd!@C5=ep=R`$LiVsZBlP4~e9 zx8mPab5uxrJaH6H5`x`!?^t>rBcbvB z$=YNck|a&tks~N*@$`^#nh+ZOW9m_(L|x!%ZF}*eh~KOpS@6jo-^cYsf2jdJ9(yC- z8CgHy4G%>Zo52oar#l$m$m`pO`J#{-Hnvmt37WWGGQI0?WjHps_Bvlyg~5oR;t$sN z$(Ol4@cSLP@##};gzk5W>@R7xO(`9;Nd2U-317N^o59kP?~2?x;%q-LXPE+prUvpl z^q~}KKF`_6owD_4*G$M8oOkAXd=EJOI32UBD?$>Jh#B_wFCOReXie`v64#t0XOYu^ zbK!@kyH^mSn72sb$sMfwXpeiG#A_dINX@9k*xT+6d7}&-K@0*i3hR}@N5MBJyBjt4 zjol*H+gomdRXWmT@!uo;(F2n{G9~f_M0S#HBg*d1X!z~_sO?phFhR;V#&vWUyB?9a zWvtd+OqA+xeep62?1?JuMbKCg5$~t{WoTEajU7dOYXzZZq-5if1agXXCGV3=FQ?(W z0iE^jiT&~daq)R~SCXF;{wS1rs^{qP?)-EW{GEXqwO=c1Id-p7G>9i=wTT~dZ5ze& z6N%TJWbP^C0)HM%IE5)^4Te2zPGw2K9s{2ORpb#~-~j*t6aZ4~oq{a0cTwr{Ro(L! z8$bh4WM*TN5@m6>wRSS~@N~0815iBw0|3AQhyX};X6eMVb8rprrhlP`V0j#IHUQuo z!gras984Y9b74giKbjnhV<^fpU0PT^lo%DBKL{D4t_TS06cTplb(Z~+a|P0GIbh%d zn$SpKy4gj(t_p76!nyjhBI7R8V&o(#`rX=2|lYOQg+w) zf13^X)9gQO`sWYwzs+X-PiwQpE&sIg@AR+jM~WDw?gs||EW-l;IKLT*n|hc!I9s!L zJ39O)&$D#BKY2C{EB=LstBou5i{P#0++d}AS1#WxO(cLv43|ND$#pWav`&yZnRqm} z{hZ9lVsIn+7()HLvT56j;4M_}tQ~TD?bOP1iS{m3Aw2}N#Gw!y1f$aVIRdUk8>MyP zo|-zznZ`I$&FA77odKhlE&%14m^d37DJ>-A%WS8!cyn*iqlk5XO0b_HtT%-`=@PZ$ zWQP0!+1_-Pc8;0Wh?;+J?yE8-F0Bf%Z~5R^-o5C*ol{dGb)**~eSzP?^l69M!Z)^@ z5~9f<`aa7{{|ZIA6&uq-DIs%Tlk{BbDzO@s@#Sgot85$GJdC~Ob`T|j6g5F;Xq{+4 z7sFaVT^zZ{aRmFZ*vL#83CHVFrWx_R(X|(ZgYLJ%A#D|FZ_6~Ru-(_mQ{E4sCVdpB zd@c~(4x$KsU+R$NeojAVpQzxR4p0u0n7tH@Z@?2ht`5y}54}{vDi%r+9imC)qD*EI z66KTga8s7}$UQUg8Zvfddcec{`uL#aDj{cC<0)sK@UQ+f*A<<5_3TUW7XSe6Z-2^| zx>^3+p}2zDe>&3Pv%+6!xcZ9IvIw?+-CUr~X{TkGk=bY@9^8=jB(kQ(Lo_Zo~efnlfkm@FdsI`&HDPi$s%Lk zC=t0~>ox46y=Q+rmBROj8e!H?rfse@~X}^Jg1Z-(VC}CCM+YOy%2}OR(%ER)2`6t~{&prf$u3(ew(m zy)3kx^Ti^IH9#>>yIXnH45@X_M4tRbQ6O9^j47=oyp9KvJtO$FZzDJnt;FSBqYWD-unc%)c@>tQYadKp{IIlZreO~J?9D= zqC3@}IZfDvVVw13G9z5LD8weS3eFrc%zbgpoawnD#x;cgB|TyZ4i4iwM83STUS(e; z8+~c=&JQ8CDJQ*8_tj9yg6B8f%dFGso`p}mToM&Wx;%!zT7p>Z&sz$F{htiakMA(S z8e>9ES!;_6xz0kzcMypCL%SyQKFx`XMDf9iBR|J}dplc^czBLRuW?METTa|s2%M`k zU}%gRl@cC7kbkf`g!KOSYsYl(#@borq>)zkIUD)tJ!n0AT0-=DUdku#5ss{5*GaRIEQS?}D+{|9zBq~B>AhAI$DMecLYuOpx3h+w zzsvpJ^l=-FSt^CJxo!H}(f|PyM>HZV50ZzlgS%ZP!+^+TCN1;Fb}p0a<^_9MHK0yJ z%H|aEY8Mn^xMHx090>?#+r=4!E#rVNG-4g^=pt~ygJsl{IJhhQ(*a-y;C>d?&hYS2 z-j}-7a$WnLS530e_KiiFh~ORmNU|JaVwowykFQ2V1gJ3;0;t>@akR(+iJ(nBHPaC< zZ=#Pnzb2Cy9<0PvRk5&ZDB`4~oCF$WO zF5{?mawPYUCQy0Olqgx2C?S^AhRxN)&S_Hrx(cNjm9EN1ov(}8MReu0wjZgZkzK0X zb)>pkwW8A1FCE}(skcMnJy!J46o_i#(}ZtR<)z70r_B9QiPIDJLB5|KzfU)p8VqA? z+nDrdTqi{9u{^*<(qbjARjj@>!cebyKf+dOz#37h#b=SJmQOg~#~IJ-t;0Xav)Fh} z+h~49$o;jksU@hj!rbyY2IohznDmPi1GQOHBCZhsnguvz*GnxN&(t2sjD9YzohSqZ z*7r$Cz9NlpoExhue4|zL5!A&2i6yGL{q;>E#tFVauVU@5=~AQnysN+HdRU zx^X2VA&{@}1DRJ`nu3Z^KW;%Z@l>#4l}h(Sk7GF2lt*nMX5+E6m+ss_)~wrl_l0|v z$*{Mhw}VNqL5H#zp%-d8rV&kvt$v2f+0J76jAf^-YsC=BTuDjeBBuS=feMs^zPBS` zx}T%Ttf3tx#Li-Rpf}88?8ag_xl^=fe=HxW7*PfK8dm8!i)p?^9WRm6E~GcaPkKR{ zb%U4t@#UCcl;jjtR%5jY`+@Qff{Uh%BHY6gep*+kLc(t(U}d4J@y?{JEogO0;^pY7 zG{bmo|HTXiQV`Kw(bRXgK8XZD%}OD3pqZZ(jsV+&7pg|4smMnFnFE@r?Q1bNqHz7I zNkOkW3YO}D@wu+xQSV^S+2%bybc#m0DT_;gtr)Qe6EQz>;1~-$NrZK?)t32~fqpw3 z2TKPz_cxbeR9q(0AN}C!9sJB`QuZx5F3Tvit$nlc~}#OxiN|Hug?x1Gw2*-@pTkUm7ZbK zT|s}MSbH?8Z~Qw$A=5CIXz6*ApF{M@Z|1~ka}-q>Em>w2Wi8$erXFz2L$)nqpsa|x z+?INV<G87_=!y{X76-yi}9dPFPwvjA)0jm3ZmHqY{J9K_E44-Z3KuZmtL;luMxi_?Xne7=1Jh2nI zhC46rR_cYj>&f2UOW*v=s1ILUOb_0v04>H+mYzBgE)gRM5TX0GjsP19tDwcLgRL2z zJwyIUUDerMY}vjuvvJ;5CnCUuFGJ&lJ25trI*E0mHWIWrTSvF5i-7!{Fdsn*UP$?;zv1>W#k{{|_O@Z{`011pglR{+1Dv_HX6?r(y8lh5S9( i`7H#J@oypjr_e`59`QMtdiKZjq4gZc-{t?;)&Bz$lzadH literal 0 HcmV?d00001 diff --git a/examples/knx-demo-diy/knx-demo-diy-tp.xml b/examples/knx-demo-diy/knx-demo-diy-tp.xml new file mode 100644 index 0000000..d748668 --- /dev/null +++ b/examples/knx-demo-diy/knx-demo-diy-tp.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/knx-demo-diy/knx-demo-diy.ino b/examples/knx-demo-diy/knx-demo-diy.ino new file mode 100644 index 0000000..00d3c6f --- /dev/null +++ b/examples/knx-demo-diy/knx-demo-diy.ino @@ -0,0 +1,138 @@ +#include +#include + +#ifdef ARDUINO_ARCH_ESP8266 +#include +#endif + +// create named references for easy access to group objects +#define goCurrent knx.getGroupObject(1) +#define goMax knx.getGroupObject(2) +#define goMin knx.getGroupObject(3) +#define goReset knx.getGroupObject(4) + +// If you don't want a global knx object, for example because you want +// to more finely control it's construction, this is an example +// of how to do so. Define KNX_NO_AUTOMATIC_GLOBAL_INSTANCE +// and then you can DIY a knx object as shown below. In this case we use +// the ESP32's secondary UART and late-bind the ISR function in setup(). +Esp32Platform knxPlatform(&Serial2); +Bau07B0 knxBau(knxPlatform); +KnxFacade knx(knxBau); + +ICACHE_RAM_ATTR void myButtonPressed() +{ + // Debounce + static uint32_t lastpressed=0; + if (millis() - lastpressed > 200) + { + knx.toggleProgMode(); + lastpressed = millis(); + } +} + +float currentValue = 0; +float maxValue = 0; +float minValue = RAND_MAX; +long lastsend = 0; + +void measureTemp() +{ + long now = millis(); + if ((now - lastsend) < 2000) + return; + + lastsend = now; + int r = rand(); + currentValue = (r * 1.0) / (RAND_MAX * 1.0); + currentValue *= 100 * 100; + + // write new value to groupobject + goCurrent.value(currentValue); + + if (currentValue > maxValue) + { + maxValue = currentValue; + goMax.value(maxValue); + } + + if (currentValue < minValue) + { + minValue = currentValue; + goMin.value(minValue); + } +} + +// callback from reset-GO +void resetCallback(GroupObject& go) +{ + if (go.value()) + { + maxValue = 0; + minValue = 10000; + } +} + +void setup() +{ + knx.setButtonISRFunction(myButtonPressed); + + Serial.begin(115200); + ArduinoPlatform::SerialDebug = &Serial; + + Serial2.begin(19200); // KNX, pin 16,17 on EPS32 + + randomSeed(millis()); + +#ifdef ARDUINO_ARCH_ESP8266 + WiFiManager wifiManager; + wifiManager.autoConnect("knx-demo"); +#endif + + // read adress table, association table, groupobject table and parameters from eeprom + knx.readMemory(); + + // print values of parameters if device is already configured + if (knx.configured()) + { + // register callback for reset GO + goReset.callback(resetCallback); + goReset.dataPointType(DPT_Trigger); + goCurrent.dataPointType(DPT_Value_Temp); + goMin.dataPointType(DPT_Value_Temp); + goMax.dataPointType(DPT_Value_Temp); + + Serial.print("Timeout: "); + Serial.println(knx.paramByte(0)); + Serial.print("Zykl. senden: "); + Serial.println(knx.paramByte(1)); + Serial.print("Min/Max senden: "); + Serial.println(knx.paramByte(2)); + Serial.print("Aenderung senden: "); + Serial.println(knx.paramByte(3)); + Serial.print("Abgleich: "); + Serial.println(knx.paramByte(4)); + } + + // pin or GPIO the programming led is connected to. Default is LED_BUILTIN + // knx.ledPin(LED_BUILTIN); + // 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.buttonPin(0); + + // start the framework. + knx.start(); +} + +void loop() +{ + // don't delay here to much. Otherwise you might lose packages or mess up the timing with ETS + knx.loop(); + + // only run the application code if the device was configured with ETS + if (!knx.configured()) + return; + + measureTemp(); +} diff --git a/examples/knx-demo-diy/platformio-ci.ini b/examples/knx-demo-diy/platformio-ci.ini new file mode 100644 index 0000000..2071160 --- /dev/null +++ b/examples/knx-demo-diy/platformio-ci.ini @@ -0,0 +1,24 @@ +;PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +; +;--- ESP32 ----------------------------------------------- + +[env:esp32dev_tp] +platform = espressif32 +board = esp32dev +framework = arduino +lib_deps = + knx + +build_flags = + -DMASK_VERSION=0x07B0 + -Wno-unknown-pragmas + -DKNX_NO_AUTOMATIC_GLOBAL_INSTANCE diff --git a/examples/knx-demo-diy/platformio.ini b/examples/knx-demo-diy/platformio.ini new file mode 100644 index 0000000..011e390 --- /dev/null +++ b/examples/knx-demo-diy/platformio.ini @@ -0,0 +1,33 @@ +;PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html +[platformio] +; We have to keep libdeps dir out the project directory otherwise, +; library scanner seems to have issues so compilation fails +libdeps_dir = /tmp/libdeps +src_dir = . + + +;--- ESP32 ----------------------------------------------- + +[env:esp32dev_tp] +platform = espressif32 +board = esp32dev +framework = arduino +; We consider that the this projects is opened within its project directory +; while working with VS Code. +lib_extra_dirs = ../../../ + +lib_deps = + knx + +build_flags = + -DMASK_VERSION=0x07B0 + -Wno-unknown-pragmas + -DKNX_NO_AUTOMATIC_GLOBAL_INSTANCE diff --git a/examples/knx-demo/platformio.ini b/examples/knx-demo/platformio.ini index 0371159..edd9954 100644 --- a/examples/knx-demo/platformio.ini +++ b/examples/knx-demo/platformio.ini @@ -59,6 +59,7 @@ framework = arduino lib_extra_dirs = ../../../ lib_deps = + WifiManager knx build_flags = diff --git a/src/arduino_platform.cpp b/src/arduino_platform.cpp index 44651cb..65ec833 100644 --- a/src/arduino_platform.cpp +++ b/src/arduino_platform.cpp @@ -12,11 +12,12 @@ ArduinoPlatform::ArduinoPlatform(HardwareSerial* knxSerial) : _knxSerial(knxSeri void ArduinoPlatform::fatalError() { - const int period = 200; while (true) { #ifdef LED_BUILTIN - if ((millis() % period) > (period / 2)) + static const long LED_BLINK_PERIOD = 200; + + if ((millis() % LED_BLINK_PERIOD) > (LED_BLINK_PERIOD / 2)) digitalWrite(LED_BUILTIN, HIGH); else digitalWrite(LED_BUILTIN, LOW); diff --git a/src/esp32_platform.cpp b/src/esp32_platform.cpp index 844f446..cc902dd 100644 --- a/src/esp32_platform.cpp +++ b/src/esp32_platform.cpp @@ -10,7 +10,7 @@ Esp32Platform::Esp32Platform() : ArduinoPlatform(&Serial1) { } -Esp32Platform::Esp32Platform( HardwareSerial* s) : ArduinoPlatform(s) +Esp32Platform::Esp32Platform(HardwareSerial* s) : ArduinoPlatform(s) { } @@ -58,10 +58,9 @@ void Esp32Platform::closeMultiCast() bool Esp32Platform::sendBytesMultiCast(uint8_t * buffer, uint16_t len) { //printHex("<- ",buffer, len); - int result = 0; - result = _udp.beginMulticastPacket(); - result = _udp.write(buffer, len); - result = _udp.endPacket(); + _udp.beginMulticastPacket(); + _udp.write(buffer, len); + _udp.endPacket(); return true; } diff --git a/src/esp32_platform.h b/src/esp32_platform.h index 7cc641a..3c006ba 100644 --- a/src/esp32_platform.h +++ b/src/esp32_platform.h @@ -8,7 +8,7 @@ class Esp32Platform : public ArduinoPlatform { public: Esp32Platform(); - Esp32Platform( HardwareSerial* s); + Esp32Platform(HardwareSerial* s); // ip stuff uint32_t currentIpAddress() override; diff --git a/src/esp_platform.cpp b/src/esp_platform.cpp index 11984ae..f453fea 100644 --- a/src/esp_platform.cpp +++ b/src/esp_platform.cpp @@ -61,10 +61,9 @@ void EspPlatform::closeMultiCast() bool EspPlatform::sendBytesMultiCast(uint8_t * buffer, uint16_t len) { //printHex("<- ",buffer, len); - int result = 0; - result = _udp.beginPacketMulticast(_mulitcastAddr, _mulitcastPort, WiFi.localIP()); - result = _udp.write(buffer, len); - result = _udp.endPacket(); + _udp.beginPacketMulticast(_mulitcastAddr, _mulitcastPort, WiFi.localIP()); + _udp.write(buffer, len); + _udp.endPacket(); return true; } diff --git a/src/esp_platform.h b/src/esp_platform.h index 6fe820d..cd026d8 100644 --- a/src/esp_platform.h +++ b/src/esp_platform.h @@ -8,7 +8,7 @@ class EspPlatform : public ArduinoPlatform { public: EspPlatform(); - EspPlatform( HardwareSerial* s); + EspPlatform(HardwareSerial* s); // ip stuff uint32_t currentIpAddress() override; diff --git a/src/knx/memory.cpp b/src/knx/memory.cpp index 9869fdf..a5945cb 100644 --- a/src/knx/memory.cpp +++ b/src/knx/memory.cpp @@ -2,10 +2,6 @@ #include #include "bits.h" -#ifndef KNX_FLASH_SIZE -# define KNX_FLASH_SIZE 8192 -#endif - Memory::Memory(Platform& platform, DeviceObject& deviceObject) : _platform(platform), _deviceObject(deviceObject) { diff --git a/src/knx/memory.h b/src/knx/memory.h index 06215ef..715c2b4 100644 --- a/src/knx/memory.h +++ b/src/knx/memory.h @@ -9,6 +9,10 @@ #define MAXSAVE 5 #define MAXTABLEOBJ 4 +#ifndef KNX_FLASH_SIZE +# define KNX_FLASH_SIZE 1024 +#endif + class MemoryBlock { public: diff --git a/src/knx_facade.cpp b/src/knx_facade.cpp index 86ebea9..ee92547 100644 --- a/src/knx_facade.cpp +++ b/src/knx_facade.cpp @@ -2,72 +2,72 @@ #include "knx/bits.h" -#ifdef ARDUINO_ARCH_SAMD - // predefined global instance for TP or RF or TP/RF coupler - #if MASK_VERSION == 0x07B0 - KnxFacade knx; - #elif MASK_VERSION == 0x27B0 - KnxFacade knx; - #elif MASK_VERSION == 0x2920 - KnxFacade knx; - #else - #error Mask version not supported on ARDUINO_ARCH_SAMD +#ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE + + #if (defined(ARDUINO_ARCH_STM32) || \ + defined(ARDUINO_ARCH_ESP32) || \ + defined(ARDUINO_ARCH_ESP8266) || \ + defined(ARDUINO_ARCH_SAMD)) + + // Only ESP8266 and ESP32 have this define. For all other platforms this is just empty. + #ifndef ICACHE_RAM_ATTR + #define ICACHE_RAM_ATTR + #endif + + ICACHE_RAM_ATTR void buttonUp() + { + static uint32_t lastpressed=0; + if (millis() - lastpressed > 200){ + knx.toggleProgMode(); + lastpressed = millis(); + } + } #endif -#elif defined(ARDUINO_ARCH_ESP8266) - // predefined global instance for TP or IP or TP/IP coupler - #if MASK_VERSION == 0x07B0 - KnxFacade knx; - #elif MASK_VERSION == 0x57B0 - KnxFacade knx; - #elif MASK_VERSION == 0x091A - KnxFacade knx; - #else - #error Mask version not supported on ARDUINO_ARCH_ESP8266 + #ifdef ARDUINO_ARCH_SAMD + // predefined global instance for TP or RF or TP/RF coupler + #if MASK_VERSION == 0x07B0 + KnxFacade knx(buttonUp); + #elif MASK_VERSION == 0x27B0 + KnxFacade knx(buttonUp); + #elif MASK_VERSION == 0x2920 + KnxFacade knx(buttonUp); + #else + #error "Mask version not supported on ARDUINO_ARCH_SAMD" + #endif + + #elif defined(ARDUINO_ARCH_ESP8266) + // predefined global instance for TP or IP or TP/IP coupler + #if MASK_VERSION == 0x07B0 + KnxFacade knx(buttonUp); + #elif MASK_VERSION == 0x57B0 + KnxFacade knx(buttonUp); + #elif MASK_VERSION == 0x091A + KnxFacade knx(buttonUp); + #else + #error "Mask version not supported on ARDUINO_ARCH_ESP8266" + #endif + + #elif defined(ARDUINO_ARCH_ESP32) + // predefined global instance for TP or IP or TP/IP coupler + #if MASK_VERSION == 0x07B0 + KnxFacade knx(buttonUp); + #elif MASK_VERSION == 0x57B0 + KnxFacade knx(buttonUp); + #elif MASK_VERSION == 0x091A + KnxFacade knx(buttonUp); + #else + #error "Mask version not supported on ARDUINO_ARCH_ESP32" + #endif + + #elif defined(ARDUINO_ARCH_STM32) + #if MASK_VERSION == 0x07B0 + KnxFacade knx(buttonUp); + #else + #error "Mask version not supported on ARDUINO_ARCH_STM32" + #endif + #else // Non-Arduino platforms and Linux platform + // no predefined global instance #endif -#elif defined(ARDUINO_ARCH_ESP32) - // predefined global instance for TP or IP or TP/IP coupler - #if MASK_VERSION == 0x07B0 - KnxFacade knx; - #elif MASK_VERSION == 0x57B0 - KnxFacade knx; - #elif MASK_VERSION == 0x091A - KnxFacade knx; - #else - #error Mask version not supported on ARDUINO_ARCH_ESP8266 - #endif - -#elif defined(ARDUINO_ARCH_STM32) - #if MASK_VERSION == 0x07B0 - KnxFacade knx; - #else - #error Mask version not supported on ARDUINO_ARCH_STM32 - #endif -#else // Non-Arduino platforms and Linux platform - // no predefined global instance -#endif - -// Only ESP8266 and ESP32 have this define. For all other platforms this is just empty. -#ifndef ICACHE_RAM_ATTR - #define ICACHE_RAM_ATTR -#endif - -#if (defined(ARDUINO_ARCH_STM32) || \ - defined(ARDUINO_ARCH_ESP32) || \ - defined(ARDUINO_ARCH_ESP8266) || \ - defined(ARDUINO_ARCH_SAMD)) -ICACHE_RAM_ATTR void buttonUp() -{ - static uint32_t lastpressed=0; - if (millis() - lastpressed > 200){ - knx._toogleProgMode = true; - lastpressed = millis(); - } -} -#elif defined(__linux__) -void buttonUp() -{ - // no de-bounce on linux platform, just satisfy the compiler -} -#endif \ No newline at end of file +#endif // KNX_NO_AUTOMATIC_GLOBAL_INSTANCE diff --git a/src/knx_facade.h b/src/knx_facade.h index d499fe4..27e024a 100644 --- a/src/knx_facade.h +++ b/src/knx_facade.h @@ -10,33 +10,41 @@ #ifdef ARDUINO_ARCH_SAMD #include "samd_platform.h" - void buttonUp(); + #ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE + void buttonUp(); + #endif #elif defined(ARDUINO_ARCH_ESP8266) - #include "esp_platform.h" - void buttonUp(); + #include "esp_platform.h" + #ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE + void buttonUp(); + #endif #elif defined(ARDUINO_ARCH_ESP32) - #define LED_BUILTIN 13 - #include "esp32_platform.h" - void buttonUp(); + #define LED_BUILTIN 13 + #include "esp32_platform.h" + #ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE + void buttonUp(); + #endif #elif defined(ARDUINO_ARCH_STM32) - #include "stm32_platform.h" - void buttonUp(); + #include "stm32_platform.h" + #ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE + void buttonUp(); + #endif #elif __linux__ - #define LED_BUILTIN 0 - #include "linux_platform.h" - void buttonUp(); + #define LED_BUILTIN 0 + #include "linux_platform.h" #else - #define LED_BUILTIN 5 // see GPIO_PinConfig gpioPinConfigs[] - #include "cc1310_platform.h" - extern void buttonUp(); + #define LED_BUILTIN 5 // see GPIO_PinConfig gpioPinConfigs[] + #include "cc1310_platform.h" + #ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE + extern void buttonUp(); + #endif #endif typedef uint8_t* (*SaveRestoreCallback)(uint8_t* buffer); +typedef void (*IsrFunctionPtr)(); template class KnxFacade : private SaveRestore { - friend void buttonUp(); - public: KnxFacade() : _platformPtr(new P()), _bauPtr(new B(*_platformPtr)), _bau(*_bauPtr) { @@ -44,6 +52,19 @@ template class KnxFacade : private SaveRestore _bau.addSaveRestore(this); } + KnxFacade(B& bau) : _bau(bau) + { + manufacturerId(0xfa); + _bau.addSaveRestore(this); + } + + KnxFacade(IsrFunctionPtr buttonISRFunction) : _platformPtr(new P()), _bauPtr(new B(*_platformPtr)), _bau(*_bauPtr) + { + manufacturerId(0xfa); + _bau.addSaveRestore(this); + setButtonISRFunction(buttonISRFunction); + } + virtual ~KnxFacade() { if (_bauPtr) @@ -53,12 +74,6 @@ template class KnxFacade : private SaveRestore delete _platformPtr; } - KnxFacade(B& bau) : _bau(bau) - { - manufacturerId(0xfa); - _bau.addSaveRestore(this); - } - P& platform() { return *_platformPtr; @@ -89,6 +104,14 @@ template class KnxFacade : private SaveRestore _bau.deviceObject().progMode(value); } + /** + * To be called by ISR handling on button press. + */ + void toggleProgMode() + { + _toggleProgMode = true; + } + bool configured() { return _bau.configured(); @@ -181,10 +204,10 @@ template class KnxFacade : private SaveRestore digitalWrite(ledPin(), HIGH - _ledPinActiveOn); } } - if (_toogleProgMode) + if (_toggleProgMode) { progMode(!progMode()); - _toogleProgMode = false; + _toggleProgMode = false; } _bau.loop(); } @@ -216,21 +239,30 @@ template class KnxFacade : private SaveRestore void start() { - pinMode(_ledPin, OUTPUT); + pinMode(ledPin(), OUTPUT); - digitalWrite(_ledPin, HIGH - _ledPinActiveOn); + digitalWrite(ledPin(), HIGH - _ledPinActiveOn); - pinMode(_buttonPin, INPUT_PULLUP); + pinMode(buttonPin(), INPUT_PULLUP); + + if (_progButtonISRFuncPtr) + { + // Workaround for https://github.com/arduino/ArduinoCore-samd/issues/587 + #if (ARDUINO_API_VERSION >= 10200) + attachInterrupt(_buttonPin, _progButtonISRFuncPtr, (PinStatus)_buttonPinInterruptOn); + #else + attachInterrupt(_buttonPin, _progButtonISRFuncPtr, _buttonPinInterruptOn); + #endif + } - // Workaround for https://github.com/arduino/ArduinoCore-samd/issues/587 - #if (ARDUINO_API_VERSION >= 10200) - attachInterrupt(_buttonPin, buttonUp, (PinStatus)_buttonPinInterruptOn); - #else - attachInterrupt(_buttonPin, buttonUp, _buttonPinInterruptOn); - #endif enabled(true); } + void setButtonISRFunction(IsrFunctionPtr progButtonISRFuncPtr) + { + _progButtonISRFuncPtr = progButtonISRFuncPtr; + } + void setSaveCallback(SaveRestoreCallback func) { _saveCallback = func; @@ -303,9 +335,10 @@ template class KnxFacade : private SaveRestore uint32_t _buttonPin = 0; SaveRestoreCallback _saveCallback = 0; SaveRestoreCallback _restoreCallback = 0; - bool _toogleProgMode = false; + volatile bool _toggleProgMode = false; bool _progLedState = false; uint16_t _saveSize = 0; + IsrFunctionPtr _progButtonISRFuncPtr = 0; uint8_t* save(uint8_t* buffer) { @@ -334,46 +367,48 @@ template class KnxFacade : private SaveRestore } }; -#ifdef ARDUINO_ARCH_SAMD - // predefined global instance for TP or RF or TP/RF coupler - #if MASK_VERSION == 0x07B0 - extern KnxFacade knx; - #elif MASK_VERSION == 0x27B0 - extern KnxFacade knx; - #elif MASK_VERSION == 0x2920 - extern KnxFacade knx; - #else - #error "Mask version not supported on ARDUINO_ARCH_SAMD" +#ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE + #ifdef ARDUINO_ARCH_SAMD + // predefined global instance for TP or RF or TP/RF coupler + #if MASK_VERSION == 0x07B0 + extern KnxFacade knx; + #elif MASK_VERSION == 0x27B0 + extern KnxFacade knx; + #elif MASK_VERSION == 0x2920 + extern KnxFacade knx; + #else + #error "Mask version not supported on ARDUINO_ARCH_SAMD" + #endif + #elif defined(ARDUINO_ARCH_ESP8266) + // predefined global instance for TP or IP or TP/IP coupler + #if MASK_VERSION == 0x07B0 + extern KnxFacade knx; + #elif MASK_VERSION == 0x57B0 + extern KnxFacade knx; + #elif MASK_VERSION == 0x091A + extern KnxFacade knx; + #else + #error "Mask version not supported on ARDUINO_ARCH_ESP8266" + #endif + #elif defined(ARDUINO_ARCH_ESP32) + // predefined global instance for TP or IP or TP/IP coupler + #if MASK_VERSION == 0x07B0 + extern KnxFacade knx; + #elif MASK_VERSION == 0x57B0 + extern KnxFacade knx; + #elif MASK_VERSION == 0x091A + extern KnxFacade knx; + #else + #error "Mask version not supported on ARDUINO_ARCH_ESP32" + #endif + #elif defined(ARDUINO_ARCH_STM32) + // predefined global instance for TP only + #if MASK_VERSION == 0x07B0 + extern KnxFacade knx; + #else + #error "Mask version not supported on ARDUINO_ARCH_STM32" + #endif + #else // Non-Arduino platforms and Linux platform + // no predefined global instance #endif -#elif defined(ARDUINO_ARCH_ESP8266) - // predefined global instance for TP or IP or TP/IP coupler - #if MASK_VERSION == 0x07B0 - extern KnxFacade knx; - #elif MASK_VERSION == 0x57B0 - extern KnxFacade knx; - #elif MASK_VERSION == 0x091A - extern KnxFacade knx; - #else - #error "Mask version not supported on ARDUINO_ARCH_ESP8266" - #endif -#elif defined(ARDUINO_ARCH_ESP32) - // predefined global instance for TP or IP or TP/IP coupler - #if MASK_VERSION == 0x07B0 - extern KnxFacade knx; - #elif MASK_VERSION == 0x57B0 - extern KnxFacade knx; - #elif MASK_VERSION == 0x091A - extern KnxFacade knx; - #else - #error "Mask version not supported on ARDUINO_ARCH_ESP32" - #endif -#elif defined(ARDUINO_ARCH_STM32) - // predefined global instance for TP only - #if MASK_VERSION == 0x07B0 - extern KnxFacade knx; - #else - #error Mask version not supported on ARDUINO_ARCH_STM32 - #endif -#else // Non-Arduino platforms and Linux platform - // no predefined global instance -#endif +#endif // KNX_NO_AUTOMATIC_GLOBAL_INSTANCE From cddef776f710b67f7665e92325fedf7a11e509d6 Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Mon, 8 Feb 2021 20:54:49 +0100 Subject: [PATCH 3/3] move MAX,MIN,ABS to bits.h --- examples/knx-linux/main.cpp | 36 ++++++++++++++-------------- src/knx/bits.h | 14 ++++++++++- src/knx/rf_physical_layer_cc1101.cpp | 4 ---- 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/examples/knx-linux/main.cpp b/examples/knx-linux/main.cpp index b48c160..e3d7ed5 100644 --- a/examples/knx-linux/main.cpp +++ b/examples/knx-linux/main.cpp @@ -46,10 +46,10 @@ KnxFacade knx; long lastsend = 0; -#define CURR knx.getGroupObject(1) -#define MAX knx.getGroupObject(2) -#define MIN knx.getGroupObject(3) -#define RESET knx.getGroupObject(4) +#define GO_CURR knx.getGroupObject(1) +#define GO_MAX knx.getGroupObject(2) +#define GO_MIN knx.getGroupObject(3) +#define GO_RESET knx.getGroupObject(4) void measureTemp() { @@ -65,22 +65,22 @@ void measureTemp() // currentValue *= (670433.28 + 273); // currentValue -= 273; println(currentValue); - CURR.value(currentValue); + GO_CURR.value(currentValue); - double max = MAX.value(); + double max = GO_MAX.value(); if (currentValue > max) - MAX.value(currentValue); + GO_MAX.value(currentValue); - if (currentValue < (double)MIN.value()) - MIN.value(currentValue); + if (currentValue < (double)GO_MIN.value()) + GO_MIN.value(currentValue); } void resetCallback(GroupObject& go) { if (go.value()) { - MAX.valueNoSend(-273.0); - MIN.valueNoSend(670433.28); + GO_MAX.valueNoSend(-273.0); + GO_MIN.valueNoSend(670433.28); } } @@ -102,13 +102,13 @@ void setup() if (knx.configured()) { - CURR.dataPointType(Dpt(9, 1)); - MIN.dataPointType(Dpt(9, 1)); - MIN.value(670433.28); - MAX.dataPointType(Dpt(9, 1)); - MAX.valueNoSend(-273.0); - RESET.dataPointType(Dpt(1, 15)); - RESET.callback(resetCallback); + GO_CURR.dataPointType(Dpt(9, 1)); + GO_MIN.dataPointType(Dpt(9, 1)); + GO_MIN.value(670433.28); + GO_MAX.dataPointType(Dpt(9, 1)); + GO_MAX.valueNoSend(-273.0); + GO_RESET.dataPointType(Dpt(1, 15)); + GO_RESET.callback(resetCallback); printf("Timeout: %d\n", knx.paramWord(0)); printf("Zykl. senden: %d\n", knx.paramByte(2)); printf("Min/Max senden: %d\n", knx.paramByte(3)); diff --git a/src/knx/bits.h b/src/knx/bits.h index 1d29279..2a30385 100644 --- a/src/knx/bits.h +++ b/src/knx/bits.h @@ -13,6 +13,18 @@ #define ntohl(x) htonl(x) #endif +#ifndef MIN +#define MIN(a, b) ((a < b) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a, b) ((a > b) ? (a) : (b)) +#endif + +#ifndef ABS +#define ABS(x) ((x > 0) ? (x) : (-x)) +#endif + #if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_STM32) #include #elif defined(ARDUINO_ARCH_ESP8266) @@ -106,4 +118,4 @@ enum ParameterFloatEncodings #undef max #undef min // end of temporary undef -#endif \ No newline at end of file +#endif diff --git a/src/knx/rf_physical_layer_cc1101.cpp b/src/knx/rf_physical_layer_cc1101.cpp index a2ee581..c3de950 100644 --- a/src/knx/rf_physical_layer_cc1101.cpp +++ b/src/knx/rf_physical_layer_cc1101.cpp @@ -12,10 +12,6 @@ #include #include -#define MIN(a, b) ((a < b) ? (a) : (b)) -#define MAX(a, b) ((a > b) ? (a) : (b)) -#define ABS(x) ((x > 0) ? (x) : (-x)) - // Table for encoding 4-bit data into a 8-bit Manchester encoding. const uint8_t RfPhysicalLayerCC1101::manchEncodeTab[16] = {0xAA, // 0x0 Manchester encoded 0xA9, // 0x1 Manchester encoded