Difference between revisions of "16CH Binary Input"
(19 intermediate revisions by 2 users not shown) | |||
Line 17: | Line 17: | ||
! KNX connectivity | ! KNX connectivity | ||
| Mini-BCU | | Mini-BCU | ||
+ | |- | ||
+ | ! Needed libraries | ||
+ | | KONNEKTING Lib [https://github.com/KONNEKTING/KonnektingDeviceLibrary] <br/> PCF8575 I2C IO Expander [https://github.com/skywodd/pcf8574_arduino_library] | ||
|} | |} | ||
Line 89: | Line 92: | ||
== Software == | == Software == | ||
=== What you need === | === What you need === | ||
− | * Arduino IDE ( | + | * Arduino IDE ([https://www.arduino.cc/en/main/software]) |
+ | * Arduino Lib PCF8575 ([https://github.com/skywodd/pcf8574_arduino_library]) | ||
* Arduino Code for 16CH Binary Input | * Arduino Code for 16CH Binary Input | ||
* XML-File for 16CH Binary Input | * XML-File for 16CH Binary Input | ||
* KONNEKTING SUITE (link) | * KONNEKTING SUITE (link) | ||
− | |||
=== Arduino Code === | === Arduino Code === | ||
Line 144: | Line 147: | ||
const byte month = 1; | const byte month = 1; | ||
const byte year = 17; | const byte year = 17; | ||
+ | |||
+ | |||
+ | |||
+ | void alarmMatch() | ||
+ | { | ||
+ | digitalWrite(LED_YELLOW, HIGH); | ||
+ | #ifdef KDEBUG | ||
+ | Debug.println("SAFE"); | ||
+ | #endif | ||
+ | rtc.setTime(hours, minutes, seconds); | ||
+ | rtc.setAlarmTime(23, 59, 00); | ||
+ | rtc.enableAlarm(rtc.MATCH_HHMMSS); | ||
+ | run_save=true; | ||
+ | } | ||
</source> | </source> | ||
Line 153: | Line 170: | ||
#define SAVE_PIN 38 | #define SAVE_PIN 38 | ||
#define MAX_CH 15 | #define MAX_CH 15 | ||
+ | #define S0_1_pin 11 //IO6 | ||
+ | #define S0_2_pin 5 //IO7 | ||
+ | #define S0_3_pin 6 //IO8 | ||
+ | #define S0_4_pin 2 //IO9 | ||
</source> | </source> | ||
Line 163: | Line 184: | ||
digitalWrite(LED_YELLOW, HIGH); // LED indicator | digitalWrite(LED_YELLOW, HIGH); // LED indicator | ||
writeSAVE(S0_Zaehler,S0_impuls); // EEPROM write routine to save all values | writeSAVE(S0_Zaehler,S0_impuls); // EEPROM write routine to save all values | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | ==== IRQ S0 Interface ==== | ||
+ | <source lang=c> | ||
+ | void irq_S0_1(){ | ||
+ | time_S0_stopp[0] = millis(); | ||
+ | S0_impuls[0]++; | ||
+ | if(S0_impuls[0]>=zaehler_Impulse[0]) | ||
+ | {S0_Zaehler[0]++; | ||
+ | S0_impuls[0] = 0;} | ||
+ | set_S0_LED[0] = true; | ||
+ | #ifdef KDEBUG | ||
+ | Debug.print("S0_1: "); | ||
+ | Debug.println("%d",S0_impuls[0]); | ||
+ | #endif | ||
+ | } | ||
+ | |||
+ | void irq_S0_2(){ | ||
+ | time_S0_stopp[1] = millis(); | ||
+ | S0_impuls[1]++; | ||
+ | if(S0_impuls[1]>=zaehler_Impulse[1]) | ||
+ | {S0_Zaehler[1]++; | ||
+ | S0_impuls[1] = 0;} | ||
+ | set_S0_LED[1] = true; | ||
+ | #ifdef KDEBUG | ||
+ | Debug.print("S0_2: "); | ||
+ | Debug.println("%d",S0_impuls[1]); | ||
+ | #endif | ||
+ | } | ||
+ | |||
+ | void irq_S0_3(){ | ||
+ | time_S0_stopp[2] = millis(); | ||
+ | S0_impuls[2]++; | ||
+ | if(S0_impuls[2]>=zaehler_Impulse[2]) | ||
+ | {S0_Zaehler[2]++; | ||
+ | S0_impuls[2] = 0;} | ||
+ | set_S0_LED[2] = true; | ||
+ | #ifdef KDEBUG | ||
+ | Debug.print("S0_3: "); | ||
+ | Debug.println("%d",S0_impuls[2]); | ||
+ | #endif | ||
+ | } | ||
+ | |||
+ | void irq_S0_4(){ | ||
+ | time_S0_stopp[3] = millis(); | ||
+ | S0_impuls[3]++; | ||
+ | if(S0_impuls[3]>=zaehler_Impulse[3]) | ||
+ | {S0_Zaehler[3]++; | ||
+ | S0_impuls[3] = 0;} | ||
+ | set_S0_LED[3] = true; | ||
+ | #ifdef KDEBUG | ||
+ | Debug.print("S0_4: "); | ||
+ | Debug.println("%d",S0_impuls[3]); | ||
+ | #endif | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | |||
+ | ==== Setup() Routine ==== | ||
+ | <source lang=c> | ||
+ | void setup(){ | ||
+ | |||
+ | //****************** Init IO ******************************************************* | ||
+ | pinMode(LED_YELLOW, OUTPUT); | ||
+ | digitalWrite(LED_YELLOW, HIGH); | ||
+ | pinMode(S0_1_pin, INPUT); | ||
+ | pinMode(S0_2_pin, INPUT); | ||
+ | pinMode(S0_3_pin, INPUT); | ||
+ | pinMode(S0_4_pin, INPUT); | ||
+ | Wire.begin(); // join i2c bus (address optional for master) | ||
+ | |||
+ | |||
+ | //****************** Init Debug Interface ******************************************** | ||
+ | #ifdef KDEBUG | ||
+ | // Start debug serial with 9600 bauds | ||
+ | DEBUGSERIAL.begin(115200); | ||
+ | #if defined(__AVR_ATmega32U4__) || defined(__SAMD21G18A__) | ||
+ | // wait for serial port to connect. Needed for Leonardo/Micro/ProMicro/Zero only | ||
+ | while (!DEBUGSERIAL) | ||
+ | #endif | ||
+ | // make debug serial port known to debug class | ||
+ | // Means: KONNEKTING will sue the same serial port for console debugging | ||
+ | Debug.setPrintStream(&DEBUGSERIAL); | ||
+ | Debug.print(F("KONNEKTING DemoSketch\n")); | ||
+ | #endif | ||
+ | |||
+ | //****************** Init Debug KONNEKTING ******************************************** | ||
+ | Konnekting.setMemoryReadFunc(&readEeprom); | ||
+ | Konnekting.setMemoryWriteFunc(&writeEeprom); | ||
+ | Konnekting.setMemoryUpdateFunc(&updateEeprom); | ||
+ | |||
+ | // Initialize KNX enabled Arduino Board | ||
+ | Konnekting.init(KNX_SERIAL, | ||
+ | PROG_BUTTON_PIN, | ||
+ | PROG_LED_PIN, | ||
+ | MANUFACTURER_ID, | ||
+ | DEVICE_ID, | ||
+ | REVISION); | ||
+ | |||
+ | |||
+ | |||
+ | //****************** Read Parameter *************************************************** | ||
+ | #ifdef KDEBUG | ||
+ | Debug.println("READ Parameter"); | ||
+ | #endif | ||
+ | abfrage_Interval = (((uint8_t) Konnekting.getUINT8Param(0)+1)*30); // abfrage_Interval | ||
+ | sende_Zyklus_Zaehler = (uint8_t) Konnekting.getUINT8Param(1); // sende_Zyklus_Zaehler | ||
+ | zimmerMaskierung_Sendvalue = (uint8_t) Konnekting.getUINT8Param(2); // Ob HIGH oder LOW gesendet wird | ||
+ | invertLED = (uint8_t) Konnekting.getUINT8Param(3); // invertiert LED Anzeige | ||
+ | |||
+ | |||
+ | #ifdef KDEBUG | ||
+ | Debug.print("Abfrage-Interval: "); | ||
+ | Debug.println("%d",abfrage_Interval); | ||
+ | Debug.print("Zimmer Maskierung Sendvalue: "); | ||
+ | Debug.println("%d",zimmerMaskierung_Sendvalue); | ||
+ | Debug.print("Anzahl Messungen: "); | ||
+ | Debug.println("%d",filter_count); | ||
+ | Debug.print("LED invert: "); | ||
+ | Debug.println("%d",invertLED); | ||
+ | #endif | ||
+ | |||
+ | //****************** Set Sendezyklus (S0-Schnittstelle) ******************************** | ||
+ | if(sende_Zyklus_Zaehler == 1) sende_Zyklus = 6000; // 1min | ||
+ | if(sende_Zyklus_Zaehler == 2) sende_Zyklus = 60000; //10min | ||
+ | if(sende_Zyklus_Zaehler == 3) sende_Zyklus = 180000; //30min | ||
+ | if(sende_Zyklus_Zaehler == 4) sende_Zyklus = 360000; //60min | ||
+ | |||
+ | #ifdef KDEBUG | ||
+ | Debug.print("Sende-Zyklus: "); | ||
+ | Debug.println("%d",sende_Zyklus); | ||
+ | #endif | ||
+ | |||
+ | //****************** Load Settings ***************************************************** | ||
+ | //Kanal 1 - 16 Load Settings | ||
+ | for(int cha=0; cha<16; cha++) | ||
+ | { | ||
+ | CH_invert[cha] = (uint8_t) Konnekting.getUINT8Param(cha+5); | ||
+ | } | ||
+ | //S0 1 - 4 Load Settings | ||
+ | for(int cha=1; cha<5; cha++) | ||
+ | { | ||
+ | S0_aktiv[cha] = (uint8_t) Konnekting.getUINT8Param(cha+20); | ||
+ | } | ||
+ | //Zählerimpulse Load Settings | ||
+ | for(int cha=0; cha<4; cha++) | ||
+ | { | ||
+ | zaehler_Impulse[cha] = (uint16_t) Konnekting.getUINT16Param(cha+25); | ||
+ | } | ||
+ | #ifdef KDEBUG | ||
+ | Debug.println("READ Zaehler"); | ||
+ | Debug.print("S0_1 Zaehler(Imp/kwh): "); | ||
+ | Debug.println("%d",zaehler_Impulse[0]); | ||
+ | Debug.print("S0_2 Zaehler(Imp/kwh): "); | ||
+ | Debug.println("%d",zaehler_Impulse[1]); | ||
+ | Debug.print("S0_3 Zaehler(Imp/kwh): "); | ||
+ | Debug.println("%d",zaehler_Impulse[2]); | ||
+ | Debug.print("S0_4 Zaehler(Imp/kwh): "); | ||
+ | Debug.println("%d",zaehler_Impulse[3]); | ||
+ | #endif | ||
+ | |||
+ | #ifdef KDEBUG | ||
+ | Debug.println("READ Zimmermaske"); | ||
+ | #endif | ||
+ | //ZimmerMasken Load Settings | ||
+ | for(int cha=0; cha<5; cha++) | ||
+ | { | ||
+ | mask_Zimmer[cha] = (uint16_t) Konnekting.getUINT16Param(cha+29); | ||
+ | } | ||
+ | #ifdef KDEBUG | ||
+ | Debug.print("Zimmer1: "); | ||
+ | SerialUSB.println(mask_Zimmer[0], BIN); | ||
+ | Debug.print("Zimmer2: "); | ||
+ | SerialUSB.println(mask_Zimmer[1], BIN); | ||
+ | Debug.print("Zimmer3: "); | ||
+ | SerialUSB.println(mask_Zimmer[2], BIN); | ||
+ | Debug.print("Zimmer4: "); | ||
+ | SerialUSB.println(mask_Zimmer[3], BIN); | ||
+ | Debug.print("Zimmer5: "); | ||
+ | SerialUSB.println(mask_Zimmer[4], BIN); | ||
+ | #endif | ||
+ | |||
+ | #ifdef KDEBUG | ||
+ | Debug.println("READ Zaehlerstaende"); | ||
+ | #endif | ||
+ | //Read Zählerstände | ||
+ | S0_Zaehler[0] = (((uint16_t)readEeprom(1024))<<8 | readEeprom(1025)); | ||
+ | S0_Zaehler[1] = (((uint16_t)readEeprom(1026))<<8 | readEeprom(1027)); | ||
+ | S0_Zaehler[2] = (((uint16_t)readEeprom(1028))<<8 | readEeprom(1029)); | ||
+ | S0_Zaehler[3] = (((uint16_t)readEeprom(1030))<<8 | readEeprom(1031)); | ||
+ | S0_impuls[0] = (((uint16_t)readEeprom(1032))<<8 | readEeprom(1033)); | ||
+ | S0_impuls[1] = (((uint16_t)readEeprom(1034))<<8 | readEeprom(1035)); | ||
+ | S0_impuls[2] = (((uint16_t)readEeprom(1036))<<8 | readEeprom(1037)); | ||
+ | S0_impuls[3] = (((uint16_t)readEeprom(1038))<<8 | readEeprom(1039)); | ||
+ | attachInterrupt(SAVE_PIN,SAVE_State, FALLING); | ||
+ | |||
+ | #ifdef KDEBUG | ||
+ | Debug.println("Aktuelle Zähler_Impulse"); | ||
+ | Debug.print("S0_1 Zaehler(Imp): "); | ||
+ | Debug.println("%d",S0_impuls[0]); | ||
+ | Debug.print("S0_2 Zaehler(Imp): "); | ||
+ | Debug.println("%d",S0_impuls[1]); | ||
+ | Debug.print("S0_3 Zaehler(Imp): "); | ||
+ | Debug.println("%d",S0_impuls[2]); | ||
+ | Debug.print("S0_4 Zaehler(Imp): "); | ||
+ | Debug.println("%d",S0_impuls[3]); | ||
+ | #endif | ||
+ | |||
+ | S0_Zaehler_old[0] = S0_Zaehler[0]; | ||
+ | S0_Zaehler_old[1] = S0_Zaehler[1]; | ||
+ | S0_Zaehler_old[2] = S0_Zaehler[2]; | ||
+ | S0_Zaehler_old[3] = S0_Zaehler[3]; | ||
+ | |||
+ | //Sendet aktuellen Zählerstand | ||
+ | sendZaehlerStand_2(0,S0_Zaehler); | ||
+ | sendZaehlerStand_2(1,S0_Zaehler); | ||
+ | sendZaehlerStand_2(2,S0_Zaehler); | ||
+ | sendZaehlerStand_2(3,S0_Zaehler); | ||
+ | |||
+ | |||
+ | //S0 Schnittstelle | ||
+ | if (S0_aktiv[1] == 1) | ||
+ | {attachInterrupt(S0_1_pin,irq_S0_1, FALLING); | ||
+ | #ifdef KDEBUG | ||
+ | Debug.println("S0_1 aktiv"); | ||
+ | #endif | ||
+ | } | ||
+ | else { | ||
+ | #ifdef KDEBUG | ||
+ | Debug.println("S0_1 inaktiv"); | ||
+ | #endif | ||
+ | } | ||
+ | if (S0_aktiv[2] == 1) | ||
+ | {attachInterrupt(S0_2_pin,irq_S0_2, FALLING); | ||
+ | #ifdef KDEBUG | ||
+ | Debug.println("S0_2 aktiv"); | ||
+ | #endif | ||
+ | } | ||
+ | else { | ||
+ | #ifdef KDEBUG | ||
+ | Debug.println("S0_2 inaktiv"); | ||
+ | #endif | ||
+ | } | ||
+ | if (S0_aktiv[3] == 1) | ||
+ | {attachInterrupt(S0_3_pin,irq_S0_3, FALLING); | ||
+ | #ifdef KDEBUG | ||
+ | Debug.println("S0_3 aktiv"); | ||
+ | #endif | ||
+ | } | ||
+ | else { | ||
+ | #ifdef KDEBUG | ||
+ | Debug.println("S0_3 inaktiv"); | ||
+ | #endif | ||
+ | } | ||
+ | if (S0_aktiv[4] == 1) | ||
+ | {attachInterrupt(S0_4_pin,irq_S0_4, FALLING); | ||
+ | #ifdef KDEBUG | ||
+ | Debug.println("S0_4 aktiv"); | ||
+ | #endif | ||
+ | } | ||
+ | else { | ||
+ | #ifdef KDEBUG | ||
+ | Debug.println("S0_4 inaktiv"); | ||
+ | #endif | ||
+ | } | ||
+ | |||
+ | //****** State Maschine *********************** | ||
+ | //Init POrt Abfrage | ||
+ | CHState = SetVCC; | ||
+ | //Init 5min mom Leistung | ||
+ | MomL_State = state_S0; | ||
+ | |||
+ | |||
+ | //****** INIT I2C Expander ******************** | ||
+ | #ifdef KDEBUG | ||
+ | Debug.println("Expander INIT START"); | ||
+ | #endif | ||
+ | #ifdef I2COFF | ||
+ | expander.begin(0x20); | ||
+ | #endif | ||
+ | #ifdef KDEBUG | ||
+ | Debug.println("Expander IO finish"); | ||
+ | #endif | ||
+ | #ifdef I2COFF | ||
+ | expander_VCC.begin(0x23); | ||
+ | #endif | ||
+ | #ifdef KDEBUG | ||
+ | Debug.println("Expander VCC finish"); | ||
+ | #endif | ||
+ | #ifdef ANZEIGE_Platine | ||
+ | expander_LED.begin(0x24); | ||
+ | #endif | ||
+ | #ifdef KDEBUG | ||
+ | Debug.println("Expander INIT FINISH"); | ||
+ | #endif | ||
+ | // Init OUTPUTS LED | ||
+ | #ifdef I2COFF | ||
+ | for(int i=0;i<16;i++) | ||
+ | {expander.pinMode(i, INPUT_PULLUP); // WICHTIG: muss "INPUT_PULLUP" sein !!! | ||
+ | expander_VCC.pinMode(i, OUTPUT); | ||
+ | expander_VCC.digitalWrite(i,LOW); | ||
+ | #ifdef ANZEIGE_Platine | ||
+ | expander_LED.pinMode(i, OUTPUT); | ||
+ | expander_LED.digitalWrite(i,LOW); | ||
+ | #endif | ||
+ | } | ||
+ | #endif | ||
+ | |||
+ | // Daten Lesen | ||
+ | #ifdef I2COFF | ||
+ | PNS = expander.read(); | ||
+ | #endif | ||
+ | |||
+ | for(int i=0;i<16;i++) | ||
+ | { | ||
+ | //check Open_HIGH & Open_LOW | ||
+ | if(CH_invert[i]==1) //wenn Open_LOW | ||
+ | {PNS ^= 1 << i;} | ||
+ | } | ||
+ | maske = (uint16_t) PNS; | ||
+ | |||
+ | // Abfrage Ports | ||
+ | for(int i=0;i<16;i++) | ||
+ | { | ||
+ | // HW-PIN "open" | ||
+ | if( (maske & (1 << i))) | ||
+ | { ch_old[i] = false; } | ||
+ | //HW-PIN "closed" | ||
+ | else | ||
+ | { ch_old[i] = true; } | ||
+ | |||
+ | sendStatus(i,ch_old); | ||
+ | } | ||
+ | |||
+ | // CHECK Zimmer maskierung | ||
+ | for(int cha=0;cha<5;cha++){ | ||
+ | if((maske & mask_Zimmer[cha]) != 0) | ||
+ | {state_Zimmer[cha]= true;} | ||
+ | else | ||
+ | {state_Zimmer[cha]= false;} | ||
+ | #ifdef KDEBUG | ||
+ | Debug.println("%d",state_Zimmer[cha]); | ||
+ | #endif | ||
+ | // if(state_Zimmer[cha] != state_Zimmer_old[cha]) | ||
+ | send_Zimmer_Status(cha,state_Zimmer); | ||
+ | state_Zimmer_old[cha]=state_Zimmer[cha]; | ||
+ | } | ||
+ | |||
+ | //****************** Init RTC ******************************************************* | ||
+ | rtc.begin(); | ||
+ | rtc.setTime(hours, minutes, seconds); | ||
+ | rtc.setDate(day, month, year); | ||
+ | |||
+ | rtc.setAlarmTime(23, 59, 00); | ||
+ | rtc.enableAlarm(rtc.MATCH_HHMMSS); | ||
+ | rtc.attachInterrupt(alarmMatch); | ||
+ | |||
+ | |||
+ | //****************** Init Timer ******************************************************* | ||
+ | setTimer(); | ||
+ | setTimer_ms(10); | ||
+ | |||
+ | #ifdef KDEBUG | ||
+ | Debug.println("FINISH Setup"); | ||
+ | #endif | ||
+ | digitalWrite(LED_YELLOW, LOW); | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | ==== Loop() ==== | ||
+ | <source lang=c> | ||
+ | void loop(){ | ||
+ | //digitalWrite(LED_YELLOW, LOW); | ||
+ | Knx.task(); | ||
+ | if (Konnekting.isReadyForApplication()) { | ||
+ | // Sendet bei Zählerstand-Änderung | ||
+ | if(sende_Zyklus_Zaehler == 0){ | ||
+ | if(S0_aktiv[1]== 1) sendZaehlerStand(0,S0_Zaehler,S0_Zaehler_old); | ||
+ | if(S0_aktiv[2]== 1) sendZaehlerStand(1,S0_Zaehler,S0_Zaehler_old); | ||
+ | if(S0_aktiv[3]== 1) sendZaehlerStand(2,S0_Zaehler,S0_Zaehler_old); | ||
+ | if(S0_aktiv[4]== 1) sendZaehlerStand(3,S0_Zaehler,S0_Zaehler_old); | ||
+ | } | ||
+ | else if(sendZaehler_Cycle==true){ | ||
+ | sendZaehler_Cycle = false; | ||
+ | if(S0_aktiv[1]== 1) sendZaehlerStand(0,S0_Zaehler,!S0_Zaehler); | ||
+ | if(S0_aktiv[2]== 1) sendZaehlerStand(1,S0_Zaehler,!S0_Zaehler); | ||
+ | if(S0_aktiv[3]== 1) sendZaehlerStand(2,S0_Zaehler,!S0_Zaehler); | ||
+ | if(S0_aktiv[4]== 1) sendZaehlerStand(3,S0_Zaehler,!S0_Zaehler); | ||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | // Zyklisches Abfragen | ||
+ | if(isDelay==true) | ||
+ | { | ||
+ | if(inputCH<16) | ||
+ | { | ||
+ | switch(CHState) | ||
+ | { | ||
+ | case SetVCC: | ||
+ | expander_VCC.digitalWrite(inputCH,LOW); | ||
+ | CHState = ReadIO; | ||
+ | |||
+ | break; | ||
+ | case ReadIO: | ||
+ | #ifdef I2COFF | ||
+ | PNS = expander.read(); | ||
+ | #endif | ||
+ | CHState = IsInvert; | ||
+ | break; | ||
+ | case IsInvert: | ||
+ | if(CH_invert[inputCH]==1) //wenn Open_LOW | ||
+ | {PNS ^= 1 << inputCH;} | ||
+ | if(CH_invert[inputCH+1]==1) //wenn Open_LOW | ||
+ | {PNS ^= 1 << inputCH+1;} | ||
+ | CHState = MaskIO; | ||
+ | break; | ||
+ | case MaskIO: | ||
+ | |||
+ | if( (PNS & (1 << inputCH))) {ch[inputCH] = 0;} | ||
+ | else {ch[inputCH] = 1;} | ||
+ | |||
+ | if( (PNS & (1 << inputCH+1))){ch[inputCH+1] = 0;} | ||
+ | else {ch[inputCH+1] = 1;} | ||
+ | |||
+ | CHState = IsChange_CH_ONE; | ||
+ | break; | ||
+ | case IsChange_CH_ONE: | ||
+ | isChange(inputCH,ch,ch_old); | ||
+ | CHState = IsChange_CH_TWO; | ||
+ | break; | ||
+ | case IsChange_CH_TWO: | ||
+ | isChange(inputCH+1,ch,ch_old); | ||
+ | CHState = UnsetVCC; | ||
+ | break; | ||
+ | case UnsetVCC: | ||
+ | Knx.task(); | ||
+ | if( (S0_aktiv[4]==1 && inputCH==14) || (S0_aktiv[3]==1 && inputCH-1==13) || (S0_aktiv[2]==1 && inputCH==12) || (S0_aktiv[1]==1 && inputCH-1==11) ) | ||
+ | { | ||
+ | ; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | //expander_VCC.digitalWrite(inputCH,HIGH); | ||
+ | ; | ||
+ | } | ||
+ | CHState = MaskRoom; | ||
+ | break; | ||
+ | case MaskRoom: | ||
+ | if(ch[inputCH] == true) {maske |= (1<<inputCH);} | ||
+ | else {maske &= ~(1<<inputCH);} | ||
+ | if(ch[inputCH+1] == true) {maske |= (1<<inputCH+1);} | ||
+ | else {maske &= ~(1<<inputCH+1);} | ||
+ | CHState = Finish; | ||
+ | break; | ||
+ | case Finish: | ||
+ | inputCH=inputCH+2; | ||
+ | if(inputCH==16) | ||
+ | { | ||
+ | inputCH = 17; | ||
+ | isDelay = false; | ||
+ | } | ||
+ | CHState = SetVCC; | ||
+ | break; | ||
+ | }//Ende Switch | ||
+ | }//Ende Abfrage Ports | ||
+ | |||
+ | // CHECK Zimmer maskierung | ||
+ | for(int cha=0;cha<5;cha++) | ||
+ | { | ||
+ | maske2 = maske & mask_Zimmer[cha]; | ||
+ | if( maske2 != mask_Zimmer[cha]) | ||
+ | {state_Zimmer[cha]= true;} | ||
+ | else | ||
+ | {state_Zimmer[cha]= false;} | ||
+ | if(state_Zimmer[cha] != state_Zimmer_old[cha]) | ||
+ | {state_Zimmer_old[cha]=state_Zimmer[cha]; | ||
+ | send_Zimmer_Status(cha,state_Zimmer);} | ||
+ | } | ||
+ | |||
+ | }//ENDE ISDELAY | ||
+ | |||
+ | //*************************************************************************************************** | ||
+ | // S0_1 LED Blinken | ||
+ | if(set_S0_LED[0] == true) | ||
+ | { | ||
+ | set_S0_LED_time[0] = time_count; | ||
+ | S0_LED_ON[0] = true; | ||
+ | set_S0_LED[0] = false; | ||
+ | #ifdef ANZEIGE_Platine | ||
+ | expander_LED.digitalWrite(MAX_CH-12,LOW); | ||
+ | #endif | ||
+ | // Berechnung mom Leistung | ||
+ | watt_S0[0] = 3600.0/ ((time_S0_stopp[0]-time_S0_start[0])/(zaehler_Impulse[0]*1.0)); | ||
+ | #ifdef KDEBUG | ||
+ | Debug.print("W0: "); | ||
+ | Debug.println("%f", watt_S0[0]); | ||
+ | #endif | ||
+ | time_S0_start[0] = time_S0_stopp[0]; | ||
+ | } | ||
+ | if(S0_LED_ON[0] == true && (time_count > set_S0_LED_time[0]+30)) | ||
+ | { | ||
+ | S0_LED_ON[0] = false; | ||
+ | #ifdef ANZEIGE_Platine | ||
+ | expander_LED.digitalWrite(MAX_CH-12,HIGH); | ||
+ | #endif | ||
+ | } | ||
+ | |||
+ | // S0_2 LED Blinken | ||
+ | if(set_S0_LED[1] == true) | ||
+ | { | ||
+ | set_S0_LED_time[1] = time_count; | ||
+ | S0_LED_ON[1] = true; | ||
+ | set_S0_LED[1] = false; | ||
+ | #ifdef ANZEIGE_Platine | ||
+ | expander_LED.digitalWrite(MAX_CH-13,LOW); | ||
+ | #endif | ||
+ | // Berechnung mom Leistung | ||
+ | watt_S0[1] = 3600.0/ ((time_S0_stopp[1]-time_S0_start[1])/(zaehler_Impulse[1]*1.0)); | ||
+ | #ifdef KDEBUG | ||
+ | Debug.print("W1: "); | ||
+ | Debug.println("%f", watt_S0[1]); | ||
+ | #endif | ||
+ | time_S0_start[1] = time_S0_stopp[1]; | ||
+ | } | ||
+ | if(S0_LED_ON[1] == true && (time_count > set_S0_LED_time[1]+30)) | ||
+ | { | ||
+ | S0_LED_ON[1] = false; | ||
+ | #ifdef ANZEIGE_Platine | ||
+ | expander_LED.digitalWrite(MAX_CH-13,HIGH); | ||
+ | #endif | ||
+ | } | ||
+ | |||
+ | // S0_3 LED Blinken | ||
+ | if(set_S0_LED[2] == true) | ||
+ | { | ||
+ | set_S0_LED_time[2] = time_count; | ||
+ | S0_LED_ON[2] = true; | ||
+ | set_S0_LED[2] = false; | ||
+ | #ifdef ANZEIGE_Platine | ||
+ | expander_LED.digitalWrite(MAX_CH-14,LOW); | ||
+ | #endif | ||
+ | // Berechnung mom Leistung | ||
+ | watt_S0[2] = 3600.0/ ((time_S0_stopp[2]-time_S0_start[2])/(zaehler_Impulse[2]*1.0)); | ||
+ | #ifdef KDEBUG | ||
+ | Debug.print("W2: "); | ||
+ | Debug.println("%f", watt_S0[2]); | ||
+ | #endif | ||
+ | time_S0_start[2] = time_S0_stopp[2]; | ||
+ | } | ||
+ | if(S0_LED_ON[2] == true && (time_count > set_S0_LED_time[2]+30)) | ||
+ | { | ||
+ | S0_LED_ON[2] = false; | ||
+ | #ifdef ANZEIGE_Platine | ||
+ | expander_LED.digitalWrite(MAX_CH-14,HIGH); | ||
+ | #endif | ||
+ | } | ||
+ | |||
+ | // S0_4 LED Blinken | ||
+ | if(set_S0_LED[3] == true) | ||
+ | { | ||
+ | set_S0_LED_time[3] = time_count; | ||
+ | S0_LED_ON[3] = true; | ||
+ | set_S0_LED[3] = false; | ||
+ | #ifdef ANZEIGE_Platine | ||
+ | expander_LED.digitalWrite(MAX_CH-15,LOW); | ||
+ | #endif | ||
+ | // Berechnung mom Leistung | ||
+ | watt_S0[3] = 3600.0/ ((time_S0_stopp[3]-time_S0_start[3])/(zaehler_Impulse[3]*1.0)); | ||
+ | #ifdef KDEBUG | ||
+ | Debug.print("W3: "); | ||
+ | Debug.println("%f", watt_S0[3]); | ||
+ | #endif | ||
+ | time_S0_start[3] = time_S0_stopp[3]; | ||
+ | } | ||
+ | if(S0_LED_ON[3] == true && (time_count > set_S0_LED_time[3]+30)) | ||
+ | { | ||
+ | S0_LED_ON[3] = false; | ||
+ | #ifdef ANZEIGE_Platine | ||
+ | expander_LED.digitalWrite(MAX_CH-15,HIGH); | ||
+ | #endif | ||
+ | } | ||
+ | |||
+ | //*************************************************************************************************** | ||
+ | //Momentane Verlustleistung senden | ||
+ | |||
+ | if(is_5min == true) | ||
+ | { | ||
+ | switch(MomL_State) | ||
+ | { | ||
+ | case state_S0: | ||
+ | if(S0_aktiv[1]==1) | ||
+ | { | ||
+ | if(millis()-time_S0_start[0]>60000) | ||
+ | { | ||
+ | if(millis()-time_S0_start[0]>240000) // kein Impuls nach 4min | ||
+ | { | ||
+ | watt_S0[0] = 0; | ||
+ | Knx.write(33,watt_S0[0]); | ||
+ | } | ||
+ | else if(millis()-time_S0_start[0]>180000) // kein Impuls nach 3min | ||
+ | { | ||
+ | if(watt_S0[0]>(3600.0 / (180000/zaehler_Impulse[0]))) | ||
+ | {watt_S0[0] = 3600.0 / (180000/zaehler_Impulse[0]);} | ||
+ | Knx.write(33,watt_S0[0]); | ||
+ | } | ||
+ | else if(millis()-time_S0_start[0]>120000) // kein Impuls nach 2min | ||
+ | { | ||
+ | if(watt_S0[0]>(3600.0 / (120000/zaehler_Impulse[0]))) | ||
+ | {watt_S0[0] = 3600.0 / (120000/zaehler_Impulse[0]);} | ||
+ | Knx.write(33,watt_S0[0]); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | if(watt_S0[0]>(3600.0 / (60000/zaehler_Impulse[0]))) | ||
+ | {watt_S0[0] = 3600.0 / (60000/zaehler_Impulse[0]);} | ||
+ | Knx.write(33,watt_S0[0]); | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | Knx.write(33,watt_S0[0]); | ||
+ | } | ||
+ | MomL_State = state_S1; | ||
+ | break; | ||
+ | |||
+ | case state_S1: | ||
+ | if(S0_aktiv[2]==1) | ||
+ | { | ||
+ | if(millis()-time_S0_start[1]>60000) | ||
+ | { | ||
+ | if(millis()-time_S0_start[1]>240000) // kein Impuls nach 4min | ||
+ | { | ||
+ | watt_S0[1] = 0; | ||
+ | Knx.write(36,watt_S0[1]); | ||
+ | } | ||
+ | else if(millis()-time_S0_start[1]>180000) // kein Impuls nach 3min | ||
+ | { | ||
+ | if(watt_S0[1]>(3600.0 / (180000/zaehler_Impulse[1]))) | ||
+ | {watt_S0[1] = 3600.0 / (180000/zaehler_Impulse[1]);} | ||
+ | Knx.write(36,watt_S0[1]); | ||
+ | } | ||
+ | else if(millis()-time_S0_start[1]>120000) // kein Impuls nach 2min | ||
+ | { | ||
+ | if(watt_S0[1]>(3600.0 / (120000/zaehler_Impulse[1]))) | ||
+ | {watt_S0[1] = 3600.0 / (120000/zaehler_Impulse[1]);} | ||
+ | Knx.write(36,watt_S0[1]); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | if(watt_S0[1]>(3600.0 / (60000/zaehler_Impulse[1]))) | ||
+ | {watt_S0[1] = 3600.0 / (60000/zaehler_Impulse[1]);} | ||
+ | Knx.write(36,watt_S0[1]); | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | Knx.write(36,watt_S0[1]); | ||
+ | } | ||
+ | MomL_State = state_S2; | ||
+ | break; | ||
+ | case state_S2: | ||
+ | if(S0_aktiv[3]==1) | ||
+ | { | ||
+ | if(millis()-time_S0_start[2]>60000) | ||
+ | { | ||
+ | if(millis()-time_S0_start[2]>240000) // kein Impuls nach 4min | ||
+ | { | ||
+ | watt_S0[3] = 0; | ||
+ | Knx.write(39,watt_S0[2]); | ||
+ | } | ||
+ | else if(millis()-time_S0_start[2]>180000) // kein Impuls nach 3min | ||
+ | { | ||
+ | if(watt_S0[2]>(3600.0 / (180000/zaehler_Impulse[2]))) | ||
+ | {watt_S0[2] = 3600.0 / (180000/zaehler_Impulse[2]);} | ||
+ | Knx.write(39,watt_S0[2]); | ||
+ | } | ||
+ | else if(millis()-time_S0_start[2]>120000) // kein Impuls nach 2min | ||
+ | { | ||
+ | if(watt_S0[2]>(3600.0 / (120000/zaehler_Impulse[2]))) | ||
+ | {watt_S0[2] = 3600.0 / (120000/zaehler_Impulse[2]);} | ||
+ | Knx.write(39,watt_S0[2]); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | if(watt_S0[2]>(3600.0 / (60000/zaehler_Impulse[2]))) | ||
+ | {watt_S0[2] = 3600.0 / (60000/zaehler_Impulse[2]);} | ||
+ | Knx.write(39,watt_S0[2]); | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | Knx.write(39,watt_S0[2]); | ||
+ | } | ||
+ | MomL_State = state_S3; | ||
+ | break; | ||
+ | case state_S3: | ||
+ | if(S0_aktiv[4]==1) | ||
+ | { | ||
+ | if(millis()-time_S0_start[3]>60000) | ||
+ | { | ||
+ | if(millis()-time_S0_start[3]>240000) // kein Impuls nach 4min | ||
+ | { | ||
+ | watt_S0[3] = 0; | ||
+ | Knx.write(42,watt_S0[3]); | ||
+ | } | ||
+ | else if(millis()-time_S0_start[3]>180000) // kein Impuls nach 3min | ||
+ | { | ||
+ | if(watt_S0[3]>(3600.0 / (180000/zaehler_Impulse[3]))) | ||
+ | {watt_S0[3] = 3600.0 / (180000/zaehler_Impulse[3]);}//20W | ||
+ | Knx.write(42,watt_S0[3]); | ||
+ | } | ||
+ | else if(millis()-time_S0_start[3]>120000) // kein Impuls nach 2min | ||
+ | { | ||
+ | if(watt_S0[3]>(3600.0 / (120000/zaehler_Impulse[3]))) | ||
+ | {watt_S0[3] = 3600.0 / (120000/zaehler_Impulse[3]);} //30W | ||
+ | Knx.write(42,watt_S0[3]); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | if(watt_S0[3]>(3600.0 / (60000/zaehler_Impulse[3]))) | ||
+ | {watt_S0[3] = 3600.0 / (60000/zaehler_Impulse[3]);} //60W | ||
+ | Knx.write(42,watt_S0[3]); | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | Knx.write(42,watt_S0[3]); | ||
+ | } | ||
+ | MomL_State = state_S0; | ||
+ | is_5min = false; | ||
+ | break; | ||
+ | }//Switch | ||
+ | }//5min | ||
+ | |||
+ | //*************************************************************************************************** | ||
+ | // Wenn neue Zälerstände über Suite gespeichert werden | ||
+ | if(run_save == true) | ||
+ | { | ||
+ | run_save = false; | ||
+ | #ifdef KDEBUG | ||
+ | Debug.println("Set Zaehlerstand"); | ||
+ | #endif | ||
+ | sendZaehlerStand_2(0,S0_Zaehler); | ||
+ | sendZaehlerStand_2(1,S0_Zaehler); | ||
+ | sendZaehlerStand_2(2,S0_Zaehler); | ||
+ | sendZaehlerStand_2(3,S0_Zaehler); | ||
+ | writeSAVE(S0_Zaehler,S0_impuls); | ||
+ | digitalWrite(LED_YELLOW, LOW); | ||
+ | } | ||
+ | }//ENDE KONNEKTING APPLIKATION RUNING | ||
} | } | ||
</source> | </source> | ||
Line 169: | Line 938: | ||
== User Documentation == | == User Documentation == | ||
+ | |||
+ | === HowTo: upload Arduino Code === | ||
+ | Refer [[HowToUploadArduinoCode]] | ||
+ | |||
+ | * [https://github.com/KONNEKTING/KonnektingDeviceLibrary| KONNEKTING Lib] | ||
+ | * [https://github.com/skywodd/pcf8574_arduino_library| PCF8575 I2C IO Expander] | ||
+ | |||
=== SW Documentation === | === SW Documentation === | ||
+ | ==== Set "Zählerstände" over Suite ==== | ||
+ | Über die Suite kann man die Zählerstände im EEPROM überschreiben. | ||
+ | |||
==== Sperrobjekte ==== | ==== Sperrobjekte ==== | ||
==== Room classification ==== | ==== Room classification ==== |
Latest revision as of 19:54, 16 March 2018
16CH Binary Input | |
---|---|
Developer | Matthias F. |
Status | Version 1.0 finished |
Microcontroller/Board | |
KNX connectivity | Mini-BCU |
Needed libraries | KONNEKTING Lib [1] PCF8575 I2C IO Expander [2] |
Description
16ch Binary Input (potential-free)
The binary input has 16 inputs available and is used to connect eight conventional push-buttons or floating contacts such as window or relay contacts. There is one status LED for each input. In addition, the input status is sent to the bus. A separate power supply is not necessary
Technical Data:
- Operating voltage: KNX Bus voltage <10mA
- Installation type: Top Hat Rail mounting
- Number of Inputs: 16
- Number of S0-Inputs: 4 (CH13 - CH16)
- Input polling: with 100ms, 300ms or 500ms
- Typ of Inputs: potential-free / floating contacts
- Max. cable length: 30m tested (more possible)
- input voltage: 5V
- Max. input voltage: 24V (absolute rating, for <1min)
- Max. input current: 10mA
- The device makes a contact supply voltage (5V) which is not electrically isolated from the bus voltage!
- x-Inputs can configure to a "room" ("room"-GA can show if one of the windows is open in the room)
Hardware
What you need
for this device do you need:
- 1x OKW Housing
- 1x PCB Kit
- 1x Mounting Kit (screws, ...)
OKW Housing link OKW-Website:[3] 1x B6503121 RAILTEC B, 4 Module 3x B6607140 Klemmenabdeckung, flach 1x B6607145 Abdeckung KNX, flach 4x B6607142 Klemmenabdeckung, flach (1x) B6603180 Frontplatte, 4 Module (only if you don't use the "LED-Status-PCB"
PCB Kit: 1) Application PCB 2) Controller PCB 3) LED-Status PCB (for function not necessary, only to show the status of the Inputs) 4) Mini-BCU (KNX-Transceiver)
Mounting Kit 1x Micromatch cable 8x Screws
Block Diagram
Detection Circuit
Software
What you need
- Arduino IDE ([4])
- Arduino Lib PCF8575 ([5])
- Arduino Code for 16CH Binary Input
- XML-File for 16CH Binary Input
- KONNEKTING SUITE (link)
Arduino Code
Libs
#include <KonnektingDevice.h>
#include "kdevice_KNX_Bineareingang_Multi.h" -> erzeugte lib aus Konnekting
#include "Timer.h"
#include "EEPROM.h"
#include "S0_Schnittstelle.h"
#include <RTCZero.h>
#include <Wire.h>
#include <PCF8575.h> // Required for PCF8575
Debug Config
Hier hat man Möglichkeiten bestimmte Funktionen zu aktivieren/deaktivieren
// I2C-MODE (enable for normal mode)
#define I2COFF // comment this line to disable I2C mode
// DEBUG-MODE (for normal mode without debugging you must deactivate the function)
#define KDEBUG // comment this line to disable DEBUG mode
// Anzeige-Platine (enable if you use the Status LED PCB)
#define ANZEIGE_Platine
KNX Serial Config
Hier wird das Serial-IF zum KNX-Bus konfiguriert
#ifdef __AVR_ATmega328P__
#define KNX_SERIAL Serial // Nano/ProMini etc. use Serial
#elif ESP8266
#define KNX_SERIAL Serial // ESP8266 use Serial
#else
#define KNX_SERIAL Serial1 // Leonardo/Micro/Zero etc. use Serial1
#endif
RTC Config
Jeden Tag um 0UHr werden alle Daten ins EEPROM geschrieben
/* Create an rtc object */
RTCZero rtc;
/* Change these values to set the current initial time */
const byte seconds = 0;
const byte minutes = 00;
const byte hours = 00;
/* Change these values to set the current initial date */
const byte day = 1;
const byte month = 1;
const byte year = 17;
void alarmMatch()
{
digitalWrite(LED_YELLOW, HIGH);
#ifdef KDEBUG
Debug.println("SAFE");
#endif
rtc.setTime(hours, minutes, seconds);
rtc.setAlarmTime(23, 59, 00);
rtc.enableAlarm(rtc.MATCH_HHMMSS);
run_save=true;
}
define IO Config
#define PROG_LED_PIN A2
#define PROG_BUTTON_PIN A1
#define LED_YELLOW 25
#define SAVE_PIN 38
#define MAX_CH 15
#define S0_1_pin 11 //IO6
#define S0_2_pin 5 //IO7
#define S0_3_pin 6 //IO8
#define S0_4_pin 2 //IO9
Save Parameters
Funktion wird aufgerufen:
- - jeden Tag um 0Uhr
- - Wenn ein Busspannugsabfall detekiert wird
void SAVE_State(){
digitalWrite(LED_YELLOW, HIGH); // LED indicator
writeSAVE(S0_Zaehler,S0_impuls); // EEPROM write routine to save all values
}
IRQ S0 Interface
void irq_S0_1(){
time_S0_stopp[0] = millis();
S0_impuls[0]++;
if(S0_impuls[0]>=zaehler_Impulse[0])
{S0_Zaehler[0]++;
S0_impuls[0] = 0;}
set_S0_LED[0] = true;
#ifdef KDEBUG
Debug.print("S0_1: ");
Debug.println("%d",S0_impuls[0]);
#endif
}
void irq_S0_2(){
time_S0_stopp[1] = millis();
S0_impuls[1]++;
if(S0_impuls[1]>=zaehler_Impulse[1])
{S0_Zaehler[1]++;
S0_impuls[1] = 0;}
set_S0_LED[1] = true;
#ifdef KDEBUG
Debug.print("S0_2: ");
Debug.println("%d",S0_impuls[1]);
#endif
}
void irq_S0_3(){
time_S0_stopp[2] = millis();
S0_impuls[2]++;
if(S0_impuls[2]>=zaehler_Impulse[2])
{S0_Zaehler[2]++;
S0_impuls[2] = 0;}
set_S0_LED[2] = true;
#ifdef KDEBUG
Debug.print("S0_3: ");
Debug.println("%d",S0_impuls[2]);
#endif
}
void irq_S0_4(){
time_S0_stopp[3] = millis();
S0_impuls[3]++;
if(S0_impuls[3]>=zaehler_Impulse[3])
{S0_Zaehler[3]++;
S0_impuls[3] = 0;}
set_S0_LED[3] = true;
#ifdef KDEBUG
Debug.print("S0_4: ");
Debug.println("%d",S0_impuls[3]);
#endif
}
Setup() Routine
void setup(){
//****************** Init IO *******************************************************
pinMode(LED_YELLOW, OUTPUT);
digitalWrite(LED_YELLOW, HIGH);
pinMode(S0_1_pin, INPUT);
pinMode(S0_2_pin, INPUT);
pinMode(S0_3_pin, INPUT);
pinMode(S0_4_pin, INPUT);
Wire.begin(); // join i2c bus (address optional for master)
//****************** Init Debug Interface ********************************************
#ifdef KDEBUG
// Start debug serial with 9600 bauds
DEBUGSERIAL.begin(115200);
#if defined(__AVR_ATmega32U4__) || defined(__SAMD21G18A__)
// wait for serial port to connect. Needed for Leonardo/Micro/ProMicro/Zero only
while (!DEBUGSERIAL)
#endif
// make debug serial port known to debug class
// Means: KONNEKTING will sue the same serial port for console debugging
Debug.setPrintStream(&DEBUGSERIAL);
Debug.print(F("KONNEKTING DemoSketch\n"));
#endif
//****************** Init Debug KONNEKTING ********************************************
Konnekting.setMemoryReadFunc(&readEeprom);
Konnekting.setMemoryWriteFunc(&writeEeprom);
Konnekting.setMemoryUpdateFunc(&updateEeprom);
// Initialize KNX enabled Arduino Board
Konnekting.init(KNX_SERIAL,
PROG_BUTTON_PIN,
PROG_LED_PIN,
MANUFACTURER_ID,
DEVICE_ID,
REVISION);
//****************** Read Parameter ***************************************************
#ifdef KDEBUG
Debug.println("READ Parameter");
#endif
abfrage_Interval = (((uint8_t) Konnekting.getUINT8Param(0)+1)*30); // abfrage_Interval
sende_Zyklus_Zaehler = (uint8_t) Konnekting.getUINT8Param(1); // sende_Zyklus_Zaehler
zimmerMaskierung_Sendvalue = (uint8_t) Konnekting.getUINT8Param(2); // Ob HIGH oder LOW gesendet wird
invertLED = (uint8_t) Konnekting.getUINT8Param(3); // invertiert LED Anzeige
#ifdef KDEBUG
Debug.print("Abfrage-Interval: ");
Debug.println("%d",abfrage_Interval);
Debug.print("Zimmer Maskierung Sendvalue: ");
Debug.println("%d",zimmerMaskierung_Sendvalue);
Debug.print("Anzahl Messungen: ");
Debug.println("%d",filter_count);
Debug.print("LED invert: ");
Debug.println("%d",invertLED);
#endif
//****************** Set Sendezyklus (S0-Schnittstelle) ********************************
if(sende_Zyklus_Zaehler == 1) sende_Zyklus = 6000; // 1min
if(sende_Zyklus_Zaehler == 2) sende_Zyklus = 60000; //10min
if(sende_Zyklus_Zaehler == 3) sende_Zyklus = 180000; //30min
if(sende_Zyklus_Zaehler == 4) sende_Zyklus = 360000; //60min
#ifdef KDEBUG
Debug.print("Sende-Zyklus: ");
Debug.println("%d",sende_Zyklus);
#endif
//****************** Load Settings *****************************************************
//Kanal 1 - 16 Load Settings
for(int cha=0; cha<16; cha++)
{
CH_invert[cha] = (uint8_t) Konnekting.getUINT8Param(cha+5);
}
//S0 1 - 4 Load Settings
for(int cha=1; cha<5; cha++)
{
S0_aktiv[cha] = (uint8_t) Konnekting.getUINT8Param(cha+20);
}
//Zählerimpulse Load Settings
for(int cha=0; cha<4; cha++)
{
zaehler_Impulse[cha] = (uint16_t) Konnekting.getUINT16Param(cha+25);
}
#ifdef KDEBUG
Debug.println("READ Zaehler");
Debug.print("S0_1 Zaehler(Imp/kwh): ");
Debug.println("%d",zaehler_Impulse[0]);
Debug.print("S0_2 Zaehler(Imp/kwh): ");
Debug.println("%d",zaehler_Impulse[1]);
Debug.print("S0_3 Zaehler(Imp/kwh): ");
Debug.println("%d",zaehler_Impulse[2]);
Debug.print("S0_4 Zaehler(Imp/kwh): ");
Debug.println("%d",zaehler_Impulse[3]);
#endif
#ifdef KDEBUG
Debug.println("READ Zimmermaske");
#endif
//ZimmerMasken Load Settings
for(int cha=0; cha<5; cha++)
{
mask_Zimmer[cha] = (uint16_t) Konnekting.getUINT16Param(cha+29);
}
#ifdef KDEBUG
Debug.print("Zimmer1: ");
SerialUSB.println(mask_Zimmer[0], BIN);
Debug.print("Zimmer2: ");
SerialUSB.println(mask_Zimmer[1], BIN);
Debug.print("Zimmer3: ");
SerialUSB.println(mask_Zimmer[2], BIN);
Debug.print("Zimmer4: ");
SerialUSB.println(mask_Zimmer[3], BIN);
Debug.print("Zimmer5: ");
SerialUSB.println(mask_Zimmer[4], BIN);
#endif
#ifdef KDEBUG
Debug.println("READ Zaehlerstaende");
#endif
//Read Zählerstände
S0_Zaehler[0] = (((uint16_t)readEeprom(1024))<<8 | readEeprom(1025));
S0_Zaehler[1] = (((uint16_t)readEeprom(1026))<<8 | readEeprom(1027));
S0_Zaehler[2] = (((uint16_t)readEeprom(1028))<<8 | readEeprom(1029));
S0_Zaehler[3] = (((uint16_t)readEeprom(1030))<<8 | readEeprom(1031));
S0_impuls[0] = (((uint16_t)readEeprom(1032))<<8 | readEeprom(1033));
S0_impuls[1] = (((uint16_t)readEeprom(1034))<<8 | readEeprom(1035));
S0_impuls[2] = (((uint16_t)readEeprom(1036))<<8 | readEeprom(1037));
S0_impuls[3] = (((uint16_t)readEeprom(1038))<<8 | readEeprom(1039));
attachInterrupt(SAVE_PIN,SAVE_State, FALLING);
#ifdef KDEBUG
Debug.println("Aktuelle Zähler_Impulse");
Debug.print("S0_1 Zaehler(Imp): ");
Debug.println("%d",S0_impuls[0]);
Debug.print("S0_2 Zaehler(Imp): ");
Debug.println("%d",S0_impuls[1]);
Debug.print("S0_3 Zaehler(Imp): ");
Debug.println("%d",S0_impuls[2]);
Debug.print("S0_4 Zaehler(Imp): ");
Debug.println("%d",S0_impuls[3]);
#endif
S0_Zaehler_old[0] = S0_Zaehler[0];
S0_Zaehler_old[1] = S0_Zaehler[1];
S0_Zaehler_old[2] = S0_Zaehler[2];
S0_Zaehler_old[3] = S0_Zaehler[3];
//Sendet aktuellen Zählerstand
sendZaehlerStand_2(0,S0_Zaehler);
sendZaehlerStand_2(1,S0_Zaehler);
sendZaehlerStand_2(2,S0_Zaehler);
sendZaehlerStand_2(3,S0_Zaehler);
//S0 Schnittstelle
if (S0_aktiv[1] == 1)
{attachInterrupt(S0_1_pin,irq_S0_1, FALLING);
#ifdef KDEBUG
Debug.println("S0_1 aktiv");
#endif
}
else {
#ifdef KDEBUG
Debug.println("S0_1 inaktiv");
#endif
}
if (S0_aktiv[2] == 1)
{attachInterrupt(S0_2_pin,irq_S0_2, FALLING);
#ifdef KDEBUG
Debug.println("S0_2 aktiv");
#endif
}
else {
#ifdef KDEBUG
Debug.println("S0_2 inaktiv");
#endif
}
if (S0_aktiv[3] == 1)
{attachInterrupt(S0_3_pin,irq_S0_3, FALLING);
#ifdef KDEBUG
Debug.println("S0_3 aktiv");
#endif
}
else {
#ifdef KDEBUG
Debug.println("S0_3 inaktiv");
#endif
}
if (S0_aktiv[4] == 1)
{attachInterrupt(S0_4_pin,irq_S0_4, FALLING);
#ifdef KDEBUG
Debug.println("S0_4 aktiv");
#endif
}
else {
#ifdef KDEBUG
Debug.println("S0_4 inaktiv");
#endif
}
//****** State Maschine ***********************
//Init POrt Abfrage
CHState = SetVCC;
//Init 5min mom Leistung
MomL_State = state_S0;
//****** INIT I2C Expander ********************
#ifdef KDEBUG
Debug.println("Expander INIT START");
#endif
#ifdef I2COFF
expander.begin(0x20);
#endif
#ifdef KDEBUG
Debug.println("Expander IO finish");
#endif
#ifdef I2COFF
expander_VCC.begin(0x23);
#endif
#ifdef KDEBUG
Debug.println("Expander VCC finish");
#endif
#ifdef ANZEIGE_Platine
expander_LED.begin(0x24);
#endif
#ifdef KDEBUG
Debug.println("Expander INIT FINISH");
#endif
// Init OUTPUTS LED
#ifdef I2COFF
for(int i=0;i<16;i++)
{expander.pinMode(i, INPUT_PULLUP); // WICHTIG: muss "INPUT_PULLUP" sein !!!
expander_VCC.pinMode(i, OUTPUT);
expander_VCC.digitalWrite(i,LOW);
#ifdef ANZEIGE_Platine
expander_LED.pinMode(i, OUTPUT);
expander_LED.digitalWrite(i,LOW);
#endif
}
#endif
// Daten Lesen
#ifdef I2COFF
PNS = expander.read();
#endif
for(int i=0;i<16;i++)
{
//check Open_HIGH & Open_LOW
if(CH_invert[i]==1) //wenn Open_LOW
{PNS ^= 1 << i;}
}
maske = (uint16_t) PNS;
// Abfrage Ports
for(int i=0;i<16;i++)
{
// HW-PIN "open"
if( (maske & (1 << i)))
{ ch_old[i] = false; }
//HW-PIN "closed"
else
{ ch_old[i] = true; }
sendStatus(i,ch_old);
}
// CHECK Zimmer maskierung
for(int cha=0;cha<5;cha++){
if((maske & mask_Zimmer[cha]) != 0)
{state_Zimmer[cha]= true;}
else
{state_Zimmer[cha]= false;}
#ifdef KDEBUG
Debug.println("%d",state_Zimmer[cha]);
#endif
// if(state_Zimmer[cha] != state_Zimmer_old[cha])
send_Zimmer_Status(cha,state_Zimmer);
state_Zimmer_old[cha]=state_Zimmer[cha];
}
//****************** Init RTC *******************************************************
rtc.begin();
rtc.setTime(hours, minutes, seconds);
rtc.setDate(day, month, year);
rtc.setAlarmTime(23, 59, 00);
rtc.enableAlarm(rtc.MATCH_HHMMSS);
rtc.attachInterrupt(alarmMatch);
//****************** Init Timer *******************************************************
setTimer();
setTimer_ms(10);
#ifdef KDEBUG
Debug.println("FINISH Setup");
#endif
digitalWrite(LED_YELLOW, LOW);
}
Loop()
void loop(){
//digitalWrite(LED_YELLOW, LOW);
Knx.task();
if (Konnekting.isReadyForApplication()) {
// Sendet bei Zählerstand-Änderung
if(sende_Zyklus_Zaehler == 0){
if(S0_aktiv[1]== 1) sendZaehlerStand(0,S0_Zaehler,S0_Zaehler_old);
if(S0_aktiv[2]== 1) sendZaehlerStand(1,S0_Zaehler,S0_Zaehler_old);
if(S0_aktiv[3]== 1) sendZaehlerStand(2,S0_Zaehler,S0_Zaehler_old);
if(S0_aktiv[4]== 1) sendZaehlerStand(3,S0_Zaehler,S0_Zaehler_old);
}
else if(sendZaehler_Cycle==true){
sendZaehler_Cycle = false;
if(S0_aktiv[1]== 1) sendZaehlerStand(0,S0_Zaehler,!S0_Zaehler);
if(S0_aktiv[2]== 1) sendZaehlerStand(1,S0_Zaehler,!S0_Zaehler);
if(S0_aktiv[3]== 1) sendZaehlerStand(2,S0_Zaehler,!S0_Zaehler);
if(S0_aktiv[4]== 1) sendZaehlerStand(3,S0_Zaehler,!S0_Zaehler);
}
// Zyklisches Abfragen
if(isDelay==true)
{
if(inputCH<16)
{
switch(CHState)
{
case SetVCC:
expander_VCC.digitalWrite(inputCH,LOW);
CHState = ReadIO;
break;
case ReadIO:
#ifdef I2COFF
PNS = expander.read();
#endif
CHState = IsInvert;
break;
case IsInvert:
if(CH_invert[inputCH]==1) //wenn Open_LOW
{PNS ^= 1 << inputCH;}
if(CH_invert[inputCH+1]==1) //wenn Open_LOW
{PNS ^= 1 << inputCH+1;}
CHState = MaskIO;
break;
case MaskIO:
if( (PNS & (1 << inputCH))) {ch[inputCH] = 0;}
else {ch[inputCH] = 1;}
if( (PNS & (1 << inputCH+1))){ch[inputCH+1] = 0;}
else {ch[inputCH+1] = 1;}
CHState = IsChange_CH_ONE;
break;
case IsChange_CH_ONE:
isChange(inputCH,ch,ch_old);
CHState = IsChange_CH_TWO;
break;
case IsChange_CH_TWO:
isChange(inputCH+1,ch,ch_old);
CHState = UnsetVCC;
break;
case UnsetVCC:
Knx.task();
if( (S0_aktiv[4]==1 && inputCH==14) || (S0_aktiv[3]==1 && inputCH-1==13) || (S0_aktiv[2]==1 && inputCH==12) || (S0_aktiv[1]==1 && inputCH-1==11) )
{
;
}
else
{
//expander_VCC.digitalWrite(inputCH,HIGH);
;
}
CHState = MaskRoom;
break;
case MaskRoom:
if(ch[inputCH] == true) {maske |= (1<<inputCH);}
else {maske &= ~(1<<inputCH);}
if(ch[inputCH+1] == true) {maske |= (1<<inputCH+1);}
else {maske &= ~(1<<inputCH+1);}
CHState = Finish;
break;
case Finish:
inputCH=inputCH+2;
if(inputCH==16)
{
inputCH = 17;
isDelay = false;
}
CHState = SetVCC;
break;
}//Ende Switch
}//Ende Abfrage Ports
// CHECK Zimmer maskierung
for(int cha=0;cha<5;cha++)
{
maske2 = maske & mask_Zimmer[cha];
if( maske2 != mask_Zimmer[cha])
{state_Zimmer[cha]= true;}
else
{state_Zimmer[cha]= false;}
if(state_Zimmer[cha] != state_Zimmer_old[cha])
{state_Zimmer_old[cha]=state_Zimmer[cha];
send_Zimmer_Status(cha,state_Zimmer);}
}
}//ENDE ISDELAY
//***************************************************************************************************
// S0_1 LED Blinken
if(set_S0_LED[0] == true)
{
set_S0_LED_time[0] = time_count;
S0_LED_ON[0] = true;
set_S0_LED[0] = false;
#ifdef ANZEIGE_Platine
expander_LED.digitalWrite(MAX_CH-12,LOW);
#endif
// Berechnung mom Leistung
watt_S0[0] = 3600.0/ ((time_S0_stopp[0]-time_S0_start[0])/(zaehler_Impulse[0]*1.0));
#ifdef KDEBUG
Debug.print("W0: ");
Debug.println("%f", watt_S0[0]);
#endif
time_S0_start[0] = time_S0_stopp[0];
}
if(S0_LED_ON[0] == true && (time_count > set_S0_LED_time[0]+30))
{
S0_LED_ON[0] = false;
#ifdef ANZEIGE_Platine
expander_LED.digitalWrite(MAX_CH-12,HIGH);
#endif
}
// S0_2 LED Blinken
if(set_S0_LED[1] == true)
{
set_S0_LED_time[1] = time_count;
S0_LED_ON[1] = true;
set_S0_LED[1] = false;
#ifdef ANZEIGE_Platine
expander_LED.digitalWrite(MAX_CH-13,LOW);
#endif
// Berechnung mom Leistung
watt_S0[1] = 3600.0/ ((time_S0_stopp[1]-time_S0_start[1])/(zaehler_Impulse[1]*1.0));
#ifdef KDEBUG
Debug.print("W1: ");
Debug.println("%f", watt_S0[1]);
#endif
time_S0_start[1] = time_S0_stopp[1];
}
if(S0_LED_ON[1] == true && (time_count > set_S0_LED_time[1]+30))
{
S0_LED_ON[1] = false;
#ifdef ANZEIGE_Platine
expander_LED.digitalWrite(MAX_CH-13,HIGH);
#endif
}
// S0_3 LED Blinken
if(set_S0_LED[2] == true)
{
set_S0_LED_time[2] = time_count;
S0_LED_ON[2] = true;
set_S0_LED[2] = false;
#ifdef ANZEIGE_Platine
expander_LED.digitalWrite(MAX_CH-14,LOW);
#endif
// Berechnung mom Leistung
watt_S0[2] = 3600.0/ ((time_S0_stopp[2]-time_S0_start[2])/(zaehler_Impulse[2]*1.0));
#ifdef KDEBUG
Debug.print("W2: ");
Debug.println("%f", watt_S0[2]);
#endif
time_S0_start[2] = time_S0_stopp[2];
}
if(S0_LED_ON[2] == true && (time_count > set_S0_LED_time[2]+30))
{
S0_LED_ON[2] = false;
#ifdef ANZEIGE_Platine
expander_LED.digitalWrite(MAX_CH-14,HIGH);
#endif
}
// S0_4 LED Blinken
if(set_S0_LED[3] == true)
{
set_S0_LED_time[3] = time_count;
S0_LED_ON[3] = true;
set_S0_LED[3] = false;
#ifdef ANZEIGE_Platine
expander_LED.digitalWrite(MAX_CH-15,LOW);
#endif
// Berechnung mom Leistung
watt_S0[3] = 3600.0/ ((time_S0_stopp[3]-time_S0_start[3])/(zaehler_Impulse[3]*1.0));
#ifdef KDEBUG
Debug.print("W3: ");
Debug.println("%f", watt_S0[3]);
#endif
time_S0_start[3] = time_S0_stopp[3];
}
if(S0_LED_ON[3] == true && (time_count > set_S0_LED_time[3]+30))
{
S0_LED_ON[3] = false;
#ifdef ANZEIGE_Platine
expander_LED.digitalWrite(MAX_CH-15,HIGH);
#endif
}
//***************************************************************************************************
//Momentane Verlustleistung senden
if(is_5min == true)
{
switch(MomL_State)
{
case state_S0:
if(S0_aktiv[1]==1)
{
if(millis()-time_S0_start[0]>60000)
{
if(millis()-time_S0_start[0]>240000) // kein Impuls nach 4min
{
watt_S0[0] = 0;
Knx.write(33,watt_S0[0]);
}
else if(millis()-time_S0_start[0]>180000) // kein Impuls nach 3min
{
if(watt_S0[0]>(3600.0 / (180000/zaehler_Impulse[0])))
{watt_S0[0] = 3600.0 / (180000/zaehler_Impulse[0]);}
Knx.write(33,watt_S0[0]);
}
else if(millis()-time_S0_start[0]>120000) // kein Impuls nach 2min
{
if(watt_S0[0]>(3600.0 / (120000/zaehler_Impulse[0])))
{watt_S0[0] = 3600.0 / (120000/zaehler_Impulse[0]);}
Knx.write(33,watt_S0[0]);
}
else
{
if(watt_S0[0]>(3600.0 / (60000/zaehler_Impulse[0])))
{watt_S0[0] = 3600.0 / (60000/zaehler_Impulse[0]);}
Knx.write(33,watt_S0[0]);
}
}
else
Knx.write(33,watt_S0[0]);
}
MomL_State = state_S1;
break;
case state_S1:
if(S0_aktiv[2]==1)
{
if(millis()-time_S0_start[1]>60000)
{
if(millis()-time_S0_start[1]>240000) // kein Impuls nach 4min
{
watt_S0[1] = 0;
Knx.write(36,watt_S0[1]);
}
else if(millis()-time_S0_start[1]>180000) // kein Impuls nach 3min
{
if(watt_S0[1]>(3600.0 / (180000/zaehler_Impulse[1])))
{watt_S0[1] = 3600.0 / (180000/zaehler_Impulse[1]);}
Knx.write(36,watt_S0[1]);
}
else if(millis()-time_S0_start[1]>120000) // kein Impuls nach 2min
{
if(watt_S0[1]>(3600.0 / (120000/zaehler_Impulse[1])))
{watt_S0[1] = 3600.0 / (120000/zaehler_Impulse[1]);}
Knx.write(36,watt_S0[1]);
}
else
{
if(watt_S0[1]>(3600.0 / (60000/zaehler_Impulse[1])))
{watt_S0[1] = 3600.0 / (60000/zaehler_Impulse[1]);}
Knx.write(36,watt_S0[1]);
}
}
else
Knx.write(36,watt_S0[1]);
}
MomL_State = state_S2;
break;
case state_S2:
if(S0_aktiv[3]==1)
{
if(millis()-time_S0_start[2]>60000)
{
if(millis()-time_S0_start[2]>240000) // kein Impuls nach 4min
{
watt_S0[3] = 0;
Knx.write(39,watt_S0[2]);
}
else if(millis()-time_S0_start[2]>180000) // kein Impuls nach 3min
{
if(watt_S0[2]>(3600.0 / (180000/zaehler_Impulse[2])))
{watt_S0[2] = 3600.0 / (180000/zaehler_Impulse[2]);}
Knx.write(39,watt_S0[2]);
}
else if(millis()-time_S0_start[2]>120000) // kein Impuls nach 2min
{
if(watt_S0[2]>(3600.0 / (120000/zaehler_Impulse[2])))
{watt_S0[2] = 3600.0 / (120000/zaehler_Impulse[2]);}
Knx.write(39,watt_S0[2]);
}
else
{
if(watt_S0[2]>(3600.0 / (60000/zaehler_Impulse[2])))
{watt_S0[2] = 3600.0 / (60000/zaehler_Impulse[2]);}
Knx.write(39,watt_S0[2]);
}
}
else
Knx.write(39,watt_S0[2]);
}
MomL_State = state_S3;
break;
case state_S3:
if(S0_aktiv[4]==1)
{
if(millis()-time_S0_start[3]>60000)
{
if(millis()-time_S0_start[3]>240000) // kein Impuls nach 4min
{
watt_S0[3] = 0;
Knx.write(42,watt_S0[3]);
}
else if(millis()-time_S0_start[3]>180000) // kein Impuls nach 3min
{
if(watt_S0[3]>(3600.0 / (180000/zaehler_Impulse[3])))
{watt_S0[3] = 3600.0 / (180000/zaehler_Impulse[3]);}//20W
Knx.write(42,watt_S0[3]);
}
else if(millis()-time_S0_start[3]>120000) // kein Impuls nach 2min
{
if(watt_S0[3]>(3600.0 / (120000/zaehler_Impulse[3])))
{watt_S0[3] = 3600.0 / (120000/zaehler_Impulse[3]);} //30W
Knx.write(42,watt_S0[3]);
}
else
{
if(watt_S0[3]>(3600.0 / (60000/zaehler_Impulse[3])))
{watt_S0[3] = 3600.0 / (60000/zaehler_Impulse[3]);} //60W
Knx.write(42,watt_S0[3]);
}
}
else
Knx.write(42,watt_S0[3]);
}
MomL_State = state_S0;
is_5min = false;
break;
}//Switch
}//5min
//***************************************************************************************************
// Wenn neue Zälerstände über Suite gespeichert werden
if(run_save == true)
{
run_save = false;
#ifdef KDEBUG
Debug.println("Set Zaehlerstand");
#endif
sendZaehlerStand_2(0,S0_Zaehler);
sendZaehlerStand_2(1,S0_Zaehler);
sendZaehlerStand_2(2,S0_Zaehler);
sendZaehlerStand_2(3,S0_Zaehler);
writeSAVE(S0_Zaehler,S0_impuls);
digitalWrite(LED_YELLOW, LOW);
}
}//ENDE KONNEKTING APPLIKATION RUNING
}
XML-File
User Documentation
HowTo: upload Arduino Code
Refer HowToUploadArduinoCode
SW Documentation
Set "Zählerstände" over Suite
Über die Suite kann man die Zählerstände im EEPROM überschreiben.
Sperrobjekte
Room classification
Die Raumerkennung funktioniert als 16Bit Wert und jedes Bit ist ein Input. Möchte man also alle 16 Inputs zusammen als "Zimmer" definieren, dann wäre das: b1111111111111111 = 65535
oder frei, Inputs wären hier: 1,5,6,10,14,15 b1000110001000110 = 35910
Die Dezimalwerte kann man in die Suite eingeben, beim parameterisieren werden diese Werte ins EEPROM geschrieben. Bei jedem Start werden die Daten ins RAM geladen und decodiert. Aktuell können so 5 "Zimmer" definiert werden
HW Documentation
Circuit diagram
One VCC Output-Pin shares two Input-Channels. Because of that always two Channels will monitoring together.
S0 interface Up to four Inputs can configure to an S0-interface. possible CH are Ch13 - Ch16 note: If you configure only one Ch to an S0-Interface on an VCC-Output pair, then the output of the other CH can not be switched off, because the VCC-Output voltage supply always both Channels.
Developer Documentation
State Machine
START -> Loop over all Channels (activation Outputs x + y -> readout inputs x + y -> update Status LED -> deactivation Outputs x + y)