@@ -12,12 +12,9 @@ sub KM271_CloseDev($);
sub KM271_SimpleWrite (@) ;
sub KM271_SimpleRead ($) ;
sub KM271_crc ($) ;
sub KM271_setbits ($$) ;
sub KM271_setbits ($$$ ) ;
sub KM271_Reading ($$) ;
my % sets = (
) ;
my $ stx = pack ( 'H*' , "02" ) ;
my $ dle = pack ( 'H*' , "10" ) ;
my $ etx = pack ( 'H*' , "03" ) ;
@@ -31,110 +28,131 @@ my $logmode = pack('H*', "EE00001003FD");
# http://www.buderus.de/pdf/unterlagen/0063061377.pdf
my % km271_trhash =
(
"8 000 " = > 'Betriebswerte_1_HK1' , # 76, 4 [repeat]
"8 001 " = > 'Betriebswerte_2_HK1' , # 0 (22:33), 2 (7:33)
"8 002 " = > 'Vorlaufsolltemperatur_HK1' , # 50-65
"8003" = > 'Vorlaufisttemperatur_HK1' , # Schwingt um soll herum
"8004" = > 'Raumsolltemperatur_HK1' , # 34 (22:33) 42 (7:33)
"8005" = > 'Raumisttemperatur_HK1' ,
"8006" = > 'Einschaltoptimierungszeit_HK1' ,
"8007" = > 'Ausschaltoptimierungszeit_HK1' ,
"8008" = > 'Pumpenleistung_HK1' , # 0/100 == Ladepumpe
"8009" = > 'Mischerstellung_HK1' ,
"800a" = > 'nicht_belegt' ,
"800b" = > 'nicht_belegt' ,
"800c" = > 'Heizkennlinie_HK1_bei_+_10_Grad' , # bei Umschaltung tag/nacht
"800d" = > 'Heizkennlinie_HK1_bei_0_Grad' , # bei Umschaltung tag/nacht
"800e" = > 'Heizkennlinie_HK1_bei_-_10_Grad' , # bei Umschaltung tag/nacht
"800f" = > 'nicht_belegt' ,
"8010" = > 'nicht_belegt' ,
"8011" = > 'nicht_belegt' ,
"007e " = > "Manuell_WW" ,
"0085 " = > "Manuell_ZirkulationsPumpe" ,
"0093 " = > "Manuell_Uhrzeit" ,
"8112 " = > 'Betriebswerte_1_HK2' ,
"8113 " = > 'Betriebswerte_1_HK2' ,
"8114 " = > 'Vorlaufsolltemperatur_HK2' ,
"81 15" = > 'Vorlaufisttemperatur_HK2' ,
"8116" = > 'Raumsolltemperatur_HK2' ,
"8117" = > 'Raumisttemperatur_HK2' ,
"8118" = > 'Einschaltoptimierungszeit_HK2' ,
"8119" = > 'Ausschaltoptimierungszeit_HK2' ,
"811a" = > 'Pumpenleistung_HK2' ,
"811b" = > 'Mischerstellung_HK2' ,
"811c" = > 'nicht_belegt' ,
"811d" = > 'nicht_belegt' ,
"811e" = > 'Heizkennlinie_HK2_bei_+_10_Grad' , # == HK1 - (1 bis 3)
"811f" = > 'Heizkennlinie_HK2_bei_0_Grad' , # == HK1 - (1 bis 3)
"8120" = > 'Heizkennlinie_HK2_bei_-_10_Grad' , # == HK1 - (1 bis 3)
"8121" = > 'nicht_belegt' ,
"8122" = > 'nicht_belegt' ,
"8123" = > 'nicht_belegt' ,
"0300 " = > "Tagwechsel_1" ,
"0307 " = > "Tagwechsel_2" ,
"030e " = > "Tagwechsel_3" ,
"03 15" = > "Tagwechsel_4" ,
"8424 " = > 'Betriebswerte_1_WW' ,
"8425" = > 'Betriebswerte_2_WW' , # 0 64 96 104 225 228
"8426 " = > 'Warmwassersolltemperatur' , # 10/55
"8427 " = > 'Warmwasseristtemperatur' , # 32-55
"8428 " = > 'Warmwasseroptimierungszeit' ,
"8429 " = > 'Ladepumpe' , # 0 1 (an/aus?)
"0400 " = > "NoData" ,
"8000 " = > 'HK1_Betriebswerte1' , # 76, 4 [repeat]
"8001 " = > 'HK1_Betriebswerte2' , # 0 (22:33), 2 (7:33)
"8002 " = > 'HK1_Vorlaufsolltemperatur' , # 50-65
"8003 " = > 'HK1_Vorlaufisttemperatur' , # Schwingt um soll herum
"8004" = > 'HK1_Raumsolltemperatur' , # 34 (22:33) 42 (7:33)
"8005" = > 'HK1_Raumisttemperatur' ,
"8006" = > 'HK1_Einschaltoptimierungszeit' ,
"8007" = > 'HK1_Ausschaltoptimierungszeit' ,
"8008" = > 'HK1_Pumpenleistung' , # 0/100 == Ladepumpe
"8009" = > 'HK1_Mischerstellung' ,
"800c" = > 'HK1_Heizkennlinie_bei_+_10_Grad' , # bei Umschaltung tag/nacht
"800d" = > 'HK1_Heizkennlinie_bei_0_Grad' , # bei Umschaltung tag/nacht
"800e" = > 'HK1_Heizkennlinie_bei_-_10_Grad' , # bei Umschaltung tag/nacht
"8112" = > 'HK2_Betriebswerte1' ,
"8113" = > 'HK2_Betriebswerte2' ,
"8114" = > 'HK2_Vorlaufsolltemperatur' ,
"8115" = > 'HK2_Vorlaufisttemperatur' ,
"8116" = > 'HK2_Raumsolltemperatur' ,
"8117" = > 'HK2_Raumisttemperatur' ,
"8118" = > 'HK2_Einschaltoptimierungszeit' ,
"8119" = > 'HK2_Ausschaltoptimierungszeit' ,
"811a" = > 'HK2_Pumpenleistung' ,
"811b" = > 'HK2_Mischerstellung' ,
"811e" = > 'HK2_Heizkennlinie_bei_+_10_Grad' , # == HK1 - (1 bis 3 Grad)
"811f" = > 'HK2_Heizkennlinie_bei_0_Grad' , # == HK1 - (1 bis 3 Grad)
"8120" = > 'HK2_Heizkennlinie_bei_-_10_Grad' , # == HK1 - (1 bis 3 Grad)
# 1377, page 11
"8424" = > 'WW_Betriebswerte1' ,
"8425" = > 'WW_Betriebswerte2' , # 0 64 96 104 225 228
"8426" = > 'WW_Solltemperatur' , # 10/55
"8427" = > 'WW_Isttemperatur' , # 32-55
"8428" = > 'WW_Einschaltoptimierungszeit' ,
"8429" = > 'WW_Ladepumpe' , # 0 1 (an/aus?)
# 1377, page 13
"882a" = > 'Kesselv orlaufsolltemperatur' ,
"882b" = > 'Kesselv orlaufisttemperatur' , # == Vorlaufisttemperatur_HK1
"882c" = > 'Brennere inschalttemperatur' , # 5-81
"882d" = > 'Brennera usschalttemperatur' , # 19-85
"882e" = > 'Kesseli ntegral_1 ' , # 0-23
"882f" = > 'Kesseli ntegral_2 ' , # 0-255
"8830" = > 'Kesself ehler' ,
"8831" = > 'Kesselb etrieb' , # 0 2 32 34
"8832" = > 'Brennera nsteuerung' , # 0 1 (an/aus?)
"882a" = > 'Kessel_V orlaufsolltemperatur' ,
"882b" = > 'Kessel_V orlaufisttemperatur' , # == Vorlaufisttemperatur_HK1
"882c" = > 'Brenner_E inschalttemperatur' , # 5-81
"882d" = > 'Brenner_A usschalttemperatur' , # 19-85
"882e" = > 'Kessel_I ntegralHB ' , # 0-23
"882f" = > 'Kessel_I ntegralLB ' , # 0-255
"8830" = > 'Kessel_F ehler' ,
"8831" = > 'Kessel_B etrieb' , # 0 2 32 34
"8832" = > 'Brenner_A nsteuerung' , # 0 1 (an/aus?)
"8833" = > 'Abgastemperatur' ,
"8834" = > 'modulare_ Brenner_Stellwert' ,
"8835 " = > 'nicht_belegt ' ,
"8836 " = > 'Brennerl aufzeit_ 1_Minuten_Byte2' ,
"8837 " = > 'Brennerl aufzeit_ 1_Minuten_Byte1 ' , # 176
"8838 " = > 'Brennerl aufzeit_1 _Minuten_Byte0' , # 0-255 (Minuten)
"8839 " = > 'Brennerl aufzeit_ 2_Minuten_Byte2 ' ,
"883a " = > 'Brennerl aufzeit_ 2_Minuten_Byte1 ' ,
"883b" = > 'Brennerlaufzeit_2_Minuten_Byte0' ,
"8834" = > 'Brenner_Stellwert' ,
"8836 " = > 'Brenner_Laufzeit1_Minuten2 ' ,
"8837 " = > 'Brenner_L aufzeit1_Minuten1' , # 176
"8838 " = > 'Brenner_L aufzeit1_Minuten0 ' , # 0-255 (Minuten)
"8839 " = > 'Brenner_L aufzeit2 _Minuten2' ,
"883a " = > 'Brenner_L aufzeit2_Minuten1 ' ,
"883b " = > 'Brenner_L aufzeit2_Minuten0 ' ,
# 1377, page 16
"893c" = > 'Aussentemperatur' , # 0 1 254 255
"893d" = > 'gedaempfte_ Aussentemperatur' , # 0 1 2
"893d" = > 'Aussentemperatur_gedaempft ' , # 0 1 2
"893e" = > 'Versionsnummer_VK' ,
"893f" = > 'Versionsnummer_NK' ,
"8940" = > 'Modulkennung' ,
"8941" = > 'nicht_belegt' ,
) ;
# Do not generate fhem events for the following high volume telegrams
# the % represents the relative nr of messages in an unfiltered stream.
# You can switch them on with attr all_km271_events
my % km271_ignore = (
"Vorlaufisttemperatur_HK1 " = > 1 , # 23%
my % km271_noevent = (
"HK1_ Vorlaufisttemperatur" = > 1 , # 23% of all messages
"HK2_Vorlaufisttemperatur" = > 1 ,
"Kesselvorlaufisttemperatur" = > 1 , # 23%, same as Vorlaufisttemperatur_HK1
"Kesseli ntegral_1 " = > 1 , # 8%, ??
"Kesseli ntegral_2 " = > 1 , # 38%, ??
"Kessel_I ntegralHB " = > 1 , # 8%, ??
"Kessel_I ntegralLB " = > 1 , # 38%, ??
) ;
my @ km271_Betriebswerte_1_HK = (
my @ km271_HK_ Betriebswerte1 = (
"Ausschaltoptimierung" , "Einschaltoptimierung" , "Automatik" ,
"Warmwasservorrang" , "Estrichtrocknung" , "Ferien" , "Frostschutz" , "Manuell" ,
) ;
my @ km271_Betriebswerte_2_HK = (
my @ km271_HK_ Betriebswerte2 = (
"Sommer" , "Tag" , "keine Kommunikation mit FB" , "FB fehlerhhaft" ,
"Fehler Vorlauff<66> hler" , "maximaler Vorlauf" , "externer St<53> reingang" , "frei" ,
) ;
my @ km271_Betriebswerte_1_WW = (
my @ km271_WW_ Betriebswerte1 = (
"Automatik" , "Desinfektion" , "Nachladung" , "Ferien" , "Fehler Desinfektion" ,
"Fehler Fuehler" , "Fehler WW bleibt kalt" , "Fehler Anode" ,
) ;
my @ km271_Betriebswerte_2_WW = (
my @ km271_WW_ Betriebswerte2 = (
"Laden" , "Manuell" , "Nachladen" , "Ausschaltoptimierung" ,
"Einschaltoptimierung" , "Tag" , "Warm" , "Vorrang" ,
) ;
my @ km271_Kesselb etrieb = (
my @ km271_Kessel_B etrieb = (
"Tag" , "Automatik" , "Sommer" , "Bit3" , "Bit4" , "Bit5" , "Bit6" , "Bit7" ,
) ;
my @ km271_WW_Ladepumpe = (
"Ladepumpe" , "Zirkulationspumpe" , "Absenkung Solar" ,
"Bit3" , "Bit4" , "Bit5" , "Bit6" , "Bit7" ,
) ;
my % km271_set_betriebsart = (
"manuell_nacht" = > 0 ,
"manuell_tag" = > 1 ,
"automatik" = > 2 ,
) ;
my % km271_sets = (
"hk1_nachtsoll" = > "07006565%02x656565" , # 0.5 celsius
"hk1_tagsoll" = > "0700656565%02x6565" , # 0.5 celsius
"hk1_betriebsart" = > "070065656565%02x65" ,
"ww_soll" = > "0C07656565%02x6565" , # 1.0 celsius
"ww_betriebsart" = > "0C0E%02x6565656565" ,
) ;
@@ -150,6 +168,8 @@ KM271_Initialize($)
$ hash - > { UndefFn } = "KM271_Undef" ;
$ hash - > { SetFn } = "KM271_Set" ;
$ hash - > { AttrList } = "do_not_notify:1,0 all_km271_events loglevel:0,1,2,3,4,5,6" ;
my @ a = ( ) ;
$ hash - > { SENDBUFFER } = \ @ a ;
}
#####################################
@@ -193,12 +213,29 @@ KM271_Set($@)
my ( $ hash , @ a ) = @ _ ;
return "\"set KM271\" needs at least one parameter" if ( @ a < 2 ) ;
return "Unknown argument $a[1], choose one of " . join ( " " , sort keys % sets )
if ( ! defined ( $ sets { $ a [ 1 ] } ) ) ;
my $ name = shift @ a ;
my $ type = shift @ a ;
my $ arg = join ( "" , @ a ) ;
my $ fmt = $ km271_sets { $ a [ 1 ] } ;
return "Unknown argument $a[1], choose one of " .
join ( " " , sort keys % km271_sets ) if ( ! defined ( $ fmt ) ) ;
my $ val = $ a [ 2 ] ;
my $ numeric_arg = ( $ val =~ m/^[.0-9]+$/ ) ;
if ( $ a [ 1 ] =~ m/^hk.*soll$/ ) {
return "Argument must be numeric (between 10 and 30)" if ( ! $ numeric_arg ) ;
$ val *= 2 ;
}
if ( $ a [ 1 ] =~ m/^ww.*soll$/ ) {
return "Argument must be numeric (between 30 and 60)" if ( ! $ numeric_arg ) ;
}
if ( $ a [ 1 ] =~ m/_betriebsart/ ) {
$ val = $ km271_set_betriebsart { $ val } ;
return "Unknown arg, use one of " .
join ( " " , sort keys % km271_set_betriebsart ) if ( ! defined ( $ val ) ) ;
}
my $ data = sprintf ( $ fmt , $ val ) ;
push @ { $ hash - > { SENDBUFFER } } , $ data ;
KM271_SimpleWrite ( $ hash , $ stx ) if ( ! $ hash - > { WAITING } ) ;
return undef ;
}
@@ -211,6 +248,7 @@ KM271_Read($)
{
my ( $ hash ) = @ _ ;
my $ name = $ hash - > { NAME } ;
my ( $ data , $ crc ) ;
my $ buf = KM271_SimpleRead ( $ hash ) ;
Log GetLogLevel ( $ name , 5 ) , "KM271 RAW: " . unpack ( 'H*' , $ buf ) ;
@@ -221,86 +259,125 @@ KM271_Read($)
return ;
}
$ buf = unpack ( 'H*' , $ buf ) ;
if ( $ buf eq "02" ) {
if ( $ buf eq "02" ) { # KM271: Want to send
$ hash - > { PARTIAL } = "" ;
KM271_SimpleWrite ( $ hash , $ dle ) ;
KM271_SimpleWrite ( $ hash , $ dle ) ; # We are ready
$ hash - > { WAITING } = 1 ;
return ;
}
if ( ! $ hash - > { WAITING } ) { # Send data
if ( $ buf eq "10" ) {
if ( $ hash - > { DATASENT } ) { # ACK Data
delete ( $ hash - > { DATASENT } ) ;
return ;
}
$ data = pop @ { $ hash - > { SENDBUFFER } } ;
$ data =~ s/10/1010/g ;
$ crc = KM271_crc ( $ data ) ;
KM271_SimpleWrite ( $ hash , $ data . "1003$crc" ) ; # Send the data
$ hash - > { DATASENT } = 1 ;
}
if ( $ buf eq "15" && $ hash - > { DATASENT } ) { # NACK from the KM271
Log 1 , "$name: NACK!" ;
delete ( $ hash - > { DATASENT } ) ;
return ;
}
}
$ hash - > { PARTIAL } . = $ buf ;
my $ len = length ( $ hash - > { PARTIAL } ) ;
return if ( $ hash - > { PARTIAL } !~ m/^(.*)1003(..)$/ ) ;
my ( $ data , $ crc ) = ( $ 1 , $ 2 ) ;
( $ data , $ crc ) = ( $ 1 , $ 2 ) ;
if ( KM271_crc ( $ data ) ne $ crc ) {
Log 1 , "Wrong CRC in $hash->{PARTIAL}: $crc vs. " . KM271_crc ( $ data ) ;
$ hash - > { PARTIAL } = "" ;
KM271_SimpleWrite ( $ hash , $ nak ) ;
return ;
}
KM271_SimpleWrite ( $ hash , $ dle ) ;
KM271_SimpleWrite ( $ hash , $ dle ) ; # Data received ok
delete ( $ hash - > { WAITING } ) ;
if ( $ hash - > { SENDBUFFER } ) {
KM271_SimpleWrite ( $ hash , $ stx )
}
$ data =~ s/1010/10/g ;
if ( $ data =~ m/^(8...)(..)/ ) {
my ( $ fn , $ arg ) = ( $ 1 , $ 2 ) ;
my $ msg = $ km271_trhash { $ fn } ;
$ msg = "UNKNOWN_$fn" if ( ! $ msg ) ;
my $ tn = TimeNow ( ) ;
my $ val = hex ( $ arg ) ;
my $ ignore = $ km271_ignore { $ msg } ;
if ( $ msg = ~ m/Aussentemperatur / ) {
$ val = $ val - 256 if ( $ val > 128 ) ;
if ( $ data ! ~ m/^(....)(.*) / ) {
Log 1 , "$name: Bogus message: $data" ;
return ;
}
} elsif ( $ msg =~ m/Brennerlaufzeit_(.)_Minuten_Byte(.)/ ) {
my ( $ idx , $ no ) = ( $ 1 , $ 2 ) ;
my ( $ fn , $ arg ) = ( $ 1 , $ 2 ) ;
my $ msg = $ km271_trhash { $ fn } ;
$ msg = "UNKNOWN_$fn" if ( ! $ msg ) ;
my $ tn = TimeNow ( ) ;
my $ val = unpack ( 'H*' , $ arg ) ;
my $ gen_notify = $ km271_noevent { $ msg } ? 0 : 1 ;
$ gen_notify = KM271_attr ( $ name , "all_km271_events" )
if ( ! $ gen_notify ) ;
if ( $ no == 2 || $ no == 1 ) {
$ ignore = 1 ;
} else {
$ msg = "Brennerlaufzeit_${idx}_Minuten" ;
$ val = KM271_Reading ( $ hash , $ msg . "_Byte2" ) * 65536 +
KM271_Reading ( $ hash , $ msg . "_Byte1" ) * 256 +
$ val ;
}
if ( $ msg eq "NoData" ) {
$ gen_notify = 0 ;
} elsif ( $ msg =~ m/Betriebswerte_1_HK / ) {
$ val = KM271_setbits ( $ val , \ @ km271_Betriebswerte_1_HK ) ;
} elsif ( $ msg =~ m/^UNKNOWN / ) {
$ val = $ data ;
$ gen_notify = 0 ;
} elsif ( $ msg =~ m/Aussentemperatur/ ) {
$ val = $ val - 256 if ( $ val > 128 ) ;
} elsif ( $ msg =~ m/Betriebswerte_2_HK / ) {
$ val = KM271_setbits ( $ val , \ @ km271_Betriebswerte_2_HK ) ;
} elsif ( $ msg =~ m/Brenner_Laufzeit(.)_Minuten(.) / ) {
my ( $ idx , $ no ) = ( $ 1 , $ 2 ) ;
} els if( $ msg =~ m/Betriebswerte_1_WW/ ) {
$ val = KM271_setbits ( $ val , \ @ km271_Betriebswerte_1_WW ) ;
} elsif ( $ msg =~ m/Betriebswerte_2_WW/ ) {
$ val = KM271_setbits ( $ val , \ @ km271_Betriebswerte_2_WW ) ;
} elsif ( $ msg =~ m/Brenneransteuerung/ ) {
$ val = ( $ val ? "an" : "aus" ) ;
} elsif ( $ msg =~ m/Kesselbetrieb/ ) {
$ val = KM271_setbits ( $ val , \ @ km271_Kesselbetrieb ) ;
if( $ no == 2 || $ no == 1 ) {
$ gen_notify = 0 ;
} else {
$ msg = "Brenner_Laufzeit${idx}_Minuten" ;
$ val = KM271_Reading ( $ hash , $ msg . "2" ) * 65536 +
KM271_Reading ( $ hash , $ msg . "1" ) * 256 +
$ val ;
}
Log GetLogLevel ( $ name , 4 ) , "KM271 $name: $msg $val" ;
$ hash - > { READINGS } { $ msg } { TIME } = $ tn ;
$ hash - > { READINGS } { $ msg } { VAL } = $ val ;
if ( KM271_attr ( $ name , "all_km271_events" ) || ! $ ignore ) {
DoTrigger ( $ name , "$msg: $val" ) ;
}
} elsif ( $ msg =~ m/HK._Betriebswerte/ ) {
$ val = KM271_setbits ( $ val , \ @ km271_HK_Betriebswerte1 , "leer" ) ;
} elsif ( $ data eq "04000701c4024192" ) {
# No data message
} elsif ( $ msg =~ m/HK._Betriebswerte2/ ) {
$ val = KM271_setbits ( $ val , \ @ km271_HK_Betriebswerte2 , "leer" ) ;
} else {
Log 1 , "$name: UNKNOWN $data" ;
} elsif ( $ msg =~ m/WW_Betriebswerte1/ ) {
$ val = KM271_setbits ( $ val , \ @ km271_WW_Betriebswerte1 , "aus" ) ;
} elsif ( $ msg =~ m/WW_Betriebswerte2/ ) {
$ val = KM271_setbits ( $ val , \ @ km271_WW_Betriebswerte2 , "aus" ) ;
} elsif ( $ msg =~ m/Brenner_Ansteuerung/ ) {
$ val = ( $ val ? "an" : "aus" ) ;
} elsif ( $ msg =~ m/Kessel_Betrieb/ ) {
$ val = KM271_setbits ( $ val , \ @ km271_Kessel_Betrieb , "aus" ) ;
} elsif ( $ msg =~ m/WW_Ladepumpe/ ) {
$ val = KM271_setbits ( $ val , \ @ km271_WW_Ladepumpe , "aus" ) ;
} elsif ( $ msg =~ m/HK?_Raum.*temperatur/ ) {
$ val = $ val / 2 ;
}
$ val = $ arg if ( length ( $ arg ) > 2 ) ;
Log GetLogLevel ( $ name , 4 ) , "KM271 $name: $msg $val" ;
$ hash - > { READINGS } { $ msg } { TIME } = $ tn ;
$ hash - > { READINGS } { $ msg } { VAL } = $ val ;
DoTrigger ( $ name , "$msg: $val" ) if ( $ gen_notify ) ;
$ hash - > { PARTIAL } = "" ;
}
@@ -321,7 +398,7 @@ sub
KM271_SimpleWrite (@)
{
my ( $ hash , $ msg ) = @ _ ;
$ hash - > { Dev } - > write ( $ msg ) ;
$ hash - > { Dev } - > write ( $ msg ) if ( $ hash - > { DeviceName } ) ;
}
########################
@@ -393,18 +470,21 @@ KM271_OpenDev($)
$ po - > parity ( 'none' ) ;
$ po - > stopbits ( 1 ) ;
$ po - > handshake ( 'none' ) ;
$ po - > write ( $ logmode ) ;
$ hash - > { STATE } = "Initialized" ;
#$po->write($logmode);
push @ { $ hash - > { SENDBUFFER } } , "EE0000" ;
KM271_SimpleWrite ( $ hash , $ stx ) ;
Log 3 , "$dev opened" ;
return undef ;
}
sub
KM271_setbits ($$)
KM271_setbits ($$$ )
{
my ( $ val , $ arr ) = @ _ ;
my ( $ val , $ arr , $ nulltxt ) = @ _ ;
my $ bit = 1 ;
my @ ret ;
@@ -412,7 +492,7 @@ KM271_setbits($$)
push ( @ ret , $ arr - > [ $ idx ] ) if ( $ val & $ bit ) ;
$ bit *= 2 ;
}
return "keine Bits gesetzt" if ( ! @ ret ) ;
return $ nulltxt if ( ! @ ret ) ;
return join ( "," , @ ret ) ;
}