Hilfe bei der Umsetzung von Java in PHP

Hallo Joachim,
es ist richtig, dass der Sensor nur „Abstandsdaten“ sendet. Die Berechnung über den Füllstand wird dann in der Software gemacht. In der Hersteller-App sind daher auch die verschiedenen Gastankarten hinterlegt um dann die Prozentwerte zu errechnen.

Grüße Stefan

…aktuell sehe ich zwei „große“ Herausforderungen:

  1. Wie bekomme ich es mit einem Raspberry Pi hin, dass die Bluetooth-Daten ins IP-Symcon kommen? Muss ich dazu regelmäßig pollen? Da wäre ich für eine Idee dankbar, möglicherweise hat schon jemand etwas ähnliches umgesetzt…

  2. Wie ist der Datensatz zu interpretieren und mathematisch zu behandeln? Da wird sicherlich einiges „Try and Error“ sein, aber ich hoffe das man das mit den Informationen irgendwie hinbekommt.

Joachim

Hallo Joachim,
ich habe mal wieder ein wenig getestet. Aktuell hole ich mir die Daten folgendermaßen:
Auf dem Raspberry habe ich zum testen folgenden Befehl laufen:

btmon 2>&1 | nc -u 192.168.X.X 8765

Dieser sendet die Daten von btmon an einen UDP-Serversocket mit dem Port 8765 in Symcon.
Bei mir läuft im Hintergrund SymconBTP, daher muss ich folgenden Befehl nicht ausführen:

hcitool -i hci0 lescan > /dev/null 2>&1

An dem UDP-Sockel in IPS hängt dann ein Cutter mit den Einstellungen:
Linkes Trennzeichen: „> HCI“
Rechtes Trennzeichen: „RSSI:“
Ohne Anführungszeichen. Dadurch wird zwar der RSSI-Wert abgeschnitten, ist mir aber erst mal egal.

An dem Cutter hängt dann eine Register-Variable mit folgendem Skript:

$mac = "AA:BB:CC:DD:EE:FF";

if ($_IPS['SENDER'] == "RegisterVariable") {
   $Data = $_IPS['VALUE'];
    if (strpos($Data,$mac)){
        $start = strpos($Data,"Data:");
        echo "Start: ".$start;
        $ende = strpos($Data,chr(10),$start);
        echo "Ende: ".$ende;
        $data = substr($Data,$start,$ende-$start);
        echo $data;
    }
}

Die MAC-Adresse deines Sensors findest du in der APP auf dem Smartphone. Jetzt ist der Daten-String in der Variable $data - fehlt nur noch die Auswertung.
Dazu sind folgende Links interessant:
tankcheck.ts

Mopeka_React-Native

Leider scheint es so zu sein, dass man mit btmon nicht das vollständige Datenpaket bekommt. Daher verwende ich gerade hcitooladv. Damit bekomme ich 31 Bytes an Daten. + RSSI. Das scheint zu passen, da btmon auch Data length: 31 anzeigt.

Aber mit dem Verrechnen der Daten bin ich noch nicht weiter gekommen. Es könnte sogar sein, dass nur die Echo-Zeit des Ultraschalls übergeben wird. Da gibt es im Link oben Berechnungen, wo auch eine Konstante für den Gastyp hinterlegt ist.

Grüße
Stefan

Hallo Stefan,

das sind doch viele wichtige Erkenntnisse!
Habe den Sensor noch nicht, aber mit Deinen Hinweisen, den Informationen aus dem Internet und der App bekommen wir das schon hin!

Joachim

Hallo Stefan,

leider habe ich den Sensor noch nicht erhalten.
Hier gibt es einen Discord-Channel dafür.
Zur Batterie-Spannung steht da z.B. folgendes:

Except for this from Mopeka:
Bits 0-6: Battery voltage scaled such that [0 to 127] represents value [0 to
3.96875 volts]. In other words, read the lower 7 bits into an integer, and
divide by 32 to get the battery voltage

Kannst ja mal schauen ob es bei Dir passt…

Joachim

Den Discord muss ich mir mal anschauen.
Worauf zu achten ist… Es gibt zwei verschiedene Chipsätze, welche auch unterschiedlich ausgewertet werden.
UUID → ADA0 → cc2540
UUID → FEE5 → nrf52

Ich habe den cc2540…

Also mit den Infos vom Github Projekt mopeka_pro_check hab ich den Pro-Sensor auswerten können.

$pro = "AA:BB:CC:DD:EE:FF";

$Data = "AA:BB:CC:DD:EE:FF-0dff590003582f1541ddeeffb0dd0302e5feb3";

   if (strpos($Data,$pro)!==FALSE){
        //echo $Data;
        //01.01.2022, 08:09:10 | Register Variable    | 
        //AA:BB:CC:DD:EE:FF-  
        //0d ff 59 00 03 58  2f   DD EE FF  94  bd  b0    e4    03 02 e5 fe  
        //MA MA HW BAT TEMP Q  Q  MAC MAC MAC XACEL YACEL
        //1  2  3  4   5    6  7  8   9   10  11    12
        //b3 - RSSI
        $start = strpos($Data,"-")+1;
        $data = substr($Data,$start);
        echo "Pro Sensor: ".$data.PHP_EOL;
        //self.ReadingQualityStars = data[7] >> 6
        //""" Confidence or Quality of the reading on a scale of 0-3.  Higher is more confident """
        // self._raw_tank_level = ((int(data[7]) << 8) + data[6]) & 0x3FFF
        $tank = (hexdec($data[16].$data[17])<<8+hexdec($data[14].$data[15]))&0x3FFF;
        echo "Tank: ".$tank.PHP_EOL;
        $quality = hexdec($data[16].$data[17])>>6;
        echo "Quality: ".$quality.PHP_EOL;
        //self._raw_battery = data[4] & 0x7F
        $bat = hexdec($data[10].$data[11])&0x7F;
        echo "Bat raw: ".$bat.PHP_EOL;
        $batvolt = $bat / 32.0;
        echo "Bat volt: ".$batvolt.PHP_EOL;
        //"""Battery Percentage based on 3 volt CR2032 battery"""
        //percent = ((self.BatteryVoltage - 2.2) / 0.65) * 100
        $batper = (($batvolt - 2.2)/0.65)*100;
        echo "Bat percent: ".$batper.PHP_EOL;
        //  self._raw_temp = data[5] & 0x7F
        $temp_raw = hexdec($data[12].$data[13])&0x7F;
        $temp = $temp_raw - 40;
        echo "Temperatur: ".$temp.PHP_EOL;
        /*
        self._raw_tank_level
            * (
                MOPEKA_TANK_LEVEL_COEFFICIENTS_PROPANE[0]
                + (MOPEKA_TANK_LEVEL_COEFFICIENTS_PROPANE[1] * self._raw_temp)
                + (
                    MOPEKA_TANK_LEVEL_COEFFICIENTS_PROPANE[2]
                    * self._raw_temp
                    * self._raw_temp
                )
            )

            # converting sensor value to height - contact Mopeka for other fluids/gases
            MOPEKA_TANK_LEVEL_COEFFICIENTS_PROPANE = (0.573045, -0.002822, -0.00000535)
        */
        $level_mm = $tank * (0.573045+(-0.002822*$temp_raw)+(-0.00000535*$temp_raw*$temp_raw));
        echo "Füllstand: ".$level_mm."mm".PHP_EOL;
   }

Mal sehen, ob sich ohne Informationen vom Hersteller auch der Standard-Sensor auswerten lässt.
Hier sind ja auch Konstanten mit dabei, welche man sich nicht ableiten kann.

Grüße
Stefan

Beim Standard-Sensor hab ich bis jetzt Temperatur, Batterie.
Gerade hänge ich am Füllstand. Dazu müsste ich das hier verstehen und in php umsetzen:

{
      let ndx = 0
      let last_time = 0
      let w = 6

      for (let q = 0; q < 12; q += 1) {
        let bitpos = q * 10
        let bytepos = Math.floor(bitpos / 8)
        let off = bitpos % 8
        let v = mfrData[w + bytepos] + mfrData[w + bytepos + 1] * 256
        /* jslint bitwise: true */
        v = v >>> off
        let dt = (v & 0x1f) + 1
        v = v >>> 5
        let amp = v & 0x1f
        /* jslint bitwise: false */
        let this_time = last_time + dt
        last_time = this_time

        if (this_time > 255) {
          break
        }

        if (!amp) {
          continue
        }
        amp -= 1
        amp *= 4
        amp += 6

        this.adv[ndx] = {
          a: amp,
          i: this_time * 2,
        }
        ndx += 1
      }
    }

Hallo Stefan,

das ist ja klasse wie weit Du schon gekommen bist!!

Leider habe ich bisher den Sensor erhalten (habe das jetzt schon mal per Mail nachgefragt), noch irgendeine Antwort von MOPEKA - Service ist also nicht so deren Business…

Wie auch immer: Irgendwie bekommen wir das schon hin. Ich hatte ja bereits ein passendes Modul begonnen, bisher steht da aber nur ein ganz simples Grundgerüst.

Ich werde mit Deinen Information schon mal das eine oder andere vorbereiten können - ohne es ausprobieren zu können.

Was ist mir bekannt:

  • I/O ist ein UDP-Serversocket
  • die bekannten Statusvariablen Temperatur, Batterie-Spannung, Füllstand
  • gelesen habe ich auch von Empfangsqualität? Oder ist das nur in der Pro-Version?
  • um das Modul einstellen zu können wird sicherlich irgendwie eine Rolle spielen: MAC, Flaschengröße (5kg, 11kg oder sonst etwas)

All diese Dinge kann ich ohne Sensor schon gut vorbereiten, wenn Dir noch etwas einfällt bitte melden!

Joachim

Hallo Stefan,

der Sensor wird nach Aussage leider erst in der nächsten Woche versandt…

Da ich daher noch nicht mal in der App gucken kann, was da so eingestellt werden muss - und ich davon ausgehe das es im Modul nachgebildet werden muss - kannst Du mir ggf. schon mal ein paar Screenshots daraus senden.

Ganz spannend wäre wie die Daten aus dem UDP-I/O ankommen, dann könnte ich da sicherlich schon mal etwas umsetzen bzw. vorbereiten.

Das HCITOOL könnte man schon mal zum Scan benutzen, etwas in der Richtung habe ich bereits vorbereitet, das würde aber ohne Aufwand nur lokal probieren, man könnte es mit der phpseclib aber auch „remote“ machen. Habe ich in anderen Modulen schon so realisiert.

Klasse wäre halt, wenn man es remote nur „anstossen“ würde und dann die Daten per Socket an IP-Symcon gesendet würde…

Joachim

Hallo Joachim,
wie die Daten rein kommen, kommt drauf an, was wir unter linux verwenden wollen. hcitooladv / btmon / hcidump. Aber ist fast egal. Ich tendiere momentan zu btmon weil es gut verfügbar ist und ausreichend Daten liefert. Es würden sich nur die Bytenummern verschieben. Wahrscheinlich ist es am einfachsten sich an den Daten von btmon zu orientieren - alles andere liefert nur mehr Daten, die dann eben zurecht gestutzt werden.
Daten von hcitooladv auf UDP-Socket:

18.03.2022, 20:14:28 | RECEIVED [127.0.0.1:56936] | 34:14:B5:4B:AC:38-1aff0d000002ff27ecb7cf7efbecb3df7e07121c300006374bac380302a0adb7<LF>

Daten die btmon liefert:

0002ff27ecb7cf7efbecb3dfbe060954c04005164bac38

Leider kann ich nur wenige Werte in der App anzeigen lassen.
Der Füllstand ist ca. 10cm. Quality-Stars sind 3 und das Batteriesymbol scheint 100% zu haben.

Hier noch ein paar Notizen von mir:

// Standard
        //34:14:B5:4B:AC:38-
        //1a ff 0d 00 00 02 ff 28  ed b3 df 7e fb ed b7 cf 7e 04 17 5c 70 c1 01 f4 4b  ac  38 03       02 a0 ad        bc
        //               HW BAT                                                    MAC MAC MAC          =ADA0 UUID?  RSSI
        //      0  1  2  3  4   5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 20 21 22  23  24  25      26 27 28 29        30
        //if (mfr[0] !== 0x0d || mfr[1] !== 0x00) {return null} 
        // hwid = mfrData[3] if ((hwid & 1) === 1) { this.hwFamily = 'xl'} else {this.hwFamily = 'gen2'}
        //this.battery = (mfrData[4] / 256.0) * 2.0 + 1.5
        //id = utils.byte2str(mfr[22]) + ':' + utils.byte2str(mfr[23]) + ':' + utils.byte2str(mfr[24])

        $start = strpos($Data,"-")+1;
        $data = substr($Data,$start);
        echo "Std Sensor: ".$data.PHP_EOL;

        $bat = (hexdec($data[12].$data[13])/256.0)*2.0+1.5;
        echo "Bat: ".$bat.PHP_EOL;

        //let tmp = mfrData[5] & 0x3f
        //if (mfrData[5] == 0x3f && mfrData[4] == 0xff) {
        //this.corrupted = true
        // this.temperature = (tmp - 25.0) * 1.776964
        $temp = (hexdec($data[14].$data[15])-25.0)*1.776964;
        echo "Temp: ".$temp.PHP_EOL;

Grüße
Stefan

Bezogen auf diese Daten habe ich mal folgendes erstellt:

$Result = hex2ByteArray("0002ff27ecb7cf7efbecb3dfbe060954c04005164bac38");
print_r($Result);
return;

function hex2ByteArray($hexString) {
  $string = hex2bin($hexString);
  return unpack('C*', $string);
}

Dadurch wird der String in ein Array von Bytes konvertiert:

Array
(
    [1] => 0
    [2] => 2
    [3] => 255
    [4] => 39
    [5] => 236
    [6] => 183
    [7] => 207
    [8] => 126
    [9] => 251
    [10] => 236
    [11] => 179
    [12] => 223
    [13] => 190
    [14] => 6
    [15] => 9
    [16] => 84
    [17] => 192
    [18] => 64
    [19] => 5
    [20] => 22
    [21] => 75
    [22] => 172
    [23] => 56
)

Damit lässt sich wahrscheinlich besser arbeiten als mit den String-Operationen.

btmon hat auch eine Option -s SOCKET, [–server] Start monitor server socket. Habe aber nichts gefunden wie man das verwendet und ob es das ist was man möchte…

Joachim

…habe mal versucht Dein Skript oben an PHP anzupassen - einige Dinge sind mir aber unklar:

$ndx = 0;
$last_time = 0;
$w = 6;

for ($q = 0; $q < 12; $q += 1) {
        $bitpos = $q * 10;
        $bytepos = floor($bitpos / 8);
        $off = $bitpos % 8; 
        $v = $mfrData[$w + $bytepos] + $mfrData[$w + $bytepos + 1] * 256; // unklar woher $mfrData kommt
        /* jslint bitwise: true */
        $v = $v >> $off; // Bitoperation?
        $dt = ($v & 0x1f) + 1;
        $v = $v >> 5; // Bitoperation?
        $amp = $v & 0x1f;
        /* jslint bitwise: false */
        $this_time = $last_time + $dt;
        $last_time = $this_time;

        if ($this_time > 255) {
          break;
        }

        if (!$amp) {
          continue;
        }
        $amp -= 1; // unklar
        $amp *= 4; // unklar
        $amp += 6; // unklar

        $adv[$ndx] = array("a" -> $amp, "i" -> $this_time * 2); // unklar was das soll
        $ndx += 1;
     
}

Das Script, welches es zu verstehen gibt ist von: sample.ts
Vielleicht schaust du mal rein, dann erschließt sich vielleicht das ein oder andere.

Hallo Stefan,

mein Sensor ist doch schon heute gekommen! :grin:
Bei einen hcitool lescan ist er sichtbar, dabei wird die komplette MAC sichtbar, an Daten bin ich aber auf die Schnelle nicht gekommen. Wenn Du mir mal aufzeigen könntest, wie man da schnell mit einem der Tools an Daten komme wäre das sicher hilfreich.

Heute ist aber auch der Geburtstag einer meiner Töchter - von daher wird da heute nicht ganz so viel passieren.

Joachim

Hallo Joachim,
um schnell an Daten zu kommen ist es am einfachsten, du machst zwei Konsolen auf dem RPI auf.
Auf der ersten lässt du btmon laufen.
Auf der zweiten machst du ein hcitool lescan.

Dann bekommst du die Daten am btmon.
Für das Modul wäre es vielleicht sinnvoll nur die drei letzten stellen der MAC-Adresse zu filtern, da man diese direkt aus der Smarphone-App ablesen kann. (Oder die Möglichkeit sowohl eine vollständige MAC anzugeben und wahlweise die verkürzte)

Grüße
Stefan

Hallo Stefan,

ich habe jetzt mal hier einen Datensatz:

HCI Event: LE Meta Event (0x3e) plen 43                                                                                         #45 [hci0] 432.000551
      LE Advertising Report (0x02)
        Num reports: 1
        Event type: Non connectable undirected - ADV_NONCONN_IND (0x03)
        Address type: Public (0x00)
        Address: FC:45:C3:BD:57:16 (OUI FC-45-C3)
        Data length: 31
        Company: Texas Instruments Inc. (13)
          Data: 0002a4270d749081061a48c081040460908003c7bd5716
        16-bit Service UUIDs (partial): 1 entry
          Unknown (0xada0)
        RSSI: -54 dBm (0xca)

Aus meiner Sicht brauchen wir dringend Unterstützung für den Bluetooth I/O…(unabhängig von der Auswertung bzw. Verarbeitung der reinkommenden Daten)

Joachim

Hallo Stefan,

was wir wir denn schon gesichert über die ankommenden Daten:
[1] => 0
[2] => 2
[3] => 164
[4] => 39
[5] => 13
[6] => 116
[7] => 144
[8] => 129
[9] => 6
[10] => 26
[11] => 72
[12] => 192 - Batterie 1
[13] => 129 - Batterie 2
[14] => 4 - Temperatur 1
[15] => 4 - Temperatur 2
[16] => 96 - Tank 1
[17] => 144 - Tank 2
[18] => 128
[19] => 3
[20] => 199
[21] => 189 - MAC 4 Die letzten drei „Stellen“ der MAC-Adresse
[22] => 87 - MAC 5
[23] => 22 - MAC 6

Ich habe mal eingetragen was ich aus dem Thread zusammentragen konnte, unsicher bin ich bei den Eintragungen ob die o-basierenden sind (meine Aufstellung ist ja 1-basierend!)

Schaue mal bitte ob ich das so korrekt gemacht habe und ob ich etwas vergessen habe…

Joachim

Hallo Joachim,
zur Zählung kannst du mal oben in den „Notizen“ schauen. Ich weiß aber net, warum du Batterie und Temperatur so weit hinten eingeordnet hast. Normal müssten die beiden Werte auch nur ein Byte lang sein.
Bei mir sind sie nur so weit hinten, weil ich mit den Strings gearbeitet hatte und meine Daten auch länger waren (da nicht von btmon).
Bei dir müsste [2] → HardwareID, [3] → Batterie, [4] → Temperatur sein.