LÖSUNG:Dekodieren Temperatur Feuchte T/H sensor von PEARL NC7427(NC7415) 433MHz

Begonnen von maxtox, 25 September 2016, 19:37:33

Vorheriges Thema - Nächstes Thema

FHEm2005

Hallo Jürgs,

ich muss da etwas zurückrudern, weil ich mich offensichtlich falsch bzw. unklar ausgedrückt habe und dadurch Erwartungen geweckt habe, die nicht zutreffen. Ich habe KEINE Temperatur und keine Humidity-daten in Klarschrift, sondern genau die Daten, die Du auch empfängst. Es bleibt auch bei mir ein Unkown Code.

Sorry für das Missverständnis.
Gruß Eberhard
Raspi3: FHEM, CULV3 (V1.61), EnOcean Pi 868, nanoCUL433, HUE-Bridge; Raspi4: Node-red, MQTT, Gaszähler auslesen mit ESP32-CAM

juergs

ZitatSorry für das Missverständnis.

:) Kein Problem und danke für die Rückmeldung.

Da es die Kombi  CUL-Dekodierung und FHEM-Modul für diesen Sensor noch nicht gibt,
habe ich mich für diesen Weg entschieden, der für mich einfacher machbar war.
Da ich mich auch erst in die Thematik Empfangs-seitig  einarbeiten musste.

Sogar jetzt auch, zumindest ein bisschen die Arbeit von Sidey und bjoernH noch mehr wertschätzen kann,
die das super machen ...

ZitatLLLL MMMM HHHH    HHHH MMMM LLLL
0111 0101 0110 => 0110 0101 0111 => 657 = 0x657 = 1623 - 900 = 723 /10 = 72.3 (F)  = 22,38 °C  = 22,4  °C in der Anzeige.

Mal schauen, wie weit ich heute komme.
433ProtocolTransponder.ino
Hardware-Setup
Dann kann man die Daten des "Billig"-Solar-Sensors als LaCrosse CUL_TX- oder Hideki-Sensor-Daten empfangen.
Vielleicht geht die Firmwareanpassung dann als zweiter Schritt in Signalduino oder aculfw über....

Grüße,
Jürgen

FHEm2005

Ich habe mir mal ein excel-Sheet "gebaut" um ein Verständnis der  Werte zu bekommen. Dabei arbeite ich grundsätzlich mit einem Array, in welchem in die Zellen immer ein Halbnibbel (2Bit) ausgewertet werden.

Ich habe es mal beigefügt und die hinterlegten Formeln sichtbar gelassen, Vielleicht helfen sie beim Programmieren, was ich in dieser Tiefe nicht kann. Bitte nur in das gelbe Feld den übermittelten Wert eintragen. Im blauen Feld stehen dann die Ergebnisse. Die müssten mit der Anzeige übereinstimmen. Tut es bei mir jedenfalls.

Gruß Eberhard

Ergänzung:
Bei mir wurde ein neues device angelegt mit dem Device CUL_TCM97001. In den Reading steht der empfangene String als state zur Verfügung. Könnte man nicht einfacherweise die Temperatur und die Feuchte als Userreading mit hinterlegter Programmierug ausführen?

Edit: Excel-Datei hatte im Batterie und Kanalnoch Fehler, Deshalb neue Bilder und XLS
Raspi3: FHEM, CULV3 (V1.61), EnOcean Pi 868, nanoCUL433, HUE-Bridge; Raspi4: Node-red, MQTT, Gaszähler auslesen mit ESP32-CAM

FHEm2005

Es ist geschafft! Trotz nur rudimentärer Perl-Kenntnisse habe ich den empfangenen Code decodiert und die einzelnen Teile als User Reading dargestellt.

Hier die Definition der UserReadings:
stateBIN {my $s = (ReadingsVal($name,'state',''));
my @worte = split(/\s+/,$s);
$s = @worte[1];
my @chars = split(//,$s);
my $char = "";
my $u = "";
my $t = "";
for (my $i=0; $i <= 11; $i++) {
$char = @chars[$i];
if ($char eq "0")   { $t ="0000";}
elsif ($char eq "1"){$t = "0001";}
elsif ($char eq "2"){$t = "0010";}
elsif ($char eq "3"){$t = "0011";}
elsif ($char eq "4"){$t = "0100";}
elsif ($char eq "5"){$t = "0101";}
elsif ($char eq "6"){$t = "0110";}
elsif ($char eq "7"){$t = "0111";}
elsif ($char eq "8"){$t = "1000";}
elsif ($char eq "9"){$t = "1001";}
elsif (($char eq "a")|| ($char eq "A")){$t = "1010";}
elsif (($char eq "b")|| ($char eq "B")){$t = "1011";}
elsif (($char eq "c")|| ($char eq "C")){$t = "1100";}
elsif (($char eq "d")|| ($char eq "D")){$t = "1101";}
elsif (($char eq "e")|| ($char eq "E")){$t = "1110";}
elsif (($char eq "f")|| ($char eq "F")){$t = "1111";}
$u= "$u$t";
}
return($u);},

LD {my $s = (ReadingsVal($name,'stateBIN',''));
my @worte = split(//,$s);
my $t = @worte[0].@worte[1];
return($t);},

ID {my $s = (ReadingsVal($name,'stateBIN',''));
my @worte = split(//,$s);
my $u="";
for (my $i=2; $i <= 9; $i++) {
my $t= @worte[$i];
$u="$u$t";}
$u=oct( "0b$u" );
return($u);},

BAT {my $s = (ReadingsVal($name,'stateBIN',''));
my @worte = split(//,$s);
my $t = @worte[10].@worte[11];
if ($t eq "00"){$t="schwach";}
elsif ($t eq "01"){$t="gut";}
elsif ($t eq "10"){$t="top";}
return($t);},

CH {my $s = (ReadingsVal($name,'stateBIN',''));
my @worte = split(//,$s);
my $t = @worte[12].@worte[13];
if ($t eq "00"){$t="1";}
elsif ($t eq "01"){$t="2";}
elsif ($t eq "10"){$t="3";}
return($t);},

TEMP {my $s = (ReadingsVal($name,'stateBIN',''));
my @worte = split(//,$s);
my $u="";
my $t="";
my $i=0;
for ($i=22; $i <= 25; $i++) {
$t= @worte[$i];
$u="$u$t";}
for ($i=18; $i <= 21; $i++) {
$t= @worte[$i];
$u="$u$t";};
for ($i=14; $i <= 17; $i++) {
$t= @worte[$i];
$u="$u$t";}
$u=oct( "0b$u" );
$u= ((($u-900)/10)-32)*5/9;
$u= int(10 * $u + 0.5) / 10;
return($u);},

HUM {my $s = (ReadingsVal($name,'stateBIN',''));
my @worte = split(//,$s);
my $u="";
my $t="";
my $i=0;
for ($i=30; $i <= 33; $i++) {
$t= @worte[$i];
$u="$u$t";}
for ($i=26; $i <= 29; $i++) {
$t= @worte[$i];
$u="$u$t";};
$u=oct( "0b$u" );
$u= int($u + 0.5);
return($u);},


Dabei ist stateBIN der in Binärzehlen umgewandelte empfangene Code. Die Leading Bits stehen unter LD,  der Empfangskanal steht in CH, die Geräte-ID unter ID (wo sonst auch) und TEMP und HUM sind selbsterklärend. Der Zustand der Batterie wird in KLartext dargestellt (top, gut, schwach).

Ich weiß ncht wie sich diese Definition mit zwei Sensoren und nach Batteriewechsel verhält. Das werde ich noch mal austesten. Ich hoffe, dass ein neues Devise anleget wird.

Gruß Eberhard

EDIT: Hinweis: Empfang der Daten mit nanoCUl und a-culfw (V 1.21.00 a-culfw Build: 70)
Raspi3: FHEM, CULV3 (V1.61), EnOcean Pi 868, nanoCUL433, HUE-Bridge; Raspi4: Node-red, MQTT, Gaszähler auslesen mit ESP32-CAM

mahowi

Du kannst die hex-Werte aus dem state auch relativ simpel in einen Binärwert umrechnen:
sprintf( "%048b", hex( $hexvalue ) );

Das rechnet einen HEX-Wert in einen Binärwert, mit führenden Nullen bis auf 48 Stellen. So wird aus "HEX: 1B0055908040" "BIN: 000110110000000001010101100100001000000001000000".
$hexvalue entsprechend durch Deinen State ersetzen.
CUBe (MAX): HT, FK | CUBe (SlowRF): ESA2000WZ
JeeLink: LaCrosse | nanoCUL433: Smartwares SHS-51001-EU, EM1000GZ
ZME_UZB1: GreenWave PowerNode, Popp Thermostat | SIGNALDuino: HE877, X10 MS14A, Revolt NC-5462,  IT Steckdosen + PIR
tado° | Milight | HUE, Lightify | SmarterCoffee

FHEm2005

Danke mahowi,
habe es eingearbeitet und gleichzeitig einige Readings geändert:
TEMP => temperature
HUM => humidity
BAT => battery
CH => channel

Das hat den Vorteil, dass bei Verwendung von dewpoint zusätzlich die Readings abs Feuchte und dewpoint erscheinen.

stateBIN {my $s = (ReadingsVal($name,'state',''));
my @worte = split(/\s+/,$s);
$s = @worte[1];
my $u=sprintf( "%048b", hex( $s ) );
return($u);},

LD {my $s = (ReadingsVal($name,'stateBIN',''));
my @worte = split(//,$s);
my $t = @worte[0].@worte[1];
return($t);},

ID {my $s = (ReadingsVal($name,'stateBIN',''));
my @worte = split(//,$s);
my $u="";
for (my $i=2; $i <= 9; $i++) {
my $t= @worte[$i];
$u="$u$t";}
$u=oct( "0b$u" );
return($u);},

battery {my $s = (ReadingsVal($name,'stateBIN',''));
my @worte = split(//,$s);
my $t = @worte[10].@worte[11];
if ($t eq "00"){$t="schwach";}
elsif ($t eq "01"){$t="gut";}
elsif ($t eq "10"){$t="top";}
return($t);},

channel {my $s = (ReadingsVal($name,'stateBIN',''));
my @worte = split(//,$s);
my $t = @worte[12].@worte[13];
if ($t eq "00"){$t="1";}
elsif ($t eq "01"){$t="2";}
elsif ($t eq "10"){$t="3";}
return($t);},

temperature {my $s = (ReadingsVal($name,'stateBIN',''));
my @worte = split(//,$s);
my $u="";
my $t="";
my $i=0;
for ($i=22; $i <= 25; $i++) {
$t= @worte[$i];
$u="$u$t";}
for ($i=18; $i <= 21; $i++) {
$t= @worte[$i];
$u="$u$t";};
for ($i=14; $i <= 17; $i++) {
$t= @worte[$i];
$u="$u$t";}
$u=oct( "0b$u" );
$u= ((($u-900)/10)-32)*5/9;
$u= int(10 * $u + 0.5) / 10;
return($u);},

humidity {my $s = (ReadingsVal($name,'stateBIN',''));
my @worte = split(//,$s);
my $u="";
my $t="";
my $i=0;
for ($i=30; $i <= 33; $i++) {
$t= @worte[$i];
$u="$u$t";}
for ($i=26; $i <= 29; $i++) {
$t= @worte[$i];
$u="$u$t";};
$u=oct( "0b$u" );
$u= int($u + 0.5);
return($u);},


Gruß Eberhard
Raspi3: FHEM, CULV3 (V1.61), EnOcean Pi 868, nanoCUL433, HUE-Bridge; Raspi4: Node-red, MQTT, Gaszähler auslesen mit ESP32-CAM

juergs

Hallo Zusammen,

schön zu sehen, dass es hier auch vorwärts geht.

Meine Beobachtung war, dass nach einem der letzten Updates von FHEM die ausgegeben Readings
zwischenzeitlich nicht mehr binär zu den übertragenen Daten passten. (Stimmt jetzt aber wieder ... siehe Screenshot)

Zitat[3]:
--------------------------------------------------------------------------------------------
0                          1                          2                         3                          4
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
--------------------------------------------------------------------------------------------
0 0 0 0 0 0 0 1 1 1 0 0 0 1 1 1 0 1 0 1 0 0 0 1 1 0 0 0 0 0 0 0 1 0 0 0 1 0 1 1 1 1
----[raw]----------------------------------------------------------------------------------
LD: 00 |ID: 00000111 |BAT: 00 |CH: 01 |TEMP: 110101000110 |HUM: 00000010 |CRC: 00101111
----[swapped]--------------------------------------------------------------------------
LD: 00 |ID: 11100000 |BAT: 00 |CH: 10 |TEMP: 011000101011 |HUM: 01000000 |CRC: 11110100
---------------------------------------------------------------------------------------
ld:   0
id:   7
id_c:   7
bat:   0
ch:   1
tempF:    0x64D   (1613-900=713 / 10 = 71.3 °F => °C = (°F-32)/1.8  =‪ 21,83 °C )
tempC:    21.83
hum:   2       <--- dieser Sensor sendet die Feuchte falsch (?! ) zeigt sie aber richtig an. (Batterie? Mein 2ter + 3ter senden richtig)
crc:   47
Habe jetzt Temperatur + Feuchte dekodiert.
Als nächstes  kann ich dann das LaCrosse-Protokoll setzen und dann senden.
Da die LaCrosse-MaxId = 127 ist, kappe ich das höchste Bit der ID  einfach ab.
Da ich den uint64_t-Datentyp nehme und die Bitdaten in einem Byte-Array speichere, war einges an Bitfummlei erforderlich.
Dann kamen noch Endian-Probleme bei Union/Struct dazu (swappen) ...
Vielleicht gibt es da doch noch eine elegantere Lösung (bitSet-Funktionen usw. gehen nur auf byte-Daten).

TODOS:

  • Repetions zwischenpuffern.(Ringpuffer)
  • nach dem Protokoll-Durchlauf (nach ca. 865ms ab 1. Protokoll) LaCrosseDaten senden.
  • auf CRC-Prüfung verzichte ich.
  • Erkennung: "bit 11 is set if manual send button was pressed".

Arduino-Code dafür ist hier  zu finden.

Grüße,
Jürgen

FHEm2005

Hallo,

Ich habe jetzt zwei Dinge gemacht:
1. Die Definition der Userreadings gestrafft und in der Art des Moduls angepasst
2. Eine Teil-Errgänzung des Moduls geschrieben. Das soll für Björn lediglich eine Hilfe sein das Gerät dem Hauptmodul hinzuzufügen. Deshalb habe ich nur einen Teil bearbeitet. Wie das Ganze letztendlich im Modul eingebunden wird, weiß ich nicht, weil ich das Modul nicht kenne.

1. Userreadings:
stateHEX {my $s = (ReadingsVal($name,'state',''));
my @a = split(/\s+/,$s);
$s = @a[1];
return($s);},

LD {my $s = (ReadingsVal($name,'stateHEX',''));
my @a = split(//,$s);
my $ld = (hex($a[0])& 0xC);
$ld = sprintf( "%02b", hex( $ld ) );
return($ld);},

ID {my $s = (ReadingsVal($name,'stateHEX',''));
my @a = split("",$s);
my $u="";
$u = (hex($a[0].$a[1].$a[2])& 0x3FC) >> 2;
return($u);},

battery {my $s = (ReadingsVal($name,'stateHEX',''));
my @a = split(//,$s);
my $t = (hex(@a[2]) & 0x3);
if ($t eq "0"){$t="schwach";}
elsif ($t eq "1"){$t="gut";}
elsif ($t eq "2"){$t="top";}
return($t);},

channel {my $s = (ReadingsVal($name,'stateHEX',''));
my @a = split(//,$s);
my $t = (hex(@a[3]) & 0xC) >> 2;
if ($t eq "0"){$t="1";}
elsif ($t eq "1"){$t="2";}
elsif ($t eq "2"){$t="3";}
return($t);},

temperature {my $s = (ReadingsVal($name,'stateHEX',''));
my @a = split("",$s);
my $t= (hex($a[3].$a[4])& 0x3C) >> 2;
$t += ((hex($a[4].$a[5])& 0x3C) >> 2) * 16;
$t += ((hex($a[5].$a[6])& 0x3C) >> 2) * 256;
$t = (($t-900)/10 - 32)*5/9;
$t = int(10 * $t + 0.5) / 10;
return($t);},

humidity {my $s = (ReadingsVal($name,'stateHEX',''));
my @a = split("",$s);
my $t= (hex($a[6].$a[7])& 0x3C) >> 2;
$t += ((hex($a[7].$a[8])& 0x3C) >> 2) * 16;
return($t);},


2. Die Teilergänzung "NC_7427" für das Modul :
[font=courier]if ($readedModel eq "NC_7427" || (hex($a[0]) == 0x5 && $readedModel eq "Unknown")) {
# PEARL NC7427-675,
# /----------------------------------------------------- Leading-Sequence always 00
# /  /--------------------------------------------------- ID, changes after every battery change
#    /  /    /------------------------------------------- Battery state 10 == Ok, 01 == not bad, 00 == bad
#   / /   / /----------------------------------------- Channel 00 == Ch1, 01 == Ch2, 10 == Ch3
# / / / /  /--------------------------------------- Temperature
# /  / /  /  /   /---------------------------- Humidity
#    /  /    /  / / /    /------------------- CRC
#        /  /        /  /  /           /         /
#       00 01101100 00 01 001101000110 01000010 00010100000000
# Bit   01 23456789 01 23 456789012345 67890123 45678901234567
#                   1           2          3          4
# Byte  |  1 | 2 |  3  |  4 | 5 | 6 | 7  | 8 |  9 |10 | 11| 12

$temp  = (hex($a[3].$a[4])& 0x3C) >> 2;
$temp += ((hex($a[4].$a[5])& 0x3C) >> 2) * 16;
$temp += ((hex($a[5].$a[6])& 0x3C) >> 2) * 256;
$temp  = (($t-900)/10 - 32)*5/9;
$temp  = int(10 * $t + 0.5) / 10;

$humidity  = (hex($a[6].$a[7])& 0x3C) >> 2;
$humidity += ((hex($a[7].$a[8])& 0x3C) >> 2) * 16;

      if (checkValues($temp, $humidity)) {
      $model="NC_7427";
      $channel = (hex(@a[3]) & 0xC) >> 2;;
      $batbit = (hex(@a[2]) & 0x3);
      $batbit = ~$batbit & 0x1; # Bat bit umdrehen
      $mode = (hex($a[3]) & 0x4) >> 2;
     
        if ($deviceCode ne $idType1)  # new naming convention     
      {
  if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
          {
             Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
            } else {
             $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
            }
      }     
     
      $def = $modules{CUL_TCM97001}{defptr}{$deviceCode};
      if($def) {
        $name = $def->{NAME};
      }
           
        if(!$def) {
          Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
          return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode";
        }
        $hashumidity = TRUE;
        $hasbatcheck = TRUE;
        $hasmode = FALSE;
        $packageOK = TRUE;
        $haschannel = TRUE;
        $readedModel=$model;
      } else {
          $name = "Unknown";
      }
}[/font]


Ich hoffe, dass Björn (oder jemand anderes) Zeit hat das Modul CUL_TCM97001 um diesen Teil "NC_7427" zu erweitern.

Viele Grüße
Eberhard
Raspi3: FHEM, CULV3 (V1.61), EnOcean Pi 868, nanoCUL433, HUE-Bridge; Raspi4: Node-red, MQTT, Gaszähler auslesen mit ESP32-CAM

FHEm2005

Hallo,

weiß Jemand wie das CRC welches zwischen bit34 und bit41 liegt gebildet wird? Wie geht das Modul CUL_TCM97001 mit einem nicht bekannten CRC um?

Gruß Eberhard

Raspi3: FHEM, CULV3 (V1.61), EnOcean Pi 868, nanoCUL433, HUE-Bridge; Raspi4: Node-red, MQTT, Gaszähler auslesen mit ESP32-CAM

juergs

Hallo Eberhard,


boolean crcValid(unsigned long value, byte checksum)
// check if received crc is correct for received value
{
    byte calculatedChecksum = 0;
    for (int i = 0; i < 8; i++) calculatedChecksum += (byte)(value >> (i * 4));
    calculatedChecksum &= 0xF;
    return calculatedChecksum == checksum;
}


Konnte es aber nicht verifizieren:
unsigned long = 32 Bit = 4 Byte, scheint in diesem Fall nicht zureichen ?!

Jürgen

FHEm2005

Hallo Jürgen,

wenn Du den Trailer von "00" nicht berücksichtigen würdest, kämst Du auf genau 32 Bit Nutzdaten:

ID = 8 bit, Batteriecheck=2 bit, KanalNr.=2 bit, Temperatur=12 bit und Feuchte= 8 bit. Ob das aber richtig ist entzieht sich meiner Kenntnis, weil die die Berechnung nicht nachvollziehen kann.

Trotzdem versuche ich es mal. Die Subroutine übergibt einmal die 32-bit-Zahl mit dem Namen value und die vom Sensor gelieferte checksum als Byte. Die calculatedChecksum wird mit checksum verglichen und und das Ergebnis des Vergleiches als True oder False zurückgeliefert. So weit - so gut. Wenn nicht die Schleife wäre  >:(.

Die Schleife wird 8-mal durchlaufen. Die 32-bit werden achtmal um je 4 bit nach rechts geschoben. Aber was zu Teufel soll (byte)(value >> (i * 4))? Wie wird denn addiert? Ich könnte doch über einen Wert von 255 kommen und dann reichen die 8 bit nicht mehr aus. Oder? Hinzu kommt, dass das Ergebnis in einem Halbbyte mit 0xF binär addiert wird. Was ist mit dem anderen Nibble? Wohin mit dem Überlauf über die 8bit?

Ich habe ein Bitmuster der Nutzdaten (ohne Trailer) von: 0110 0111 0001 0111 0001 0101 0111 0010 . ID ist "103", BAT ist mit "00" als schlapp gekennzeichnet, Kanal ist "2",Temp ist nach Berechnung "4.6°C" und die rel. Feuchte ist "39%" (berechnet).
Die mitgelieferte CRC lautet: 0010 1101 also 45.

Wie komme ich jetzt auf die berechnete CRC?

Gruß Eberhard

Raspi3: FHEM, CULV3 (V1.61), EnOcean Pi 868, nanoCUL433, HUE-Bridge; Raspi4: Node-red, MQTT, Gaszähler auslesen mit ESP32-CAM

juergs

 Hallo Eberhard,

ich bin gerade noch am tüfteln wie ich die Dekodierung am performantesten hinbekomme.
Ich habe dabei folgende Probleme:

1. Die Wahl der Bitdekodierung basiert auf "stateless" Messen der Dauer jedes hereinkommenden Low-Impulses in der Interruptroutine.
Dabei darf die Ausführungszeit innerhalb des Interrupts nicht länger als die Low-Impulsphase sein. Mit serieller Ausgabe wird
das dann natürlich nichts. Deshalb hole ich mir die Einzelbits in ein Bytearray mit pro Byte ein Bit-Auflösung.
Das funktioniert schon zuverlässig gut.

2. Das ByteArray wird in die loop-Funktion übergeben und aus dem Array wird eine uint64_t Variable befüllt und soll vorerst in ein Ringpuffer gespeichert werden.
Dann wollte ich die "automatische" Dekodierung mittels Übergabe an Union/Struct erzielen,
hatte aber nicht die Endianness/Bitorder des GCC berücksichtigt. Also musste ich die Byteorder im ByteArray tauschen.
Dann kommt noch die Datenstruktur des Protokolls dazu, wo die High- und Low Nibbels z.B. der Temperatur  getauscht werden müssen.
Das Ganze braucht Zeit, die während der Repetitions  nicht zur Verfügung steht.
Muss also die weitere Verarbeitung  ausserhalb der Erfassung der 6 Repetitions gelegt werden:
-> Ringpuffer + zeitverzögerte Verarbeitung -> in die Pausezeiten legen. (Algorithums dafür ausarbeiten ...)

3. Deshalb muss ich den Ablauf heute noch mal überdenken und versuchen etwas anders zu strukturieren.
Bin also leider noch nicht so weit, um mich um den CRC zu kümmern.

Aber versuchen wir mal aufzudröseln:

(byte)(value >> (i * 4))

Die "value" kommt ja mit uint32_t-Format rein. Das sind effektiv 4 Bytes à 8 Nibble (4Bit = 1 HalbByte).
Es werden jeweils 4 Bit in eine byte-Variable geschoben ( for i=0; i<8 ...)
(also immer ein Halbbyte). Das AND-en mit 0xf soll sicherstellen, dass die oberen Bits (7..4)  wirklich "0" sind.
Das heißt einfach, dass alle Nibble (Bit 0..3) aus "value" in einer einer Byte-Variablen (uin8_t) aufaddiert werden.
Mehr scheint es nicht zu sein. Aber der Algoritmus muss nicht unbedingt passen (also ohne Gewähr).
Das ließe sich auch auf uint64_t-Format (8 Byte) aufbohren ...
Man müsste also ausprobieren was aufsummiert werden soll, damit es passt ...

Zitat
byte lead : 2;
byte id : 8;
byte bat : 2;
byte chan : 2;
unsigned short temp : 12;
byte hum : 8;
byte crc : 8;

2+8+2+2+12+8+8 = 42 (?), ließe man die Lead-Bits weg, die immer 0 sind, wären es 40 ....
Das ist aber eher untergeordnet, da ich mir aus dem "Bit"-ByteArray ja alle Datentypen auch direkt "konstruieren" kann   :D

Zitat//
________________01234567890123456789012345678901   = 32   ohne lead und crc.
uint32_t value = 0b10111111000101000011011000010010;  // CRC selbst: 0001.1100

Die trailing "0" hängen nur an der FHEM-Ausgabe dran und sind nicht im Protokoll vorhanden:
Zitat---------------------------------------------------------------------------------------
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
0 0 1 0 1 1 1 1 1 1 0 0 0 1 0 1 0 0 0 0 1 1 0 1 1 0 0 0 0 1 0 0 1 0 0 0 0 1 1 1 0 0
---------------------------------------------------------------------------------------
Aber: "Learning by doing" + "Nobody is perfect" ...  ;)

/Edit: die Probleme handle ich mir im Wesentlichen durch die seriellen Debugausgaben ein.  (;-((

Grüße,
Jürgen

juergs

Ausprobieren kann man das auch online: hier (ggf. auch in Perl hier.)


#include <stdio.h>
#include <stdint.h>

uint8_t checkSumme=0b00011100;

//uint8_t crcValid(uint32_t value, uint8_T checksum);
uint8_t crcValid(uint32_t value, uint8_t checksum)
// check if received crc is correct for received value
{
    uint8_t calculatedChecksum = 0;
    printf("*** calc: %u\n",value);
    int i = 0;
    for (i = 0; i < 8; i++)
    {
        uint8_t nibble = (uint8_t)(value >> (i * 4));
        calculatedChecksum += nibble;
        printf("*** zwischen:  val: %u \t- nibble: %d \t- sum: %d \n",value, nibble, calculatedChecksum);
    }
   
    calculatedChecksum &= 0xF;
    return calculatedChecksum == checksum;
}
int main()
{
    /* Unknown Code: 1CC311848280, 01C8D5808440, 01C8958085C0 */
    printf("Hello, World!\n");
    //.................01234567890123456789012345678901234567890123456789
    //uint32_t value = 0x1CC311848280 ;   //TODO:  hier noch passend angeben ....   
    //.................01234567890123456789012345678901                  2345678901
    uint32_t value = 0b10111111000101000011011000010010;  // CRC selbst: 0001.1100
    uint8_t ret = crcValid(value, checkSumme);
   
    printf("*** isChecksumOk: %d \n\n", ret);
    return 0;
}

FHEm2005

Hallo Jürgen,

Zitat2+8+2+2+12+8+8 = 42 (?), ließe man die Lead-Bits weg, die immer 0 sind, wären es 40 ....

Die CRC-Berechnung sollt m.E. nicht die CRC-bits selber einschließen, sondern nur die reinen Nutzdaten zur Berechning heranziehen; und das sind dann 32 bit!. Ich vergaß zu erwähnen, dass die letzten 6 bits auch immer "0" sind.

Ich weiß zwar nicht, was dein Ziel genau ist, ich würde wegen der Zeitprobleme zuerst die CRC prüfen und dann das Byte-Array bei bestandener CRC-Prüfung aus dem Puffer nehmen und die Wiederholungen, da mit identischen CRC, verwerfen. Danach hast Du alle Zeit der Welt das Array aufzubereiten. Deshalb ist es IMHO so wichtig die CRC-Prüfung hinzubekommen.
Deshalb zum Thema CRC  ;):
ZitatEs werden jeweils 4 Bit in eine byte-Variable geschoben ( for i=0; i<8 ...)
Das müssten dann die ersten 4 Bit von rechts sein, also "0010" aus meinem Beispiel.
ZitatDas AND-en mit 0xf soll sicherstellen, dass die oberen Bits (7..4)  wirklich "0" sind.
Angenommen danach enthält (byte) das Muster "xxxx 0010", dabei ist x beliebig, entweder 1 oder 0. Durch die binäre Addition bleiben die beliebigen "1" auf dem WErt "1", da 1 &1 =1 ergibt . Nur mir einem negierten Halbbyte bekommst du ein definiertes "0". Oder sehe ich das was falsch?.
Zitatin einer einer Byte-Variablen (uin8_t) aufaddiert werden.
Diese Werte werden aber dezimal aufsummiert. Der Überlauf geht dann in die 4 oberen Bits.

Der Code aus Deiner sub crcValid ist doch ein C-Code. Die Schleife wirkt ja nur auf die eine Zeile. Wenn Die fertig ist, addierst Du dem Ergebnis binär die 4 höherwertigen Bits hinzu. ERgebnis: "1" bleibt "1" und "0" bleibt "0". Für die Schreibweise würde ich die Schleife in {} packen, dann ist alles besser lesbar (für mich wenigstes) 8).

Da sind noch viel Fragen für mich offen.

Viele Grüße
Eberhard
Raspi3: FHEM, CULV3 (V1.61), EnOcean Pi 868, nanoCUL433, HUE-Bridge; Raspi4: Node-red, MQTT, Gaszähler auslesen mit ESP32-CAM

juergs

Hallo Eberhard,

ZitatRC-Berechnung sollt m.E. nicht die CRC-bits selber einschließen
Zustimmung!

Zitatwegen der Zeitprobleme zuerst die CRC prüfen und dann das Byte-Array bei bestandener CRC-Prüfung aus dem Puffer nehmen und die Wiederholungen, da mit identischen CRC, verwerfen. Danach hast Du alle Zeit der Welt das Array aufzubereiten.
Ja stimmt, allerding kommen meine Daten über alle Repetitions korrekt an, obwohl mein 433MHz-Bereich hier sehr "überfüllt" ist.
Allerdings liegt es wohl daran, dass der Sensor 1m neben der Antenne liegt.  ;D

Zitat
//______________01234567890123456789012345678901_____________2345678901
uint32_t value = 0b10111111000101000011011000010010;  // CRC selbst: 0001.1100

Nehmen wir dieses Beispiel: "1011.1111.0001.0100.0011.0110.0001.0010"

i=0  shift=0  Nibble = 0010   Nibble & 0xF =  0000.0010      sum = 2   // Anm.: Nibble ist vor dem AND = 0001.0010  (!)
i=1  shift=4  Nibble =  0001   Nibble & 0xF = 0000.0001      sum = 2 + 1 = 3
i=2  shift=8  Nibble =  0110   Nibble & 0xF = 0000.0110      sum = 2 + 1 + 6 = 9

usw....   OK?
ZitatDiese Werte werden aber dezimal aufsummiert. Der Überlauf geht dann in die 4 oberen Bits.
Ja + ja. Aber die Summe steht ja in "calculatedChecksum" und die bleibt vom And unangetatstet.
Der mögliche Überlauf bei > 255 wird verworfen ...

ZitatDie Schleife wirkt ja nur auf die eine Zeile.

So sieht man es besser:

Zitatfor (i = 0; i < 8; i++)
    {
        uint8_t nibble = (uint8_t)(value >> (i * 4));
        calculatedChecksum += nibble;
        printf("*** zwischen:  val: %u \t- nibble: %d \t- sum: %d \n",value, nibble, calculatedChecksum);
    }

Zitat
*** calc: 3205772818                                                                                                                                                                                 
*** zwischen: [0 ] val: 3205772818        chksum: 28     - nibble: 18    - sum: 18                                                                                                                     
*** zwischen: [1 ] val: 3205772818        chksum: 28     - nibble: 97    - sum: 115                                                                                                                   
*** zwischen: [2 ] val: 3205772818        chksum: 28     - nibble: 54    - sum: 169                                                                                                                   
*** zwischen: [3 ] val: 3205772818        chksum: 28     - nibble: 67    - sum: 236                                                                                                                   
*** zwischen: [4 ] val: 3205772818        chksum: 28     - nibble: 20    - sum: 0                                                                                                                     
*** zwischen: [5 ] val: 3205772818        chksum: 28     - nibble: 241   - sum: 241                                                                                                                   
*** zwischen: [6 ] val: 3205772818        chksum: 28     - nibble: 191   - sum: 176                                                                                                                   
*** zwischen: [7 ] val: 3205772818        chksum: 28     - nibble: 11    - sum: 187                                                                                                                   
*** checksum == 28 = false   // da 187                                                                                                                                                                                     
                                   


besser ...  ?