#include #include #define NUMIOS 18 #define goInputOnOff(i) knx.getGroupObject(i + 1) #define goOutputOnOff(i) knx.getGroupObject(i + 1 + NUMIOS) #define goOutputScaling(i) knx.getGroupObject(i + 1 + NUMIOS * 2) #define goOutputOnOffStatus(i) knx.getGroupObject(i + 1 + NUMIOS * 3) #define goOutputScalingStatus(i) knx.getGroupObject(i + 1 + NUMIOS * 4) typedef enum { CONFIG_NONE = 0, CONFIG_IN = 1, CONFIG_IN_TOGGLE = 2, CONFIG_IN_TOGGLE_ON = 3, CONFIG_OUT = 4, CONFIG_OUT_ON = 5, CONFIG_OUT_PWM = 6, CONFIG_OUT_PWM_ON = 7, } config_t; inline void isrIn(uint8_t pin, uint8_t toggle); // workaround for the weird Arduino interrupt handling #define ISR_IN(NUM, TOGGLE) void isrIn##NUM##_##TOGGLE(){isrIn(NUM, TOGGLE);} ISR_IN(0,0); ISR_IN(1,0); ISR_IN(2,0); ISR_IN(3,0); ISR_IN(4,0); ISR_IN(5,0); ISR_IN(6,0); ISR_IN(7,0); ISR_IN(8,0); ISR_IN(9,0); ISR_IN(10,0); ISR_IN(11,0); ISR_IN(12,0); ISR_IN(13,0); ISR_IN(14,0); ISR_IN(15,0); ISR_IN(16,0); ISR_IN(17,0); ISR_IN(0,1); ISR_IN(1,1); ISR_IN(2,1); ISR_IN(3,1); ISR_IN(4,1); ISR_IN(5,1); ISR_IN(6,1); ISR_IN(7,1); ISR_IN(8,1); ISR_IN(9,1); ISR_IN(10,1); ISR_IN(11,1); ISR_IN(12,1); ISR_IN(13,1); ISR_IN(14,1); ISR_IN(15,1); ISR_IN(16,1); ISR_IN(17,1); const uint32_t pinTbl[NUMIOS] = {PA15, PB3, PB4, PB5, PB6, PB7, PB8, PB9, PC13, PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PB0}; void (*inCbTbl[NUMIOS * 2])(void) = {isrIn0_0, isrIn1_0, isrIn2_0, isrIn3_0, isrIn4_0, isrIn5_0, isrIn6_0, isrIn7_0, isrIn8_0, isrIn9_0, isrIn10_0, isrIn11_0, isrIn12_0, isrIn13_0, isrIn14_0, isrIn15_0, isrIn16_0, isrIn17_0, isrIn0_1, isrIn1_1, isrIn2_1, isrIn3_1, isrIn4_1, isrIn5_1, isrIn6_1, isrIn7_1, isrIn8_1, isrIn9_1, isrIn10_1, isrIn11_1, isrIn12_1, isrIn13_1, isrIn14_1, isrIn15_1, isrIn16_1, isrIn17_1}; volatile uint16_t val[NUMIOS]; uint32_t lastEvent[NUMIOS]; // callback for OnOff events from KNX void outOnOff(GroupObject& go) { uint8_t pin = go.asap() - 1 - NUMIOS; val[pin] = go.value(); val[pin] &= 1; digitalWrite(pinTbl[pin], val[pin]); goOutputOnOffStatus(pin).value(val[pin]); } // callback for OnOff events from KNX on PWM pins void outOnOffPWM(GroupObject& go) { uint8_t pin = go.asap() - 1 - NUMIOS; uint8_t tmp; if(go.value()){ val[pin] |= 0x100; analogWrite(pinTbl[pin], val[pin] & 0xff); }else{ val[pin] &= 0xff; analogWrite(pinTbl[pin], 0); } tmp = val[pin] >> 8; goOutputOnOffStatus(pin).value(tmp); } // callback for 0-100% events from KNX void outScaling(GroupObject& go) { uint8_t pin = go.asap() - NUMIOS * 2 - 1; uint8_t tmp; if(val[pin] > 0xFF){ tmp = *go.valueRef(); val[pin] = tmp | 1 << 8; analogWrite(pinTbl[pin], val[pin] & 0xff); }else{ tmp = *go.valueRef(); val[pin] = tmp; } *goOutputScalingStatus(pin).valueRef() = tmp; goOutputScalingStatus(pin).objectWritten(); } // callback for input interrupts inline void isrIn(uint8_t pin, uint8_t toggle) { uint32_t diff = millis() - lastEvent[pin]; if (diff >= 50 && diff <= 500){ if(toggle){ val[pin]++; }else{ val[pin] = digitalRead(pinTbl[pin]); } val[pin] &= 1; goInputOnOff(pin).value(val[pin]); } lastEvent[pin] = millis(); } void setup() { // 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()) { uint8_t progIn = knx.paramByte(0); // programming input uint8_t progLed = knx.paramByte(1); // programming LED for(uint8_t i = 0; i < NUMIOS; i++){ config_t config = (config_t) knx.paramByte(i + 2); // loop through all the pins and configure them correctly switch(config){ case CONFIG_IN: pinMode(pinTbl[i], INPUT_PULLUP); if(progIn = i + 1){ knx.buttonPin(pinTbl[i]); }else{ goInputOnOff(i).dataPointType(DPT_Switch); #if (ARDUINO_API_VERSION >= 10200) attachInterrupt(digitalPinToInterrupt(pinTbl[i]), inCbTbl[i], (PinStatus)CHANGE); #else attachInterrupt(digitalPinToInterrupt(pinTbl[i]), inCbTbl[i], CHANGE); #endif } break; case CONFIG_IN_TOGGLE: case CONFIG_IN_TOGGLE_ON: goInputOnOff(i).dataPointType(DPT_Switch); val[i] = config == CONFIG_IN_TOGGLE_ON; pinMode(pinTbl[i], INPUT_PULLUP); #if (ARDUINO_API_VERSION >= 10200) attachInterrupt(digitalPinToInterrupt(pinTbl[i]), inCbTbl[i + NUMIOS], (PinStatus)CHANGE); #else attachInterrupt(digitalPinToInterrupt(pinTbl[i]), inCbTbl[i + NUMIOS], CHANGE); #endif break; case CONFIG_OUT: case CONFIG_OUT_ON: pinMode(pinTbl[i], OUTPUT); val[i] = config == CONFIG_OUT_ON; digitalWrite(pinTbl[i], val[i]); if(progLed = i + 1){ knx.ledPin(pinTbl[i]); }else{ goOutputOnOff(i).dataPointType(DPT_Switch); goOutputOnOff(i).callback(outOnOff); goOutputOnOffStatus(i).dataPointType(DPT_Switch); } break; case CONFIG_OUT_PWM: case CONFIG_OUT_PWM_ON: pinMode(pinTbl[i], OUTPUT); val[i] = config == CONFIG_OUT_PWM_ON ? 0 : 0x1ff; analogWrite(pinTbl[i], val[i] & 0xff); goOutputOnOff(i).dataPointType(DPT_Switch); goOutputOnOff(i).callback(outOnOffPWM); goOutputOnOffStatus(i).dataPointType(DPT_Switch); goOutputScaling(i).dataPointType(DPT_Scaling); goOutputScaling(i).callback(outScaling); goOutputScalingStatus(i).dataPointType(DPT_Scaling); break; } } } // start the framework. knx.start(); } void loop() { // don't delay here too 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; }