MQTT mit Mosquitto auf debian host, Mikrotik KNOT Router OS und Mikrotik Blutooth Beacons

reinhard@finalmedia.de Tue 27 Dec 2022 08:46:07 PM CET

Zunächst auf deinem debian host mosquitto installieren


apt-get update
apt-get install mosquitto mosquitto-tools

Dann einen Listen angeben und an *alle* verfügbaren IPs des Hosts binden (wenn wir nicht mit proxy arbeiten und das jetzt als Test sehen) und ein passwort hashfile erzeugen. Dazu wie nachfolgend beschrieben Konfigurationsdateien erzeugen


cat << +++EOF+++ > /etc/mosquitto/conf.d/main.conf
listener 1883 0.0.0.0
password_file /etc/mosquitto/passwords
+++EOF+++

chmod 640 /etc/mosquitto/conf.d/main.conf
chown root:mosquitto /etc/mosquitto/conf.d/main.conf

Und eine Passwort-Datei erzeugen


touch /etc/mosquitto/passwords
chmod 640 /etc/mosquitto/passwords
chown root:mosquitto /etc/mosquitto/passwords

Als root einen neuen MQTT Benutzer und Passwort hinzufügen. BENUTZERNAME musst du natürlich durch den gewünschten Namen ersetzen.

Wir definieren zwei Benutzer. Einen namens request01, der dann subscription liest. und einen

mqtt-mikrotik01

der publisher ist und die bluetooth beacon daten übermitteln wird.


mosquitto_passwd /etc/mosquitto/passwords request01
mosquitto_passwd /etc/mosquitto/passwords mikrotik01

Gib jeweils ein anderes Kennwort ein.

Den Service neustarten


systemctl enable mosquitto
systemctl restart mosquitto

In einem Terminal eine subscription auf ALLE Topics zum Test starten:


mosquitto_sub -u "request01"  -P "HIER_PASSWORT_DES_REQUEST_USERS" -h "HIER_IP_DEINES_DEBIAN_HOSTS" -t "#"

Parallel in einem anderen Terminal eine subscription auf "devices/mikrotik/messages/events" starten, die wir im nachfolgenden Script definieren werden.


mosquitto_sub -u "request01"  -P "HIER_PASSWORT_DES_REQUEST_USERS" -h "HIER_IP_DEINES_DEBIAN_HOSTS" -t "devices/mikrotik/messages/events"

MQTT in Mikrotik RouterOS in einem Mikrotik KNOT nutzen

Wir möchten Bluetooth Beacons als MQTT Messages an Broker, unseren debian Host senden/publishen. Dazu also via ssh mit dem Mikrotik knot verbinden und dort diese Befehle ausführen, um den debian host als broker zu hinterlegen, sowie ein kleines iotbroker Script zu erstellen, das wir dann periodisch auf dem Mikrotik Knot alle 5 Sekunden ausführen:


iot mqtt brokers add name=broker01 address="HIER_IP_DEINES_DEBIAN_HOSTS" port=1883 ssl=no username="mikrotik01" password="HIER_PASSWORT_DES_MIKROTIK_MOSQUITTO_USERS"
iot mqtt publish broker=broker01 message=testnachricht topic=test
system script add name=iotbroker
system scheduler add interval=5 on-event=iotbroker name=iotbroker
system script edit value-name=source iotbroker

Dann den nachfolgenden Code als das Script pasten. Und strg+o (Save und Quit) drücken. Im Script wird auf den Broker "broker01" verwiesen, den wir zuvor unter iot mqtt brokers erzeugt haben.



  # Required packages: iot

    ################################ Configuration ################################
    # Name of an existing MQTT broker that should be used for publishing
    :local broker "broker01"

    # MQTT topic where the message should be published
    :local topic "devices/mikrotik/messages/events/"

    # Interface whose MAC should be used as 'Locator ID'
    :local locatorIface "ether1"

    # POSIX regex for filtering advertisement Bluetooth addresses. E.g. "^BC:33:AC"
    # would only include addresses which start with those 3 octets.
    # To disable this filter, set it to ""
    :local addressRegex ""

    # POSIX regex for filtering Bluetooth advertisements based on their data. Same
    # usage as with 'addressRegex'.
    :local advertisingDataRegex ""

    # Signal strength filter. E.g. -40 would only include Bluetooth advertisements
    # whose signal strength is stronger than -40dBm.
    # To disable this filter, set it to ""
    :local rssiThreshold "-60"

    #################################### System ###################################
    :put ("[*] Gathering system info...")
    :local ifaceMac [/interface get [/interface find name=$locatorIface] mac-address]
    :local cpuLoad [/system resource get cpu-load]
    :local freeMemory [/system resource get free-memory]
    :local usedMemory ([/system resource get total-memory] - $freeMemory)
    :local rosVersion [/system package get value-name=version \
        [/system package find where name ~ "^routeros"]]
    :local model [/system routerboard get value-name=model]
    :local serialNumber [/system routerboard get value-name=serial-number]
    # Health is a bit iffy since '/system health' does not have 'find' in ROS6
    :local health [/system health print as-value]
    :local supplyVoltage 0
    :local boardTemp 0
    :foreach entry in=$health do={
        :if ($entry->"name" = "voltage") do={:set $supplyVoltage ($entry->"value")}
        :if ($entry->"name" = "board-temperature1") do={:set $boardTemp ($entry->"value")}
    }

    ################################## Bluetooth ##################################
    :put ("[*] Gathering Bluetooth info...")
    :global btOldestAdvertisementTimestamp
    :if ([:typeof $btOldestAdvertisementTimestamp] = "nothing") do={
        # First time this script has been run since booting, need to initialize
        # persistent variables
        :set $btOldestAdvertisementTimestamp 0
    }
  :local btProcessingStart [/system clock get time]
    :local advertisements [/iot bluetooth scanners advertisements print detail \
        as-value where \
            epoch > $btOldestAdvertisementTimestamp and \
            address ~ $addressRegex and \
            data ~ $advertisingDataRegex and \
            rssi > $rssiThreshold
    ]
    :local advJson ""
    :local advCount 0
    :local advSeparator ""
    :local lastAdvTimestamp 0
    # Remove semicolons from MAC/Bluetooth addresses
    :local minimizeMac do={
        :local minimized
        :local lastIdx ([:len $address] - 1)
        :for idx from=0 to=$lastIdx step=1 do={
            :local char [:pick $address $idx]
            :if ($char != ":") do={
                :set $minimized "$minimized$char"
            }
        }
        :return $minimized
    }

    :foreach adv in=$advertisements do={
        :local address ($adv->"address")
        :local ts ($adv->"epoch")
        :local rssi ($adv->"rssi")
        :local ad ($adv->"data")
        :local obj "\
            {\
                \"id\":\"$[$minimizeMac address=$address]\",\
                \"ts\":$ts,\
                \"rssi\":$rssi,\
                \"ed\":{\
                    \"ad\":\"$ad\"\
                }\
            }"
        :set $advCount ($advCount + 1)
        :set $lastAdvTimestamp $ts
        # Ensure that the last object is not terminated by a comma
        :set $advJson "$advJson$advSeparator$obj"
        :if ($advSeparator = "") do={
            :set $advSeparator ","
        }
    }

    :set $btOldestAdvertisementTimestamp $lastAdvTimestamp
    :put ("[*] Found $advCount new advertisements \
        (processing time: $[([/system clock get time] - $btProcessingStart)])")

    #################################### MQTT #####################################
    :local message \
        "{\
            \"clientId\":\"$[/iot mqtt brokers get value-name=client-id \
                [/iot mqtt brokers find name=$broker]]\",\
            \"t\":0,\
            \"v\":1,\
            \"OldestAdvertisementTimestamp\":$btOldestAdvertisementTimestamp,\
            \"locs\":[{\
                \"id\":\"$[$minimizeMac address=$ifaceMac]\",\
                \"tags\":[$advJson],\
                \"ed\":{\
                    \"model\":\"$model\",\
                    \"sn\":\"$serialNumber\",\
                    \"ros\":\"$rosVersion\",\
                    \"cpu\":$cpuLoad,\
                    \"umem\":$usedMemory,\
                    \"fmem\":$freeMemory,\
                    \"psu\":$supplyVoltage,\
                    \"temp\":$boardTemp\
                }\
            }]\
        }"
    :log info "$message";
    :put ("[*] Total message size: $[:len $message] bytes")
    :put ("[*] Sending message to MQTT broker...")
    /iot mqtt publish broker=$broker topic=$topic message=$message
    :put ("[*] Done")

Alternativ: Blutooth Beacon Stream ohne MQTT, direkt via ssh von einem Mikrotik KNOT via RouterOS lesen


ssh user@router "iot bluetooth scanners advertisements print terse follow-only detail proplist=length,data,address where length>0"

Diese lässt sich dann auch gezielt auf ein beacon filtern, hier z.B. auf den data= tag und dort die Beschleunigungsdaten, z.B. wenn ein TG-BT5-IN als Beacon eingesetzt wird und nur solche gewhitelistet sind:


...
| stdbuf -i0 -o0 grep "comment=name_aus_whiteliste" \
| stdbuf -i0 -o0 cut -d = -f5 \
| stdbuf -i0 -o0 cut -c 27-30 \
| stdbuf -i0 -o0 grep -E -v "0000|ff00" \
| stdbuf -i0 -o0 tai64n \
| stdbuf -i0 -o0 tai64nlocal

Beachte: In die Whitelist lassen sich derzeit maximal 8 Einträge eintragen