Skip to content

Instantly share code, notes, and snippets.

@bohwaz
Created October 13, 2021 22:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bohwaz/0d7c6d702e7f69cc7f1f129987de3cc8 to your computer and use it in GitHub Desktop.
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.
<?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