Difference between revisions of "16CH Binary Input"

From KONNEKTING Wiki
Jump to navigation Jump to search
 
(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 (link)
+
* 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
16CH Binary Input Overview.jpg
16CH Binary Input comp.jpg
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)
16CH Binary Input PCBs.jpg


Mounting Kit
1x Micromatch cable
8x Screws 


without housing


Block Diagram

16CH Binary INput Block Diagramm.PNG

Detection Circuit

16CH Binary Input Schematic.png


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
16CH Binary Input Suite ZimmerErkennung.PNG

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.


16Ch Input
12Ch Input + 4Ch S0-Interface

Developer Documentation

State Machine

START -> Loop over all Channels (activation Outputs x + y -> readout inputs x + y -> update Status LED -> deactivation Outputs x + y)

Build It

Links