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 0000000..ccb1363 Binary files /dev/null and b/examples/knx-demo-diy/knx-demo-diy-tp.knxprod differ 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/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/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; + } + } +} 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/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/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/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 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