16CH Binary Input
16CH Binary Input | |
---|---|
Developer | Matthias F. |
Status | Version 1.0 finished |
Microcontroller/Board | |
KNX connectivity | Mini-BCU |
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:[1] 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 (link)
- 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;
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); }
XML-File
User Documentation
SW Documentation
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)