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-bme680/knx-bme680.ino b/examples/knx-bme680/knx-bme680.ino index b6deb6d..332bddf 100644 --- a/examples/knx-bme680/knx-bme680.ino +++ b/examples/knx-bme680/knx-bme680.ino @@ -25,7 +25,7 @@ void checkIaqSensorStatus(void); void errLeds(void); uint8_t* saveBme680State(uint8_t* buffer); -uint8_t* loadBme680State(uint8_t* buffer); +const uint8_t* loadBme680State(const uint8_t* buffer); void triggerCallback(GroupObject& go); void updateState(); @@ -53,6 +53,10 @@ void setup(void) wifiManager.autoConnect("knx-bme680"); #endif + // set save and restore callbacks + knx.setSaveCallback(saveBme680State); + knx.setRestoreCallback(loadBme680State); + // read adress table, association table, groupobject table and parameters from eeprom knx.readMemory(); @@ -60,7 +64,6 @@ void setup(void) if(knx.configured()) goTriggerSample.callback(triggerCallback); - // Configure Wire pins before this call if needed. Wire.begin(); // depends on sensor board. Try BME680_I2C_ADDR_PRIMARY if it doen't work. @@ -87,9 +90,6 @@ void setup(void) BSEC_OUTPUT_GAS_PERCENTAGE }; - knx.setSaveCallback(saveBme680State); - knx.setRestoreCallback(loadBme680State); - if (knx.configured()) { cyclSend = knx.paramInt(0); @@ -214,7 +214,7 @@ void errLeds(void) delay(100); } -uint8_t* loadBme680State(uint8_t* buffer) +const uint8_t* loadBme680State(const uint8_t* buffer) { // Existing state in EEPROM Serial.println("Reading state from EEPROM"); diff --git a/examples/knx-demo-smal-go/knx-demo.ino b/examples/knx-demo-smal-go/knx-demo.ino new file mode 100644 index 0000000..52fb386 --- /dev/null +++ b/examples/knx-demo-smal-go/knx-demo.ino @@ -0,0 +1,129 @@ +#include + +#ifdef ARDUINO_ARCH_ESP8266 +#include +#endif + +/***************************************** + * changes necessary for SMALL_GROUPOBJECT + * are commented with //** + * This project can be used with any + * of the knxprod files of the original + * knx-demo project. + *****************************************/ + +// 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) + +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, DPT_Value_Temp); //** each value access needs to done with according DPT parameter + + if (currentValue > maxValue) + { + maxValue = currentValue; + goMax.value(maxValue, DPT_Value_Temp); //** each value access needs to done with according DPT parameter + } + + if (currentValue < minValue) + { + minValue = currentValue; + goMin.value(minValue, DPT_Value_Temp); //** each value access needs to done with according DPT parameter + } +} + +// callback from reset-GO +void resetCallback(GroupObject& go) +{ + //** callbacks are now handled in the class, not per instance, + //** this means, we have to check, which GroupObject is calling back + if (go.asap() == goReset.asap()) + { + if (go.value(DPT_Trigger)) //** each value access needs to done with according DPT parameter + { + maxValue = 0; + minValue = 10000; + } + } +} + +void setup() +{ + Serial.begin(115200); + ArduinoPlatform::SerialDebug = &Serial; + + 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 + GroupObject::classCallback(resetCallback); //** callbacks are now handled per class, not per instance + //** there is no global assignment of DPT for GroupObjects + // 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); + // Is the interrup created in RISING or FALLING signal? Default is RISING + // knx.buttonPinInterruptOn(FALLING); + + // 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(); +} \ No newline at end of file diff --git a/examples/knx-demo-smal-go/platformio.ini b/examples/knx-demo-smal-go/platformio.ini new file mode 100644 index 0000000..a919602 --- /dev/null +++ b/examples/knx-demo-smal-go/platformio.ini @@ -0,0 +1,120 @@ +;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 = . + +;--- SAMD -------------------------------------------------- +; SMALL_GROUPOBJECT just tested with TP on SAMD, but should work also in other environments +[env:zeroUSB] +platform = atmelsam +board = zeroUSB +framework = arduino +; We consider that the this projects is opened within its project directory +; while working with VS Code. +lib_extra_dirs = ../../../ + +lib_deps = + SPI + https://github.com/thelsing/FlashStorage.git + knx + +build_flags = + -DMASK_VERSION=0x07B0 + -DSMALL_GROUPOBJECT + -Wno-unknown-pragmas + +; [env:adafruit_feather_m0_rf] +; platform = atmelsam +; board = adafruit_feather_m0 +; framework = arduino +; ; We consider that the this projects is opened within its project directory +; ; while working with VS Code. +; lib_extra_dirs = ../../../ + +; lib_deps = +; SPI +; https://github.com/thelsing/FlashStorage.git +; knx + +; build_flags = +; -DMASK_VERSION=0x27B0 +; -Wno-unknown-pragmas +;----------------------------------------------------------- + + +;--- ESP8266 ----------------------------------------------- +#[env:nodemcuv2_ip] +#platform = espressif8266 +#board = nodemcuv2 +#framework = arduino +; We consider that the this projects is opened within its project directory +; while working with VS Code. +#lib_extra_dirs = ../../../ + +#lib_deps = +# WifiManager +# knx + +#build_flags = +# -DMASK_VERSION=0x57B0 +# -Wno-unknown-pragmas + +; [env:nodemcuv2_tp] +; platform = espressif8266 +; board = nodemcuv2 +; framework = arduino +; ; We consider that the this projects is opened within its project directory +; ; while working with VS Code. +; lib_extra_dirs = ../../../ + +; lib_deps = +; WifiManager +; knx + +; build_flags = +; -DMASK_VERSION=0x07B0 +; -Wno-unknown-pragmas + +;--------------------------------------------------------- + + +;--- ESP32 ----------------------------------------------- +; [env:esp32dev_ip] +; 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=0x57B0 +; -Wno-unknown-pragmas + +; [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 diff --git a/examples/knx-demo/knx-demo.ino b/examples/knx-demo/knx-demo.ino index f062daf..a72b214 100644 --- a/examples/knx-demo/knx-demo.ino +++ b/examples/knx-demo/knx-demo.ino @@ -1,6 +1,7 @@ +#include #include -#ifdef ARDUINO_ARCH_ESP8266 +#if MASK_VERSION != 0x07B0 && (defined ARDUINO_ARCH_ESP8266 || defined ARDUINO_ARCH_ESP32) #include #endif @@ -59,7 +60,7 @@ void setup() randomSeed(millis()); -#ifdef ARDUINO_ARCH_ESP8266 +#if MASK_VERSION != 0x07B0 && (defined ARDUINO_ARCH_ESP8266 || defined ARDUINO_ARCH_ESP32) WiFiManager wifiManager; wifiManager.autoConnect("knx-demo"); #endif diff --git a/examples/knx-demo/platformio-ci.ini b/examples/knx-demo/platformio-ci.ini index e2b04e2..d2056ac 100644 --- a/examples/knx-demo/platformio-ci.ini +++ b/examples/knx-demo/platformio-ci.ini @@ -40,7 +40,7 @@ build_flags = -DUSE_DATASECURE [env:nodemcuv2_tp] -platform = espressif8266 +platform = espressif8266@^2 board = nodemcuv2 framework = arduino lib_deps = @@ -61,6 +61,7 @@ platform = espressif32 board = esp32dev framework = arduino lib_deps = + https://github.com/tzapu/WiFiManager.git knx build_flags = diff --git a/examples/knx-demo/platformio.ini b/examples/knx-demo/platformio.ini index edd9954..a706ccb 100644 --- a/examples/knx-demo/platformio.ini +++ b/examples/knx-demo/platformio.ini @@ -51,7 +51,7 @@ build_flags = # -Wno-unknown-pragmas [env:nodemcuv2_tp] -platform = espressif8266 +platform = espressif8266@^2 board = nodemcuv2 framework = arduino ; We consider that the this projects is opened within its project directory @@ -59,7 +59,7 @@ framework = arduino lib_extra_dirs = ../../../ lib_deps = - WifiManager + WifiManager@0.15.0 knx build_flags = @@ -79,6 +79,7 @@ framework = arduino lib_extra_dirs = ../../../ lib_deps = + https://github.com/tzapu/WiFiManager.git knx build_flags = diff --git a/examples/knx-linux-coupler/fdsk.cpp b/examples/knx-linux-coupler/fdsk.cpp index 8d9b28c..bb62d57 100644 --- a/examples/knx-linux-coupler/fdsk.cpp +++ b/examples/knx-linux-coupler/fdsk.cpp @@ -82,7 +82,7 @@ int FdskCalculator::toBase32(uint8_t* in, long length, uint8_t*& out, bool usePa int next = 1; int bitsLeft = 8; - while (count < bufSize && (bitsLeft > 0 || next < length)) + while (bitsLeft > 0 || next < length) { if (bitsLeft < 5) { diff --git a/examples/knx-linux/fdsk.cpp b/examples/knx-linux/fdsk.cpp index 8d9b28c..bb62d57 100644 --- a/examples/knx-linux/fdsk.cpp +++ b/examples/knx-linux/fdsk.cpp @@ -82,7 +82,7 @@ int FdskCalculator::toBase32(uint8_t* in, long length, uint8_t*& out, bool usePa int next = 1; int bitsLeft = 8; - while (count < bufSize && (bitsLeft > 0 || next < length)) + while (bitsLeft > 0 || next < length) { if (bitsLeft < 5) { 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; } } } diff --git a/examples/knx-usb/platformio-ci.ini b/examples/knx-usb/platformio-ci.ini index 4e3322b..ce01930 100644 --- a/examples/knx-usb/platformio-ci.ini +++ b/examples/knx-usb/platformio-ci.ini @@ -8,7 +8,7 @@ ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [env:adafruit_feather_m0] -platform = atmelsam +platform = atmelsam@6.0.1 board = adafruit_feather_m0 framework = arduino diff --git a/examples/knx-usb/platformio.ini b/examples/knx-usb/platformio.ini index c4b69f1..2d7a710 100644 --- a/examples/knx-usb/platformio.ini +++ b/examples/knx-usb/platformio.ini @@ -13,7 +13,7 @@ libdeps_dir = /tmp/libdeps [env:adafruit_feather_m0] -platform = atmelsam +platform = atmelsam@6.0.1 board = adafruit_feather_m0 framework = arduino ; We consider that the this projects is opened within its project directory @@ -27,7 +27,7 @@ board_build.usb_product="KNX RF - USB Interface" lib_deps = SPI - Adafruit TinyUSB Library + Adafruit TinyUSB Library@0.7.1 https://github.com/thelsing/FlashStorage.git knx diff --git a/src/arduino_platform.cpp b/src/arduino_platform.cpp index 65ec833..8c58dfb 100644 --- a/src/arduino_platform.cpp +++ b/src/arduino_platform.cpp @@ -2,9 +2,17 @@ #include "knx/bits.h" #include +#ifndef KNX_NO_SPI #include +#endif +#ifndef KNX_NO_PRINT Stream* ArduinoPlatform::SerialDebug = &Serial; +#endif + +ArduinoPlatform::ArduinoPlatform() : _knxSerial(nullptr) +{ +} ArduinoPlatform::ArduinoPlatform(HardwareSerial* knxSerial) : _knxSerial(knxSerial) { @@ -27,7 +35,8 @@ void ArduinoPlatform::fatalError() void ArduinoPlatform::knxUart( HardwareSerial* serial ) { - closeUart(); + if (_knxSerial) + closeUart(); _knxSerial = serial; setupUart(); } @@ -94,6 +103,7 @@ size_t ArduinoPlatform::readBytesUart(uint8_t *buffer, size_t length) return length; } +#ifndef KNX_NO_SPI void ArduinoPlatform::setupSpi() { SPI.begin(); @@ -111,7 +121,9 @@ int ArduinoPlatform::readWriteSpi(uint8_t *data, size_t len) SPI.transfer(data, len); return 0; } +#endif +#ifndef KNX_NO_PRINT void printUint64(uint64_t value, int base = DEC) { char buf[8 * sizeof(uint64_t) + 1]; @@ -284,3 +296,4 @@ void println(void) { ArduinoPlatform::SerialDebug->println(); } +#endif // KNX_NO_PRINT diff --git a/src/arduino_platform.h b/src/arduino_platform.h index 10c10e6..3ef5047 100644 --- a/src/arduino_platform.h +++ b/src/arduino_platform.h @@ -2,11 +2,10 @@ #include "Arduino.h" -extern Stream& _serialDBG; - class ArduinoPlatform : public Platform { public: + ArduinoPlatform(); ArduinoPlatform(HardwareSerial* knxSerial); // basic stuff @@ -24,11 +23,14 @@ class ArduinoPlatform : public Platform virtual size_t readBytesUart(uint8_t* buffer, size_t length); //spi +#ifndef KNX_NO_SPI void setupSpi() override; void closeSpi() override; int readWriteSpi (uint8_t *data, size_t len) override; - +#endif +#ifndef KNX_NO_PRINT static Stream* SerialDebug; +#endif protected: HardwareSerial* _knxSerial; diff --git a/src/cc1310_platform.cpp b/src/cc1310_platform.cpp index ae7bc7b..18e1289 100644 --- a/src/cc1310_platform.cpp +++ b/src/cc1310_platform.cpp @@ -134,6 +134,7 @@ void delayMicroseconds (unsigned int howLong) ClockP_usleep(howLong); } +#ifndef KNX_NO_PRINT size_t write(uint8_t c) { #if defined(PRINT_UART) @@ -402,6 +403,7 @@ void println(double num) // default: print 10 digits println(num, 10); } +#endif // KNX_NO_PRINT uint32_t digitalRead(uint32_t dwPin) { diff --git a/src/esp32_platform.cpp b/src/esp32_platform.cpp index cc902dd..6f49934 100644 --- a/src/esp32_platform.cpp +++ b/src/esp32_platform.cpp @@ -6,7 +6,10 @@ #include "knx/bits.h" -Esp32Platform::Esp32Platform() : ArduinoPlatform(&Serial1) +Esp32Platform::Esp32Platform() +#ifndef KNX_NO_DEFAULT_UART + : ArduinoPlatform(&Serial1) +#endif { } @@ -34,6 +37,14 @@ void Esp32Platform::macAddress(uint8_t * addr) esp_wifi_get_mac(WIFI_IF_STA, addr); } +uint32_t Esp32Platform::uniqueSerialNumber() +{ + uint64_t chipid = ESP.getEfuseMac(); + uint32_t upperId = (chipid >> 32) & 0xFFFFFFFF; + uint32_t lowerId = (chipid & 0xFFFFFFFF); + return (upperId ^ lowerId); +} + void Esp32Platform::restart() { println("restart"); @@ -81,6 +92,18 @@ int Esp32Platform::readBytesMultiCast(uint8_t * buffer, uint16_t maxLen) return len; } +bool Esp32Platform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len) +{ + IPAddress ucastaddr(htonl(addr)); + println("sendBytesUniCast endPacket fail"); + if(_udp.beginPacket(ucastaddr, port) == 1) { + _udp.write(buffer, len); + if(_udp.endPacket() == 0) println("sendBytesUniCast endPacket fail"); + } + else println("sendBytesUniCast beginPacket fail"); + return true; +} + uint8_t * Esp32Platform::getEepromBuffer(uint16_t size) { EEPROM.begin(size); @@ -89,6 +112,7 @@ uint8_t * Esp32Platform::getEepromBuffer(uint16_t size) void Esp32Platform::commitToEeprom() { + EEPROM.getDataPtr(); // trigger dirty flag in EEPROM lib to make sure data will be written to flash EEPROM.commit(); } diff --git a/src/esp32_platform.h b/src/esp32_platform.h index 3c006ba..07b0d75 100644 --- a/src/esp32_platform.h +++ b/src/esp32_platform.h @@ -16,6 +16,9 @@ public: uint32_t currentDefaultGateway() override; void macAddress(uint8_t* addr) override; + // unique serial number + uint32_t uniqueSerialNumber() override; + // basic stuff void restart(); @@ -25,6 +28,9 @@ public: bool sendBytesMultiCast(uint8_t* buffer, uint16_t len) override; int readBytesMultiCast(uint8_t* buffer, uint16_t maxLen) override; + //unicast + bool sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len) override; + //memory uint8_t* getEepromBuffer(uint16_t size); void commitToEeprom(); diff --git a/src/esp_platform.cpp b/src/esp_platform.cpp index f453fea..14a7c58 100644 --- a/src/esp_platform.cpp +++ b/src/esp_platform.cpp @@ -7,7 +7,10 @@ #include "knx/bits.h" -EspPlatform::EspPlatform() : ArduinoPlatform(&Serial) +EspPlatform::EspPlatform() +#ifndef KNX_NO_DEFAULT_UART + : ArduinoPlatform(&Serial) +#endif { } @@ -35,6 +38,11 @@ void EspPlatform::macAddress(uint8_t * addr) wifi_get_macaddr(STATION_IF, addr); } +uint32_t EspPlatform::uniqueSerialNumber() +{ + return ESP.getChipId(); +} + void EspPlatform::restart() { println("restart"); @@ -43,9 +51,9 @@ void EspPlatform::restart() void EspPlatform::setupMultiCast(uint32_t addr, uint16_t port) { - _mulitcastAddr = htonl(addr); - _mulitcastPort = port; - IPAddress mcastaddr(_mulitcastAddr); + _multicastAddr = htonl(addr); + _multicastPort = port; + IPAddress mcastaddr(_multicastAddr); Serial.printf("setup multicast addr: %s port: %d ip: %s\n", mcastaddr.toString().c_str(), port, WiFi.localIP().toString().c_str()); @@ -61,7 +69,7 @@ void EspPlatform::closeMultiCast() bool EspPlatform::sendBytesMultiCast(uint8_t * buffer, uint16_t len) { //printHex("<- ",buffer, len); - _udp.beginPacketMulticast(_mulitcastAddr, _mulitcastPort, WiFi.localIP()); + _udp.beginPacketMulticast(_multicastAddr, _multicastPort, WiFi.localIP()); _udp.write(buffer, len); _udp.endPacket(); return true; @@ -84,6 +92,18 @@ int EspPlatform::readBytesMultiCast(uint8_t * buffer, uint16_t maxLen) return len; } +bool EspPlatform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len) +{ + IPAddress ucastaddr(htonl(addr)); + println("sendBytesUniCast endPacket fail"); + if(_udp.beginPacket(ucastaddr, port) == 1) { + _udp.write(buffer, len); + if(_udp.endPacket() == 0) println("sendBytesUniCast endPacket fail"); + } + else println("sendBytesUniCast beginPacket fail"); + return true; +} + uint8_t * EspPlatform::getEepromBuffer(uint16_t size) { EEPROM.begin(size); diff --git a/src/esp_platform.h b/src/esp_platform.h index cd026d8..c535bae 100644 --- a/src/esp_platform.h +++ b/src/esp_platform.h @@ -16,6 +16,9 @@ class EspPlatform : public ArduinoPlatform uint32_t currentDefaultGateway() override; void macAddress(uint8_t* addr) override; + // unique serial number + uint32_t uniqueSerialNumber() override; + // basic stuff void restart(); @@ -25,13 +28,16 @@ class EspPlatform : public ArduinoPlatform bool sendBytesMultiCast(uint8_t* buffer, uint16_t len) override; int readBytesMultiCast(uint8_t* buffer, uint16_t maxLen) override; + //unicast + bool sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len) override; + //memory uint8_t* getEepromBuffer(uint16_t size); void commitToEeprom(); private: WiFiUDP _udp; - uint32_t _mulitcastAddr; - uint16_t _mulitcastPort; + uint32_t _multicastAddr; + uint16_t _multicastPort; }; #endif diff --git a/src/knx/address_table_object.h b/src/knx/address_table_object.h index 20d15f4..a8990c2 100644 --- a/src/knx/address_table_object.h +++ b/src/knx/address_table_object.h @@ -2,9 +2,9 @@ #include "table_object.h" /** - * This class represents the group address table. It provides a mapping between tranport layer + * This class represents the group address table. It provides a mapping between transport layer * service access points (TSAP) and group addresses. The TSAP can be imagined as an index to the array - * of group adresses. + * of group addresses. * * See section 4.10 of @cite knx:3/5/1 for further details. * It implements realisation type 7 (see section 4.10.7 of @cite knx:3/5/1). @@ -13,9 +13,9 @@ class AddressTableObject : public TableObject { public: /** - * The contructor. + * The constructor. * - * @param memory This parameter is only passed to the custructor of TableObject an not used by this class. + * @param memory This parameter is only passed to the constructor of TableObject and is not used by this class. */ AddressTableObject(Memory& memory); const uint8_t* restore(const uint8_t* buffer) override; @@ -35,7 +35,7 @@ class AddressTableObject : public TableObject /** * Get the TSAP mapped to a group address. * - * @param groupAddress the group address of whicht to get the TSAP for. + * @param groupAddress the group address of which to get the TSAP for. * * @return the TSAP if found or zero if no tsap was found. */ diff --git a/src/knx/application_layer.cpp b/src/knx/application_layer.cpp index 7103974..581f8bd 100644 --- a/src/knx/application_layer.cpp +++ b/src/knx/application_layer.cpp @@ -66,7 +66,7 @@ void ApplicationLayer::dataGroupIndication(HopCountType hopType, Priority priori case GroupValueWrite: _bau.groupValueWriteIndication(asap, priority, hopType, secCtrl, data, len); default: - /* other apdutypes ar not valid here. If the appear do nothing */ + /* other apdutypes are not valid here. If they appear do nothing */ break; } } @@ -908,7 +908,7 @@ void ApplicationLayer::groupValueSend(ApduType type, AckType ack, uint16_t asap, uint8_t* apdudata = apdu.data(); if (dataLength == 0) { - // data size is six bit or less. So store int first byte + // data size is six bit or less. So store in first byte *apdudata &= ~0x3f; *apdudata |= (*data & 0x3f); } @@ -916,7 +916,7 @@ void ApplicationLayer::groupValueSend(ApduType type, AckType ack, uint16_t asap, { memcpy(apdudata + 1, data, dataLength); } - // no need to check if there is a tsap. This is a response, so the read got trough + // no need to check if there is a tsap. This is a response, so the read got through uint16_t tsap = (uint16_t)_assocTable->translateAsap(asap); dataGroupRequest(ack, hopType, priority, tsap, apdu, secCtrl); dataGroupIndication(hopType, priority, tsap, apdu, secCtrl); @@ -1129,7 +1129,7 @@ void ApplicationLayer::individualIndication(HopCountType hopType, Priority prior _bau.keyWriteAppLayerConfirm(priority, hopType, tsap, secCtrl, data[1]); break; default: - print("Indiviual-indication: unhandled APDU-Type: "); + print("Individual-indication: unhandled APDU-Type: "); println(apdu.type()); } } @@ -1240,7 +1240,7 @@ void ApplicationLayer::individualConfirm(AckType ack, HopCountType hopType, Prio _bau.keyWriteResponseConfirm(ack, priority, hopType, tsap, secCtrl, data[1], status); break; default: - print("Indiviual-confirm: unhandled APDU-Type: "); + print("Individual-confirm: unhandled APDU-Type: "); println(apdu.type()); } } diff --git a/src/knx/application_layer.h b/src/knx/application_layer.h index ae6da70..c2d4578 100644 --- a/src/knx/application_layer.h +++ b/src/knx/application_layer.h @@ -11,7 +11,7 @@ class TransportLayer; * This is an implementation of the application layer as specified in @cite knx:3/5/1. * It provides methods for the BusAccessUnit to do different things and translates this * call to an APDU and calls the correct method of the TransportLayer. - * It also takes calls from TransportLayer, decodes the submitted APDU and calls the coresponding + * It also takes calls from TransportLayer, decodes the submitted APDU and calls the corresponding * methods of the BusAccessUnit class. */ class ApplicationLayer @@ -31,12 +31,12 @@ class ApplicationLayer void associationTableObject(AssociationTableObject& assocTable); // from transport layer - // Note: without data secure feature, the application layer is just used with SecurtyControl.dataSecurity = None + // Note: without data secure feature, the application layer is just used with SecurityControl.dataSecurity = None // hooks that can be implemented by derived class (e.g. SecureApplicationLayer) #pragma region Transport - Layer - Callbacks /** - * Somebody send us an APDU via multicast communiation. See 3.2 of @cite knx:3/3/4. + * Somebody send us an APDU via multicast communication. See 3.2 of @cite knx:3/3/4. * See also ApplicationLayer::dataGroupConfirm and TransportLayer::dataGroupRequest. * This method is called by the TransportLayer. * @@ -51,7 +51,7 @@ class ApplicationLayer */ virtual void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu); /** - * Report the status of an APDU that we sent via multicast communiation back to us. See 3.2 of @cite knx:3/3/4. + * Report the status of an APDU that we sent via multicast communication back to us. See 3.2 of @cite knx:3/3/4. * See also ApplicationLayer::dataGroupConfirm and TransportLayer::dataGroupRequest. This method is called by * the TransportLayer. * diff --git a/src/knx/application_program_object.cpp b/src/knx/application_program_object.cpp index e6628d2..598d0fe 100644 --- a/src/knx/application_program_object.cpp +++ b/src/knx/application_program_object.cpp @@ -29,6 +29,29 @@ ApplicationProgramObject::ApplicationProgramObject(Memory& memory) TableObject::initializeProperties(sizeof(properties), properties); } +uint8_t* ApplicationProgramObject::save(uint8_t* buffer) +{ + uint8_t programVersion[5]; + property(PID_PROG_VERSION)->read(programVersion); + buffer = pushByteArray(programVersion, 5, buffer); + + return TableObject::save(buffer); +} + +const uint8_t* ApplicationProgramObject::restore(const uint8_t* buffer) +{ + uint8_t programVersion[5]; + buffer = popByteArray(programVersion, 5, buffer); + property(PID_PROG_VERSION)->write(programVersion); + + return TableObject::restore(buffer); +} + +uint16_t ApplicationProgramObject::saveSize() +{ + return TableObject::saveSize() + 5; // sizeof(programVersion) +} + uint8_t * ApplicationProgramObject::data(uint32_t addr) { return TableObject::data() + addr; diff --git a/src/knx/application_program_object.h b/src/knx/application_program_object.h index 87b743d..c989a1c 100644 --- a/src/knx/application_program_object.h +++ b/src/knx/application_program_object.h @@ -7,6 +7,9 @@ class ApplicationProgramObject : public TableObject { public: ApplicationProgramObject(Memory& memory); + uint8_t* save(uint8_t* buffer) override; + const uint8_t* restore(const uint8_t* buffer) override; + uint16_t saveSize() override; uint8_t* data(uint32_t addr); uint8_t getByte(uint32_t addr); uint16_t getWord(uint32_t addr); diff --git a/src/knx/association_table_object.cpp b/src/knx/association_table_object.cpp index c78b244..0880fc7 100644 --- a/src/knx/association_table_object.cpp +++ b/src/knx/association_table_object.cpp @@ -25,7 +25,7 @@ uint16_t AssociationTableObject::entryCount() uint16_t AssociationTableObject::getTSAP(uint16_t idx) { - if (idx < 0 || idx >= entryCount()) + if (idx >= entryCount()) return 0; return ntohs(_tableData[2 * idx + 1]); @@ -33,7 +33,7 @@ uint16_t AssociationTableObject::getTSAP(uint16_t idx) uint16_t AssociationTableObject::getASAP(uint16_t idx) { - if (idx < 0 || idx >= entryCount()) + if (idx >= entryCount()) return 0; return ntohs(_tableData[2 * idx + 2]); diff --git a/src/knx/bau_systemB.cpp b/src/knx/bau_systemB.cpp index f6795ec..3d1bfa7 100644 --- a/src/knx/bau_systemB.cpp +++ b/src/knx/bau_systemB.cpp @@ -62,7 +62,7 @@ uint8_t BauSystemB::checkmasterResetValidity(EraseCode eraseCode, uint8_t channe case EraseCode::ResetIA: { // TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER) - println("ResetAP requested. Not implemented yet."); + println("ResetIA requested. Not implemented yet."); return successCode; } case EraseCode::ResetLinks: @@ -500,7 +500,7 @@ bool BauSystemB::restartRequest(uint16_t asap, const SecurityControl secCtrl) void BauSystemB::connectConfirm(uint16_t tsap) { - if (_restartState == Connecting && tsap >= 0) + if (_restartState == Connecting) { /* restart connection is confirmed, go to the next state */ _restartState = Connected; @@ -532,7 +532,7 @@ void BauSystemB::nextRestartState() } break; case Restarted: - /* restart is finished, we send a discommect */ + /* restart is finished, we send a disconnect */ if (millis() - _restartDelay > 30) { applicationLayer().disconnectRequest(SystemPriority); diff --git a/src/knx/bau_systemB_device.cpp b/src/knx/bau_systemB_device.cpp index 43d70f7..37289af 100644 --- a/src/knx/bau_systemB_device.cpp +++ b/src/knx/bau_systemB_device.cpp @@ -114,9 +114,13 @@ void BauSystemBDevice::updateGroupObject(GroupObject & go, uint8_t * data, uint8 memcpy(goData, data, length); go.commFlag(Updated); +#ifdef SMALL_GROUPOBJECT + GroupObject::processClassCallback(go); +#else GroupObjectUpdatedHandler handler = go.callback(); if (handler) handler(go); +#endif } bool BauSystemBDevice::configured() diff --git a/src/knx/bits.cpp b/src/knx/bits.cpp index 0a668f7..9b04145 100644 --- a/src/knx/bits.cpp +++ b/src/knx/bits.cpp @@ -8,6 +8,7 @@ const uint8_t* popByte(uint8_t& b, const uint8_t* data) return data; } +#ifndef KNX_NO_PRINT void printHex(const char* suffix, const uint8_t *data, size_t length, bool newline) { print(suffix); @@ -21,6 +22,7 @@ void printHex(const char* suffix, const uint8_t *data, size_t length, bool newli println(); } } +#endif const uint8_t* popWord(uint16_t& w, const uint8_t* data) { diff --git a/src/knx/bits.h b/src/knx/bits.h index c7fee15..b513ef9 100644 --- a/src/knx/bits.h +++ b/src/knx/bits.h @@ -63,6 +63,7 @@ typedef void (*voidFuncPtr)(void); void attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode); #endif +#ifndef KNX_NO_PRINT void print(const char[]); void print(char); void print(unsigned char, int = DEC); @@ -87,6 +88,11 @@ void println(double); void println(void); void printHex(const char* suffix, const uint8_t *data, size_t length, bool newline = true); +#else +#define print(...) do {} while(0) +#define println(...) do {} while(0) +#define printHex(...) do {} while(0) +#endif const uint8_t* popByte(uint8_t& b, const uint8_t* data); const uint8_t* popWord(uint16_t& w, const uint8_t* data); diff --git a/src/knx/config.h b/src/knx/config.h index 0b3ce64..f937510 100644 --- a/src/knx/config.h +++ b/src/knx/config.h @@ -60,6 +60,22 @@ // Define via a compiler -D flag if required // #define USE_DATASECURE +// option to have GroupObjects (KO in German) use 8 bytes mangement information RAM instead of 19 bytes +// see knx-demo-small-go for example +// this option might be also set via compiler flag -DSMALL_GROUPOBJECT if required +//#define SMALL_GROUPOBJECT + +// Some defines to reduce footprint +// Do not perform conversion from KNXValue(const char*) to other types, it mainly avoids the expensive strtod +//#define KNX_NO_STRTOx_CONVERSION +// Do not print messages +//#define KNX_NO_PRINT +// Do not use SPI (Arduino variants) +//#define KNX_NO_SPI +// Do not use the default UART (Arduino variants), it must be defined by ArduinoPlatform::knxUart +// (combined with other flags (HWSERIAL_NONE for stm32) - avoid allocation of RX/TX buffers for all serial lines) +//#define KNX_NO_DEFAULT_UART + #endif #if !defined(MASK_VERSION) diff --git a/src/knx/data_property.cpp b/src/knx/data_property.cpp index 1b68565..1bf0223 100644 --- a/src/knx/data_property.cpp +++ b/src/knx/data_property.cpp @@ -49,7 +49,7 @@ uint8_t DataProperty::write(uint16_t start, uint8_t count, const uint8_t* data) start -= 1; if (start + count > _currentElements) { - //reallocate memory for _data + // reallocate memory for _data uint8_t* oldData = _data; size_t oldDataSize = _currentElements * ElementSize(); diff --git a/src/knx/device_object.h b/src/knx/device_object.h index 092fdfd..a7d80d4 100644 --- a/src/knx/device_object.h +++ b/src/knx/device_object.h @@ -39,5 +39,5 @@ public: uint8_t defaultHopCount(); private: uint8_t _prgMode = 0; - uint16_t _ownAddress = 0; + uint16_t _ownAddress = 65535; // 15.15.255; }; diff --git a/src/knx/dpt.h b/src/knx/dpt.h index 5fc45fe..f6dcef3 100644 --- a/src/knx/dpt.h +++ b/src/knx/dpt.h @@ -357,6 +357,7 @@ #define DPT_FlaggedScaling Dpt(239, 1) #define DPT_CombinedPosition Dpt(240, 800) #define DPT_StatusSAB Dpt(241, 800) +#define DPT_Colour_RGBW Dpt(251, 600) class Dpt { diff --git a/src/knx/dptconvert.cpp b/src/knx/dptconvert.cpp index ea55d73..6fc644f 100644 --- a/src/knx/dptconvert.cpp +++ b/src/knx/dptconvert.cpp @@ -22,12 +22,6 @@ int KNX_Decode_Value(uint8_t* payload, size_t payload_length, const Dpt& datatyp if (datatype.mainGroup == 2 && datatype.subGroup >= 1 && datatype.subGroup <= 12 && datatype.index <= 1) return busValueToBinaryControl(payload, payload_length, datatype, value); // DPT 3.* - Step Control - if (datatype.mainGroup == 3 && datatype.subGroup >= 7 && datatype.subGroup <= 8 && datatype.index <= 1) - return busValueToStepControl(payload, payload_length, datatype, value); - // DPT 4.* - Character// DPT 2.* - Binary Control - if (datatype.mainGroup == 2 && datatype.subGroup >= 1 && datatype.subGroup <= 12 && datatype.index <= 1) - return busValueToBinaryControl(payload, payload_length, datatype, value); - // DPT 3.* - Step Control if (datatype.mainGroup == 3 && datatype.subGroup >= 7 && datatype.subGroup <= 8 && datatype.index <= 1) return busValueToStepControl(payload, payload_length, datatype, value); // DPT 4.* - Character @@ -42,8 +36,8 @@ int KNX_Decode_Value(uint8_t* payload, size_t payload_length, const Dpt& datatyp // DPT 6.020 - Status with Mode if (datatype.mainGroup == 6 && datatype.subGroup == 20 && datatype.index <= 5) return busValueToStatusAndMode(payload, payload_length, datatype, value); - // DPT 7.001/7.010/7.011/7.012/7.013 - Unsigned 16 Bit Integer - if (datatype.mainGroup == 7 && (datatype.subGroup == 1 || (datatype.subGroup >= 10 && datatype.subGroup <= 13)) && !datatype.index) + // DPT 7.001/7.010/7.011/7.012/7.013/7.600 - Unsigned 16 Bit Integer + if (datatype.mainGroup == 7 && (datatype.subGroup == 1 || (datatype.subGroup >= 10 && datatype.subGroup <= 13) || (datatype.subGroup == 600)) && !datatype.index) return busValueToUnsigned16(payload, payload_length, datatype, value); // DPT 7.002-DPT 7.007 - Time Period if (datatype.mainGroup == 7 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index) @@ -55,108 +49,7 @@ int KNX_Decode_Value(uint8_t* payload, size_t payload_length, const Dpt& datatyp if (datatype.mainGroup == 8 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index) return busValueToTimeDelta(payload, payload_length, datatype, value); // DPT 9.* - 16 Bit Float - if (datatype.mainGroup == 9 && ((datatype.subGroup >= 1 && datatype.subGroup <= 11 && datatype.subGroup != 9) || (datatype.subGroup >= 20 && datatype.subGroup <= 28)) && !datatype.index) - return busValueToFloat16(payload, payload_length, datatype, value); - // DPT 10.* - Time and Weekday - if (datatype.mainGroup == 10 && datatype.subGroup == 1 && datatype.index <= 1) - return busValueToTime(payload, payload_length, datatype, value); - // DPT 11.* - Date - if (datatype.mainGroup == 11 && datatype.subGroup == 1 && !datatype.index) - return busValueToDate(payload, payload_length, datatype, value); - // DPT 12.* - Unsigned 32 Bit Integer - if (datatype.mainGroup == 12 && datatype.subGroup == 1 && !datatype.index) - return busValueToUnsigned32(payload, payload_length, datatype, value); - // DPT 13.001/13.002/13.010-13.015 - Signed 32 Bit Integer - if (datatype.mainGroup == 13 && (datatype.subGroup == 1 || datatype.subGroup == 2 || (datatype.subGroup >= 10 && datatype.subGroup <= 15)) && !datatype.index) - return busValueToSigned32(payload, payload_length, datatype, value); - // DPT 13.100 - Long Time Period - if (datatype.mainGroup == 13 && datatype.subGroup == 100 && !datatype.index) - return busValueToLongTimePeriod(payload, payload_length, datatype, value); - // DPT 14.* - 32 Bit Float - if (datatype.mainGroup == 14 && datatype.subGroup <= 79 && !datatype.index) - return busValueToFloat32(payload, payload_length, datatype, value); - // DPT 15.* - Access Data - if (datatype.mainGroup == 15 && !datatype.subGroup && datatype.index <= 5) - return busValueToAccess(payload, payload_length, datatype, value); - // DPT 16.* - String - if (datatype.mainGroup == 16 && datatype.subGroup <= 1 && !datatype.index) - return busValueToString(payload, payload_length, datatype, value); - // DPT 17.* - Scene Number - if (datatype.mainGroup == 17 && datatype.subGroup == 1 && !datatype.index) - return busValueToScene(payload, payload_length, datatype, value); - // DPT 18.* - Scene Control - if (datatype.mainGroup == 18 && datatype.subGroup == 1 && datatype.index <= 1) - return busValueToSceneControl(payload, payload_length, datatype, value); - // DPT 19.* - Date and Time - if (datatype.mainGroup == 19 && datatype.subGroup == 1 && (datatype.index <= 3 || datatype.index == 9 || datatype.index == 10)) - return busValueToDateTime(payload, payload_length, datatype, value); - // DPT 26.* - Scene Info - if (datatype.mainGroup == 26 && datatype.subGroup == 1 && datatype.index <= 1) - return busValueToSceneInfo(payload, payload_length, datatype, value); - // DPT 28.* - Unicode String - if (datatype.mainGroup == 28 && datatype.subGroup == 1 && !datatype.index) - return busValueToUnicode(payload, payload_length, datatype, value); - // DPT 29.* - Signed 64 Bit Integer - if (datatype.mainGroup == 29 && datatype.subGroup >= 10 && datatype.subGroup <= 12 && !datatype.index) - return busValueToSigned64(payload, payload_length, datatype, value); - // DPT 219.* - Alarm Info - if (datatype.mainGroup == 219 && datatype.subGroup == 1 && datatype.index <= 10) - return busValueToAlarmInfo(payload, payload_length, datatype, value); - // DPT 221.* - Serial Number - if (datatype.mainGroup == 221 && datatype.subGroup == 1 && datatype.index <= 1) - return busValueToSerialNumber(payload, payload_length, datatype, value); - // DPT 217.* - Version - if (datatype.mainGroup == 217 && datatype.subGroup == 1 && datatype.index <= 2) - return busValueToVersion(payload, payload_length, datatype, value); - // DPT 225.001/225.002 - Scaling Speed and Scaling Step Time - if (datatype.mainGroup == 225 && datatype.subGroup >= 1 && datatype.subGroup <= 2 && datatype.index <= 1) - return busValueToScaling(payload, payload_length, datatype, value); - // DPT 225.003 - Next Tariff - if (datatype.mainGroup == 225 && datatype.subGroup == 3 && datatype.index <= 1) - return busValueToTariff(payload, payload_length, datatype, value); - // DPT 231.* - Locale - if (datatype.mainGroup == 231 && datatype.subGroup == 1 && datatype.index <= 1) - return busValueToLocale(payload, payload_length, datatype, value); - // DPT 232.600 - RGB - if (datatype.mainGroup == 232 && datatype.subGroup == 600 && !datatype.index) - return busValueToRGB(payload, payload_length, datatype, value); - // DPT 234.* - Language and Region - if (datatype.mainGroup == 234 && datatype.subGroup >= 1 && datatype.subGroup <= 2 && !datatype.index) - return busValueToLocale(payload, payload_length, datatype, value); - // DPT 235.* - Active Energy - if (datatype.mainGroup == 235 && datatype.subGroup == 1 && datatype.index <= 3) - return busValueToActiveEnergy(payload, payload_length, datatype, value); - // DPT 238.* - Scene Config - if (datatype.mainGroup == 238 && datatype.subGroup == 1 && datatype.index <= 2) - return busValueToSceneConfig(payload, payload_length, datatype, value); - // DPT 239.* - Flagged Scaling - if (datatype.mainGroup == 239 && datatype.subGroup == 1 && datatype.index <= 1) - return busValueToFlaggedScaling(payload, payload_length, datatype, value); - if (datatype.mainGroup == 4 && datatype.subGroup >= 1 && datatype.subGroup <= 2 && !datatype.index) - return busValueToCharacter(payload, payload_length, datatype, value); - // DPT 5.* - Unsigned 8 Bit Integer - if (datatype.mainGroup == 5 && ((datatype.subGroup >= 1 && datatype.subGroup <= 6 && datatype.subGroup != 2) || datatype.subGroup == 10) && !datatype.index) - return busValueToUnsigned8(payload, payload_length, datatype, value); - // DPT 6.001/6.010 - Signed 8 Bit Integer - if (datatype.mainGroup == 6 && (datatype.subGroup == 1 || datatype.subGroup == 10) && !datatype.index) - return busValueToSigned8(payload, payload_length, datatype, value); - // DPT 6.020 - Status with Mode - if (datatype.mainGroup == 6 && datatype.subGroup == 20 && datatype.index <= 5) - return busValueToStatusAndMode(payload, payload_length, datatype, value); - // DPT 7.001/7.010/7.011/7.012/7.013 - Unsigned 16 Bit Integer - if (datatype.mainGroup == 7 && (datatype.subGroup == 1 || (datatype.subGroup >= 10 && datatype.subGroup <= 13)) && !datatype.index) - return busValueToUnsigned16(payload, payload_length, datatype, value); - // DPT 7.002-DPT 7.007 - Time Period - if (datatype.mainGroup == 7 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index) - return busValueToTimePeriod(payload, payload_length, datatype, value); - // DPT 8.001/8.010/8.011 - Signed 16 Bit Integer - if (datatype.mainGroup == 8 && (datatype.subGroup == 1 || datatype.subGroup == 10 || datatype.subGroup == 11) && !datatype.index) - return busValueToSigned16(payload, payload_length, datatype, value); - // DPT 8.002-DPT 8.007 - Time Delta - if (datatype.mainGroup == 8 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index) - return busValueToTimeDelta(payload, payload_length, datatype, value); - // DPT 9.* - 16 Bit Float - if (datatype.mainGroup == 9 && ((datatype.subGroup >= 1 && datatype.subGroup <= 11 && datatype.subGroup != 9) || (datatype.subGroup >= 20 && datatype.subGroup <= 28)) && !datatype.index) + if (datatype.mainGroup == 9 && ((datatype.subGroup >= 1 && datatype.subGroup <= 11) || (datatype.subGroup >= 20 && datatype.subGroup <= 29)) && !datatype.index) return busValueToFloat16(payload, payload_length, datatype, value); // DPT 10.* - Time and Weekday if (datatype.mainGroup == 10 && datatype.subGroup == 1 && datatype.index <= 1) @@ -233,6 +126,9 @@ int KNX_Decode_Value(uint8_t* payload, size_t payload_length, const Dpt& datatyp // DPT 239.* - Flagged Scaling if (datatype.mainGroup == 239 && datatype.subGroup == 1 && datatype.index <= 1) return busValueToFlaggedScaling(payload, payload_length, datatype, value); + // DPT 251.600 - RGBW + if (datatype.mainGroup == 251 && datatype.subGroup == 600 && datatype.index <= 1) + return busValueToRGBW(payload, payload_length, datatype, value); } return false; } @@ -260,8 +156,8 @@ int KNX_Encode_Value(const KNXValue& value, uint8_t* payload, size_t payload_len // DPT 6.020 - Status with Mode if (datatype.mainGroup == 6 && datatype.subGroup == 20 && datatype.index <= 5) return valueToBusValueStatusAndMode(value, payload, payload_length, datatype); - // DPT 7.001/7.010/7.011/7.012/7.013 - Unsigned 16 Bit Integer - if (datatype.mainGroup == 7 && (datatype.subGroup == 1 || (datatype.subGroup >= 10 && datatype.subGroup <= 13)) && !datatype.index) + // DPT 7.001/7.010/7.011/7.012/7.013/7.600 - Unsigned 16 Bit Integer + if (datatype.mainGroup == 7 && (datatype.subGroup == 1 || (datatype.subGroup >= 10 && datatype.subGroup <= 13) || datatype.subGroup == 600) && !datatype.index) return valueToBusValueUnsigned16(value, payload, payload_length, datatype); // DPT 7.002-DPT 7.007 - Time Period if (datatype.mainGroup == 7 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index) @@ -273,7 +169,7 @@ int KNX_Encode_Value(const KNXValue& value, uint8_t* payload, size_t payload_len if (datatype.mainGroup == 8 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index) return valueToBusValueTimeDelta(value, payload, payload_length, datatype); // DPT 9.* - 16 Bit Float - if (datatype.mainGroup == 9 && ((datatype.subGroup >= 1 && datatype.subGroup <= 11 && datatype.subGroup != 9) || (datatype.subGroup >= 20 && datatype.subGroup <= 28)) && !datatype.index) + if (datatype.mainGroup == 9 && ((datatype.subGroup >= 1 && datatype.subGroup <= 11 ) || (datatype.subGroup >= 20 && datatype.subGroup <= 29)) && !datatype.index) return valueToBusValueFloat16(value, payload, payload_length, datatype); // DPT 10.* - Time and Weekday if (datatype.mainGroup == 10 && datatype.subGroup == 1 && datatype.index <= 1) @@ -350,6 +246,9 @@ int KNX_Encode_Value(const KNXValue& value, uint8_t* payload, size_t payload_len // DPT 239.* - Flagged Scaling if (datatype.mainGroup == 239 && datatype.subGroup == 1 && datatype.index <= 1) return valueToBusValueFlaggedScaling(value, payload, payload_length, datatype); + // DPT 251.600 - RGBW + if (datatype.mainGroup == 251 && datatype.subGroup == 600 && datatype.index <= 1) + return valueToBusValueRGBW(value, payload, payload_length, datatype); return false; } @@ -904,12 +803,27 @@ int busValueToRGB(const uint8_t* payload, size_t payload_length, const Dpt& data { ASSERT_PAYLOAD(3); uint32_t rgb = unsigned16FromPayload(payload, 0) * 256 + unsigned8FromPayload(payload, 2); - if (rgb > 16777215) - return false; value = rgb; return true; } +int busValueToRGBW(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value) +{ + ASSERT_PAYLOAD(6); + switch (datatype.index) { + case 0: // The RGBW value + { + uint32_t rgbw = unsigned32FromPayload(payload, 0); + value = rgbw; + } + return true; + case 1: // The mask bits only + value = unsigned8FromPayload(payload,5); + return true; + } + return false; +} + int busValueToFlaggedScaling(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value) { ASSERT_PAYLOAD(2); @@ -993,7 +907,7 @@ int valueToBusValueStepControl(const KNXValue& value, uint8_t* payload, size_t p int valueToBusValueCharacter(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype) { - if ((uint64_t)value < INT64_C(0) || (uint64_t)value > INT64_C(255) || (datatype.subGroup == 1 && (uint64_t)value > INT64_C(127))) + if ((uint64_t)value > INT64_C(255) || (datatype.subGroup == 1 && (uint64_t)value > INT64_C(127))) return false; unsigned8ToPayload(payload, payload_length, 0, (uint64_t)value, 0xFF); return true; @@ -1260,7 +1174,7 @@ int valueToBusValueAccess(const KNXValue& value, uint8_t* payload, size_t payloa break; case 5: { - if ((uint64_t)value < INT64_C(0) || (uint64_t)value > INT64_C(15)) + if ((uint64_t)value > INT64_C(15)) return false; bcdToPayload(payload, payload_length, 7, (uint64_t)value); break; @@ -1551,7 +1465,7 @@ int valueToBusValueScaling(const KNXValue& value, uint8_t* payload, size_t paylo { uint32_t duration = value; - if (duration < INT64_C(0) || duration > INT64_C(65535)) + if (duration > INT64_C(65535)) return false; ENSURE_PAYLOAD(3); @@ -1578,7 +1492,7 @@ int valueToBusValueTariff(const KNXValue& value, uint8_t* payload, size_t payloa { uint32_t duration = value; - if (duration < INT64_C(0) || duration > INT64_C(65535)) + if (duration > INT64_C(65535)) return false; ENSURE_PAYLOAD(3); @@ -1626,6 +1540,24 @@ int valueToBusValueRGB(const KNXValue& value, uint8_t* payload, size_t payload_l return true; } +int valueToBusValueRGBW(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype) +{ + switch(datatype.index) + { + case 0: // RGBW + { + uint32_t rgbw = (uint32_t)value; + unsigned32ToPayload(payload, payload_length, 0, rgbw, 0xffffffff); // RGBW + } + break; + case 1: // Mask bits + unsigned8ToPayload(payload, payload_length, 5, (uint8_t)value, 0x0f); + break; + + } + return true; +} + int valueToBusValueFlaggedScaling(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype) { switch (datatype.index) @@ -1654,7 +1586,6 @@ int valueToBusValueActiveEnergy(const KNXValue& value, uint8_t* payload, size_t { case 0: { - if ((int64_t)value < INT64_C(-2147483648) || (int64_t)value > INT64_C(2147483647)) return false; ENSURE_PAYLOAD(6); diff --git a/src/knx/dptconvert.h b/src/knx/dptconvert.h index 9256b51..f341df8 100644 --- a/src/knx/dptconvert.h +++ b/src/knx/dptconvert.h @@ -78,6 +78,7 @@ int busValueToScaling(const uint8_t* payload, size_t payload_length, const Dpt& int busValueToTariff(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value); int busValueToLocale(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value); int busValueToRGB(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value); +int busValueToRGBW(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value); int busValueToFlaggedScaling(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value); int busValueToActiveEnergy(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value); @@ -116,6 +117,7 @@ int valueToBusValueScaling(const KNXValue& value, uint8_t* payload, size_t paylo int valueToBusValueTariff(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype); int valueToBusValueLocale(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype); int valueToBusValueRGB(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype); +int valueToBusValueRGBW(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype); int valueToBusValueFlaggedScaling(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype); int valueToBusValueActiveEnergy(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype); diff --git a/src/knx/group_object.cpp b/src/knx/group_object.cpp index 2ed11d9..db92058 100644 --- a/src/knx/group_object.cpp +++ b/src/knx/group_object.cpp @@ -4,23 +4,30 @@ #include "datapoint_types.h" #include "group_object_table_object.h" +#ifdef SMALL_GROUPOBJECT +GroupObjectUpdatedHandler GroupObject::_updateHandlerStatic = 0; +#endif +GroupObjectTableObject* GroupObject::_table = 0; + GroupObject::GroupObject() { _data = 0; _commFlag = Ok; - _table = 0; _dataLength = 0; +#ifndef SMALL_GROUPOBJECT _updateHandler = 0; +#endif } GroupObject::GroupObject(const GroupObject& other) { _data = new uint8_t[other._dataLength]; _commFlag = other._commFlag; - _table = other._table; _dataLength = other._dataLength; _asap = other._asap; +#ifndef SMALL_GROUPOBJECT _updateHandler = other._updateHandler; +#endif memcpy(_data, other._data, _dataLength); } @@ -175,6 +182,24 @@ size_t GroupObject::sizeInTelegram() return asapValueSize(code); } +#ifdef SMALL_GROUPOBJECT +GroupObjectUpdatedHandler GroupObject::classCallback() +{ + return _updateHandlerStatic; +} + +void GroupObject::classCallback(GroupObjectUpdatedHandler handler) +{ + _updateHandlerStatic = handler; +} + +void GroupObject::processClassCallback(GroupObject& ko) +{ + if (_updateHandlerStatic != 0) + _updateHandlerStatic(ko); +} + +#else void GroupObject::callback(GroupObjectUpdatedHandler handler) { _updateHandler = handler; @@ -185,6 +210,7 @@ GroupObjectUpdatedHandler GroupObject::callback() { return _updateHandler; } +#endif void GroupObject::value(const KNXValue& value, const Dpt& type) { @@ -205,7 +231,7 @@ bool GroupObject::tryValue(KNXValue& value, const Dpt& type) return KNX_Decode_Value(_data, _dataLength, type, value); } - +#ifndef SMALL_GROUPOBJECT void GroupObject::dataPointType(Dpt value) { _datapointType = value; @@ -240,7 +266,7 @@ void GroupObject::valueNoSend(const KNXValue& value) { valueNoSend(value, _datapointType); } - +#endif void GroupObject::valueNoSend(const KNXValue& value, const Dpt& type) { diff --git a/src/knx/group_object.h b/src/knx/group_object.h index 46499d9..91c8b56 100644 --- a/src/knx/group_object.h +++ b/src/knx/group_object.h @@ -133,6 +133,8 @@ class GroupObject * (in german "KO-Nr") */ uint16_t asap(); + +#ifndef SMALL_GROUPOBJECT /** * register a callback for this group object. The registered callback will be called if the group object was changed from the bus. */ @@ -141,16 +143,12 @@ class GroupObject * returns the registered callback */ GroupObjectUpdatedHandler callback(); +#endif /** * return the current value of the group object. * @param type the datapoint type used for the conversion. If this doesn't fit to the group object the returned value is invalid. */ KNXValue value(const Dpt& type); - /** - * return the current value of the group object. The datapoint type must be set with dataPointType(). Otherwise the returned - * value is invalid. - */ - KNXValue value(); /** * set the current value of the group object and changes the state of the group object to ::WriteRequest. * @param value the value the group object is set to @@ -159,13 +157,6 @@ class GroupObject * The parameters must fit the group object. Otherwise it will stay unchanged. */ void value(const KNXValue& value, const Dpt& type); - /** - * set the current value of the group object and changes the state of the group object to ::WriteRequest. - * @param value the value the group object is set to - * - * The parameters must fit the group object and dhe datapoint type must be set with dataPointType(). Otherwise it will stay unchanged. - */ - void value(const KNXValue& value); /** * set the current value of the group object. * @param value the value the group object is set to @@ -174,13 +165,6 @@ class GroupObject * The parameters must fit the group object. Otherwise it will stay unchanged. */ void valueNoSend(const KNXValue& value, const Dpt& type); - /** - * set the current value of the group object. - * @param value the value the group object is set to - * - * The parameters must fit the group object and dhe datapoint type must be set with dataPointType(). Otherwise it will stay unchanged. - */ - void valueNoSend(const KNXValue& value); /** * set the current value of the group object. * @param value the value the group object is set to @@ -191,6 +175,27 @@ class GroupObject * @returns true if the value of the group object was changed successfully. */ bool tryValue(KNXValue& value, const Dpt& type); + +#ifndef SMALL_GROUPOBJECT + /** + * return the current value of the group object. The datapoint type must be set with dataPointType(). Otherwise the returned + * value is invalid. + */ + KNXValue value(); + /** + * set the current value of the group object and changes the state of the group object to ::WriteRequest. + * @param value the value the group object is set to + * + * The parameters must fit the group object and dhe datapoint type must be set with dataPointType(). Otherwise it will stay unchanged. + */ + void value(const KNXValue& value); + /** + * set the current value of the group object. + * @param value the value the group object is set to + * + * The parameters must fit the group object and dhe datapoint type must be set with dataPointType(). Otherwise it will stay unchanged. + */ + void valueNoSend(const KNXValue& value); /** * set the current value of the group object. * @param value the value the group object is set to @@ -209,15 +214,32 @@ class GroupObject * sets the datapoint type of the group object. */ void dataPointType(Dpt value); +#else + /** + * Alternative callback processing: register one global callback for all group object. + * The registered callback will be called if any group object was changed from the bus. + * The callback method has to dispatch to the correct handler for this group object. + */ + static GroupObjectUpdatedHandler classCallback(); + static void classCallback(GroupObjectUpdatedHandler handler); + static void processClassCallback(GroupObject& ko); +#endif private: + // class members + static GroupObjectTableObject* _table; +#ifdef SMALL_GROUPOBJECT + static GroupObjectUpdatedHandler _updateHandlerStatic; +#endif + size_t asapValueSize(uint8_t code); - GroupObjectUpdatedHandler _updateHandler; size_t goSize(); uint16_t _asap = 0; ComFlag _commFlag = Ok; uint8_t* _data = 0; uint8_t _dataLength = 0; - GroupObjectTableObject* _table = 0; +#ifndef SMALL_GROUPOBJECT + GroupObjectUpdatedHandler _updateHandler; Dpt _datapointType; +#endif }; diff --git a/src/knx/ip_parameter_object.cpp b/src/knx/ip_parameter_object.cpp index d894dab..dcbbba3 100644 --- a/src/knx/ip_parameter_object.cpp +++ b/src/knx/ip_parameter_object.cpp @@ -6,7 +6,7 @@ #include "data_property.h" #include "callback_property.h" -//224.0.23.12 +// 224.0.23.12 #define DEFAULT_MULTICAST_ADDR ((uint32_t)0xE000170C) IpParameterObject::IpParameterObject(DeviceObject& deviceObject, Platform& platform): _deviceObject(deviceObject), @@ -46,7 +46,7 @@ IpParameterObject::IpParameterObject(DeviceObject& deviceObject, Platform& platf return 1; } - pushInt(io->_platform.currentIpAddress(), data); + pushInt(htonl(io->_platform.currentIpAddress()), data); return 1; }), new CallbackProperty(this, PID_CURRENT_SUBNET_MASK, false, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv0, @@ -59,7 +59,7 @@ IpParameterObject::IpParameterObject(DeviceObject& deviceObject, Platform& platf return 1; } - pushInt(io->_platform.currentSubnetMask(), data); + pushInt(htonl(io->_platform.currentSubnetMask()), data); return 1; }), new CallbackProperty(this, PID_CURRENT_DEFAULT_GATEWAY, false, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv0, @@ -72,7 +72,7 @@ IpParameterObject::IpParameterObject(DeviceObject& deviceObject, Platform& platf return 1; } - pushInt(io->_platform.currentDefaultGateway(), data); + pushInt(htonl(io->_platform.currentDefaultGateway()), data); return 1; }), new DataProperty(PID_IP_ADDRESS, true, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv3), diff --git a/src/knx/knx_ip_device_information_dib.cpp b/src/knx/knx_ip_device_information_dib.cpp index c6a5aa5..a13ad96 100644 --- a/src/knx/knx_ip_device_information_dib.cpp +++ b/src/knx/knx_ip_device_information_dib.cpp @@ -35,7 +35,7 @@ uint16_t KnxIpDeviceInformationDIB::individualAddress() const } -void KnxIpDeviceInformationDIB::indiviudalAddress(uint16_t value) +void KnxIpDeviceInformationDIB::individualAddress(uint16_t value) { pushWord(value, _data + 4); } @@ -65,7 +65,7 @@ void KnxIpDeviceInformationDIB::serialNumber(const uint8_t* value) } -uint32_t KnxIpDeviceInformationDIB::routingMulicastAddress() const +uint32_t KnxIpDeviceInformationDIB::routingMulticastAddress() const { return getInt(_data + 14); } diff --git a/src/knx/knx_ip_device_information_dib.h b/src/knx/knx_ip_device_information_dib.h index 8acad60..32f24ba 100644 --- a/src/knx/knx_ip_device_information_dib.h +++ b/src/knx/knx_ip_device_information_dib.h @@ -16,12 +16,12 @@ class KnxIpDeviceInformationDIB : public KnxIpDIB uint8_t status() const; void status(uint8_t value); uint16_t individualAddress() const; - void indiviudalAddress(uint16_t value); + void individualAddress(uint16_t value); uint16_t projectInstallationIdentifier() const; void projectInstallationIdentifier(uint16_t value); const uint8_t* serialNumber() const; void serialNumber(const uint8_t* value); - uint32_t routingMulicastAddress() const; + uint32_t routingMulticastAddress() const; void routingMulticastAddress(uint32_t value); const uint8_t* macAddress() const; void macAddress(const uint8_t* value); diff --git a/src/knx/knx_ip_search_response.cpp b/src/knx/knx_ip_search_response.cpp index d051416..c4d8830 100644 --- a/src/knx/knx_ip_search_response.cpp +++ b/src/knx/knx_ip_search_response.cpp @@ -19,7 +19,7 @@ KnxIpSearchResponse::KnxIpSearchResponse(IpParameterObject& parameters, DeviceOb _deviceInfo.code(DEVICE_INFO); _deviceInfo.medium(0x20); //KNX-IP FIXME get this value from somewhere else _deviceInfo.status(deviceObject.progMode()); - _deviceInfo.indiviudalAddress(parameters.propertyValue(PID_KNX_INDIVIDUAL_ADDRESS)); + _deviceInfo.individualAddress(parameters.propertyValue(PID_KNX_INDIVIDUAL_ADDRESS)); _deviceInfo.projectInstallationIdentifier(parameters.propertyValue(PID_PROJECT_INSTALLATION_ID)); _deviceInfo.serialNumber(deviceObject.propertyData(PID_SERIAL_NUMBER)); _deviceInfo.routingMulticastAddress(parameters.propertyValue(PID_ROUTING_MULTICAST_ADDRESS)); diff --git a/src/knx/knx_value.cpp b/src/knx/knx_value.cpp index 0a7c104..c8d9f54 100644 --- a/src/knx/knx_value.cpp +++ b/src/knx/knx_value.cpp @@ -187,7 +187,7 @@ KNXValue& KNXValue::operator=(const int16_t value) KNXValue& KNXValue::operator=(const int32_t value) { - _value.boolValue = value; + _value.intValue = value; _type = IntType; return *this; } @@ -318,7 +318,7 @@ uint64_t KNXValue::ulongValue() const switch (_type) { case ULongType: - return _value.uintValue; + return _value.ulongValue; case BoolType: return _value.boolValue ? 1 : 0; case UCharType: @@ -343,7 +343,11 @@ uint64_t KNXValue::ulongValue() const case DoubleType: return (uint64_t)_value.doubleValue; case StringType: +#ifndef KNX_NO_STRTOx_CONVERSION return (uint64_t)strtoul(_value.stringValue, NULL, 0); +#else + return 0; +#endif } return 0; } @@ -444,7 +448,11 @@ int64_t KNXValue::longValue() const case DoubleType: return (int64_t)_value.doubleValue; case StringType: +#ifndef KNX_NO_STRTOx_CONVERSION return strtol(_value.stringValue, NULL, 0); +#else + return 0; +#endif } return 0; } @@ -476,7 +484,11 @@ double KNXValue::doubleValue() const case LongType: return _value.longValue; case StringType: +#ifndef KNX_NO_STRTOx_CONVERSION return strtod(_value.stringValue, NULL); +#else + return 0; +#endif } return 0; } diff --git a/src/knx/memory.h b/src/knx/memory.h index 75c4480..6014ed4 100644 --- a/src/knx/memory.h +++ b/src/knx/memory.h @@ -10,7 +10,7 @@ #define MAXTABLEOBJ 4 #ifndef KNX_FLASH_SIZE -# define KNX_FLASH_SIZE 1024 +#define KNX_FLASH_SIZE 1024 #endif class MemoryBlock diff --git a/src/knx/platform.cpp b/src/knx/platform.cpp index 78ce42c..2674c3c 100644 --- a/src/knx/platform.cpp +++ b/src/knx/platform.cpp @@ -74,6 +74,11 @@ uint32_t Platform::currentDefaultGateway() void Platform::macAddress(uint8_t *data) {} +uint32_t Platform::uniqueSerialNumber() +{ + return 0x01020304; +} + void Platform::setupMultiCast(uint32_t addr, uint16_t port) {} diff --git a/src/knx/platform.h b/src/knx/platform.h index 1ac61e0..1251d0e 100644 --- a/src/knx/platform.h +++ b/src/knx/platform.h @@ -20,6 +20,9 @@ class Platform virtual uint32_t currentDefaultGateway(); virtual void macAddress(uint8_t* data); + // unique serial number + virtual uint32_t uniqueSerialNumber(); + // basic stuff virtual void restart() = 0; virtual void fatalError() = 0; diff --git a/src/knx/rf_medium_object.cpp b/src/knx/rf_medium_object.cpp index fb143f3..5416649 100644 --- a/src/knx/rf_medium_object.cpp +++ b/src/knx/rf_medium_object.cpp @@ -31,7 +31,7 @@ RfMediumObject::RfMediumObject() resultData[2] = 0xFF; // permanent bidirectional device resultLength = 3; }), -/* This properties are used in NMP_LinkBudget_Measure to diagnose the Link Budget of the communication. +/* These properties are used in NMP_LinkBudget_Measure to diagnose the Link Budget of the communication. This in not implemented yet. new DataProperty(PID_RF_DIAG_SA_FILTER_TABLE, true, PDT_GENERIC_03, 8, ReadLv3 | WriteLv3), new DataProperty(PID_RF_DIAG_BUDGET_TABLE, false, PDT_GENERIC_03, 8, ReadLv3 | WriteLv0), diff --git a/src/knx/rf_physical_layer_cc1101.h b/src/knx/rf_physical_layer_cc1101.h index ff5e7ab..76065d3 100644 --- a/src/knx/rf_physical_layer_cc1101.h +++ b/src/knx/rf_physical_layer_cc1101.h @@ -21,19 +21,19 @@ extern void delayMicroseconds (unsigned int howLong); /*----------------------[CC1101 - misc]---------------------------------------*/ #define CRYSTAL_FREQUENCY 26000000 -#define CFG_REGISTER 0x2F //47 registers -#define FIFOBUFFER 0x42 //size of Fifo Buffer +2 for rssi and lqi -#define RSSI_OFFSET_868MHZ 0x4E //dec = 74 -#define TX_RETRIES_MAX 0x05 //tx_retries_max -#define ACK_TIMEOUT 250 //ACK timeout in ms -#define CC1101_COMPARE_REGISTER 0x00 //register compare 0=no compare 1=compare -#define BROADCAST_ADDRESS 0x00 //broadcast address +#define CFG_REGISTER 0x2F // 47 registers +#define FIFOBUFFER 0x42 // size of Fifo Buffer +2 for rssi and lqi +#define RSSI_OFFSET_868MHZ 0x4E // dec = 74 +#define TX_RETRIES_MAX 0x05 // tx_retries_max +#define ACK_TIMEOUT 250 // ACK timeout in ms +#define CC1101_COMPARE_REGISTER 0x00 // register compare 0=no compare 1=compare +#define BROADCAST_ADDRESS 0x00 // broadcast address #define CC1101_FREQ_315MHZ 0x01 #define CC1101_FREQ_434MHZ 0x02 #define CC1101_FREQ_868MHZ 0x03 #define CC1101_FREQ_915MHZ 0x04 -#define CC1101_TEMP_ADC_MV 3.225 //3.3V/1023 . mV pro digit -#define CC1101_TEMP_CELS_CO 2.47 //Temperature coefficient 2.47mV per Grad Celsius +#define CC1101_TEMP_ADC_MV 3.225 // 3.3V/1023 . mV pro digit +#define CC1101_TEMP_CELS_CO 2.47 // Temperature coefficient 2.47mV per Grad Celsius /*---------------------------[CC1101 - R/W offsets]---------------------------*/ #define WRITE_SINGLE_BYTE 0x00 @@ -43,12 +43,12 @@ extern void delayMicroseconds (unsigned int howLong); /*---------------------------[END R/W offsets]--------------------------------*/ /*------------------------[CC1101 - FIFO commands]----------------------------*/ -#define TXFIFO_BURST 0x7F //write burst only -#define TXFIFO_SINGLE_BYTE 0x3F //write single only -#define RXFIFO_BURST 0xFF //read burst only -#define RXFIFO_SINGLE_BYTE 0xBF //read single only -#define PATABLE_BURST 0x7E //power control read/write -#define PATABLE_SINGLE_BYTE 0xFE //power control read/write +#define TXFIFO_BURST 0x7F // write burst only +#define TXFIFO_SINGLE_BYTE 0x3F // write single only +#define RXFIFO_BURST 0xFF // read burst only +#define RXFIFO_SINGLE_BYTE 0xBF // read single only +#define PATABLE_BURST 0x7E // power control read/write +#define PATABLE_SINGLE_BYTE 0xFE // power control read/write /*---------------------------[END FIFO commands]------------------------------*/ /*----------------------[CC1101 - config register]----------------------------*/ diff --git a/src/knx/rf_physical_layer_cc1310.cpp b/src/knx/rf_physical_layer_cc1310.cpp index 35749a4..2016b95 100644 --- a/src/knx/rf_physical_layer_cc1310.cpp +++ b/src/knx/rf_physical_layer_cc1310.cpp @@ -132,8 +132,8 @@ void RfPhysicalLayerCC1310::setOutputPowerLevel(int8_t dBm) rfPowerTableSize = PROP_RF_txPowerTableSize; } - //if max power is requested then the CCFG_FORCE_VDDR_HH must be set in - //the ccfg + // if max power is requested then the CCFG_FORCE_VDDR_HH must be set in + // the ccfg #if (CCFG_FORCE_VDDR_HH != 0x1) if((newValue.paType == RF_TxPowerTable_DefaultPA) && (dBm == rfPowerTable[rfPowerTableSize-2].power)) diff --git a/src/knx/router_object.cpp b/src/knx/router_object.cpp index 1bc1d5b..b7c7b62 100644 --- a/src/knx/router_object.cpp +++ b/src/knx/router_object.cpp @@ -84,7 +84,6 @@ void RouterObject::initialize(CouplerModel model, uint8_t objIndex, DptMedium me Property* tableProperties[] = { new DataProperty( PID_COUPLER_SERVICES_CONTROL, true, PDT_GENERIC_01, 1, ReadLv3 | WriteLv0, (uint8_t) 0), // written by ETS TODO: implement - new DataProperty( PID_MCB_TABLE, false, PDT_GENERIC_08, 1, ReadLv3 | WriteLv0), // TODO: improve: move to TableObject once segment size handling is clear new DataProperty( PID_FILTER_TABLE_USE, true, PDT_BINARY_INFORMATION, 1, ReadLv3 | WriteLv0, (uint16_t) 0 ), // default: invalid filter table, do not use, written by ETS new FunctionProperty(this, PID_ROUTETABLE_CONTROL, // Command Callback of PID_ROUTETABLE_CONTROL @@ -452,27 +451,9 @@ void RouterObject::beforeStateChange(LoadState& newState) if (newState != LS_LOADED) return; - // calculate crc16-ccitt for PID_MCB_TABLE - updateMcb(); - _filterTableGroupAddresses = (uint16_t*)data(); } -void RouterObject::updateMcb() -{ - uint8_t mcb[propertySize(PID_MCB_TABLE)]; - - static constexpr uint32_t segmentSize = kFilterTableSize; - uint16_t crc16 = crc16Ccitt(data(), segmentSize); - - pushInt(segmentSize, &mcb[0]); // Segment size - pushByte(0x00, &mcb[4]); // CRC control byte -> 0: always valid -> according to coupler spec. it shall always be a valid CRC - pushByte(0xFF, &mcb[5]); // Read access 4 bits + Write access 4 bits (unknown: value taken from real coupler device) - pushWord(crc16, &mcb[6]); // CRC-16 CCITT of filter table - - property(PID_MCB_TABLE)->write(mcb); -} - void RouterObject::masterReset(EraseCode eraseCode, uint8_t channel) { if (eraseCode == FactoryReset) diff --git a/src/knx/router_object.h b/src/knx/router_object.h index 22faa56..e43838d 100644 --- a/src/knx/router_object.h +++ b/src/knx/router_object.h @@ -52,8 +52,6 @@ private: void commandClearSetGroupAddress(uint16_t startAddress, uint16_t endAddress, bool bitIsSet); bool statusClearSetGroupAddress(uint16_t startAddress, uint16_t endAddress, bool bitIsSet); - void updateMcb(); - bool _rfSbcRoutingEnabled = false; bool _ipSbcRoutingEnabled = false; uint16_t* _filterTableGroupAddresses = 0; diff --git a/src/knx/table_object.cpp b/src/knx/table_object.cpp index d1be31c..909337a 100644 --- a/src/knx/table_object.cpp +++ b/src/knx/table_object.cpp @@ -31,6 +31,8 @@ uint8_t* TableObject::save(uint8_t* buffer) { buffer = pushByte(_state, buffer); + buffer = pushInt(_size, buffer); + if (_data) buffer = pushInt(_memory.toRelative(_data), buffer); else @@ -46,6 +48,8 @@ const uint8_t* TableObject::restore(const uint8_t* buffer) buffer = popByte(state, buffer); _state = (LoadState)state; + buffer = popInt(_size, buffer); + uint32_t relativeAddress = 0; buffer = popInt(relativeAddress, buffer); @@ -80,6 +84,8 @@ bool TableObject::allocTable(uint32_t size, bool doFill, uint8_t fillByte) if (doFill) memset(_data, fillByte, size); + _size = size; + return true; } @@ -229,7 +235,7 @@ void TableObject::errorCode(ErrorCode errorCode) uint16_t TableObject::saveSize() { - return 5 + InterfaceObject::saveSize(); + return 5 + InterfaceObject::saveSize() + sizeof(_size); } void TableObject::initializeProperties(size_t propertiesSize, Property** properties) @@ -267,6 +273,21 @@ void TableObject::initializeProperties(size_t propertiesSize, Property** propert pushInt(obj->tableReference(), data); return 1; }), + new CallbackProperty(this, PID_MCB_TABLE, false, PDT_GENERIC_08, 1, ReadLv3 | WriteLv0, + [](TableObject* obj, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t { + if (obj->_state != LS_LOADED) + return 0; // need to check return code for invalid + + uint32_t segmentSize = obj->_size; + uint16_t crc16 = crc16Ccitt(obj->data(), segmentSize); + + pushInt(segmentSize, data); // Segment size + pushByte(0x00, data + 4); // CRC control byte -> 0: always valid + pushByte(0xFF, data + 5); // Read access 4 bits + Write access 4 bits + pushWord(crc16, data + 6); // CRC-16 CCITT of data + + return 1; + }), new DataProperty(PID_ERROR_CODE, false, PDT_ENUM8, 1, ReadLv3 | WriteLv0, (uint8_t)E_NO_FAULT) }; //TODO: missing @@ -284,4 +305,4 @@ void TableObject::initializeProperties(size_t propertiesSize, Property** propert memcpy(allProperties + propertyCount, ownProperties, sizeof(ownProperties)); InterfaceObject::initializeProperties(sizeof(allProperties), allProperties); -} +} \ No newline at end of file diff --git a/src/knx/table_object.h b/src/knx/table_object.h index fc487e9..c4213b3 100644 --- a/src/knx/table_object.h +++ b/src/knx/table_object.h @@ -28,7 +28,7 @@ class TableObject: public InterfaceObject uint8_t* save(uint8_t* buffer) override; const uint8_t* restore(const uint8_t* buffer) override; uint16_t saveSize() override; -protected: + protected: /** * This method is called before the interface object enters a new ::LoadState. * If there is a error changing the state newState should be set to ::LS_ERROR and errorCode() @@ -47,7 +47,7 @@ protected: void errorCode(ErrorCode errorCode); void initializeProperties(size_t propertiesSize, Property** properties) override; - + private: uint32_t tableReference(); bool allocTable(uint32_t size, bool doFill, uint8_t fillByte); @@ -68,4 +68,11 @@ protected: LoadState _state = LS_UNLOADED; Memory& _memory; uint8_t *_data = 0; + + /** + * used to store size of data() in allocTable(), needed for calculation of crc in PID_MCB_TABLE. + * This value is also saved and restored. + * The size of the memory block cannot be used because it is changed during alignment to page size. + */ + uint32_t _size = 0; }; diff --git a/src/knx/tpuart_data_link_layer.cpp b/src/knx/tpuart_data_link_layer.cpp index fe0cb8f..96f41f7 100644 --- a/src/knx/tpuart_data_link_layer.cpp +++ b/src/knx/tpuart_data_link_layer.cpp @@ -11,6 +11,9 @@ #include #include +// Activate trace output +//#define DBG_TRACE + // NCN5120 //#define NCN5120 @@ -75,25 +78,28 @@ #define U_STOP_MODE_IND 0x2B #define U_SYSTEM_STAT_IND 0x4B -//loop states -#define IDLE 0 -#define RX_FIRST_BYTE 1 -#define RX_L_DATA 2 -#define RX_WAIT_DATA_CON 3 -#define TX_FRAME 4 +//tx states +enum { + TX_IDLE, + TX_FRAME, + TX_WAIT_ECHO, + TX_WAIT_CONN +}; -#define BYTE_TIMEOUT 10 //milli seconds +//rx states +enum { + RX_WAIT_START, + RX_L_DATA, + RX_WAIT_EOP +}; + +#define EOP_TIMEOUT 2 //milli seconds; end of layer-2 packet gap #define CONFIRM_TIMEOUT 500 //milli seconds #define RESET_TIMEOUT 100 //milli seconds void TpUartDataLinkLayer::loop() { - _receiveBuffer[0] = 0x29; - _receiveBuffer[1] = 0; - uint8_t* buffer = _receiveBuffer + 2; - uint8_t rxByte; - if (!_enabled) { if (millis() - _lastResetChipTime > 1000) @@ -107,265 +113,317 @@ void TpUartDataLinkLayer::loop() if (!_enabled) return; - switch (_loopState) - { - case IDLE: - if (_platform.uartAvailable()) - { - _loopState = RX_FIRST_BYTE; - } - else - { - if (!_waitConfirm && !isTxQueueEmpty()) + // Signals to communicate from rx part with the tx part + bool isEchoComplete = false; // Flag that a complete echo is received + uint8_t dataConnMsg = 0; // The DATA_CONN message just seen or 0 + do { + _receiveBuffer[0] = 0x29; + _receiveBuffer[1] = 0; + uint8_t* buffer = _receiveBuffer + 2; + uint8_t rxByte; + bool isEOP = (millis() - _lastByteRxTime > EOP_TIMEOUT); // Flag that an EOP gap is seen + switch (_rxState) + { + case RX_WAIT_START: + if (_platform.uartAvailable()) { - loadNextTxFrame(); - _loopState = TX_FRAME; + rxByte = _platform.readUart(); +#ifdef DBG_TRACE + print(rxByte, HEX); +#endif + _lastByteRxTime = millis(); + + // Check for layer-2 packets + _RxByteCnt = 0; + _xorSum = 0; + if ((rxByte & L_DATA_MASK) == L_DATA_STANDARD_IND) + { + buffer[_RxByteCnt++] = rxByte; + _xorSum ^= rxByte; + _RxByteCnt++; //convert to L_DATA_EXTENDED + _convert = true; + _rxState = RX_L_DATA; +#ifdef DBG_TRACE + println("RLS"); +#endif + break; + } + else if ((rxByte & L_DATA_MASK) == L_DATA_EXTENDED_IND) + { + buffer[_RxByteCnt++] = rxByte; + _xorSum ^= rxByte; + _convert = false; + _rxState = RX_L_DATA; +#ifdef DBG_TRACE + println("RLX"); +#endif + break; + } + + // Handle all single byte packets here + else if ((rxByte & L_DATA_CON_MASK) == L_DATA_CON) + { + dataConnMsg = rxByte; + } + else if (rxByte == L_POLL_DATA_IND) + { + // not sure if this can happen + println("got L_POLL_DATA_IND"); + } + else if ((rxByte & L_ACKN_MASK) == L_ACKN_IND) + { + // this can only happen in bus monitor mode + println("got L_ACKN_IND"); + } + else if (rxByte == U_RESET_IND) + { + println("got U_RESET_IND"); + } + else if ((rxByte & U_STATE_IND) == U_STATE_IND) + { + print("got U_STATE_IND:"); + if (rxByte & 0x80) print (" SC"); + if (rxByte & 0x40) print (" RE"); + if (rxByte & 0x20) print (" TE"); + if (rxByte & 0x10) print (" PE"); + if (rxByte & 0x08) print (" TW"); + println(); + } + else if ((rxByte & U_FRAME_STATE_MASK) == U_FRAME_STATE_IND) + { + print("got U_FRAME_STATE_IND: 0x"); + print(rxByte, HEX); + println(); + } + else if ((rxByte & U_CONFIGURE_MASK) == U_CONFIGURE_IND) + { + print("got U_CONFIGURE_IND: 0x"); + print(rxByte, HEX); + println(); + } + else if (rxByte == U_FRAME_END_IND) + { + println("got U_FRAME_END_IND"); + } + else if (rxByte == U_STOP_MODE_IND) + { + println("got U_STOP_MODE_IND"); + } + else if (rxByte == U_SYSTEM_STAT_IND) + { + print("got U_SYSTEM_STAT_IND: 0x"); + while (true) + { + int tmp = _platform.readUart(); + if (tmp < 0) + continue; + + print(tmp, HEX); + break; + } + println(); + } + else + { + print("got UNEXPECTED: 0x"); + print(rxByte, HEX); + println(); + } } + break; + case RX_L_DATA: + if (isEOP) + { + _rxState = RX_WAIT_START; + print("EOP inside RX_L_DATA"); + printHex(" => ", buffer, _RxByteCnt); + break; + } + if (!_platform.uartAvailable()) + break; + _lastByteRxTime = millis(); + rxByte = _platform.readUart(); +#ifdef DBG_TRACE + print(rxByte, HEX); +#endif + if (_RxByteCnt == MAX_KNX_TELEGRAM_SIZE) + { + _rxState = RX_WAIT_EOP; + println("invalid telegram size"); + } + else + { + buffer[_RxByteCnt++] = rxByte; + } + + if (_RxByteCnt == 7) + { + //Destination Address + payload available + _xorSum ^= rxByte; + //check if echo + if (_sendBuffer != nullptr && (!((buffer[0] ^ _sendBuffer[0]) & ~0x20) && !memcmp(buffer + _convert + 1, _sendBuffer + 1, 5))) + { //ignore repeated bit of control byte + _isEcho = true; + } + else + { + _isEcho = false; + } + + //convert into Extended.ind + if (_convert) + { + uint8_t payloadLength = buffer[6] & 0x0F; + buffer[1] = buffer[6] & 0xF0; + buffer[6] = payloadLength; + } + + if (!_isEcho) + { + uint8_t c = 0x10; + + // The bau knows everything and could either check the address table object (normal device) + // or any filter tables (coupler) to see if we are addressed. + + //check if individual or group address + bool isGroupAddress = (buffer[1] & 0x80) != 0; + uint16_t addr = getWord(buffer + 4); + + if (_cb.isAckRequired(addr, isGroupAddress)) + { + c |= 0x01; + } + + // Hint: We can send directly here, this doesn't disturb other transmissions + _platform.writeUart(c); + } + } + else if (_RxByteCnt == buffer[6] + 7 + 2) + { + //complete Frame received, payloadLength+1 for TCPI +1 for CRC + if (rxByte == (uint8_t)(~_xorSum)) + { + //check if crc is correct + if (_isEcho && _sendBuffer != NULL) + { + //check if it is realy an echo, rx_crc = tx_crc + if (rxByte == _sendBuffer[_sendBufferLength - 1]) + _isEcho = true; + else + _isEcho = false; + } + if (_isEcho) + { + isEchoComplete = true; + } + else + { + frameBytesReceived(_receiveBuffer, _RxByteCnt + 2); + } + _rxState = RX_WAIT_START; +#ifdef DBG_TRACE + println("RX_WAIT_START"); +#endif + } + else + { + println("frame with invalid crc ignored"); + _rxState = RX_WAIT_EOP; + } + } + else + { + _xorSum ^= rxByte; + } + break; + case RX_WAIT_EOP: + if (isEOP) + { + _RxByteCnt = 0; + _rxState = RX_WAIT_START; +#ifdef DBG_TRACE + println("RX_WAIT_START"); +#endif + break; + } + if (!_platform.uartAvailable()) + break; + _lastByteRxTime = millis(); + rxByte = _platform.readUart(); +#ifdef DBG_TRACE + print(rxByte, HEX); +#endif + break; + default: + break; + } + } while (_rxState == RX_L_DATA); + + // Check for spurios DATA_CONN message + if (dataConnMsg && _txState != TX_WAIT_CONN && _txState != TX_WAIT_ECHO) { + println("got unexpected L_DATA_CON"); + } + + switch (_txState) + { + case TX_IDLE: + if (!isTxQueueEmpty()) + { + loadNextTxFrame(); + _txState = TX_FRAME; +#ifdef DBG_TRACE + println("TX_FRAME"); +#endif } break; case TX_FRAME: if (sendSingleFrameByte() == false) { - _waitConfirm = true; _waitConfirmStartTime = millis(); - _loopState = IDLE; + _txState = TX_WAIT_ECHO; +#ifdef DBG_TRACE + println("TX_WAIT_ECHO"); +#endif } break; - case RX_FIRST_BYTE: - rxByte = _platform.readUart(); - _lastByteRxTime = millis(); - _RxByteCnt = 0; - _xorSum = 0; - if ((rxByte & L_DATA_MASK) == L_DATA_STANDARD_IND) + case TX_WAIT_ECHO: + case TX_WAIT_CONN: + if (isEchoComplete) { - buffer[_RxByteCnt++] = rxByte; - _xorSum ^= rxByte; - _RxByteCnt++; //convert to L_DATA_EXTENDED - _convert = true; - _loopState = RX_L_DATA; - break; + _txState = TX_WAIT_CONN; +#ifdef DBG_TRACE + println("TX_WAIT_CONN"); +#endif } - else if ((rxByte & L_DATA_MASK) == L_DATA_EXTENDED_IND) + else if (dataConnMsg) { - buffer[_RxByteCnt++] = rxByte; - _xorSum ^= rxByte; - _convert = false; - _loopState = RX_L_DATA; - break; - } - else if ((rxByte & L_DATA_CON_MASK) == L_DATA_CON) - { - println("got unexpected L_DATA_CON"); - } - else if (rxByte == L_POLL_DATA_IND) - { - // not sure if this can happen - println("got L_POLL_DATA_IND"); - } - else if ((rxByte & L_ACKN_MASK) == L_ACKN_IND) - { - // this can only happen in bus monitor mode - println("got L_ACKN_IND"); - } - else if (rxByte == U_RESET_IND) - { - println("got U_RESET_IND"); - } - else if ((rxByte & U_STATE_IND) == U_STATE_IND) - { - print("got U_STATE_IND: 0x"); - print(rxByte, HEX); - println(); - } - else if ((rxByte & U_FRAME_STATE_MASK) == U_FRAME_STATE_IND) - { - print("got U_FRAME_STATE_IND: 0x"); - print(rxByte, HEX); - println(); - } - else if ((rxByte & U_CONFIGURE_MASK) == U_CONFIGURE_IND) - { - print("got U_CONFIGURE_IND: 0x"); - print(rxByte, HEX); - println(); - } - else if (rxByte == U_FRAME_END_IND) - { - println("got U_FRAME_END_IND"); - } - else if (rxByte == U_STOP_MODE_IND) - { - println("got U_STOP_MODE_IND"); - } - else if (rxByte == U_SYSTEM_STAT_IND) - { - print("got U_SYSTEM_STAT_IND: 0x"); - while (true) - { - int tmp = _platform.readUart(); - if (tmp < 0) - continue; - - print(tmp, HEX); - break; + bool waitEcho = (_txState == TX_WAIT_ECHO); + if (waitEcho) { + println("L_DATA_CON without echo"); } - println(); - } - else - { - print("got UNEXPECTED: 0x"); - print(rxByte, HEX); - println(); - } - _loopState = IDLE; - break; - case RX_L_DATA: - if (millis() - _lastByteRxTime > BYTE_TIMEOUT) - { - _RxByteCnt = 0; - _loopState = IDLE; - println("Timeout during RX_L_DATA"); - break; - } - if (!_platform.uartAvailable()) - break; - _lastByteRxTime = millis(); - rxByte = _platform.readUart(); - - if (_RxByteCnt == MAX_KNX_TELEGRAM_SIZE) - { - _loopState = IDLE; - println("invalid telegram size"); - } - else - { - buffer[_RxByteCnt++] = rxByte; - } - - if (_RxByteCnt == 7) - { - //Destination Address + payload available - _xorSum ^= rxByte; - //check if echo - if (_sendBuffer != nullptr && (!((buffer[0] ^ _sendBuffer[0]) & ~0x20) && !memcmp(buffer + _convert + 1, _sendBuffer + 1, 5))) - { //ignore repeated bit of control byte - _isEcho = true; - } - else - { - _isEcho = false; - } - - //convert into Extended.ind - if (_convert) - { - uint8_t payloadLength = buffer[6] & 0x0F; - buffer[1] = buffer[6] & 0xF0; - buffer[6] = payloadLength; - } - - if (!_isEcho) - { - uint8_t c = 0x10; - - // The bau knows everything and could either check the address table object (normal device) - // or any filter tables (coupler) to see if we are addressed. - - //check if individual or group address - bool isGroupAddress = (buffer[1] & 0x80) != 0; - uint16_t addr = getWord(buffer + 4); - - if (_cb.isAckRequired(addr, isGroupAddress)) - { - c |= 0x01; - } - - _platform.writeUart(c); - } - } - else if (_RxByteCnt == buffer[6] + 7 + 2) - { - //complete Frame received, payloadLength+1 for TCPI +1 for CRC - if (rxByte == (uint8_t)(~_xorSum)) - { - //check if crc is correct - if (_isEcho && _sendBuffer != NULL) - { - //check if it is realy an echo, rx_crc = tx_crc - if (rxByte == _sendBuffer[_sendBufferLength - 1]) - _isEcho = true; - else - _isEcho = false; - } - if (_isEcho) - { - _loopState = RX_WAIT_DATA_CON; - } - else - { - frameBytesReceived(_receiveBuffer, _RxByteCnt + 2); - _loopState = IDLE; - } - } - else - { - println("frame with invalid crc ignored"); - _loopState = IDLE; - } - } - else - { - _xorSum ^= rxByte; - } - break; - case RX_WAIT_DATA_CON: - if (!_platform.uartAvailable()) - break; - rxByte = _platform.readUart(); - _lastByteRxTime = millis(); - if ((rxByte & L_DATA_CON_MASK) == L_DATA_CON) - { - //println("L_DATA_CON received"); - dataConBytesReceived(_receiveBuffer, _RxByteCnt + 2, ((rxByte & SUCCESS) > 0)); - _waitConfirm = false; + dataConBytesReceived(_receiveBuffer, _RxByteCnt + 2, !waitEcho && ((dataConnMsg & SUCCESS) > 0)); delete[] _sendBuffer; _sendBuffer = 0; _sendBufferLength = 0; - _loopState = IDLE; + _txState = TX_IDLE; } - else + else if (millis() - _waitConfirmStartTime > CONFIRM_TIMEOUT) { - //should not happen - println("expected L_DATA_CON not received"); - dataConBytesReceived(_receiveBuffer, _RxByteCnt + 2, false); - _waitConfirm = false; + println("L_DATA_CON not received within expected time"); + uint8_t cemiBuffer[MAX_KNX_TELEGRAM_SIZE]; + cemiBuffer[0] = 0x29; + cemiBuffer[1] = 0; + memcpy((cemiBuffer + 2), _sendBuffer, _sendBufferLength); + dataConBytesReceived(cemiBuffer, _sendBufferLength + 2, false); delete[] _sendBuffer; _sendBuffer = 0; _sendBufferLength = 0; - _loopState = IDLE; + _txState = TX_IDLE; +#ifdef DBG_TRACE + println("TX_IDLE"); +#endif } break; - default: - break; - } - - if (_waitConfirm) - { - if (millis() - _waitConfirmStartTime > CONFIRM_TIMEOUT) - { - println("L_DATA_CON not received within expected time"); - uint8_t cemiBuffer[MAX_KNX_TELEGRAM_SIZE]; - cemiBuffer[0] = 0x29; - cemiBuffer[1] = 0; - memcpy((cemiBuffer + 2), _sendBuffer, _sendBufferLength); - dataConBytesReceived(cemiBuffer, _sendBufferLength + 2, false); - _waitConfirm = false; - delete[] _sendBuffer; - _sendBuffer = 0; - _sendBufferLength = 0; - if (_loopState == RX_WAIT_DATA_CON) - _loopState = IDLE; - } } } @@ -496,16 +554,21 @@ bool TpUartDataLinkLayer::sendSingleFrameByte() cmd[0] = U_L_DATA_END_REQ | _TxByteCnt; cmd[1] = _sendBuffer[_TxByteCnt]; +#ifdef DBG_TRACE + print(cmd[1], HEX); +#endif _platform.writeUart(cmd, 2); _TxByteCnt++; - return true; } - else + + // Check for last byte send + if (_TxByteCnt >= _sendBufferLength) { _TxByteCnt = 0; return false; } + return true; } void TpUartDataLinkLayer::addFrameTxQueue(CemiFrame& frame) diff --git a/src/knx/tpuart_data_link_layer.h b/src/knx/tpuart_data_link_layer.h index 61fe533..35305f3 100644 --- a/src/knx/tpuart_data_link_layer.h +++ b/src/knx/tpuart_data_link_layer.h @@ -31,16 +31,15 @@ class TpUartDataLinkLayer : public DataLinkLayer private: bool _enabled = false; - bool _waitConfirm = false; uint8_t* _sendBuffer = 0; uint16_t _sendBufferLength = 0; uint8_t _receiveBuffer[MAX_KNX_TELEGRAM_SIZE]; - uint8_t _loopState = 0; + uint8_t _txState = 0; + uint8_t _rxState = 0; uint16_t _RxByteCnt = 0; uint16_t _TxByteCnt = 0; uint8_t _oldIdx = 0; bool _isEcho = false; - bool _isAddressed = false; bool _convert = false; uint8_t _xorSum = 0; uint32_t _lastByteRxTime; diff --git a/src/knx_facade.h b/src/knx_facade.h index db62dae..b1ccb1b 100644 --- a/src/knx_facade.h +++ b/src/knx_facade.h @@ -8,6 +8,10 @@ #include "knx/bau2920.h" #include "knx/bau57B0.h" +#ifndef USERDATA_SAVE_SIZE +#define USERDATA_SAVE_SIZE 0 +#endif + #ifdef ARDUINO_ARCH_SAMD #include "samd_platform.h" #ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE @@ -45,7 +49,8 @@ #endif #endif -typedef uint8_t* (*SaveRestoreCallback)(uint8_t* buffer); +typedef const uint8_t* (*RestoreCallback)(const uint8_t* buffer); +typedef uint8_t* (*SaveCallback)(uint8_t* buffer); typedef void (*IsrFunctionPtr)(); template class KnxFacade : private SaveRestore @@ -54,18 +59,21 @@ template class KnxFacade : private SaveRestore KnxFacade() : _platformPtr(new P()), _bauPtr(new B(*_platformPtr)), _bau(*_bauPtr) { manufacturerId(0xfa); + bauNumber(platform().uniqueSerialNumber()); _bau.addSaveRestore(this); } KnxFacade(B& bau) : _bau(bau) { manufacturerId(0xfa); + bauNumber(platform().uniqueSerialNumber()); _bau.addSaveRestore(this); } KnxFacade(IsrFunctionPtr buttonISRFunction) : _platformPtr(new P()), _bauPtr(new B(*_platformPtr)), _bau(*_bauPtr) { manufacturerId(0xfa); + bauNumber(platform().uniqueSerialNumber()); _bau.addSaveRestore(this); setButtonISRFunction(buttonISRFunction); } @@ -226,7 +234,7 @@ template class KnxFacade : private SaveRestore { _bau.deviceObject().bauNumber(value); } - + void orderNumber(const uint8_t* value) { _bau.deviceObject().orderNumber(value); @@ -236,7 +244,7 @@ template class KnxFacade : private SaveRestore { _bau.deviceObject().hardwareType(value); } - + void version(uint16_t value) { _bau.deviceObject().version(value); @@ -268,12 +276,12 @@ template class KnxFacade : private SaveRestore _progButtonISRFuncPtr = progButtonISRFuncPtr; } - void setSaveCallback(SaveRestoreCallback func) + void setSaveCallback(SaveCallback func) { _saveCallback = func; } - void setRestoreCallback(SaveRestoreCallback func) + void setRestoreCallback(RestoreCallback func) { _restoreCallback = func; } @@ -286,6 +294,40 @@ template class KnxFacade : private SaveRestore return _bau.parameters().data(addr); } + // paramBit(address, shift) + // get state of a parameter as a boolean like "enable/disable", ... + // Declaration in XML file: + // ... + // + // + // + // + // + // + // ... + // + // + // + // + // + // + // + // + // + // ... + // Usage in code : + // if ( knx.paramBit(1,1)) + // { + // //do somthings .... + // } + bool paramBit(uint32_t addr, uint8_t shift) + { + if (!_bau.configured()) + return 0; + + return (bool) ((_bau.parameters().getByte(addr) >> (7-shift)) & 0x01); + } + uint8_t paramByte(uint32_t addr) { if (!_bau.configured()) @@ -293,7 +335,20 @@ template class KnxFacade : private SaveRestore return _bau.parameters().getByte(addr); } + + // Same usage than paramByte(addresse) for signed parameters + // Declaration in XML file + // + // + // + int8_t paramSignedByte(uint32_t addr) + { + if (!_bau.configured()) + return 0; + return (int8_t) _bau.parameters().getByte(addr); + } + uint16_t paramWord(uint32_t addr) { if (!_bau.configured()) @@ -338,11 +393,11 @@ template class KnxFacade : private SaveRestore uint32_t _ledPin = LED_BUILTIN; uint32_t _buttonPinInterruptOn = RISING; uint32_t _buttonPin = 0; - SaveRestoreCallback _saveCallback = 0; - SaveRestoreCallback _restoreCallback = 0; + SaveCallback _saveCallback = 0; + RestoreCallback _restoreCallback = 0; volatile bool _toggleProgMode = false; bool _progLedState = false; - uint16_t _saveSize = 0; + uint16_t _saveSize = USERDATA_SAVE_SIZE; IsrFunctionPtr _progButtonISRFuncPtr = 0; uint8_t* save(uint8_t* buffer) @@ -353,7 +408,7 @@ template class KnxFacade : private SaveRestore return buffer; } - uint8_t* restore(uint8_t* buffer) + const uint8_t* restore(const uint8_t* buffer) { if (_restoreCallback != 0) return _restoreCallback(buffer); diff --git a/src/linux_platform.cpp b/src/linux_platform.cpp index 6c53aaf..b1ff65f 100644 --- a/src/linux_platform.cpp +++ b/src/linux_platform.cpp @@ -501,6 +501,7 @@ void LinuxPlatform::setupUart() } } +#ifndef KNX_NO_PRINT void printUint64(uint64_t value, int base = DEC) { char buf[8 * sizeof(uint64_t) + 1]; @@ -707,6 +708,7 @@ void println(void) { printf("\n"); } +#endif // KNX_NO_PRINT void pinMode(uint32_t dwPin, uint32_t dwMode) { diff --git a/src/samd_platform.cpp b/src/samd_platform.cpp index db94c61..f370a33 100644 --- a/src/samd_platform.cpp +++ b/src/samd_platform.cpp @@ -6,7 +6,10 @@ #include #include -SamdPlatform::SamdPlatform() : ArduinoPlatform(&Serial1) +SamdPlatform::SamdPlatform() +#ifndef KNX_NO_DEFAULT_UART + : ArduinoPlatform(&Serial1) +#endif { } @@ -14,6 +17,26 @@ SamdPlatform::SamdPlatform( HardwareSerial* s) : ArduinoPlatform(s) { } +uint32_t SamdPlatform::uniqueSerialNumber() +{ + #if defined (__SAMD51__) + // SAMD51 from section 9.6 of the datasheet + #define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x008061FC) + #define SERIAL_NUMBER_WORD_1 *(volatile uint32_t*)(0x00806010) + #define SERIAL_NUMBER_WORD_2 *(volatile uint32_t*)(0x00806014) + #define SERIAL_NUMBER_WORD_3 *(volatile uint32_t*)(0x00806018) + #else + //#elif defined (__SAMD21E17A__) || defined(__SAMD21G18A__) || defined(__SAMD21E18A__) || defined(__SAMD21J18A__) + // SAMD21 from section 9.3.3 of the datasheet + #define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x0080A00C) + #define SERIAL_NUMBER_WORD_1 *(volatile uint32_t*)(0x0080A040) + #define SERIAL_NUMBER_WORD_2 *(volatile uint32_t*)(0x0080A044) + #define SERIAL_NUMBER_WORD_3 *(volatile uint32_t*)(0x0080A048) + #endif + + return SERIAL_NUMBER_WORD_0 ^ SERIAL_NUMBER_WORD_1 ^ SERIAL_NUMBER_WORD_2 ^ SERIAL_NUMBER_WORD_3; +} + void SamdPlatform::restart() { println("restart"); diff --git a/src/samd_platform.h b/src/samd_platform.h index 6b45c83..dd1765d 100644 --- a/src/samd_platform.h +++ b/src/samd_platform.h @@ -10,6 +10,9 @@ public: SamdPlatform(); SamdPlatform( HardwareSerial* s); + // unique serial number + uint32_t uniqueSerialNumber() override; + void restart(); uint8_t* getEepromBuffer(uint16_t size); void commitToEeprom(); diff --git a/src/stm32_platform.cpp b/src/stm32_platform.cpp index e05ab34..8fbfaa3 100644 --- a/src/stm32_platform.cpp +++ b/src/stm32_platform.cpp @@ -1,10 +1,13 @@ #include "stm32_platform.h" #ifdef ARDUINO_ARCH_STM32 -#include +#include #include "knx/bits.h" -Stm32Platform::Stm32Platform() : ArduinoPlatform(&Serial2) +Stm32Platform::Stm32Platform() +#ifndef KNX_NO_DEFAULT_UART + : ArduinoPlatform(&Serial2) +#endif { } @@ -17,6 +20,11 @@ Stm32Platform::~Stm32Platform() delete [] _eepromPtr; } +uint32_t Stm32Platform::uniqueSerialNumber() +{ + return HAL_GetUIDw0() ^ HAL_GetUIDw1() ^ HAL_GetUIDw2(); +} + void Stm32Platform::restart() { NVIC_SystemReset(); diff --git a/src/stm32_platform.h b/src/stm32_platform.h index 04870ec..056a327 100644 --- a/src/stm32_platform.h +++ b/src/stm32_platform.h @@ -8,6 +8,9 @@ public: Stm32Platform( HardwareSerial* s); ~Stm32Platform(); + // unique serial number + uint32_t uniqueSerialNumber() override; + // basic stuff void restart();