Funkdaten mitloggen und simulieren

Facebooktwittergoogle_pluspinterestlinkedinmailFacebooktwittergoogle_pluspinterestlinkedinmail

Oft kommt an den Punkt Daten einer Fernbedienung oder ähnlichem simulieren zu wollen. Ein kleines Howto zeigt, in einfachen Schritten, wie man rangehen muss.


Vorbereitungen

Jede Entwicklung benötigt Hard- sowie Software. Die in dieser Anleitung benutzten Dinge sind:

Hardware

bild-von-busware-deCUL-Stickwww.busware.de/tiki-index.php?page=CUL

Der CUL-Stick ist das Herzstück dieser Anleitung. In der aktuellen Version 3 kann er 868 sowie 433 MHz empfangen sowie senden. Ältere Sticks (V2) können auf dieser Bandbreite nur senden und müssen für 868 extra konfiguriert werden. Zum Daten mitschneiden geht die V2-Version leider nicht, zum Daten simulieren jedoch schon.

Auf dem CUL-Stick befindet sich ein TI CC1101– bzw CC1100-Transceiver. Der CUL V2-Stick hat lediglich einen Die empfohlene Firmware ist culfw (siehe weiter unten).

Es reicht 1 Stick, um jedoch zu Testen empfiehlt sich noch ein zweiter.


Software

  • Linux Debian

Alle Schritte, inbesondere die dafür benutzte Software, im Howto sind utner Debian ausgeführt. Ein anderes OS wird auch gehen.

Die Firmware, um CUL zum Laufen zu bringen ist, heißt culfw. Sie kann schon viel und wird stetig weiterentwickelt. So kann man selbst noch einige Features einbauen.

Um Daten zu empfangen oder senden braucht man ein Programm wie z.B. CuteCom.

  • Scriptsprache wie PHP

Als Skriptsprache zur Interpretation und der Daten wird in dieser Anleitung PHP genommen.


CUL-Stick installieren / flashen

Der erste Schritt ist die Installation des CUL-Sticks. Je nachdem welche Version man hat sind verschiedene Schritte zu tun.

CUL V2

Der CUL-Stick kommuniziert standardmäßig nur auf 433 MHz. Wenn das zu untersuchende Gerät auf 868 MHz funkt, muss vorher noch einiges in der Firmware eingestellt werden. Dazu die ersten Schritte hier.

Danach kann normal geflashed werden.

Zum Aufzeichnen (und späteren Interpretieren) ist der CUL V2 NICHT geeignet. Er kann lediglich Daten senden und kann als Fernbedienung zum Testen genutzt werden.

  1. Unter www.culfw.de/culfw.html#Links die aktuelle Version des culfw (Der Firmware von CUL) runterladen und entpacken. Derzeit ist es die 1.52.
  2. Im culfw-Verzeichnis /culfw/clib die Datei cc1100.c die Zeilen für FREQ2, FREQ1 und FREQ0 ändern:
       0x06, // 0B FSCTRL1  *0F    06    152kHz IF Frquency
       0x00, // 0C FSCTRL0   00    00    
       0x10, // 0D FREQ2    *1E    21    868.3 (def:800MHz)
       0xB0, // 0E FREQ1    *C4    65    
       0x71, // 0F FREQ0    *EC    e8    
       0x55, // 10 MDMCFG4  *8C    55    bWidth 325kHz
       0xe4, // 11 MDMCFG3  *22   *43    Drate:1500 #1
    
  3. Kompiliere das Projekt neu. Führe im Ordner i>/culfw/Devices/CUL folgenden Befehl aus.
    make
    
  4. Es ist möglich, dass beim Kompilieren folgender Fehler passiert:
    error: 'clock_div_1' undeclared (first use in this function)
    

    Dann müssen die Header-Files der avr-libs geändert und Schritt 2 wiederholt werden. Ändere die Datei power.h in /user64/avr/include/avr wiefolgt:

    || defined(__AVR_ATmega32HVB__) \
    || defined(__AVR_ATmega32M1__) \
    || defined(__AVR_ATmega32U2__) \
    || defined(__AVR_ATmega32U4__) \
    || defined(__AVR_ATmega32U6__) \
    
  5. Jetzt kann man den CUL-Stick mit der fertigen culfw-Version flashen. Dabei wird beim Anstecken des Sticks der Taster zum Flashen gedrückt und erst längere Zeit später losgelassen. Wenn die LED nicht blinkt, ist der CUL-Stick im Flash-Modus. Wenn doch, muss er wieder abgezogen und erneut mit Drücken der Taste drangesteckt werden.
    Man kann auch nachschauen, ob der CUL-Stick im Flash-Modus ist, indem man folgenden Befehl ausführt:

    lsusb
    

    Wenn unter anderem die Geräte-ID 03eb:2ffa Atmel Corp. gefunden wird, ist der CUL-Stick im Flash-Modus.
    Jetzt kann die Firmware aus dem Ordner /Devices/CUL heraus geflashed werden:

    sudo make usbprogram_v2
    
  6. Nach dem Flashen ist ein Gerät /dev/ttyACM0 oder ähnlich vorhanden.

Daten aufzeichnen

CuteCom benutzen

Nachdem der CUL-Stick die richtige, aktuelle Firmware draufhat, muss man ihn mit CuteCom zum Laufen kriegen. CuteCom kommuniziert dabei mit dem CUL-Stick und sagt, was dieser machen soll.

Vorher sollte man sich noch die Befehlsreferenz von culfw unter www.culfw.de/commandref.html anschauen.

In diesem Bereich wird immer vom ttACM0 die Rede sein. Wenn man mehrere CUL-Sticks installiert hat ist es jeweils der ttyACM1, ttyACM2 usw.

Zuerst muss man CuteCom starten. Um auf das ttyACM0 zuzugreifen benötigt es SuperUser-Rechte. Dafür also:

sudo cutecom

Im CuteCom müssen folgende Parameter gesetzt werden:

Parameter Wert
Device /dev/ttyACM0
Hex output gechecked

Mit einem Klick auf Open Device wird die Verbindung hergestellt. Jetzt kann mit dem CUL-Stick kommuniziert werden.

Dazu hier ein paar Tests und Erklärungen.


Version

Folgender Befehl schaut, welche culfw-Version installiert ist. Es sollte V 1.46 oder größer sein.

V

Ankommende Daten anzeigen

Ein wichtiger, hier benötigter, Befehl ist das Daten aufzeichnen. CuteCom wird alle Daten anzeigen, die empfangen wurden. Der Befehl sieht folgendermaßen aus:

X<RR>

<RR> sind dabei 2 hexadezimale Zahlen, die z.B. wiefolgt aussehen könnten:

Befehl Beschreibung
X00 keine Daten anzeigen
X07 alle ankommenden Daten anzeigen
X08 steigende (r) und fallende (f) Flanke sowie Nachrichtenende (.) anzeigen
X10 Zeit zwischen 2 Flanken anzeigen
X80 Signalstärke anzeigen (a … schlecht -> p … gut)

Wenn man mehr Einstellungen aktiv haben möchte, addiert man einfach die einzelnen Elemente. Wir wollen sowohl Signalstärke und die Zeitdauer zwischen zwei Flanken sowie die Flanken selbst anzeigen:

X98

Wenn Daten gesendet werden kriegen wir beispielsweise folgende Antwort:

62 72 7e 66 2d

Dies sind alles ASCII-Zeichen, insgesamt 5 Stück. Sie heißen folgendes:

Befehl Beschreibung
62 72 7e 66 2d Zuerst wird die Signalstärke gesendet. 62 ist ein b – Somit eine schlechte Signalstärke. Der Verlauf sieht folgendermaßen aus: [schlecht]abcdefghijklmnop[gut].
62 72 7e 66 2d Als ASCII-Zeichen ist dies ein r somit, beginnt hier die steigende Flanke.
62 72 7e 66 2d Dies ist die Zeitdauer, wie lang der Wert auf „1“ bleibt.
62 72 7e 66 2d Dies ist die fallende Flanke, als ASCII-Zeichen ein f.
62 72 7e 66 2d Zuletzt kommt die Zeitdauer, wie lang der Wert auf „0“ bleibt.

Diese Daten können jetzt über lange Zeit aufgenommen werden. Dann werden sie kopiert und in einem Skript weiterverarbeitet.


Daten interpretieren

Ein kleines selbstgebautes Skript wird jetzt benötigt. Wir haben beispielsweise folgende Daten:

00000000: 62 72 7e 66 2d 62 72 f8   66 3b 62 72 f1 66 35 62 
00000010: 72 f1 66 38 62 72 e7 66   3a 63 72 f6 66 36 62 72 
00000020: f7 66 2b 62 72 f7 66 41   62 72 ea 66 30 62 72 01

config.php

In der config.php werden wichtige Einstellungen zur Skriptbenutzung gespeichert.

<?php
/******************************************************
 DEBUG ----- set for which step you want to see debug-information.
 example: -------- $_DEBUG[0] = 0 - no information for step 1
 ******************************************************/
$_DEBUG = array(1,1,1,1,1,1,1);

/**************************************************
 minRSSI -------- which signal strength you want to show in minimum. Value between 1 (weak) to 16 (strong)
 ***************************************************/
$_minRSSI = 14;

/********************************
 TIME ---- show elapsed time of everything
 ********************************/
$_TIME = true;
?>

index.php

Folgende Datei gibt lediglich ein Eingabefenster der Daten.

<span style="font-family:Courier;font-size:13px">

<h5>V.121221</h5>


<form action="index2.php" method="post">
  <textarea name="data" cols="70" rows="50"></textarea>
  <input type="submit" value="interprete"/>
</form>


 
</span>

index2.php

Die ganze Skript-Logik steckt in der index2.php. Hier werden die Daten angezeigt und verarbeitet.

<span style="font-family:Courier;font-size:13px">

<h5>V.130110</h5>



<?php
require('config.php');
if($_TIME) $scripttime = microtime();    // stop time of script-execution

/***********************************************************
 GET DATA
 --------
 save every posted data

   print example
   --------------
   00000000: 63 72 07 66 da 62 72 50 66 19 62 72 aa 66 5a 62
************************************************************/
if($_TIME) $timestart = microtime();                                                                                     // stop time of step-execution
echo "<strong style='text-decoration:underline'>1. get data</strong>
";
if(!isset($_POST['data']) || $_POST['data']=="") exit("<strong style='color:red;'>could not find data</strong>");     // if no data is posted
$data = $_POST['data'];
if($_DEBUG[0]) echo "<span style='font-size:10px'>".str_replace("\n","
",$data)."</span>
";
if($_TIME) echo "DONE: ".round(microtime()-$timestart, 6)." &micro;s.
";
echo "
";

/****************************************************
 CONVERT DATA
 ------------
 escape unneeded chars and extract important raw data

   print example:
   ---------------
   63 72 07 66 da 62 72 50 66 19 62 72 aa 66 5a
*****************************************************/
if($_TIME) $timestart = microtime();                                             // stop time of step-execution
echo "<strong style='text-decoration:underline'>2. convert data</strong>
";
$array = explode("\n", $data);                                                        // delete line break
$data = "";
// deleting "00000000:"
foreach($array as $value) {
        $data = $data . substr($value, 10);
}
$data = str_replace("  ", " ", $data);                                                // delete double space
$data = str_replace("\r", "", $data);                                                // delete \r
$data = str_replace("\n", "", $data);                                                // delete \n
if($_DEBUG[1]) echo "<span style='font-size:10px'>".$data."</span>

";
$data = str_replace(" ", "", $data);                                          // delete every space
$data = str_split($data, 2);                                                     // every signal value in one field element
if($_TIME) echo "DONE: ".round(microtime()-$timestart, 6)." &micro;s.
";
echo "
";

/*********************************************************************************
 DELETE DATA WITH WORSE RSSI
 ---------------------------
 search first data packets which are send with RSSI less than $_minRSSI and delete
**********************************************************************************/
if($_TIME) $timestart = microtime();
echo "<strong style='text-decoration:underline'>3. delete data with worse RSSI</strong>
<span style='font-size:10px'>";
$i=0;
$count=0;
while($i<=count($data { // if signal is less than $_minRSSI and there is a rising and a falling edge ---> delete complete signal (5 Bits)
        if#2 {
                if($_DEBUG[2]) echo "deleted: ".$data[$i]." ".$data[$i+1]." ".$data[$i+2]." ".$data[$i+3]." ".$data[$i+4]."
";
                array_splice($data,0,5);
        }
        // if signal is less than $_minRSSI, but it is not a correct signal --> delete only one bit
        // or: if signal-strength is enough, but there is no rising / falling edge --> no correct signal --> delete only one bit
        elseif(hexdec($data[$i])<$_minRSSI+96 || #3) { if($_DEBUG[2]) echo "deleted: ".$data[$i]." "; array_splice($data,0,1); } // if signal is stronger than $_minRSSI --> startsignal is found
        else break;
        $count++;
}
echo "</span>deleted ".$count." entries";
if($_TIME) echo "
DONE: ".round(microtime()-$timestart, 6)." &micro;s.
";
echo "
";

/*************************************************************
 DELETE WRONG DATA PACKAGES
 --------------------------
 check correct data format: 5 byte and rising and falling edge
**************************************************************/
if($_TIME) $timestart = microtime();
echo "<strong style='text-decoration:underline'>4. delete wrong data packages</strong>
<span style='font-size:10px'>";
$i=0;
$count=0;
while($i<=count($data)) { // if data-block is not long enough --> end of array
        if(!isset($data[$i]) || !isset($data[$i+1]) || !isset($data[$i+2]) || !isset($data[$i+3]) || !isset($data[$i+4])) break;
        // if signal has enough RSSI and correct structure (SignalStrength-RisingEdge-Time-FallingEdge-Time) --> go to next package (bit+5)
        if(hexdec($data[$i])>=$_minRSSI+96 && $data[$i+1]=="72" && $data[$i+3]=="66") {
                $i=$i+5;
        }
        // if data is not useable --> delete bit
        else {
                if($_DEBUG[3]) echo "deleted: ".$data[$i]."
";
                array_splice($data,$i,1);
                $count++;
        }
}
echo "</span>deleted ".$count." entries
";
if($_TIME) echo "DONE: ".round(microtime()-$timestart, 6)." &micro;s.
";
echo "
";

/********************************************************************************
 LAST CHECK
 ----------
 split data in needable packages, delete edge: {QUALITY-TIME_ON_ONE-TIME_ON_ZERO}
*********************************************************************************/
if($_TIME) $timestart = microtime();
echo "<strong style='text-decoration:underline'>5. last check</strong>
<span style='font-size:10px'>";
$data = implode("",$data);
$data = str_split($data,10);
$i=0;
$count=0;

while($i<count($data)) { // if data-length is less than 10 --> delete
        if(strlen($data[$i])<10) {
                if($_DEBUG[4]) echo "deleted: ".$data[$i]."
";
                array_splice($data,$i,1);
                $count++;
                continue;
        }
        $data[$i] = substr($data[$i],0,2).substr($data[$i],4,2).substr($data[$i],8,2);    // delete edges
        $i++;
}
echo "</span>deleted ".$count." entries - finally ".count($data)." entries
";
if($_TIME) echo "DONE: ".round(microtime()-$timestart, 6)." &micro;s.
";
echo "
";

/*********
 VISUALIZE
**********/
if($_TIME) $timestart = microtime();
echo "<strong style='text-decoration:underline'>6. visualize</strong>
<span style='font-size:10px'>";
$j=0;

foreach($data as $value) {
        $quality = hexdec(substr($value,0,2))-96;
        $signal1 = round(hexdec(substr($value,2,2))/2,0);
        $signal0 = round(hexdec(substr($value,4,2))/2,0);

        // Qualität
        if($_DEBUG[5]) {
                if($quality<=6) $color="red";
                elseif($quality<=12) $color="yellow";
                else $color="green";
                echo "<span style='color:".$color."'>";
                for($i=0;$i<$quality;$i++) echo "&#9646;";
                for($i=0;$i<16-$quality;$i++) echo "&#9647;";
                if($quality<10) echo "</span> 0".$quality."/16";
                else echo "</span> ".$quality."/16";
                echo " - ".$signal1."/".$signal0."
";

                echo "&nbsp;";
                for($i=0;$i<$signal1;$i++) echo "_";
                echo "
|";
                for($i=0;$i<$signal1;$i++) echo "&nbsp;";
                echo "|
|";
                for($i=0;$i<$signal1;$i++) echo "&nbsp;";
                echo "|";
                for($i=0;$i<$signal0;$i++) echo "_";

                echo "

";
                if($j%$_SEPERATOR==$_SEPERATOR-1) echo "
<hr/>";
                $j++;
        }
}
echo "</span>";
if($_TIME) echo "DONE: ".round(microtime()-$timestart, 6)." &micro;s.
";
echo "
";

/**********
 statistics
***********/
if($_TIME) $timestart = microtime();
echo "<strong style='text-decoration:underline'>7. statistics</strong>
<span style='font-size:10px'>";

for($i=0;$i<=127;$i++) $count1[$i] = 0;
$i=0;
// count 1
while($i<count($data)) {
        $value = round(hexdec(substr($data[$i],2,2))/2,0);
        $count1[$value]++;
        $i++;
}
for($i=0;$i<=127;$i++) $count0[$i] = 0;
$i=0;
// count 0
while($i<count($data)) {
        $value = round(hexdec(substr($data[$i],4,2))/2,0);
        $count0[$value]++;
        $i++;
}
if($_DEBUG[6]) {
        echo "

<table style='font-family:Courier;font-size:13px'>

<tr>";
        for($i=1;$i<=49;$i++) echo "

<th style='border-right:1px dotted black;text-align:center'>".$i."</th>


";
        echo "</tr>


<tr>";
        for($i=1;$i<=49;$i++) echo "

<td style='border-right:1px solid black;text-align:center'>".$count1[$i]."</td>


";
        echo "</tr>


<tr>";
        for($i=1;$i<=49;$i++) echo "

<td style='border-right:1px solid black;text-align:center'>".$count0[$i]."</td>


";
        echo "</tr>


<tr>

<td colspan='60' style='height:2px;background:black'></td>

</tr>


<tr>";
        for($i=50;$i<=98;$i++) echo "

<th style='border-right:1px dotted black;text-align:center'>".$i."</th>


";
        echo "</tr>


<tr>";
        for($i=50;$i<=98;$i++) echo "

<td style='border-right:1px solid black;text-align:center'>".$count1[$i]."</td>


";
        echo "</tr>


<tr>";
        for($i=50;$i<=98;$i++) echo "

<td style='border-right:1px solid black;text-align:center'>".$count0[$i]."</td>


";
        echo "</tr>


<tr>

<td colspan='60' style='height:2px;background:black'></td>

</tr>


<tr>";
        for($i=99;$i<=127;$i++) echo "

<th style='border-right:1px dotted black;text-align:center'>".$i."</th>


";
        echo "</tr>


<tr>";
        for($i=99;$i<=127;$i++) echo "

<td style='border-right:1px solid black;text-align:center'>".$count1[$i]."</td>


";
        echo "</tr>


<tr>";
        for($i=99;$i<=127;$i++) echo "

<td style='border-right:1px solid black;text-align:center'>".$count0[$i]."</td>


";
        echo "</tr>

</table>


";
}
echo "</span>";
if($_TIME) echo "DONE: ".round(microtime()-$timestart, 6)." &micro;s.
";
echo "
";
if($_TIME) echo "EVERYTHING COMPLETE IN: ".round(microtime()-$scripttime, 6)." &micro;s.
";

?>

</span>

Das ist lediglich ein Beispiel-Skript, daraus kann später noch mehr gemacht werden.

Sich wiederholende Befehle müssen nicht immer die gleiche Zeitdauer bei dem Wert „0“ oder „1“ haben. Manchmal ist er „50ms“ auf „1“, manchmal nur „40ms“. Deswegen lohnt es sich mehrere Male eine Befehl mitzuloggen und einen Mittelwert zu nehmen.


Daten verstehen

Für jeden, der eine bestimmte Anzahl von Tasten hat, die er nachbauen will (wie bei einer Fernbedienung), reicht es bis hierhin. Jetzt können alle Daten erzeugt, geloggt und angezeigt werden. Dieser Punkt kann für diese Leute übersprungen werden.

Wer jedoch eigene Daten erzeugen will, muss das System verstehen, wie die bereits empfangenen Daten erzeugt wurden sind. Das kann mitunter sehr kniffelig sein.

Daten werden meist nicht in Rohform gesendet um z.B. gleichanteilsfrei zu sein. Als Beispiel zur Dekodierung der Daten nehme ich den Manchester-Code. Hier passiert folgende Umwandlung:

gesendete Daten wirkliche Daten
01 0
10 1

Jede 01 ist somit eine 0 – jede 10 ist somit eine 1. Der Manchester-Code ist eine einfache Variante um Daten zu kodieren. Normalerweise hat man es mit schwereren, nirgendwo dokumentierten, Verfahren zu tun. Es ist ein rätselraten und kann nur mit eine großen Menge an Daten erraten werden.


Daten simulieren

Der letzte Schritt ist das Ausprobieren, ob es funktioniert, wenn man ähnliche Daten sendet und versucht die Fernbedienung zu sein. Dafür eignen sich beide CUL-Sticks (V2 oder V3). Man kann sogar, wenn man beide hat, schauen, ob der gewünschte Befehl geschickt wurde.

Der Sendebefehl bei culfw heißt wiefolgt: (siehe www.culfw.de/commandref.html)

GssNnprHHLLhhllDDDD

In der Referenz steht beschrieben, wie man die einzelnen Längen des „0“ oder „1“-Wertes setzt. Danach sollte das gewünschte Signal ankommen.

Ein Kommentar

  1. Hallo,

    ich habe ein RaspberryPI und heute das CC1101 Transiver Modul bekommen. Mit Minicom und Ihren Blog konnte ich zumindestens irgendwas empfangen. Senden konnte ich auch irgendwas. Kontrollieren tue ich das mit der SDRSharp Software + DVB-T USB Empfänger.
    Ich erhofte mir eigentlich ohne FHEM die ein Datenpaket als Datei ab zu speichern um es dann weiter verarbeiten zu können.

    Irgend eine Idee für mich?

    MfG
    Andree

Kommentar hinterlassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.