Created
October 13, 2021 22:15
-
-
Save bohwaz/0d7c6d702e7f69cc7f1f129987de3cc8 to your computer and use it in GitHub Desktop.
Reads data from LYWSD03MMC Xiaomi Mijia temperature sensor and stores it in a SQLite database.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Reads data from LYWSD03MMC Xiaomi Mijia temperature sensor | |
* and stores it in a SQLite database. | |
* | |
* @author BohwaZ <https://bohwaz.net/> | |
* @see https://github.com/davi-domo/Xiaomi-Mijia-LYWSD03MMC-/blob/main/mijia_bd/MIJIA_MULTI_BD.sh | |
* @see https://smhosy.blogspot.com/2021/01/quelle-temperature-et-hydrometrie-dans.html | |
* @see https://www.fanjoe.be/?p=3911 | |
* | |
* Usage: php script.php MAC_ADDRESS NAME SQLITE_DB_PATH MAX_READS | |
* | |
* MAC_ADDRESS = MAC address of the Bluetooth device (use 'hcitool lescan') | |
* NAME = Name of this sensor (in case you have multiple sensors) | |
* SQLITE_DB_PATH = Path to SQLite database, eg. /tmp/temperatures.sqlite | |
* MAX_READS = Stop the script after X notifications read from the sensor | |
* (1 = will stop immediately) | |
*/ | |
if ($argc != 5) { | |
printf("Usage: %s MAC_ADDRESS NAME SQLITE_DB_PATH MAX_READS\n", $argv[0]); | |
exit; | |
} | |
list(, $mac_address, $name, $db_path, $max_reads) = $argv; | |
$db = new \SQLite3($db_path); | |
$db->exec('CREATE TABLE IF NOT EXISTS stats ( | |
id INTEGER PRIMARY KEY, | |
datetime TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
mac_address TEXT, | |
name TEXT, | |
temperature REAL, -- Temperature in °C: 22,98 | |
humidity INTEGER, -- Humidity in %: 63% | |
battery INTEGER -- Battery level in % | |
);'); | |
$cmd = sprintf('gatttool -b %s --char-write-req --handle="0x0038" --value="0100" --listen', $mac_address); | |
print("Waiting for sensor"); | |
$process = proc_open($cmd, [1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $pipes); | |
stream_set_blocking($pipes[1], false); | |
stream_set_blocking($pipes[2], false); | |
$data = null; | |
$reads = 0; | |
while (!$data) { | |
$line = fgets($pipes[1], 1024); | |
$error = fgets($pipes[2], 1024); | |
if ($error) { | |
fclose($pipes[1]); | |
fclose($pipes[2]); | |
echo "\nFailed! " . $error . PHP_EOL; | |
proc_terminate($process); | |
exit(proc_close($process)); | |
} | |
if (preg_match('/Notification handle.*value: (.*)/', trim($line), $match)) { | |
store_data($match[1]); | |
if (++$reads >= $max_reads) { | |
break; | |
} | |
} | |
sleep(1); | |
echo "."; | |
} | |
fclose($pipes[1]); | |
fclose($pipes[2]); | |
proc_terminate($process); | |
proc_close($process); | |
function store_data(string $data): void | |
{ | |
global $db, $mac_address, $name; | |
$result = explode(' ', $data); | |
$temperature = hexdec($result[1] . $result[0]) / 100; | |
$humidity = hexdec($result[2]); | |
$battery = hexdec($result[4] . $result[3]) / 1000; | |
// https://www.fanjoe.be/?p=3911 | |
// Ma conclusion est que la plage de 0 à 100% de la batterie correspond à 2,1v à 3v | |
$battery = (($battery - 2.1) / 0.9) * 100; | |
printf("\ntemp=%f°C, humidity=%d%%, battery=%d%%", $temperature, $humidity, $battery); | |
$st = $db->prepare('INSERT INTO stats (mac_address, name, temperature, humidity, battery) VALUES (?, ?, ?, ?, ?);'); | |
$st->bindValue(1, strtoupper($mac_address)); | |
$st->bindValue(2, $name); | |
$st->bindValue(3, (float) $temperature); | |
$st->bindValue(4, (int) $humidity); | |
$st->bindValue(5, (int) $battery); | |
$st->execute(); | |
} | |
$db->close(); | |
echo "\n"; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment