DHT22+RaspZW2の温湿度データを、遠く離れたFideafact.ddns.netに送ってWordPressでグラフで見る方法 Part2
構成としては、2台(1台でも複数台でも)の Raspberry Pi Zero W 2 + DHT22 から定期的に温湿度を測定し、外部サーバー Fideafact.ddns.net にHTTPで送信し、WordPress側でDB保存・グラフ表示する形です。
全体構成
DHT22 + Raspberry Pi 1
|
| HTTPS POST
v
Fideafact.ddns.net / WordPress / API
^
| HTTPS POST
|
DHT22 + Raspberry Pi 2
WordPressページでグラフ表示
おすすめ方式
- Raspberry PiでDHT22を読む
- WordPressに専用APIを作る
- 温度・湿度・端末ID・時刻をWordPressのDBへ保存
- WordPress固定ページにChart.jsでグラフ表示
1. Raspberry Pi側
DHT22配線例です。
DHT22 VCC -> 3.3V
DHT22 GND -> GND
DHT22 DATA -> GPIO4
Pythonライブラリを入れます。
sudo apt update
sudo apt install python3-pip
pip3 install adafruit-circuitpython-dht requests
sudo apt install libgpiod2
送信用スクリプト例です。
import time
import board
import adafruit_dht
import requests
DEVICE_ID = "raspi-1" # 2台目は raspi-2 などに変更
API_URL = "https://Fideafact.ddns.net/wp-json/dht22/v1/submit"
API_KEY = "your-secret-key"
dht = adafruit_dht.DHT22(board.D4)
while True:
try:
temperature = dht.temperature
humidity = dht.humidity
data = {
"device_id": DEVICE_ID,
"temperature": temperature,
"humidity": humidity,
"api_key": API_KEY
}
r = requests.post(API_URL, json=data, timeout=10)
print(r.status_code, r.text)
except Exception as e:
print("Error:", e)
time.sleep(300) # 5分ごと
自動起動は cron でできます。
crontab -e
crontabに追加:
@reboot /usr/bin/python3 /home/pi/dht22_sender.py
2. WordPress側に保存APIを作る
一番シンプルなのは、WordPressに小さな自作プラグインを入れる方法です。
wp-content/plugins/dht22-logger/dht22-logger.php を作ります。
<?php
/*
Plugin Name: DHT22 Logger
*/
add_action('rest_api_init', function () {
register_rest_route('dht22/v1', '/submit', [
'methods' => 'POST',
'callback' => 'dht22_submit',
'permission_callback' => '__return_true'
]);
register_rest_route('dht22/v1', '/data', [
'methods' => 'GET',
'callback' => 'dht22_data',
'permission_callback' => '__return_true'
]);
});
register_activation_hook(__FILE__, function () {
global $wpdb;
$table = $wpdb->prefix . 'dht22_logs';
$charset = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
device_id VARCHAR(50) NOT NULL,
temperature FLOAT NOT NULL,
humidity FLOAT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
INDEX device_time (device_id, created_at)
) $charset;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($sql);
});
function dht22_submit($request) {
global $wpdb;
$params = $request->get_json_params();
if (($params['api_key'] ?? '') !== 'your-secret-key') {
return new WP_Error('forbidden', 'Invalid API key', ['status' => 403]);
}
$wpdb->insert($wpdb->prefix . 'dht22_logs', [
'device_id' => sanitize_text_field($params['device_id']),
'temperature' => floatval($params['temperature']),
'humidity' => floatval($params['humidity']),
]);
return ['ok' => true];
}
function dht22_data($request) {
global $wpdb;
$table = $wpdb->prefix . 'dht22_logs';
return $wpdb->get_results("
SELECT device_id, temperature, humidity, created_at
FROM $table
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
ORDER BY created_at ASC
");
}
WordPress管理画面で DHT22 Logger を有効化します。
3. WordPressでグラフ表示
固定ページに以下のようなHTMLを入れます。
Chart.jsを使います。
<canvas id="tempChart"></canvas>
<canvas id="humChart"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
fetch('/wp-json/dht22/v1/data')
.then(res => res.json())
.then(rows => {
const labels = rows.map(r => r.created_at);
const devices = [...new Set(rows.map(r => r.device_id))];
const tempDatasets = devices.map(device => ({
label: device + ' 温度',
data: rows.filter(r => r.device_id === device).map(r => r.temperature),
borderWidth: 2
}));
const humDatasets = devices.map(device => ({
label: device + ' 湿度',
data: rows.filter(r => r.device_id === device).map(r => r.humidity),
borderWidth: 2
}));
new Chart(document.getElementById('tempChart'), {
type: 'line',
data: {
labels,
datasets: tempDatasets
}
});
new Chart(document.getElementById('humChart'), {
type: 'line',
data: {
labels,
datasets: humDatasets
}
});
});
</script>
これでWordPressページ上に、2台分の温度・湿度グラフが表示されます。
注意点
*APIキーは推測されにくい長い文字列にしてください
また、DHT22は読み取り失敗がそこそこあるので、Raspberry Pi側では失敗時に再試行する設計にすると安定します。
実用的には、測定間隔は 1分〜5分 くらいがおすすめです。DHT22は高頻度測定には向きません。が、DHT22を使用する場合「5分に1回送信すればよいので、1分間間で測定して、その合計の平均値を送る」やり方です。
1分間だけ複数回測定して平均し、5分に1回だけサーバーへ送信する方式は、DHT22でも実用的です。
5分周期
├─ 最初の1分間: 2〜3秒ごとに測定
│ └─ 異常値や読み取り失敗は捨てる
├─ 平均値を計算
├─ Fideafact.ddns.netへ送信
└─ 残り約4分待機
DHT22は高頻度に読めないので、2秒未満では読まない方がいいです。
1分間なら、2秒ごとで最大30回、3秒ごとで約20回測れます。安定性重視なら 3秒ごと がおすすめです。
送るデータは平均値に加えて、測定回数も入れると後で便利です。
{
"device_id": "raspi-1",
"temperature": 24.6,
"humidity": 53.2,
"sample_count": 20
}
さらに良くするなら、単純平均よりも 外れ値除去つき平均 が向いています。
例:
1分間で20回測定
↓
明らかに変な値を除外
↓
上下1〜2個を捨てる
↓
残りを平均
↓
5分ごとに送信
DHT22はたまに読み取り失敗や飛び値が出るので、この方式だとグラフがかなり滑らかになります。
結論としては、DHT22をそのまま使うなら「3秒ごとに1分間測定 → 平均 → 5分ごと送信」が現実的でおすすめです。
センサーをSHT45に変える場合でも、この平均化方式は有効です。
参考
import time
import traceback
import os
import sys
import board
import adafruit_dht
import requests
SERVER_URL = "https://fideafact.ddns.net/sensor"
INTERVAL = 600
LOCKFILE = "/tmp/dht22.lock"
# 多重起動防止
def check_lock():
if os.path.exists(LOCKFILE):
with open(LOCKFILE) as f:
old_pid = f.read().strip()
if old_pid and os.path.exists(f"/proc/{old_pid}"):
print(f"既に起動中 (PID {old_pid})、終了します", flush=True)
sys.exit(1)
with open(LOCKFILE, "w") as f:
f.write(str(os.getpid()))
def cleanup_lock():
try:
os.remove(LOCKFILE)
except OSError:
pass
check_lock()
def create_dht():
return adafruit_dht.DHT22(board.D4, use_pulseio=False)
dht = create_dht()
error_count = 0
MAX_RETRY = 3
try:
while True:
try:
print("測定開始", flush=True)
try:
temp = dht.temperature
humidity = dht.humidity
error_count = 0
except RuntimeError as e:
print(f"読み取りエラー(継続): {e}", flush=True)
error_count += 1
except OSError as e:
print(f"DHT/GPIOエラー(継続): {type(e).__name__}: {e}", flush=True)
error_count += 1
else:
if temp is None or humidity is None:
print("Noneのため送信しない", flush=True)
else:
print(f"温度: {temp:.1f}°C 湿度: {humidity:.1f}%", flush=True)
payload = {"temperature": temp, "humidity": humidity}
try:
r = requests.post(SERVER_URL, json=payload, timeout=10)
print(f"送信結果: HTTP {r.status_code}", flush=True)
print(f"応答本文: {r.text[:300]}", flush=True)
r.raise_for_status()
except requests.RequestException as e:
print(f"HTTP送信エラー(継続): {type(e).__name__}: {e}", flush=True)
# 連続エラー時は即座に再初期化(待機せず)
if error_count >= MAX_RETRY:
print(f"連続{error_count}回エラー → DHT再初期化", flush=True)
try:
dht.exit()
except Exception:
pass
time.sleep(3)
dht = create_dht()
error_count = 0
except Exception:
print("想定外エラー:", flush=True)
traceback.print_exc()
finally:
print(f"{INTERVAL}秒待機", flush=True)
time.sleep(INTERVAL)
finally:
cleanup_lock()
try:
dht.exit()
except Exception:
pass
以下のように変更。
- 5分ごとに送信
- 送信前に 1分間で12回測定
- 測定間隔は 5秒
- None、範囲外、読み取りエラーは除外
- 残った値から上下を捨てる
- 平均値を送信
import time
import traceback
import os
import sys
import board
import adafruit_dht
import requests
SERVER_URL = "https://fideafact.ddns.net/sensor"
SEND_INTERVAL = 300 # 5分ごとに送信
SAMPLE_COUNT = 12 # 1分間で12回測定
SAMPLE_INTERVAL = 5 # 5秒ごとに測定
LOCKFILE = "/tmp/dht22.lock"
MAX_RETRY = 3
# 明らかに変な値を除外する範囲
TEMP_MIN = -40.0
TEMP_MAX = 80.0
HUM_MIN = 0.0
HUM_MAX = 100.0
# 多重起動防止
def check_lock():
if os.path.exists(LOCKFILE):
with open(LOCKFILE) as f:
old_pid = f.read().strip()
if old_pid and os.path.exists(f"/proc/{old_pid}"):
print(f"既に起動中 (PID {old_pid})、終了します", flush=True)
sys.exit(1)
with open(LOCKFILE, "w") as f:
f.write(str(os.getpid()))
def cleanup_lock():
try:
os.remove(LOCKFILE)
except OSError:
pass
def create_dht():
return adafruit_dht.DHT22(board.D4, use_pulseio=False)
def is_valid_reading(temp, humidity):
if temp is None or humidity is None:
return False
if not (TEMP_MIN <= temp <= TEMP_MAX):
return False
if not (HUM_MIN <= humidity <= HUM_MAX):
return False
return True
def trimmed_average(values):
"""
上下の値を捨てて平均する。
データ数が多ければ上下2個、少なければ上下1個を捨てる。
"""
if not values:
return None
values = sorted(values)
n = len(values)
if n >= 8:
trim = 2
elif n >= 4:
trim = 1
else:
trim = 0
trimmed = values[trim:n - trim] if trim > 0 else values
if not trimmed:
return None
return sum(trimmed) / len(trimmed)
def measure_for_one_minute(dht):
global_error_count = 0
readings = []
print("1分間の測定開始", flush=True)
for i in range(SAMPLE_COUNT):
try:
temp = dht.temperature
humidity = dht.humidity
if is_valid_reading(temp, humidity):
readings.append({
"temperature": temp,
"humidity": humidity
})
print(
f"{i + 1}/{SAMPLE_COUNT}: 温度 {temp:.1f}°C 湿度 {humidity:.1f}%",
flush=True
)
else:
print(
f"{i + 1}/{SAMPLE_COUNT}: 異常値のため除外 temp={temp}, humidity={humidity}",
flush=True
)
except RuntimeError as e:
print(f"{i + 1}/{SAMPLE_COUNT}: 読み取りエラー(継続): {e}", flush=True)
global_error_count += 1
except OSError as e:
print(f"{i + 1}/{SAMPLE_COUNT}: DHT/GPIOエラー(継続): {type(e).__name__}: {e}", flush=True)
global_error_count += 1
if i < SAMPLE_COUNT - 1:
time.sleep(SAMPLE_INTERVAL)
temperatures = [r["temperature"] for r in readings]
humidities = [r["humidity"] for r in readings]
avg_temp = trimmed_average(temperatures)
avg_humidity = trimmed_average(humidities)
return avg_temp, avg_humidity, len(readings), global_error_count
check_lock()
dht = create_dht()
error_count = 0
try:
while True:
cycle_start = time.time()
try:
avg_temp, avg_humidity, valid_count, read_errors = measure_for_one_minute(dht)
error_count += read_errors
if avg_temp is None or avg_humidity is None:
print("有効な測定値が不足しているため送信しません", flush=True)
error_count += 1
else:
print(
f"平均値: 温度 {avg_temp:.1f}°C 湿度 {avg_humidity:.1f}% "
f"有効測定数 {valid_count}/{SAMPLE_COUNT}",
flush=True
)
payload = {
"temperature": round(avg_temp, 2),
"humidity": round(avg_humidity, 2),
"sample_count": valid_count
}
try:
r = requests.post(SERVER_URL, json=payload, timeout=10)
print(f"送信結果: HTTP {r.status_code}", flush=True)
print(f"応答本文: {r.text[:300]}", flush=True)
r.raise_for_status()
error_count = 0
except requests.RequestException as e:
print(f"HTTP送信エラー(継続): {type(e).__name__}: {e}", flush=True)
if error_count >= MAX_RETRY:
print(f"連続{error_count}回エラー → DHT再初期化", flush=True)
try:
dht.exit()
except Exception:
pass
time.sleep(3)
dht = create_dht()
error_count = 0
except Exception:
print("想定外エラー:", flush=True)
traceback.print_exc()
finally:
elapsed = time.time() - cycle_start
wait_time = max(0, SEND_INTERVAL - elapsed)
print(f"{wait_time:.0f}秒待機", flush=True)
time.sleep(wait_time)
finally:
cleanup_lock()
try:
dht.exit()
except Exception:
pass
このコードでは、1サイクルが合計で約5分になります。
つまり 約1分測定 → 平均値を送信 → 残り約4分待機 です。
DHT22なら SAMPLE_INTERVAL = 5 はかなり安全です。もっと細かくしたい場合でも、DHT22では 2秒未満 にはしない方がよいです。
サーバー受け取る側の更なる効率化 BESTバージョン
fideafact.ddns.netで受け取ったデータはwordpresでDB保存・グラフ表示する形ですが。これをWordpressプラグインでする方法もあります。
WordPressプラグイン化する方法が一番きれいです。
Raspberry Piから https://fideafact.ddns.net/wp-json/dht22/v1/submit にPOSTし、WordPress側のプラグインが受信・DB保存・グラフ表示まで担当します。
プラグインでやる内容はこの3つです。
1. REST APIでデータ受信
2. WordPress DBに保存
3. ショートコードでグラフ表示
プラグイン構成
wp-content/plugins/dht22-logger/
└─ dht22-logger.php
dht22-logger.php
<?php
/*
Plugin Name: DHT22 Logger
Description: Receive DHT22 temperature/humidity data and display charts.
Version: 1.0.0
*/
if (!defined('ABSPATH')) {
exit;
}
define('DHT22_LOGGER_API_KEY', 'your-secret-key');
register_activation_hook(__FILE__, 'dht22_logger_activate');
function dht22_logger_activate() {
global $wpdb;
$table = $wpdb->prefix . 'dht22_logs';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
device_id VARCHAR(64) NOT NULL DEFAULT 'default',
temperature FLOAT NOT NULL,
humidity FLOAT NOT NULL,
sample_count INT UNSIGNED DEFAULT 1,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY device_time (device_id, created_at)
) $charset_collate;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($sql);
}
add_action('rest_api_init', function () {
register_rest_route('dht22/v1', '/submit', [
'methods' => 'POST',
'callback' => 'dht22_logger_submit',
'permission_callback' => '__return_true',
]);
register_rest_route('dht22/v1', '/data', [
'methods' => 'GET',
'callback' => 'dht22_logger_data',
'permission_callback' => '__return_true',
]);
});
function dht22_logger_submit(WP_REST_Request $request) {
global $wpdb;
$params = $request->get_json_params();
if (($params['api_key'] ?? '') !== DHT22_LOGGER_API_KEY) {
return new WP_Error('forbidden', 'Invalid API key', ['status' => 403]);
}
$temperature = isset($params['temperature']) ? floatval($params['temperature']) : null;
$humidity = isset($params['humidity']) ? floatval($params['humidity']) : null;
if ($temperature === null || $humidity === null) {
return new WP_Error('bad_request', 'temperature and humidity are required', ['status' => 400]);
}
if ($temperature < -40 || $temperature > 80 || $humidity < 0 || $humidity > 100) {
return new WP_Error('bad_request', 'Value out of range', ['status' => 400]);
}
$device_id = sanitize_text_field($params['device_id'] ?? 'default');
$sample_count = isset($params['sample_count']) ? intval($params['sample_count']) : 1;
$wpdb->insert(
$wpdb->prefix . 'dht22_logs',
[
'device_id' => $device_id,
'temperature' => $temperature,
'humidity' => $humidity,
'sample_count' => $sample_count,
'created_at' => current_time('mysql'),
],
['%s', '%f', '%f', '%d', '%s']
);
return [
'ok' => true,
'id' => $wpdb->insert_id,
];
}
function dht22_logger_data(WP_REST_Request $request) {
global $wpdb;
$hours = intval($request->get_param('hours') ?: 24);
if ($hours < 1 || $hours > 168) {
$hours = 24;
}
$device_id = sanitize_text_field($request->get_param('device_id') ?: '');
$table = $wpdb->prefix . 'dht22_logs';
if ($device_id !== '') {
return $wpdb->get_results(
$wpdb->prepare(
"SELECT device_id, temperature, humidity, sample_count, created_at
FROM $table
WHERE device_id = %s
AND created_at >= DATE_SUB(NOW(), INTERVAL %d HOUR)
ORDER BY created_at ASC",
$device_id,
$hours
)
);
}
return $wpdb->get_results(
$wpdb->prepare(
"SELECT device_id, temperature, humidity, sample_count, created_at
FROM $table
WHERE created_at >= DATE_SUB(NOW(), INTERVAL %d HOUR)
ORDER BY created_at ASC",
$hours
)
);
}
add_shortcode('dht22_chart', 'dht22_logger_chart_shortcode');
function dht22_logger_chart_shortcode($atts) {
$atts = shortcode_atts([
'hours' => '24',
], $atts);
$hours = intval($atts['hours']);
if ($hours < 1 || $hours > 168) {
$hours = 24;
}
ob_start();
?>
<div style="max-width: 100%; margin: 20px 0;">
<canvas id="dht22TempChart" height="120"></canvas>
<canvas id="dht22HumChart" height="120" style="margin-top: 30px;"></canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
(function () {
const hours = <?php echo json_encode($hours); ?>;
fetch('<?php echo esc_url(rest_url('dht22/v1/data')); ?>?hours=' + hours)
.then(response => response.json())
.then(rows => {
const devices = [...new Set(rows.map(row => row.device_id))];
const colors = [
'#e53935',
'#1e88e5',
'#43a047',
'#fb8c00',
'#8e24aa'
];
const labels = [...new Set(rows.map(row => row.created_at))];
function buildDataset(field, labelSuffix) {
return devices.map((device, index) => {
const deviceRows = rows.filter(row => row.device_id === device);
return {
label: device + ' ' + labelSuffix,
data: deviceRows.map(row => ({
x: row.created_at,
y: Number(row[field])
})),
borderColor: colors[index % colors.length],
backgroundColor: colors[index % colors.length],
borderWidth: 2,
tension: 0.25
};
});
}
new Chart(document.getElementById('dht22TempChart'), {
type: 'line',
data: {
datasets: buildDataset('temperature', '温度')
},
options: {
parsing: false,
responsive: true,
scales: {
x: {
type: 'category',
labels: labels,
ticks: {
maxTicksLimit: 10
}
},
y: {
title: {
display: true,
text: '温度 °C'
}
}
}
}
});
new Chart(document.getElementById('dht22HumChart'), {
type: 'line',
data: {
datasets: buildDataset('humidity', '湿度')
},
options: {
parsing: false,
responsive: true,
scales: {
x: {
type: 'category',
labels: labels,
ticks: {
maxTicksLimit: 10
}
},
y: {
title: {
display: true,
text: '湿度 %'
},
min: 0,
max: 100
}
}
}
});
});
})();
</script>
<?php
return ob_get_clean();
}
Raspberry Pi側の送信先
Python側はこのURLに送ります。
SERVER_URL = "https://fideafact.ddns.net/wp-json/dht22/v1/submit"
payloadには api_key と device_id を入れます。
payload = {
"device_id": "raspi-1",
"temperature": round(avg_temp, 2),
"humidity": round(avg_humidity, 2),
"sample_count": valid_count,
"api_key": "your-secret-key"
}
2台目はこうします。
"device_id": "raspi-2"
グラフ表示方法
WordPressの固定ページや投稿に、これを貼ります。
[dht22_chart hours="24"]
過去7日分なら:
[dht22_chart hours="168"]
これで2台分の温度・湿度が同じグラフに表示されます。
APIキーの your-secret-key は、長いランダム文字列に変えてください。
例:dht22_9sK3xQ7mP2vL8nR4zA1bY6
「Flaskで受け取ってMySQL保存」するpyコード
import mysql.connector
app = Flask(__name__)
def get_db():
return mysql.connector.connect(
host='localhost',
user='sensoruser',
password='ttnx3teb',
database='sensor_db'
)
@app.route('/sensor', methods=['POST'])
def receive():
data = request.json
conn = get_db()
cur = conn.cursor()
cur.execute(
"INSERT INTO readings (temperature, humidity) VALUES (%s, %s)>
(data['temperature'], data['humidity'])
)
conn.commit()
conn.close()
return 'OK', 200
@app.route('/sensor/data', methods=['GET'])
def get_data():
conn = get_db()
cur = conn.cursor()
cur.execute("""
SELECT temperature, humidity, created_at
FROM readings
WHERE created_at > NOW() - INTERVAL 7 DAY
ORDER BY created_at ASC
""")
rows = cur.fetchall()
conn.close()
result = [{'temperature': r[0], 'humidity': r[1], 'time': str(r[2>
return {'data': result}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
方向性としては「Flaskで受け取ってMySQL保存」はできます。
ただし、このコードのままだと 構文エラーで起動しません。さらに、WordPressでDB保存・表示したい場合は、WordPressプラグイン方式の方が管理しやすいです。
主な問題です。
- Flask と request の import がありません
- SQL文が途中で壊れています
VALUES (%s, %s)> の > が不要 - result の行が途中で壊れています
str(r[2> になっている - device_id がないので2台のRaspberry Piを区別できません
- APIキー認証がないので誰でも投稿できてしまいます
- DBパスワードをコードに直書きしています
- いま貼られたパスワードは公開扱いになるので、可能なら変更した方が安全です
修正版の最小例はこうです。
from flask import Flask, request, jsonify
import mysql.connector
import os
app = Flask(__name__)
API_KEY = os.environ.get("SENSOR_API_KEY", "change-this-key")
def get_db():
return mysql.connector.connect(
host="localhost",
user="sensoruser",
password=os.environ.get("SENSOR_DB_PASSWORD"),
database="sensor_db"
)
@app.route("/sensor", methods=["POST"])
def receive():
data = request.get_json(silent=True)
if not data:
return jsonify({"error": "invalid json"}), 400
if data.get("api_key") != API_KEY:
return jsonify({"error": "invalid api key"}), 403
try:
device_id = str(data.get("device_id", "unknown"))[:64]
temperature = float(data["temperature"])
humidity = float(data["humidity"])
sample_count = int(data.get("sample_count", 1))
except (KeyError, TypeError, ValueError):
return jsonify({"error": "invalid data"}), 400
if not (-40 <= temperature <= 80):
return jsonify({"error": "temperature out of range"}), 400
if not (0 <= humidity <= 100):
return jsonify({"error": "humidity out of range"}), 400
conn = get_db()
cur = conn.cursor()
cur.execute(
"""
INSERT INTO readings
(device_id, temperature, humidity, sample_count)
VALUES
(%s, %s, %s, %s)
""",
(device_id, temperature, humidity, sample_count)
)
conn.commit()
cur.close()
conn.close()
return jsonify({"ok": True}), 200
@app.route("/sensor/data", methods=["GET"])
def get_data():
conn = get_db()
cur = conn.cursor()
cur.execute(
"""
SELECT device_id, temperature, humidity, sample_count, created_at
FROM readings
WHERE created_at > NOW() - INTERVAL 7 DAY
ORDER BY created_at ASC
"""
)
rows = cur.fetchall()
cur.close()
conn.close()
result = [
{
"device_id": r[0],
"temperature": r[1],
"humidity": r[2],
"sample_count": r[3],
"time": str(r[4])
}
for r in rows
]
return jsonify({"data": result})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
DBテーブルは例えばこうです。
CREATE TABLE readings (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
device_id VARCHAR(64) NOT NULL,
temperature FLOAT NOT NULL,
humidity FLOAT NOT NULL,
sample_count INT DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX device_time (device_id, created_at)
);
ただし、結論としてはこうです。
WordPressでグラフ表示だけしたい
→ Flaskでも可能
WordPressでDB保存・管理・固定ページ表示までしたい
→ WordPressプラグイン方式がおすすめ
Flask方式にすると、WordPressとは別に Flask サーバー、MySQL、リバースプロキシ、常駐設定を管理する必要があります。
WordPressプラグイン方式なら wp-json で受信して、そのままWordPressのDBとショートコードでグラフ表示できるので、この用途にはかなり相性がいいです。