From 21a05ae9b3b8b094dc08940c44481d22c2026b48 Mon Sep 17 00:00:00 2001 From: zelogik Date: Tue, 9 Feb 2021 02:37:14 +0100 Subject: [PATCH 01/32] Add knx-433 (#122) * knx-pzem004v30 Add first draft of a pzem-004t power/current/tension/etc.. on your main line (BE CAREFUL WITH VOLTAGE!) PZEM-004t-v30 wired on a samd21 with the use of Sercom2 Use with slight modification of: https://github.com/mandulaj/PZEM-004T-v30 View issue: https://github.com/mandulaj/PZEM-004T-v30/issues/43 * Rename examples/knx-pzem0004t.xml to examples/knx-pzem004/knx-pzem0004t.xml * Delete knx-pzem004t.knxprod * Delete pzem-004t-v30.ino * Update: 'just move to right directory' * Update knx-pzem0004t.xml * Add: ProgMode * Fix/Update/Add Add: ResetEnergy function with datetime Update: led Class Fix: many typo, errors * Cleaning Some strange bug/feature... Information is transmitted only when the device got time from KNX, I have put if (timeStatus() == timeSet && resetPeriod != 0) { resetEnergyLoop(); } But don't know if it's enough... * Create knx-433Dio.ino * Add Files: README.md .xml and knxprod files * Delete pzem-004t-v30.ino * Update pzem-004t-v30.ino --- examples/knx-433Dio/KNXto433.xml | 153 ++++++++++ examples/knx-433Dio/README.md | 32 +++ examples/knx-433Dio/knx-433.knxprod | Bin 0 -> 36360 bytes examples/knx-433Dio/knx-433Dio.ino | 368 +++++++++++++++++++++++++ examples/knx-pzem004/pzem-004t-v30.ino | 31 ++- 5 files changed, 574 insertions(+), 10 deletions(-) create mode 100644 examples/knx-433Dio/KNXto433.xml create mode 100644 examples/knx-433Dio/README.md create mode 100644 examples/knx-433Dio/knx-433.knxprod create mode 100644 examples/knx-433Dio/knx-433Dio.ino 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 0000000000000000000000000000000000000000..b3d72fc26020af03997b811ec5c495634ceb38d8 GIT binary patch literal 36360 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 literal 0 HcmV?d00001 diff --git a/examples/knx-433Dio/knx-433Dio.ino b/examples/knx-433Dio/knx-433Dio.ino new file mode 100644 index 0000000..841dd6a --- /dev/null +++ b/examples/knx-433Dio/knx-433Dio.ino @@ -0,0 +1,368 @@ +#include +//#include + +//#define DEBUGSERIAL 1 +#ifdef DEBUGSERIAL + #define DPRINT(...) SerialUSB.print(__VA_ARGS__) + #define DPRINTLN(...) SerialUSB.println(__VA_ARGS__) + #include + #include +#else + #define DPRINT(...) //now defines a blank line + #define DPRINTLN(...) //now defines a blank line +#endif + +#define goButton1 knx.getGroupObject(1) +#define goButton2 knx.getGroupObject(2) +#define goButton3 knx.getGroupObject(3) +#define goButtonAll knx.getGroupObject(4) +#define goProgMode knx.getGroupObject(5) + +//DiOremote myRemote = DiOremote(6); + +//void function2(); +//void function1(void (*)()); +//void loop() { +// function1(function2); +//} + +//Global Const +const uint8_t ets_startupTimeout[7] = {0, 1, 2, 3, 4, 5, 6}; +const uint16_t ets_timePeriod[7] = {0, 1, 5, 15, 1 * 60, 5 * 60, 15 * 60}; +const uint8_t ets_progMode[7] = {0, 1, 2 * 60, 3 * 60, 4 * 60, 5 * 60, 10 * 60}; //need knxprod update... ? +const uint8_t ledPin = LED_BUILTIN; +const uint8_t rfPin = 6; + +// //Protocol timing (in us) +// #define DiOremote_START_FRAME_1 220 +// #define DiOremote_START_FRAME_0 2675 +// #define DiOremote_THIGH 220 +// #define DiOremote_TLOW_0 350 short +// #define DiOremote_TLOW_1 1400 long +// #define DiOremote_END_FRAME_1 220 +// #define DiOremote_END_FRAME_0 10600 + +// Global Variable +bool progMode = true; +// bool codeSendindBlock = false; + +uint8_t percentCycle = 0; // better to define a global or read knx.paramByte each time... ? +uint32_t timePeriod = 0; // same here, +uint32_t timerProgMode = 0; // same here, +uint32_t ch1_on, ch1_off, ch2_on, ch2_off, ch3_on, ch3_off, chall_on, chall_off; + +class RfCode { + private: + uint8_t _rfPin; + uint32_t _codeValueOn; + uint32_t _codeValueOff; + uint8_t _GOaddress; + const uint16_t THIGH = 220, TSTART = 2675, TSHORT = 220, TLONG = 1400, TEND = 10600; + uint8_t _loopCount = 0; // fixed ==5 + bool lastState = false; + uint32_t _codePending = 0; + uint32_t _codePendingMemory = _codePending; + uint8_t _loopPending = 0; // fixed ==32 + bool _sendMsgPending= false; + + enum PulseStates { + PULSE_INIT, + PULSE_KEY1, //32 times loop + PULSE_KEY2, //32 times loop + PULSE_END + }; + PulseStates pulseStates = PULSE_INIT; + + bool pulseSend(const uint16_t delayHigh, const uint16_t delayLow){ //alike state machine? + + static bool initPulse = false; + static bool highDone = false; + static uint32_t pulseLastTime = 0; + + uint32_t currentTime = micros(); + + if (!initPulse){ + initPulse = true; + digitalWrite(_rfPin, HIGH); + pulseLastTime = currentTime; + } + else if (currentTime - pulseLastTime >= delayHigh && !highDone) + { + digitalWrite(_rfPin, LOW); + pulseLastTime = currentTime; + highDone = true; + } + else if (currentTime - pulseLastTime >= delayLow && highDone) + { + initPulse = false; + highDone = false; + } + return !initPulse; + } + + uint16_t TTime(bool invert){ + uint16_t TTIME; + + if (_codePending & 0x80000000L)// future bug if uint32_t _codePengin > 2^32 / 2 ? + { + TTIME = invert ? TSHORT : TLONG; + } + else + { + TTIME = invert ? TLONG : TSHORT; + } + return TTIME; + } + + public: + RfCode(uint8_t rfPin){ + _rfPin = rfPin; + pinMode(_rfPin, OUTPUT); + } + +// void init(uint8_t addr, uint32_t codeValueOn, uint32_t codeValueOff){ //GroupObject &device, long timeOn){ +// _codeValueOn = codeValueOn; +// _codeValueOff = codeValueOff; +// _GOaddress = addr; +// } + + void setState(bool modeOnOff, uint32_t codeValueOn, uint32_t codeValueOff){ + if (!_sendMsgPending) + { + _sendMsgPending = true; + _codeValueOn = codeValueOn; + _codeValueOff = codeValueOff; +// _GOaddress = addr; + + if (modeOnOff){ + _codePending = _codeValueOn; +// SerialUSB.println(_codePending); + } + else + { + _codePending = _codeValueOff; +// SerialUSB.println(_codePending); + } + _codePendingMemory = _codePending; + } + } + +// bool getMsgPendingState(){ +// return _sendMsgPending; +// } + + void loop(){ + if (_sendMsgPending) + { + // needed to block setState of another Class, yes FIFO is better... +// codeSendindBlock = true; + if (_loopCount < 5) + { + switch (pulseStates){ + case PULSE_INIT: + if (pulseSend(THIGH, TSTART)){ + pulseStates = PULSE_KEY1; + } + break; + case PULSE_KEY1: + if (pulseSend(THIGH, TTime(false))) + { + pulseStates = PULSE_KEY2; + } + break; + case PULSE_KEY2: + if (pulseSend(THIGH, TTime(true))) + { + if (_loopPending < 32){ + _codePending <<= 1; + _loopPending++; + pulseStates = PULSE_KEY1; // next loop ! + } + else{ // finish! + pulseStates = PULSE_END; + } + } + break; + case PULSE_END: + if (pulseSend(THIGH, TEND)){ + _loopCount++; + _codePending = _codePendingMemory; + _loopPending = 0; + pulseStates = PULSE_INIT; + } + break; + default: + break; + } + } + else + { + _loopCount = 0; + _sendMsgPending = false; +// codeSendindBlock = false; + } + } + } +}; + + +class Blinker +{ + private: + uint8_t ledPin_; // the number of the LED pin + uint32_t OnTime = 1000; // milliseconds of on-time + uint32_t OffTime = 1000; // milliseconds of off-time + bool ledState = LOW; // ledState used to set the LED + uint32_t previousMillis; // will store last time LED was updated + + void setOutput(bool state_, uint32_t currentMillis_){ + ledState = state_; + previousMillis = currentMillis_; + digitalWrite(ledPin_, state_); + } + + public: + Blinker(uint8_t pin) + { + ledPin_ = pin; + pinMode(ledPin_, OUTPUT); + previousMillis = 0; + } + + void set(uint32_t on, uint32_t off){ + OnTime = on; + OffTime = off; + } + + void loop(){ + uint32_t currentMillis = millis(); + + if((ledState == HIGH) && (currentMillis - previousMillis >= OnTime)) + { + setOutput(LOW, currentMillis); + } + else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime)) + { + setOutput(HIGH, currentMillis); + } + } +}; + +RfCode button = RfCode(5); + +Blinker led = Blinker(ledPin); + + +void callBackButton1(GroupObject& go){ + button.setState((bool)go.value(), ch1_on, ch1_off); +} +void callBackButton2(GroupObject& go){ + button.setState((bool)go.value(), ch2_on, ch2_off); +} +void callBackButton3(GroupObject& go){ + button.setState((bool)go.value(), ch3_on, ch3_off); +} +void callBackButtonAll(GroupObject& go){ + button.setState((bool)go.value(), chall_on, ch3_off); +} +void callBackProgMode(GroupObject& go){ + progMode = (bool)go.value(); +} + +void setup() { + +// #ifdef DEBUGSERIAL +// SerialUSB.begin(9600); +// while (!SerialUSB) { //wait for DEBUGING +// ; // wait for serial port to connect. Needed for native USB port only +// } +// ArduinoPlatform::SerialDebug = &SerialUSB; +// #endif + + randomSeed(millis()); + // knx.bau().deviceObject().individualAddress(1); + knx.readMemory(); + + if (knx.configured()) + { + DPRINT("Setup: KNX Configuration..."); + + progMode = false; // don't need to put device in progMode. + int confStartupTime = ets_startupTimeout[knx.paramByte(0)] * 1000; + delay(confStartupTime); // the only delay used, why make a withoutDelay function for that? + + timePeriod = ets_timePeriod[knx.paramByte(1)] * 1000; +// timerProgMode = ets_progMode[knx.paramByte(34)] * 1000; + + ch1_on = knx.paramInt(2); + ch1_off = knx.paramInt(6); + goButton1.callback(callBackButton1); + goButton1.dataPointType(DPT_Switch); + + ch2_on = knx.paramInt(10); + ch2_off = knx.paramInt(14); + goButton2.callback(callBackButton2); + goButton2.dataPointType(DPT_Switch); + + ch3_on = knx.paramInt(18); + ch3_off = knx.paramInt(22); + goButton3.callback(callBackButton3); + goButton3.dataPointType(DPT_Switch); + + chall_on = knx.paramByte(26); + chall_off = knx.paramByte(30); + goButtonAll.callback(callBackButtonAll); + goButtonAll.dataPointType(DPT_Switch); + + goProgMode.callback(callBackProgMode); + goProgMode.dataPointType(DPT_Trigger); + + DPRINTLN("Finished"); + } + + knx.ledPin(5); + knx.ledPinActiveOn(HIGH); + knx.buttonPin(9); + + knx.start(); + led.set(2000, 2000); +} + +void loop() +{ + knx.loop(); + led.loop(); + if (knx.configured() && !progMode) + { + button.loop(); + } + else if (progMode) + { + prodModeLoop(); + } + +} + +void prodModeLoop(){ // run Only if progMode triggered ( at start or callback) + + const uint32_t timerProgMode = ( 15 * 60 * 1000 ) ; // 15min + static uint32_t timerProgPrevMillis = 0; + + if (!knx.progMode()) + { + knx.progMode(true); + led.set(500, 500); + timerProgPrevMillis = millis(); + DPRINTLN("progModeLoop Start"); + } + else + { + if (millis() - timerProgPrevMillis > timerProgMode) { + knx.progMode(false); + goProgMode.value(false); + progMode = 0; + led.set(100, 100); // panic! + DPRINTLN("progModeLoop Stop"); + } + } +} diff --git a/examples/knx-pzem004/pzem-004t-v30.ino b/examples/knx-pzem004/pzem-004t-v30.ino index 2bc92ff..2bf6527 100644 --- a/examples/knx-pzem004/pzem-004t-v30.ino +++ b/examples/knx-pzem004/pzem-004t-v30.ino @@ -38,7 +38,7 @@ const uint8_t physicalCount = 6; // voltage,current,power_factor,power,energy,fr uint8_t percentCycle = 0; // better to define a global or read knx.paramByte each time... ? uint32_t timePeriod = 0; // same here, uint8_t resetPeriod = 0; //same here ... -uint8_t resetEnergy = 0; // and here... disabled/day/week/month +//uint8_t resetEnergy = 0; // and here... disabled/day/week/month bool progMode = true; @@ -54,11 +54,10 @@ struct Physical { void loop(){ // unsigned long currentMillis = millis(); // Delta Change update as defined in ETS - int32_t deltaPercent = ( 100 * ( _value - _lastValue ) / _value ); + float deltaPercent = ( 100 * ( _value - _lastValue ) / _value ); if ( percentCycle != 0 && abs(deltaPercent) >= percentCycle ) { _trigger = true; - _lastValue = _value; } // Refresh groupAddress value as defined in ETS since last update @@ -70,6 +69,7 @@ struct Physical { // UpdateGO but send to bus only if triggered by time or value change percentage if (_trigger){ knx.getGroupObject(_GOaddr).value(_value, _dpt); + _lastValue = _value; _lastMillis = millis(); _trigger = false; }else{ @@ -143,7 +143,6 @@ class Blinker Blinker led = Blinker(ledPin); - void callBackProgMode(GroupObject& go){ progMode = (bool)go.value(); } @@ -172,7 +171,7 @@ void resetCallback(GroupObject& go) { if (go.value()) { - resetEnergy = true; + pzem.resetEnergy(); goReset.value(false); } } @@ -181,7 +180,7 @@ void setup() { pinPeripheral(PIN_SERIAL2_RX, PIO_SERCOM); pinPeripheral(PIN_SERIAL2_TX, PIO_SERCOM); - SerialUSB.begin(9600); +// SerialUSB.begin(9600); Serial2.begin(9600); ArduinoPlatform::SerialDebug = &SerialUSB; @@ -238,12 +237,17 @@ void loop() { if (knx.configured() && !progMode) { refreshValueLoop(); - resetEnergyLoop(); for (uint8_t i=0; i< physicalCount; i++) { Physical[i].loop(); } + + if (timeStatus() == timeSet && resetPeriod != 0) + { + resetEnergyLoop(); + } + } else if (progMode) { @@ -257,7 +261,7 @@ void refreshValueLoop(){ if (millis() - lastPzemUpdate >= pzemInterval) { - for (uint8_t i=0; i< physicalCount; i++) + for (uint8_t i=0; i < physicalCount; i++) { float isaValue; switch (i) { //maybe a pointer or reference could be nicer... @@ -282,11 +286,18 @@ void refreshValueLoop(){ default: break; } + if(!isnan(isaValue)) { Physical[i].setValue(isaValue); } + else + { + Physical[i].setValue(-1); + } } + lastPzemUpdate = millis(); + led.set(500, 1000); } } @@ -339,14 +350,14 @@ void prodModeLoop(){ // run Only if progMode triggered ( at start or callback) { knx.progMode(true); timerProgPrevMillis = millis(); - led.set(500, 250); + led.set(50, 100); } else { if (millis() - timerProgPrevMillis > timerProgMode) { knx.progMode(false); goProgMode.value(false); - progMode = 0; + progMode = false; } } } From 164bf59166dd5030f94e6857f3e3daf447f8301f Mon Sep 17 00:00:00 2001 From: mumpf Date: Thu, 18 Feb 2021 09:40:35 +0100 Subject: [PATCH 02/32] added alternative GroupObject with less mangagement footprint (#125) * corrected float with DPT9 * Switch Programming-LED also via Bus/ETS * Again: Prog-LED switchable from bus/ETS * DPT16 (to bus) implemented * - added SMALL_GROUPOBJECT * - added knx-demo-small-go example - added config.h option (commented) - changed platformio-ci.ini with -DSMALL_GROUPOBJECT - changed plantformio.ini with tested -DSMALL_GROUPOBJECT * - removed duplicate files Co-authored-by: Waldemar Porscha Co-authored-by: Waldemar Porscha --- examples/knx-demo-smal-go/knx-demo.ino | 129 +++++++++++++++++++++++ examples/knx-demo-smal-go/platformio.ini | 120 +++++++++++++++++++++ src/knx/bau_systemB_device.cpp | 4 + src/knx/config.h | 6 ++ src/knx/group_object.cpp | 34 +++++- src/knx/group_object.h | 64 +++++++---- 6 files changed, 332 insertions(+), 25 deletions(-) create mode 100644 examples/knx-demo-smal-go/knx-demo.ino create mode 100644 examples/knx-demo-smal-go/platformio.ini 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/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/config.h b/src/knx/config.h index 0b3ce64..a042ae3 100644 --- a/src/knx/config.h +++ b/src/knx/config.h @@ -60,6 +60,12 @@ // 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 + + #endif #if !defined(MASK_VERSION) 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 7fdf5e6..6a07b5f 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 }; From 44075d80f7ca1144ce1ce8387475fca09c790e9b Mon Sep 17 00:00:00 2001 From: etrinh Date: Thu, 11 Mar 2021 09:38:06 +0100 Subject: [PATCH 03/32] Add KNX_NO_PRINT to define in project to avoid console info and reduce footprint (#127) --- src/arduino_platform.cpp | 2 ++ src/cc1310_platform.cpp | 2 ++ src/knx/bits.cpp | 2 ++ src/knx/bits.h | 6 ++++++ src/linux_platform.cpp | 2 ++ 5 files changed, 14 insertions(+) diff --git a/src/arduino_platform.cpp b/src/arduino_platform.cpp index 65ec833..d1d797c 100644 --- a/src/arduino_platform.cpp +++ b/src/arduino_platform.cpp @@ -112,6 +112,7 @@ int ArduinoPlatform::readWriteSpi(uint8_t *data, size_t len) return 0; } +#ifndef KNX_NO_PRINT void printUint64(uint64_t value, int base = DEC) { char buf[8 * sizeof(uint64_t) + 1]; @@ -284,3 +285,4 @@ void println(void) { ArduinoPlatform::SerialDebug->println(); } +#endif // KNX_NO_PRINT 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/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 2a30385..cd0ca8a 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/linux_platform.cpp b/src/linux_platform.cpp index 6501730..4ad15b3 100644 --- a/src/linux_platform.cpp +++ b/src/linux_platform.cpp @@ -502,6 +502,7 @@ void LinuxPlatform::setupUart() } } +#ifndef KNX_NO_PRINT void printUint64(uint64_t value, int base = DEC) { char buf[8 * sizeof(uint64_t) + 1]; @@ -708,6 +709,7 @@ void println(void) { printf("\n"); } +#endif // KNX_NO_PRINT void pinMode(uint32_t dwPin, uint32_t dwMode) { From 446ea1b9aa82052d6ff81fc32211ff49c6f3a710 Mon Sep 17 00:00:00 2001 From: etrinh Date: Thu, 11 Mar 2021 20:31:09 +0100 Subject: [PATCH 04/32] Fix int32 -> KNXValue conversion (#128) Add KNX_NO_SPI to reduce footprint --- src/arduino_platform.cpp | 4 ++++ src/arduino_platform.h | 3 ++- src/knx/knx_value.cpp | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/arduino_platform.cpp b/src/arduino_platform.cpp index d1d797c..169e653 100644 --- a/src/arduino_platform.cpp +++ b/src/arduino_platform.cpp @@ -2,7 +2,9 @@ #include "knx/bits.h" #include +#ifndef KNX_NO_SPI #include +#endif Stream* ArduinoPlatform::SerialDebug = &Serial; @@ -94,6 +96,7 @@ size_t ArduinoPlatform::readBytesUart(uint8_t *buffer, size_t length) return length; } +#ifndef KNX_NO_SPI void ArduinoPlatform::setupSpi() { SPI.begin(); @@ -111,6 +114,7 @@ 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) diff --git a/src/arduino_platform.h b/src/arduino_platform.h index 10c10e6..33929da 100644 --- a/src/arduino_platform.h +++ b/src/arduino_platform.h @@ -24,10 +24,11 @@ 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 static Stream* SerialDebug; protected: diff --git a/src/knx/knx_value.cpp b/src/knx/knx_value.cpp index 0a7c104..de7a4b7 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; } From 036bd54c79411f6b76342a12e4bada33f3be8ba0 Mon Sep 17 00:00:00 2001 From: etrinh Date: Thu, 1 Apr 2021 09:34:26 +0200 Subject: [PATCH 05/32] Tweak for Ram usage (#129) * Add KNX_NO_DEFAULT_UART to avoid default uart assignation for ArduinoPlatforms and derivatives, with associated defines (HWSERIAL_NONE for stm32) to recover RAM from unused RX/TX buffers Remove ArduinoPlatform(HardwareSerial* knxSerial) constructor, seems not useable by KnxFacade * Restore ArduinoPlatform::ArduinoPlatform(HardwareSerial* knxSerial) constructor * revert xxxPlatform(HardwareSerial* s) constructors --- src/arduino_platform.cpp | 9 ++++++++- src/arduino_platform.h | 5 +++-- src/esp32_platform.cpp | 5 ++++- src/esp_platform.cpp | 5 ++++- src/samd_platform.cpp | 5 ++++- src/stm32_platform.cpp | 5 ++++- 6 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/arduino_platform.cpp b/src/arduino_platform.cpp index 169e653..8c58dfb 100644 --- a/src/arduino_platform.cpp +++ b/src/arduino_platform.cpp @@ -6,7 +6,13 @@ #include #endif +#ifndef KNX_NO_PRINT Stream* ArduinoPlatform::SerialDebug = &Serial; +#endif + +ArduinoPlatform::ArduinoPlatform() : _knxSerial(nullptr) +{ +} ArduinoPlatform::ArduinoPlatform(HardwareSerial* knxSerial) : _knxSerial(knxSerial) { @@ -29,7 +35,8 @@ void ArduinoPlatform::fatalError() void ArduinoPlatform::knxUart( HardwareSerial* serial ) { - closeUart(); + if (_knxSerial) + closeUart(); _knxSerial = serial; setupUart(); } diff --git a/src/arduino_platform.h b/src/arduino_platform.h index 33929da..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 @@ -29,7 +28,9 @@ class ArduinoPlatform : public Platform 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/esp32_platform.cpp b/src/esp32_platform.cpp index cc902dd..ccf74a8 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 { } diff --git a/src/esp_platform.cpp b/src/esp_platform.cpp index f453fea..eb2b545 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 { } diff --git a/src/samd_platform.cpp b/src/samd_platform.cpp index db94c61..23f4e08 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 { } diff --git a/src/stm32_platform.cpp b/src/stm32_platform.cpp index e05ab34..ea3d405 100644 --- a/src/stm32_platform.cpp +++ b/src/stm32_platform.cpp @@ -4,7 +4,10 @@ #include #include "knx/bits.h" -Stm32Platform::Stm32Platform() : ArduinoPlatform(&Serial2) +Stm32Platform::Stm32Platform() +#ifndef KNX_NO_DEFAULT_UART + : ArduinoPlatform(&Serial2) +#endif { } From e57bbf9dbe25505a9f02d83f111d9df7852c18df Mon Sep 17 00:00:00 2001 From: OutOfSync1 Date: Mon, 12 Apr 2021 11:40:56 +0200 Subject: [PATCH 06/32] generate unique serial number (#90) (#131) * generate unique serial number (#90) * see https://github.com/ricaun/ArduinoUniqueID * calculated from ESP.getEfuseMac() on ESP32 * ESP.getChipId() on ESP8266 * SERIAL_NUMBER_WORD_0-3 on SAMD * HAL_GetUIDw0-2() on STM32 * defaults to 0x01020304 on other platforms * fix variable name for ESP platform * another fix variable name for ESP platform (need more coffee...) --- src/esp32_platform.cpp | 12 ++++++++++++ src/esp32_platform.h | 3 +++ src/esp_platform.cpp | 9 +++++++++ src/esp_platform.h | 5 ++++- src/knx/platform.cpp | 5 +++++ src/knx/platform.h | 3 +++ src/knx_facade.h | 7 +++++-- src/samd_platform.cpp | 24 ++++++++++++++++++++++++ src/samd_platform.h | 3 +++ src/stm32_platform.cpp | 9 +++++++++ src/stm32_platform.h | 3 +++ 11 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/esp32_platform.cpp b/src/esp32_platform.cpp index ccf74a8..585464f 100644 --- a/src/esp32_platform.cpp +++ b/src/esp32_platform.cpp @@ -37,6 +37,18 @@ 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); + uint32_t uniqueId = (upperId ^ lowerId); + + Serial.printf("uniqueSerialNumber: %0X ^ %0X ==> %0X\n", upperId, lowerId, uniqueId); + + return uniqueId; +} + void Esp32Platform::restart() { println("restart"); diff --git a/src/esp32_platform.h b/src/esp32_platform.h index 3c006ba..ff06179 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(); diff --git a/src/esp_platform.cpp b/src/esp_platform.cpp index eb2b545..ffb3414 100644 --- a/src/esp_platform.cpp +++ b/src/esp_platform.cpp @@ -38,6 +38,15 @@ void EspPlatform::macAddress(uint8_t * addr) wifi_get_macaddr(STATION_IF, addr); } +uint32_t EspPlatform::uniqueSerialNumber() +{ + uint32_t chipid = ESP.getChipId(); + + Serial.printf("uniqueSerialNumber: %0X\n", chipid); + + return chipid; +} + void EspPlatform::restart() { println("restart"); diff --git a/src/esp_platform.h b/src/esp_platform.h index cd026d8..04234a4 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(); @@ -30,7 +33,7 @@ class EspPlatform : public ArduinoPlatform void commitToEeprom(); private: WiFiUDP _udp; - uint32_t _mulitcastAddr; + uint32_t _mulitcastAddr; uint16_t _mulitcastPort; }; diff --git a/src/knx/platform.cpp b/src/knx/platform.cpp index 2484ba1..1816960 100644 --- a/src/knx/platform.cpp +++ b/src/knx/platform.cpp @@ -72,6 +72,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 8604484..769083f 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_facade.h b/src/knx_facade.h index 27e024a..e858440 100644 --- a/src/knx_facade.h +++ b/src/knx_facade.h @@ -49,18 +49,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); } @@ -221,7 +224,7 @@ template class KnxFacade : private SaveRestore { _bau.deviceObject().bauNumber(value); } - + void orderNumber(const uint8_t* value) { _bau.deviceObject().orderNumber(value); @@ -231,7 +234,7 @@ template class KnxFacade : private SaveRestore { _bau.deviceObject().hardwareType(value); } - + void version(uint16_t value) { _bau.deviceObject().version(value); diff --git a/src/samd_platform.cpp b/src/samd_platform.cpp index 23f4e08..c003838 100644 --- a/src/samd_platform.cpp +++ b/src/samd_platform.cpp @@ -17,6 +17,30 @@ 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 + + uint32_t uniqueId = SERIAL_NUMBER_WORD_0 ^ SERIAL_NUMBER_WORD_1 ^ SERIAL_NUMBER_WORD_2 ^ SERIAL_NUMBER_WORD_3; + + printf("uniqueSerialNumber: %0X\n", uniqueId); + + return uniqueId; +} + 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 ea3d405..9b7c979 100644 --- a/src/stm32_platform.cpp +++ b/src/stm32_platform.cpp @@ -20,6 +20,15 @@ Stm32Platform::~Stm32Platform() delete [] _eepromPtr; } +uint32_t Stm32Platform::uniqueSerialNumber() +{ + uint32_t uniqueId = HAL_GetUIDw0() ^ HAL_GetUIDw1() ^ HAL_GetUIDw2(); + + printf("uniqueSerialNumber: %0X", uniqueId); + + return uniqueId; +} + 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(); From 1343ed0b7de6502cbb2996ec7ac80653d18c385c Mon Sep 17 00:00:00 2001 From: OutOfSync1 Date: Wed, 14 Apr 2021 23:44:12 +0200 Subject: [PATCH 07/32] fixes to enable partial programming (#132) * fixes to enable partial programming (thanks to mumpf and proggerKA at KNX-UF!) * add PID_MCB_TABLE to TableObject * add CallBackProperty to send PID_MCB_TABLE * calculate crc checksum using Crc16Citt when state switches to LOAD_COMPLETED * add crc to save(), restore() and saveSize() to save crc to flash * add CallBackProperty for read and write of PID_PROG_VERSION to ApplicationProgramObject * create overrides for save, restore, and saveSize in ApplicationProgramObject to save _programVersion to flash * improve crc calculation * removed TableObject::crc16Citt method and use the one in bits.h * do not save crc in flash, instead calculate on-the-fly in CallbackProperty when state==LS_LOADED * use DataProperty to store PID_PROG_VERSION * WARNING: segmentSize calculation for crc calculation is currently not correct. Need to somehow access size of data in class that inherits from TableObject (e.g. ApplicationObject or RouterObject) * fix segment size in TableObject() * save size after TableObject::allocTable() is called. Also change save() and restore() to save _size to flash. Modify saveSize() * use _size to calculate crc value in CallbackProperty * reduce footprint, save 5 byte * add comment why _size field is needed * remove PID_MCB_TABLE from RouterObject * this is now implemented in TableObject --- src/knx/application_program_object.cpp | 23 +++++++++++++++++++++++ src/knx/application_program_object.h | 3 +++ src/knx/router_object.cpp | 19 ------------------- src/knx/router_object.h | 2 -- src/knx/table_object.cpp | 25 +++++++++++++++++++++++-- src/knx/table_object.h | 11 +++++++++-- 6 files changed, 58 insertions(+), 25 deletions(-) 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/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; }; From 14162fb1a44d8ea5cc33434d91e85e95684fb2ab Mon Sep 17 00:00:00 2001 From: OutOfSync1 Date: Wed, 14 Apr 2021 23:46:03 +0200 Subject: [PATCH 08/32] fix userdata saverestore (#133) * * fix for save/restore of userdata * change declaration of restore() in knx_facade.h to "const uint8_t* restore(const uint8_t* buffer)" to avoid calling default implementation in save_restore.h * change typedefs to separate SaveCallback and RestoreCallback * fix BME60 example, knx.setRestoreCallback() needs to use const uint8_t* as well * fix commitToEeprom for ESP32 * trigger dirty flag for ESP32 to make sure data is committed --- examples/knx-bme680/knx-bme680.ino | 4 ++-- src/esp32_platform.cpp | 1 + src/knx_facade.h | 13 +++++++------ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/examples/knx-bme680/knx-bme680.ino b/examples/knx-bme680/knx-bme680.ino index b6deb6d..f10a723 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(); @@ -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/src/esp32_platform.cpp b/src/esp32_platform.cpp index 585464f..5c28ee1 100644 --- a/src/esp32_platform.cpp +++ b/src/esp32_platform.cpp @@ -104,6 +104,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/knx_facade.h b/src/knx_facade.h index e858440..e0cecab 100644 --- a/src/knx_facade.h +++ b/src/knx_facade.h @@ -40,7 +40,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 @@ -266,12 +267,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; } @@ -336,8 +337,8 @@ 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; @@ -351,7 +352,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); From a4e74ebae5a626a2dedca83a890c86bb05555165 Mon Sep 17 00:00:00 2001 From: etrinh Date: Sat, 17 Apr 2021 11:12:19 +0200 Subject: [PATCH 09/32] Remove uniqueSerialNumber debug log to reduce footprint (#134) --- src/esp32_platform.cpp | 6 +----- src/esp_platform.cpp | 6 +----- src/samd_platform.cpp | 6 +----- src/stm32_platform.cpp | 6 +----- 4 files changed, 4 insertions(+), 20 deletions(-) diff --git a/src/esp32_platform.cpp b/src/esp32_platform.cpp index 5c28ee1..9268cf0 100644 --- a/src/esp32_platform.cpp +++ b/src/esp32_platform.cpp @@ -42,11 +42,7 @@ uint32_t Esp32Platform::uniqueSerialNumber() uint64_t chipid = ESP.getEfuseMac(); uint32_t upperId = (chipid >> 32) & 0xFFFFFFFF; uint32_t lowerId = (chipid & 0xFFFFFFFF); - uint32_t uniqueId = (upperId ^ lowerId); - - Serial.printf("uniqueSerialNumber: %0X ^ %0X ==> %0X\n", upperId, lowerId, uniqueId); - - return uniqueId; + return (upperId ^ lowerId); } void Esp32Platform::restart() diff --git a/src/esp_platform.cpp b/src/esp_platform.cpp index ffb3414..8c70b61 100644 --- a/src/esp_platform.cpp +++ b/src/esp_platform.cpp @@ -40,11 +40,7 @@ void EspPlatform::macAddress(uint8_t * addr) uint32_t EspPlatform::uniqueSerialNumber() { - uint32_t chipid = ESP.getChipId(); - - Serial.printf("uniqueSerialNumber: %0X\n", chipid); - - return chipid; + return ESP.getChipId(); } void EspPlatform::restart() diff --git a/src/samd_platform.cpp b/src/samd_platform.cpp index c003838..f370a33 100644 --- a/src/samd_platform.cpp +++ b/src/samd_platform.cpp @@ -34,11 +34,7 @@ uint32_t SamdPlatform::uniqueSerialNumber() #define SERIAL_NUMBER_WORD_3 *(volatile uint32_t*)(0x0080A048) #endif - uint32_t uniqueId = SERIAL_NUMBER_WORD_0 ^ SERIAL_NUMBER_WORD_1 ^ SERIAL_NUMBER_WORD_2 ^ SERIAL_NUMBER_WORD_3; - - printf("uniqueSerialNumber: %0X\n", uniqueId); - - return uniqueId; + return SERIAL_NUMBER_WORD_0 ^ SERIAL_NUMBER_WORD_1 ^ SERIAL_NUMBER_WORD_2 ^ SERIAL_NUMBER_WORD_3; } void SamdPlatform::restart() diff --git a/src/stm32_platform.cpp b/src/stm32_platform.cpp index 9b7c979..4e70231 100644 --- a/src/stm32_platform.cpp +++ b/src/stm32_platform.cpp @@ -22,11 +22,7 @@ Stm32Platform::~Stm32Platform() uint32_t Stm32Platform::uniqueSerialNumber() { - uint32_t uniqueId = HAL_GetUIDw0() ^ HAL_GetUIDw1() ^ HAL_GetUIDw2(); - - printf("uniqueSerialNumber: %0X", uniqueId); - - return uniqueId; + return HAL_GetUIDw0() ^ HAL_GetUIDw1() ^ HAL_GetUIDw2(); } void Stm32Platform::restart() From 95cf9df7fc3d509060742d888464230d2fca7b14 Mon Sep 17 00:00:00 2001 From: OutOfSync1 Date: Tue, 4 May 2021 16:17:27 +0200 Subject: [PATCH 10/32] fix userdata saverestore (#133) (#135) * set saveSize at compile time * make sure USERDATA_SAVE_SIZE is set to valid value --- src/knx_facade.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/knx_facade.h b/src/knx_facade.h index e0cecab..c23bdd2 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 @@ -341,7 +345,7 @@ template class KnxFacade : private SaveRestore 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) From ed54da708916f7f15668fce928d0cfd20e51bf2b Mon Sep 17 00:00:00 2001 From: OutOfSync1 Date: Tue, 4 May 2021 16:19:29 +0200 Subject: [PATCH 11/32] fix some typos (#136) * removed one section that was duplicate in dptconvert.cpp --- src/esp_platform.cpp | 8 +- src/esp_platform.h | 4 +- src/knx/address_table_object.h | 10 +-- src/knx/application_layer.cpp | 10 +-- src/knx/application_layer.h | 8 +- src/knx/bau_systemB.cpp | 4 +- src/knx/data_property.cpp | 2 +- src/knx/dptconvert.cpp | 101 ---------------------- src/knx/ip_parameter_object.cpp | 2 +- src/knx/knx_ip_device_information_dib.cpp | 4 +- src/knx/knx_ip_device_information_dib.h | 4 +- src/knx/knx_ip_search_response.cpp | 2 +- src/knx/memory.h | 2 +- src/knx/rf_medium_object.cpp | 2 +- src/knx/rf_physical_layer_cc1101.h | 30 +++---- src/knx/rf_physical_layer_cc1310.cpp | 4 +- 16 files changed, 48 insertions(+), 149 deletions(-) diff --git a/src/esp_platform.cpp b/src/esp_platform.cpp index 8c70b61..28d0b5a 100644 --- a/src/esp_platform.cpp +++ b/src/esp_platform.cpp @@ -51,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()); @@ -69,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; diff --git a/src/esp_platform.h b/src/esp_platform.h index 04234a4..80c0ad1 100644 --- a/src/esp_platform.h +++ b/src/esp_platform.h @@ -33,8 +33,8 @@ class EspPlatform : public ArduinoPlatform 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/bau_systemB.cpp b/src/knx/bau_systemB.cpp index e36656e..39bc894 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: @@ -515,7 +515,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/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/dptconvert.cpp b/src/knx/dptconvert.cpp index ea55d73..e595238 100644 --- a/src/knx/dptconvert.cpp +++ b/src/knx/dptconvert.cpp @@ -132,107 +132,6 @@ 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); - 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) - 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); } return false; } diff --git a/src/knx/ip_parameter_object.cpp b/src/knx/ip_parameter_object.cpp index d894dab..c435f1a 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), 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/memory.h b/src/knx/memory.h index 715c2b4..962437f 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/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)) From 75c863bffe89e797d39d8bb3dcd85451dbbb95dd Mon Sep 17 00:00:00 2001 From: etrinh Date: Wed, 12 May 2021 13:03:10 +0200 Subject: [PATCH 12/32] Add new KNX_NO_STRTOx_CONVERSION define for footprint reduction (#137) * Remove uniqueSerialNumber debug log to reduce footprint * add KNX_NO_STRTOx_CONVERSION to avoid expensive strtod conversion --- src/knx/config.h | 10 ++++++++++ src/knx/knx_value.cpp | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/knx/config.h b/src/knx/config.h index a042ae3..f937510 100644 --- a/src/knx/config.h +++ b/src/knx/config.h @@ -65,6 +65,16 @@ // 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 diff --git a/src/knx/knx_value.cpp b/src/knx/knx_value.cpp index de7a4b7..79ee503 100644 --- a/src/knx/knx_value.cpp +++ b/src/knx/knx_value.cpp @@ -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; } From 14462d410d0021c8aa220594744da20c9f2c715a Mon Sep 17 00:00:00 2001 From: croghostrider Date: Wed, 2 Jun 2021 17:53:46 +0200 Subject: [PATCH 13/32] Change individual address (#140) to 15.15.255 --- src/knx/device_object.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; }; From 2450c1c458851baf4d2a5e4ed8f474dba989121e Mon Sep 17 00:00:00 2001 From: croghostrider Date: Wed, 2 Jun 2021 17:55:07 +0200 Subject: [PATCH 14/32] fix ESP32 IP KNX Demo (#139) --- examples/knx-demo/knx-demo.ino | 5 +++-- examples/knx-demo/platformio-ci.ini | 1 + examples/knx-demo/platformio.ini | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/knx-demo/knx-demo.ino b/examples/knx-demo/knx-demo.ino index f062daf..e51a7d6 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 defined ARDUINO_ARCH_ESP8266 || defined ARDUINO_ARCH_ESP32 #include #endif @@ -59,7 +60,7 @@ void setup() randomSeed(millis()); -#ifdef ARDUINO_ARCH_ESP8266 +#if 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..4c76caa 100644 --- a/examples/knx-demo/platformio-ci.ini +++ b/examples/knx-demo/platformio-ci.ini @@ -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..5f393b1 100644 --- a/examples/knx-demo/platformio.ini +++ b/examples/knx-demo/platformio.ini @@ -79,6 +79,7 @@ framework = arduino lib_extra_dirs = ../../../ lib_deps = + https://github.com/tzapu/WiFiManager.git knx build_flags = From 59d1b67d6ccdd89e43b404bb85166a4902c77a9d Mon Sep 17 00:00:00 2001 From: croghostrider Date: Fri, 4 Jun 2021 17:23:23 +0200 Subject: [PATCH 15/32] add KNX IP discovery support (#141) for ESP8266 and ESP32 --- src/esp32_platform.cpp | 12 ++++++++++++ src/esp32_platform.h | 3 +++ src/esp_platform.cpp | 12 ++++++++++++ src/esp_platform.h | 3 +++ 4 files changed, 30 insertions(+) diff --git a/src/esp32_platform.cpp b/src/esp32_platform.cpp index 9268cf0..6f49934 100644 --- a/src/esp32_platform.cpp +++ b/src/esp32_platform.cpp @@ -92,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); diff --git a/src/esp32_platform.h b/src/esp32_platform.h index ff06179..07b0d75 100644 --- a/src/esp32_platform.h +++ b/src/esp32_platform.h @@ -28,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 28d0b5a..14a7c58 100644 --- a/src/esp_platform.cpp +++ b/src/esp_platform.cpp @@ -92,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 80c0ad1..c535bae 100644 --- a/src/esp_platform.h +++ b/src/esp_platform.h @@ -28,6 +28,9 @@ 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(); From 31e35695e6a373fb590631e6646f5e3fd33986b4 Mon Sep 17 00:00:00 2001 From: croghostrider Date: Tue, 8 Jun 2021 20:27:33 +0200 Subject: [PATCH 16/32] fix wrong IP (#142) --- src/knx/ip_parameter_object.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/knx/ip_parameter_object.cpp b/src/knx/ip_parameter_object.cpp index c435f1a..dcbbba3 100644 --- a/src/knx/ip_parameter_object.cpp +++ b/src/knx/ip_parameter_object.cpp @@ -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), From 6254fc9b6789adf13058c3f243f3becde8a69b3c Mon Sep 17 00:00:00 2001 From: croghostrider Date: Mon, 14 Jun 2021 08:10:48 +0200 Subject: [PATCH 17/32] fix bulid (#143) fix bulid fix bulid fix bulid --- examples/knx-demo/knx-demo.ino | 4 ++-- examples/knx-demo/platformio-ci.ini | 2 +- examples/knx-demo/platformio.ini | 4 ++-- examples/knx-usb/platformio-ci.ini | 2 +- examples/knx-usb/platformio.ini | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/knx-demo/knx-demo.ino b/examples/knx-demo/knx-demo.ino index e51a7d6..a72b214 100644 --- a/examples/knx-demo/knx-demo.ino +++ b/examples/knx-demo/knx-demo.ino @@ -1,7 +1,7 @@ #include #include -#if defined ARDUINO_ARCH_ESP8266 || defined ARDUINO_ARCH_ESP32 +#if MASK_VERSION != 0x07B0 && (defined ARDUINO_ARCH_ESP8266 || defined ARDUINO_ARCH_ESP32) #include #endif @@ -60,7 +60,7 @@ void setup() randomSeed(millis()); -#if defined ARDUINO_ARCH_ESP8266 || defined ARDUINO_ARCH_ESP32 +#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 4c76caa..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 = diff --git a/examples/knx-demo/platformio.ini b/examples/knx-demo/platformio.ini index 5f393b1..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 = 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 From 29a5802c1dc18267a38b4c95c28b5f6ec5521a23 Mon Sep 17 00:00:00 2001 From: SirSydom Date: Thu, 8 Jul 2021 18:36:10 +0200 Subject: [PATCH 18/32] added support for RP2040 (Raspberry Pi Pico) --- src/knx/bits.h | 4 +- src/knx/group_object.h | 2 +- src/knx_facade.cpp | 14 +++++- src/knx_facade.h | 16 +++++++ src/rp2040_arduino_platform.cpp | 82 +++++++++++++++++++++++++++++++++ src/rp2040_arduino_platform.h | 21 +++++++++ 6 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 src/rp2040_arduino_platform.cpp create mode 100644 src/rp2040_arduino_platform.h diff --git a/src/knx/bits.h b/src/knx/bits.h index cd0ca8a..b513ef9 100644 --- a/src/knx/bits.h +++ b/src/knx/bits.h @@ -5,7 +5,7 @@ #if defined(__linux__) #include -#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_STM32) || defined (DeviceFamily_CC13X0) +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_STM32) || defined (DeviceFamily_CC13X0) #define getbyte(x,n) (*(((uint8_t*)&(x))+n)) #define htons(x) ( (getbyte(x,0)<<8) | getbyte(x,1) ) #define htonl(x) ( (getbyte(x,0)<<24) | (getbyte(x,1)<<16) | (getbyte(x,2)<<8) | getbyte(x,3) ) @@ -25,7 +25,7 @@ #define ABS(x) ((x > 0) ? (x) : (-x)) #endif -#if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_STM32) +#if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_STM32) #include #elif defined(ARDUINO_ARCH_ESP8266) #include diff --git a/src/knx/group_object.h b/src/knx/group_object.h index 6a07b5f..91c8b56 100644 --- a/src/knx/group_object.h +++ b/src/knx/group_object.h @@ -20,7 +20,7 @@ enum ComFlag class GroupObject; #ifndef HAS_FUNCTIONAL -# if defined(__linux__) || defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_STM32) || defined (ARDUINO_ARCH_SAMD) +# if defined(__linux__) || defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_STM32) || defined (ARDUINO_ARCH_SAMD) || defined (ARDUINO_ARCH_RP2040) # define HAS_FUNCTIONAL 1 # else # define HAS_FUNCTIONAL 0 diff --git a/src/knx_facade.cpp b/src/knx_facade.cpp index ee92547..5d48c7e 100644 --- a/src/knx_facade.cpp +++ b/src/knx_facade.cpp @@ -7,7 +7,8 @@ #if (defined(ARDUINO_ARCH_STM32) || \ defined(ARDUINO_ARCH_ESP32) || \ defined(ARDUINO_ARCH_ESP8266) || \ - defined(ARDUINO_ARCH_SAMD)) + defined(ARDUINO_ARCH_SAMD) || \ + defined(ARDUINO_ARCH_RP2040)) // Only ESP8266 and ESP32 have this define. For all other platforms this is just empty. #ifndef ICACHE_RAM_ATTR @@ -35,6 +36,17 @@ #else #error "Mask version not supported on ARDUINO_ARCH_SAMD" #endif + #elif defined(ARDUINO_ARCH_RP2040) + // predefined global instance for TP or RF or TP/RF coupler + #if MASK_VERSION == 0x07B0 + KnxFacade knx(buttonUp); + #elif MASK_VERSION == 0x27B0 + KnxFacade knx(buttonUp); + #elif MASK_VERSION == 0x2920 + KnxFacade knx(buttonUp); + #else + #error "Mask version not supported on ARDUINO_ARCH_RP2040" + #endif #elif defined(ARDUINO_ARCH_ESP8266) // predefined global instance for TP or IP or TP/IP coupler diff --git a/src/knx_facade.h b/src/knx_facade.h index c23bdd2..f2830a3 100644 --- a/src/knx_facade.h +++ b/src/knx_facade.h @@ -17,6 +17,11 @@ #ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE void buttonUp(); #endif +#elif defined(ARDUINO_ARCH_RP2040) + #include "rp2040_arduino_platform.h" + #ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE + void buttonUp(); + #endif #elif defined(ARDUINO_ARCH_ESP8266) #include "esp_platform.h" #ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE @@ -387,6 +392,17 @@ template class KnxFacade : private SaveRestore #else #error "Mask version not supported on ARDUINO_ARCH_SAMD" #endif + #elif defined(ARDUINO_ARCH_RP2040) + // predefined global instance for TP or RF or TP/RF coupler + #if MASK_VERSION == 0x07B0 + extern KnxFacade knx; + #elif MASK_VERSION == 0x27B0 + extern KnxFacade knx; + #elif MASK_VERSION == 0x2920 + extern KnxFacade knx; + #else + #error "Mask version not supported on ARDUINO_ARCH_RP2040" + #endif #elif defined(ARDUINO_ARCH_ESP8266) // predefined global instance for TP or IP or TP/IP coupler #if MASK_VERSION == 0x07B0 diff --git a/src/rp2040_arduino_platform.cpp b/src/rp2040_arduino_platform.cpp new file mode 100644 index 0000000..72341f5 --- /dev/null +++ b/src/rp2040_arduino_platform.cpp @@ -0,0 +1,82 @@ +/*----------------------------------------------------- + +Plattform for Raspberry Pi Pico and other RP2040 boards + +made to work with arduino-pico - "Raspberry Pi Pico Arduino core, for all RP2040 boards" +by Earl E. Philhower III https://github.com/earlephilhower/arduino-pico +tested with V1.9.1 + +by SirSydom 2021 + +A maximum of 4kB emulated EEPROM is supported. +For more, use or own emulation (maybe with littlefs) + +----------------------------------------------------*/ + +#include "rp2040_arduino_platform.h" + +#ifdef ARDUINO_ARCH_RP2040 +#include + +#include + +//Pi Pico specific libs +#include // EEPROM emulation in flash, part of Earl E Philhowers Pi Pico Arduino support +#include // from Pico SDK +#include // from Pico SDK + + +RP2040ArduinoPlatform::RP2040ArduinoPlatform() +#ifndef KNX_NO_DEFAULT_UART + : ArduinoPlatform(&Serial1) +#endif +{ +} + +RP2040ArduinoPlatform::RP2040ArduinoPlatform( HardwareSerial* s) : ArduinoPlatform(s) +{ +} + +uint32_t RP2040ArduinoPlatform::uniqueSerialNumber() +{ + pico_unique_board_id_t id; // 64Bit unique serial number from the QSPI flash + pico_get_unique_board_id(&id); + + // use lower 4 byte and convert to unit32_t + uint32_t uid = ((uint32_t)(id.id[4]) << 24) | ((uint32_t)(id.id[5]) << 16) | ((uint32_t)(id.id[6]) << 8) | (uint32_t)(id.id[7]); + + return uid; +} + +void RP2040ArduinoPlatform::restart() +{ + println("restart"); + watchdog_reboot(0,0,0); +} + +uint8_t * RP2040ArduinoPlatform::getEepromBuffer(uint16_t size) +{ + if(size > 4096) + { + println("KNX_FLASH_SIZE to big for EEPROM emulation (max. 4kB)"); + fatalError(); + } + + uint8_t * eepromptr = EEPROM.getDataPtr(); + + if(eepromptr == nullptr) + { + EEPROM.begin(4096); + eepromptr = EEPROM.getDataPtr(); + } + + return eepromptr; +} + +void RP2040ArduinoPlatform::commitToEeprom() +{ + EEPROM.commit(); +} +#endif + + diff --git a/src/rp2040_arduino_platform.h b/src/rp2040_arduino_platform.h new file mode 100644 index 0000000..3077b21 --- /dev/null +++ b/src/rp2040_arduino_platform.h @@ -0,0 +1,21 @@ +#include "arduino_platform.h" + +#include "Arduino.h" + +#ifdef ARDUINO_ARCH_RP2040 + +class RP2040ArduinoPlatform : public ArduinoPlatform +{ +public: + RP2040ArduinoPlatform(); + RP2040ArduinoPlatform( HardwareSerial* s); + + // unique serial number + uint32_t uniqueSerialNumber() override; + + void restart(); + uint8_t* getEepromBuffer(uint16_t size); + void commitToEeprom(); +}; + +#endif From 4f6c837b78603dbdfc67c192d116ce9c78be4f57 Mon Sep 17 00:00:00 2001 From: SirSydom Date: Thu, 8 Jul 2021 19:45:33 +0200 Subject: [PATCH 19/32] added support for RP2040 (Raspberry Pi Pico) (#145) --- src/knx/bits.h | 4 +- src/knx/group_object.h | 2 +- src/knx_facade.cpp | 14 +++++- src/knx_facade.h | 16 +++++++ src/rp2040_arduino_platform.cpp | 82 +++++++++++++++++++++++++++++++++ src/rp2040_arduino_platform.h | 21 +++++++++ 6 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 src/rp2040_arduino_platform.cpp create mode 100644 src/rp2040_arduino_platform.h diff --git a/src/knx/bits.h b/src/knx/bits.h index cd0ca8a..b513ef9 100644 --- a/src/knx/bits.h +++ b/src/knx/bits.h @@ -5,7 +5,7 @@ #if defined(__linux__) #include -#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_STM32) || defined (DeviceFamily_CC13X0) +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_STM32) || defined (DeviceFamily_CC13X0) #define getbyte(x,n) (*(((uint8_t*)&(x))+n)) #define htons(x) ( (getbyte(x,0)<<8) | getbyte(x,1) ) #define htonl(x) ( (getbyte(x,0)<<24) | (getbyte(x,1)<<16) | (getbyte(x,2)<<8) | getbyte(x,3) ) @@ -25,7 +25,7 @@ #define ABS(x) ((x > 0) ? (x) : (-x)) #endif -#if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_STM32) +#if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_STM32) #include #elif defined(ARDUINO_ARCH_ESP8266) #include diff --git a/src/knx/group_object.h b/src/knx/group_object.h index 6a07b5f..91c8b56 100644 --- a/src/knx/group_object.h +++ b/src/knx/group_object.h @@ -20,7 +20,7 @@ enum ComFlag class GroupObject; #ifndef HAS_FUNCTIONAL -# if defined(__linux__) || defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_STM32) || defined (ARDUINO_ARCH_SAMD) +# if defined(__linux__) || defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_STM32) || defined (ARDUINO_ARCH_SAMD) || defined (ARDUINO_ARCH_RP2040) # define HAS_FUNCTIONAL 1 # else # define HAS_FUNCTIONAL 0 diff --git a/src/knx_facade.cpp b/src/knx_facade.cpp index ee92547..5d48c7e 100644 --- a/src/knx_facade.cpp +++ b/src/knx_facade.cpp @@ -7,7 +7,8 @@ #if (defined(ARDUINO_ARCH_STM32) || \ defined(ARDUINO_ARCH_ESP32) || \ defined(ARDUINO_ARCH_ESP8266) || \ - defined(ARDUINO_ARCH_SAMD)) + defined(ARDUINO_ARCH_SAMD) || \ + defined(ARDUINO_ARCH_RP2040)) // Only ESP8266 and ESP32 have this define. For all other platforms this is just empty. #ifndef ICACHE_RAM_ATTR @@ -35,6 +36,17 @@ #else #error "Mask version not supported on ARDUINO_ARCH_SAMD" #endif + #elif defined(ARDUINO_ARCH_RP2040) + // predefined global instance for TP or RF or TP/RF coupler + #if MASK_VERSION == 0x07B0 + KnxFacade knx(buttonUp); + #elif MASK_VERSION == 0x27B0 + KnxFacade knx(buttonUp); + #elif MASK_VERSION == 0x2920 + KnxFacade knx(buttonUp); + #else + #error "Mask version not supported on ARDUINO_ARCH_RP2040" + #endif #elif defined(ARDUINO_ARCH_ESP8266) // predefined global instance for TP or IP or TP/IP coupler diff --git a/src/knx_facade.h b/src/knx_facade.h index c23bdd2..f2830a3 100644 --- a/src/knx_facade.h +++ b/src/knx_facade.h @@ -17,6 +17,11 @@ #ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE void buttonUp(); #endif +#elif defined(ARDUINO_ARCH_RP2040) + #include "rp2040_arduino_platform.h" + #ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE + void buttonUp(); + #endif #elif defined(ARDUINO_ARCH_ESP8266) #include "esp_platform.h" #ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE @@ -387,6 +392,17 @@ template class KnxFacade : private SaveRestore #else #error "Mask version not supported on ARDUINO_ARCH_SAMD" #endif + #elif defined(ARDUINO_ARCH_RP2040) + // predefined global instance for TP or RF or TP/RF coupler + #if MASK_VERSION == 0x07B0 + extern KnxFacade knx; + #elif MASK_VERSION == 0x27B0 + extern KnxFacade knx; + #elif MASK_VERSION == 0x2920 + extern KnxFacade knx; + #else + #error "Mask version not supported on ARDUINO_ARCH_RP2040" + #endif #elif defined(ARDUINO_ARCH_ESP8266) // predefined global instance for TP or IP or TP/IP coupler #if MASK_VERSION == 0x07B0 diff --git a/src/rp2040_arduino_platform.cpp b/src/rp2040_arduino_platform.cpp new file mode 100644 index 0000000..72341f5 --- /dev/null +++ b/src/rp2040_arduino_platform.cpp @@ -0,0 +1,82 @@ +/*----------------------------------------------------- + +Plattform for Raspberry Pi Pico and other RP2040 boards + +made to work with arduino-pico - "Raspberry Pi Pico Arduino core, for all RP2040 boards" +by Earl E. Philhower III https://github.com/earlephilhower/arduino-pico +tested with V1.9.1 + +by SirSydom 2021 + +A maximum of 4kB emulated EEPROM is supported. +For more, use or own emulation (maybe with littlefs) + +----------------------------------------------------*/ + +#include "rp2040_arduino_platform.h" + +#ifdef ARDUINO_ARCH_RP2040 +#include + +#include + +//Pi Pico specific libs +#include // EEPROM emulation in flash, part of Earl E Philhowers Pi Pico Arduino support +#include // from Pico SDK +#include // from Pico SDK + + +RP2040ArduinoPlatform::RP2040ArduinoPlatform() +#ifndef KNX_NO_DEFAULT_UART + : ArduinoPlatform(&Serial1) +#endif +{ +} + +RP2040ArduinoPlatform::RP2040ArduinoPlatform( HardwareSerial* s) : ArduinoPlatform(s) +{ +} + +uint32_t RP2040ArduinoPlatform::uniqueSerialNumber() +{ + pico_unique_board_id_t id; // 64Bit unique serial number from the QSPI flash + pico_get_unique_board_id(&id); + + // use lower 4 byte and convert to unit32_t + uint32_t uid = ((uint32_t)(id.id[4]) << 24) | ((uint32_t)(id.id[5]) << 16) | ((uint32_t)(id.id[6]) << 8) | (uint32_t)(id.id[7]); + + return uid; +} + +void RP2040ArduinoPlatform::restart() +{ + println("restart"); + watchdog_reboot(0,0,0); +} + +uint8_t * RP2040ArduinoPlatform::getEepromBuffer(uint16_t size) +{ + if(size > 4096) + { + println("KNX_FLASH_SIZE to big for EEPROM emulation (max. 4kB)"); + fatalError(); + } + + uint8_t * eepromptr = EEPROM.getDataPtr(); + + if(eepromptr == nullptr) + { + EEPROM.begin(4096); + eepromptr = EEPROM.getDataPtr(); + } + + return eepromptr; +} + +void RP2040ArduinoPlatform::commitToEeprom() +{ + EEPROM.commit(); +} +#endif + + diff --git a/src/rp2040_arduino_platform.h b/src/rp2040_arduino_platform.h new file mode 100644 index 0000000..3077b21 --- /dev/null +++ b/src/rp2040_arduino_platform.h @@ -0,0 +1,21 @@ +#include "arduino_platform.h" + +#include "Arduino.h" + +#ifdef ARDUINO_ARCH_RP2040 + +class RP2040ArduinoPlatform : public ArduinoPlatform +{ +public: + RP2040ArduinoPlatform(); + RP2040ArduinoPlatform( HardwareSerial* s); + + // unique serial number + uint32_t uniqueSerialNumber() override; + + void restart(); + uint8_t* getEepromBuffer(uint16_t size); + void commitToEeprom(); +}; + +#endif From 7f5664863a9cfcb79734d6283aeb33e5c1e49e96 Mon Sep 17 00:00:00 2001 From: SirSydom Date: Wed, 22 Sep 2021 20:42:36 +0200 Subject: [PATCH 20/32] support DPT9.009 (airflow) and DPT9.029 (absolute humidity) --- src/knx/dptconvert.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/knx/dptconvert.cpp b/src/knx/dptconvert.cpp index e595238..a5d8d5b 100644 --- a/src/knx/dptconvert.cpp +++ b/src/knx/dptconvert.cpp @@ -55,7 +55,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) + 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) @@ -172,7 +172,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) From 57b1950e38de1e26e17aec6e815a6b1b6043214a Mon Sep 17 00:00:00 2001 From: SirSydom Date: Wed, 22 Sep 2021 23:51:21 +0200 Subject: [PATCH 21/32] support DPT9.009 (airflow) and DPT9.029 (absolute humidity) (#149) * added support for RP2040 (Raspberry Pi Pico) * support DPT9.009 (airflow) and DPT9.029 (absolute humidity) --- src/knx/dptconvert.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/knx/dptconvert.cpp b/src/knx/dptconvert.cpp index e595238..a5d8d5b 100644 --- a/src/knx/dptconvert.cpp +++ b/src/knx/dptconvert.cpp @@ -55,7 +55,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) + 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) @@ -172,7 +172,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) From 616599cf8b6be2fca0fe062e18903a26483f47f4 Mon Sep 17 00:00:00 2001 From: rueckix <39458989+rueckix@users.noreply.github.com> Date: Sun, 17 Oct 2021 18:19:45 +0200 Subject: [PATCH 22/32] Fix EEPROM include on STM32 (#151) The structure of the stm32 arduino core changed (https://github.com/stm32duino/Arduino_Core_STM32/tree/main/libraries/EEPROM/src). stm32_eeprom.h was moved to a subfolder. Instead, we can now include (and potentially use later), the `EEPROM.h` header. --- src/stm32_platform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stm32_platform.cpp b/src/stm32_platform.cpp index 4e70231..8fbfaa3 100644 --- a/src/stm32_platform.cpp +++ b/src/stm32_platform.cpp @@ -1,7 +1,7 @@ #include "stm32_platform.h" #ifdef ARDUINO_ARCH_STM32 -#include +#include #include "knx/bits.h" Stm32Platform::Stm32Platform() From 87edd3dfe01398f56af3bf97633dc9fbd90f6d76 Mon Sep 17 00:00:00 2001 From: Sonnengruesser <36325150+Sonnengruesser@users.noreply.github.com> Date: Mon, 13 Dec 2021 11:17:36 +0100 Subject: [PATCH 23/32] Fix callback sequence (#153) Save-/restore callbacks need to be defined before knx.readMemory() to ensure the restore callback is called. --- examples/knx-bme680/knx-bme680.ino | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/knx-bme680/knx-bme680.ino b/examples/knx-bme680/knx-bme680.ino index f10a723..332bddf 100644 --- a/examples/knx-bme680/knx-bme680.ino +++ b/examples/knx-bme680/knx-bme680.ino @@ -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); From 63ff2c5d4d15e2633872d2fcc05135f6381c77af Mon Sep 17 00:00:00 2001 From: mptei Date: Wed, 22 Dec 2021 19:59:55 +0100 Subject: [PATCH 24/32] Removed doubled code (#158) --- src/knx/dptconvert.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/knx/dptconvert.cpp b/src/knx/dptconvert.cpp index a5d8d5b..2700478 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 From eb7ae16ddee71d42ed9448a8ccee6c7367eeb15d Mon Sep 17 00:00:00 2001 From: mptei Date: Wed, 22 Dec 2021 20:00:27 +0100 Subject: [PATCH 25/32] Returning correct value for ULongType. (#157) --- src/knx/knx_value.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/knx/knx_value.cpp b/src/knx/knx_value.cpp index 79ee503..c8d9f54 100644 --- a/src/knx/knx_value.cpp +++ b/src/knx/knx_value.cpp @@ -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: From fb74931bec618c32db48f7b2732a558e083fab16 Mon Sep 17 00:00:00 2001 From: Domos <41550963+Domos-Snips@users.noreply.github.com> Date: Thu, 23 Dec 2021 20:04:34 +0100 Subject: [PATCH 26/32] Add 2 new methods: paramSignedByte and paramBit. Comments and usage inside code (#159) --- src/knx_facade.h | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/knx_facade.h b/src/knx_facade.h index f2830a3..b1ccb1b 100644 --- a/src/knx_facade.h +++ b/src/knx_facade.h @@ -294,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()) @@ -301,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()) From 12d0ea1ad89f41fb2b77d73401f45c3dc0f18112 Mon Sep 17 00:00:00 2001 From: mptei Date: Tue, 28 Dec 2021 19:53:48 +0100 Subject: [PATCH 27/32] Use full duplex in tpuart_data_link_layer. (#161) * TPUART full duplex handling * Stay in loop when RX_L_DATA. --- src/knx/tpuart_data_link_layer.cpp | 568 ++++++++++++++++------------- src/knx/tpuart_data_link_layer.h | 5 +- 2 files changed, 316 insertions(+), 257 deletions(-) diff --git a/src/knx/tpuart_data_link_layer.cpp b/src/knx/tpuart_data_link_layer.cpp index fe0cb8f..c23fcf4 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,266 +113,317 @@ void TpUartDataLinkLayer::loop() if (!_enabled) return; - switch (_loopState) - { - case IDLE: - if (_platform.uartAvailable()) - { - _loopState = RX_FIRST_BYTE; - } - else - { - if (!_waitConfirm && !isTxQueueEmpty()) + do { + _receiveBuffer[0] = 0x29; + _receiveBuffer[1] = 0; + uint8_t* buffer = _receiveBuffer + 2; + uint8_t rxByte; + bool isEchoComplete = false; // Flag that a complete echo is received + bool dataConnMsg = 0; // The DATA_CONN message just seen or 0 + 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; - } - } - break; - case TX_FRAME: - if (sendSingleFrameByte() == false) - { - _waitConfirm = true; - _waitConfirmStartTime = millis(); - _loopState = IDLE; - } - break; - case RX_FIRST_BYTE: - rxByte = _platform.readUart(); - _lastByteRxTime = millis(); - _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; - _loopState = RX_L_DATA; - break; - } - else if ((rxByte & L_DATA_MASK) == L_DATA_EXTENDED_IND) - { - 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; + rxByte = _platform.readUart(); +#ifdef DBG_TRACE + print(rxByte, HEX); +#endif + _lastByteRxTime = millis(); - print(tmp, HEX); - break; - } - 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)) + // Check for layer-2 packets + _RxByteCnt = 0; + _xorSum = 0; + if ((rxByte & L_DATA_MASK) == L_DATA_STANDARD_IND) { - c |= 0x01; + 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; } - _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) + // Handle all single byte packets here + else if ((rxByte & L_DATA_CON_MASK) == L_DATA_CON) { - //check if it is realy an echo, rx_crc = tx_crc - if (rxByte == _sendBuffer[_sendBufferLength - 1]) - _isEcho = true; - else - _isEcho = false; + dataConnMsg = rxByte; } - if (_isEcho) + else if (rxByte == L_POLL_DATA_IND) { - _loopState = RX_WAIT_DATA_CON; + // 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 { - frameBytesReceived(_receiveBuffer, _RxByteCnt + 2); - _loopState = IDLE; + 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 { - println("frame with invalid crc ignored"); - _loopState = IDLE; + _xorSum ^= rxByte; } - } - 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; - delete[] _sendBuffer; - _sendBuffer = 0; - _sendBufferLength = 0; - _loopState = IDLE; - } - else - { - //should not happen - println("expected L_DATA_CON not received"); - dataConBytesReceived(_receiveBuffer, _RxByteCnt + 2, false); - _waitConfirm = false; - delete[] _sendBuffer; - _sendBuffer = 0; - _sendBufferLength = 0; - _loopState = IDLE; - } - 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; + 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; } - } + + // 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) + { + _waitConfirmStartTime = millis(); + _txState = TX_WAIT_ECHO; +#ifdef DBG_TRACE + println("TX_WAIT_ECHO"); +#endif + } + break; + case TX_WAIT_ECHO: + case TX_WAIT_CONN: + if (isEchoComplete) + { + _txState = TX_WAIT_CONN; +#ifdef DBG_TRACE + println("TX_WAIT_CONN"); +#endif + } + else if (dataConnMsg) + { + bool waitEcho = (_txState == TX_WAIT_ECHO); + if (waitEcho) { + println("L_DATA_CON without echo"); + } + dataConBytesReceived(_receiveBuffer, _RxByteCnt + 2, !waitEcho && ((dataConnMsg & SUCCESS) > 0)); + delete[] _sendBuffer; + _sendBuffer = 0; + _sendBufferLength = 0; + _txState = TX_IDLE; + } + else 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); + delete[] _sendBuffer; + _sendBuffer = 0; + _sendBufferLength = 0; + _txState = TX_IDLE; +#ifdef DBG_TRACE + println("TX_IDLE"); +#endif + } + break; + } + } while (_rxState == RX_L_DATA); } bool TpUartDataLinkLayer::sendFrame(CemiFrame& frame) @@ -496,6 +553,9 @@ 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++; 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; From bf70e162f8c1cd74fa1aa2334c6cae59b94df559 Mon Sep 17 00:00:00 2001 From: croghostrider Date: Wed, 29 Dec 2021 17:20:46 +0100 Subject: [PATCH 28/32] fix constant comparison (#162) * fix constant comparison * . --- examples/knx-linux-coupler/fdsk.cpp | 2 +- examples/knx-linux/fdsk.cpp | 2 +- src/knx/association_table_object.cpp | 4 ++-- src/knx/bau_systemB.cpp | 2 +- src/knx/dptconvert.cpp | 10 ++++------ 5 files changed, 9 insertions(+), 11 deletions(-) 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/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 39bc894..7eee281 100644 --- a/src/knx/bau_systemB.cpp +++ b/src/knx/bau_systemB.cpp @@ -483,7 +483,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; diff --git a/src/knx/dptconvert.cpp b/src/knx/dptconvert.cpp index 2700478..9942f0a 100644 --- a/src/knx/dptconvert.cpp +++ b/src/knx/dptconvert.cpp @@ -797,8 +797,6 @@ 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; } @@ -886,7 +884,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; @@ -1153,7 +1151,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; @@ -1444,7 +1442,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); @@ -1471,7 +1469,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); From f5feefb0f260b55f84d130763e022a3270b90e76 Mon Sep 17 00:00:00 2001 From: mptei Date: Sun, 16 Jan 2022 18:25:32 +0100 Subject: [PATCH 29/32] TPUART: Avoid false EOP (#166) * Made the rx loop shorter to avoid tx buffer saturation. * sendSingleFrameByte return false on last byte * sendSingleFrameByte return false on last byte --- src/knx/tpuart_data_link_layer.cpp | 145 +++++++++++++++-------------- 1 file changed, 74 insertions(+), 71 deletions(-) diff --git a/src/knx/tpuart_data_link_layer.cpp b/src/knx/tpuart_data_link_layer.cpp index c23fcf4..96f41f7 100644 --- a/src/knx/tpuart_data_link_layer.cpp +++ b/src/knx/tpuart_data_link_layer.cpp @@ -113,13 +113,14 @@ void TpUartDataLinkLayer::loop() if (!_enabled) return; + // 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 isEchoComplete = false; // Flag that a complete echo is received - bool dataConnMsg = 0; // The DATA_CONN message just seen or 0 bool isEOP = (millis() - _lastByteRxTime > EOP_TIMEOUT); // Flag that an EOP gap is seen switch (_rxState) { @@ -356,74 +357,74 @@ void TpUartDataLinkLayer::loop() default: break; } - - // 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) - { - _waitConfirmStartTime = millis(); - _txState = TX_WAIT_ECHO; -#ifdef DBG_TRACE - println("TX_WAIT_ECHO"); -#endif - } - break; - case TX_WAIT_ECHO: - case TX_WAIT_CONN: - if (isEchoComplete) - { - _txState = TX_WAIT_CONN; -#ifdef DBG_TRACE - println("TX_WAIT_CONN"); -#endif - } - else if (dataConnMsg) - { - bool waitEcho = (_txState == TX_WAIT_ECHO); - if (waitEcho) { - println("L_DATA_CON without echo"); - } - dataConBytesReceived(_receiveBuffer, _RxByteCnt + 2, !waitEcho && ((dataConnMsg & SUCCESS) > 0)); - delete[] _sendBuffer; - _sendBuffer = 0; - _sendBufferLength = 0; - _txState = TX_IDLE; - } - else 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); - delete[] _sendBuffer; - _sendBuffer = 0; - _sendBufferLength = 0; - _txState = TX_IDLE; -#ifdef DBG_TRACE - println("TX_IDLE"); -#endif - } - 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) + { + _waitConfirmStartTime = millis(); + _txState = TX_WAIT_ECHO; +#ifdef DBG_TRACE + println("TX_WAIT_ECHO"); +#endif + } + break; + case TX_WAIT_ECHO: + case TX_WAIT_CONN: + if (isEchoComplete) + { + _txState = TX_WAIT_CONN; +#ifdef DBG_TRACE + println("TX_WAIT_CONN"); +#endif + } + else if (dataConnMsg) + { + bool waitEcho = (_txState == TX_WAIT_ECHO); + if (waitEcho) { + println("L_DATA_CON without echo"); + } + dataConBytesReceived(_receiveBuffer, _RxByteCnt + 2, !waitEcho && ((dataConnMsg & SUCCESS) > 0)); + delete[] _sendBuffer; + _sendBuffer = 0; + _sendBufferLength = 0; + _txState = TX_IDLE; + } + else 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); + delete[] _sendBuffer; + _sendBuffer = 0; + _sendBufferLength = 0; + _txState = TX_IDLE; +#ifdef DBG_TRACE + println("TX_IDLE"); +#endif + } + break; + } } bool TpUartDataLinkLayer::sendFrame(CemiFrame& frame) @@ -559,13 +560,15 @@ bool TpUartDataLinkLayer::sendSingleFrameByte() _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) From 71e8607f43c58f9c2642c10900efc335b607d930 Mon Sep 17 00:00:00 2001 From: mptei Date: Sun, 16 Jan 2022 18:26:51 +0100 Subject: [PATCH 30/32] Added support for DPT 251.600 RGBW (#167) * Added support for DPT 251.600 RGBW * style fix --- src/knx/dpt.h | 1 + src/knx/dptconvert.cpp | 45 +++++++++++++++++++++++++++++++++++++++++- src/knx/dptconvert.h | 2 ++ 3 files changed, 47 insertions(+), 1 deletion(-) 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 9942f0a..16dc9e3 100644 --- a/src/knx/dptconvert.cpp +++ b/src/knx/dptconvert.cpp @@ -126,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; } @@ -243,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; } @@ -801,6 +807,24 @@ int busValueToRGB(const uint8_t* payload, size_t payload_length, const Dpt& data 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) >> 8) + + (unsigned8FromPayload(payload, 3) << 24); + 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); @@ -1517,6 +1541,26 @@ 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; + unsigned16ToPayload(payload, payload_length, 0, rgbw >> 8, 0xffff); // RG + unsigned8ToPayload(payload, payload_length, 2, rgbw, 0xff); // B + unsigned8ToPayload(payload, payload_length, 3, rgbw >> 24, 0xff); // W + } + break; + case 1: // Mask bits + unsigned8ToPayload(payload, payload_length, 5, (uint8_t)value, 0xff); + break; + + } + return true; +} + int valueToBusValueFlaggedScaling(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype) { switch (datatype.index) @@ -1545,7 +1589,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); From 36e0a00e80160ef8bf54451eac91a0f3acb47fc6 Mon Sep 17 00:00:00 2001 From: dev-git-usr <56093270+dev-git-usr@users.noreply.github.com> Date: Sun, 16 Jan 2022 18:56:00 +0100 Subject: [PATCH 31/32] Added Datapointtype DPT 7.600 (colortemperature) (#165) * Added Datapointtype DPT 7.600 (colortemperature) * Added Decoding of Datatype --- src/knx/dptconvert.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/knx/dptconvert.cpp b/src/knx/dptconvert.cpp index 16dc9e3..bb3ecdd 100644 --- a/src/knx/dptconvert.cpp +++ b/src/knx/dptconvert.cpp @@ -36,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) @@ -156,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) From 6720f86e5cb68d404d1e2e6b72ee32648702a65a Mon Sep 17 00:00:00 2001 From: mptei Date: Wed, 19 Jan 2022 14:51:00 +0100 Subject: [PATCH 32/32] Changed color byte order from WRGB to RGBW (R MSB). (#168) --- src/knx/dptconvert.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/knx/dptconvert.cpp b/src/knx/dptconvert.cpp index bb3ecdd..6fc644f 100644 --- a/src/knx/dptconvert.cpp +++ b/src/knx/dptconvert.cpp @@ -813,8 +813,7 @@ int busValueToRGBW(const uint8_t* payload, size_t payload_length, const Dpt& dat switch (datatype.index) { case 0: // The RGBW value { - uint32_t rgbw = (unsigned32FromPayload(payload, 0) >> 8) - + (unsigned8FromPayload(payload, 3) << 24); + uint32_t rgbw = unsigned32FromPayload(payload, 0); value = rgbw; } return true; @@ -1548,13 +1547,11 @@ int valueToBusValueRGBW(const KNXValue& value, uint8_t* payload, size_t payload_ case 0: // RGBW { uint32_t rgbw = (uint32_t)value; - unsigned16ToPayload(payload, payload_length, 0, rgbw >> 8, 0xffff); // RG - unsigned8ToPayload(payload, payload_length, 2, rgbw, 0xff); // B - unsigned8ToPayload(payload, payload_length, 3, rgbw >> 24, 0xff); // W + unsigned32ToPayload(payload, payload_length, 0, rgbw, 0xffffffff); // RGBW } break; case 1: // Mask bits - unsigned8ToPayload(payload, payload_length, 5, (uint8_t)value, 0xff); + unsigned8ToPayload(payload, payload_length, 5, (uint8_t)value, 0x0f); break; }