1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Xiaomi Mijia (LYWSD03MMC)にカスタムファームウェアを入れてnode-redで受信する

Last updated at Posted at 2024-08-13

aliexpressで安く買える温湿度計はカスタムファームを入れることでBLEビーコンになる

41X05LkF3mL.SL1280.jpg

  • カスタムファームだと.01単位で温度湿度測定できて良い
  • 電池が1年くらい持つとのこと
  • OTA対応製品がまだ出回っている(2024.07現在)

OTA対応のファーム(2024.07購入:2023.08生産でOTA対応)であればスマホ上でファームウウェア入れ替えと設定が可能。

ファームウェア入れ替えと設定手順

上記に書いてある通り。flasherでMijiaを対象選択(LYWSD03...)してファームがOTA対象であることを確認したら、その時点での安定板(2024.08時点では4.7)を選んでフラッシュする。なお、他のホストとbluetooth接続していると(液晶にロゴが出ます)選択対象として出てこない。
フラッシュ後にデフォルト通りであればadvertising formatはcustom formatになっているが、念の為flasherでもう一度接続(ATC...)して設定する。

format

uint8_t size; // = 18
uint8_t uid; // = 0x16, 16-bit UUID
uint16_t UUID; // = 0x181A, GATT Service 0x181A Environmental Sensing
uint8_t MAC[6]; // [0] - lo, .. [6] - hi digits
int16_t temperature; // x 0.01 degree
uint16_t humidity; // x 0.01 %
uint16_t battery_mv; // mV
uint8_t battery_level; // 0..100 %
uint8_t counter; // measurement count
uint8_t flags; // GPIO_TRG pin (marking "reset" on circuit board) flags:
// bit0: Reed Switch, input
// bit1: GPIO_TRG pin output value (pull Up/Down)
// bit2: Output GPIO_TRG pin is controlled according to the set parameters
// bit3: Temperature trigger event
// bit4: Humidity trigger event

node-redでBLEを受信する

何を使っても良いかと思われるが、ファームウェアのフォーラム

にあったnode-red-contrib-ble-scanを利用。

nobleは自動では入らなかったのでコマンドプロンプトから。

sudo apt-get install bluetooth bluez libbluetooth-dev libudev-dev
sudo setcap cap_net_raw+eip $(eval readlink -f `which node`)
npm install noble

BLE受信とparse

受信ができるとあちこちのビーコンを拾うので、フォーラムにあったfunctionノードで対象のものだけをフィルター。
ファームウェアのフォーマットに従ってnode-red-contrib-buffer-parser(ラズパイのnode-redでは標準)でパースする。

flow.png

[
    {
        "id": "2638cecad749fa1e",
        "type": "tab",
        "label": "sensors",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "282e1cbf3dc18174",
        "type": "debug",
        "z": "2638cecad749fa1e",
        "name": "debug 1",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 560,
        "y": 320,
        "wires": []
    },
    {
        "id": "c08cddf3cd1437ba",
        "type": "BLE Scan",
        "z": "2638cecad749fa1e",
        "name": "",
        "x": 80,
        "y": 40,
        "wires": [
            [
                "a6375ab2fe258940"
            ]
        ]
    },
    {
        "id": "a6375ab2fe258940",
        "type": "function",
        "z": "2638cecad749fa1e",
        "name": "filter xiaomi",
        "func": "var payload = msg.payload;\nif (payload.address.startsWith(\"a4:c1:38\") &&\n    payload.advertisement.localName.startsWith(\"ATC_\")) {\n    var buffer = payload.advertisement.serviceData[0].data;\n    var mac = payload.address\n    msg.topic = mac;\n\n    msg.payload = {\n       \"buffer\": buffer\n    };\n    return msg;\n}\nreturn null;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 250,
        "y": 40,
        "wires": [
            [
                "1d7bbc567f124044"
            ]
        ]
    },
    {
        "id": "1d7bbc567f124044",
        "type": "buffer-parser",
        "z": "2638cecad749fa1e",
        "name": "",
        "data": "payload.buffer",
        "dataType": "msg",
        "specification": "spec",
        "specificationType": "ui",
        "items": [
            {
                "type": "int16le",
                "name": "temp",
                "offset": 6,
                "length": 1,
                "offsetbit": 0,
                "scale": "0.01",
                "mask": ""
            },
            {
                "type": "uint16le",
                "name": "humidity",
                "offset": 8,
                "length": 1,
                "offsetbit": 0,
                "scale": ".01",
                "mask": ""
            },
            {
                "type": "uint16le",
                "name": "mV",
                "offset": 10,
                "length": 1,
                "offsetbit": 0,
                "scale": "1",
                "mask": ""
            },
            {
                "type": "uint8",
                "name": "battery",
                "offset": 12,
                "length": 1,
                "offsetbit": 0,
                "scale": "1",
                "mask": ""
            }
        ],
        "swap1": "",
        "swap2": "",
        "swap3": "",
        "swap1Type": "swap",
        "swap2Type": "swap",
        "swap3Type": "swap",
        "msgProperty": "payload",
        "msgPropertyType": "str",
        "resultType": "value",
        "resultTypeType": "return",
        "multipleResult": true,
        "fanOutMultipleResult": true,
        "setTopic": true,
        "outputs": 4,
        "x": 430,
        "y": 40,
        "wires": [
            [
                "282e1cbf3dc18174",
                "339d05cfc0501b4b"
            ],
            [
                "282e1cbf3dc18174",
                "acd3a6735d4046ab"
            ],
            [
                "282e1cbf3dc18174"
            ],
            [
                "282e1cbf3dc18174"
            ]
        ]
    },
    {
        "id": "6a6ad82253b970e2",
        "type": "ui_chart",
        "z": "2638cecad749fa1e",
        "name": "temp chart",
        "group": "89dfaf28c135d6ea",
        "order": 1,
        "width": 0,
        "height": 0,
        "label": "",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": 1,
        "removeOlderPoints": "",
        "removeOlderUnit": "86400",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": false,
        "colors": [
            "#1f77b4",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "outputs": 1,
        "useDifferentColor": false,
        "className": "",
        "x": 110,
        "y": 140,
        "wires": [
            []
        ]
    },
    {
        "id": "ed92b03f0ac04d53",
        "type": "ui_text",
        "z": "2638cecad749fa1e",
        "group": "89dfaf28c135d6ea",
        "order": 2,
        "width": "2",
        "height": "1",
        "name": "temp",
        "label": "<font>   <i class=\"fa fa-thermometer-empty \"></i> </font>",
        "format": "{{msg.payload}}°C",
        "layout": "row-center",
        "className": "",
        "style": false,
        "font": "",
        "fontSize": 16,
        "color": "#000000",
        "x": 90,
        "y": 180,
        "wires": []
    },
    {
        "id": "7e964d382647eec1",
        "type": "ui_text",
        "z": "2638cecad749fa1e",
        "group": "89dfaf28c135d6ea",
        "order": 2,
        "width": "1",
        "height": "1",
        "name": "humidity",
        "label": "",
        "format": "{{msg.payload}}%",
        "layout": "row-center",
        "className": "",
        "style": false,
        "font": "",
        "fontSize": 16,
        "color": "#000000",
        "x": 100,
        "y": 240,
        "wires": []
    },
    {
        "id": "339d05cfc0501b4b",
        "type": "function",
        "z": "2638cecad749fa1e",
        "name": "two decimal",
        "func": "msg.payload = (msg.payload.toFixed(2));\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 610,
        "y": 20,
        "wires": [
            [
                "6a6ad82253b970e2",
                "ed92b03f0ac04d53"
            ]
        ]
    },
    {
        "id": "acd3a6735d4046ab",
        "type": "function",
        "z": "2638cecad749fa1e",
        "name": "two decimal",
        "func": "msg.payload = (msg.payload.toFixed(2));\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 610,
        "y": 60,
        "wires": [
            [
                "7e964d382647eec1"
            ]
        ]
    },
    {
        "id": "89dfaf28c135d6ea",
        "type": "ui_group",
        "name": "graph",
        "tab": "859daaa640503ab3",
        "order": 6,
        "disp": false,
        "width": 4,
        "collapse": false,
        "className": ""
    },
    {
        "id": "859daaa640503ab3",
        "type": "ui_tab",
        "name": "home",
        "icon": "dashboard",
        "order": 1,
        "disabled": false,
        "hidden": false
    }
]
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?