Fuka が 2026年01月31日20時31分33秒 に編集
初版
タイトルの変更
SPRESENSEでつくる簡易温度モニター
タグの変更
SPRESENSE
記事種類の変更
製作品
ライセンスの変更
(MIT) The MIT License
本文の変更
## はじめに 今回は Sony SPRESENSE を使って、温度センサー DHT11 の値を 1.54インチ 240×240 ピクセルのカラー液晶にリアルタイム表示する方法を紹介します。 単純に温度をシリアルモニタに出すだけでなく、過去 5 回分の履歴をグラフ化して表示することで、より分かりやすく温度の変化を確認できます。 また、USB接続だけでなく、単体電源でも安定して表示できるように工夫しています。 ## 使用するもの 今回の製作では以下の部品を使用しました。 Sony SPRESENSE メインボード DHT11 温湿度センサ 1.54インチ 240×240 SPI グラフィック液晶(ST7789) 配線用ジャンパーワイヤ、ブレッドボード 配線と接続 | 液晶ピン番号 | SPRESENNSE側ピン番号 | |:---:|:---| | GND | GND | | VCC | 3.3V | | SCL | D13 | | SDA | D11 | | RES | D8 | | DC | D9 | | CS | D10 | | BLK | 3.3V | 温度センサ | DHT11側ピン | SPRESENSE側ピン | |:---:|:---| | DATA | D5 | | VCC | 3.3V | | GND | GND | ソースコード ```arduino:SPRESENSE #include <SPI.h> #include <Adafruit_GFX.h> #include <Adafruit_ST7789.h> #include <DHT.h> /* ===== 色定義 ===== */ #define COLOR_GRAY 0x8410 // RGB565 グレー /* ===== ピン定義 ===== */ #define TFT_CS 10 #define TFT_DC 9 #define TFT_RST 8 #define DHT_PIN 5 #define DHT_TYPE DHT11 /* ===== オブジェクト ===== */ Adafruit_ST7789 tft(TFT_CS, TFT_DC, TFT_RST); DHT dht(DHT_PIN, DHT_TYPE); /* ===== 設定 ===== */ #define HISTORY_SIZE 5 float tempHistory[HISTORY_SIZE]; int historyIndex = 0; unsigned long lastMeasure = 0; const unsigned long interval = 10000; // 10秒(1分なら60000) static float lastValidTemp = NAN; /* ===== ヘッダ描画 ===== */ void drawHeader(float temp) { tft.fillRect(0, 0, 240, 40, ST77XX_BLACK); tft.setTextColor(ST77XX_WHITE); tft.setTextSize(2); tft.setCursor(8, 8); tft.print("ONDO : "); tft.setTextSize(3); tft.setCursor(150, 5); if (temp > -90) { tft.print(temp, 1); tft.print("C"); } else { tft.print("--.-C"); } // 区切り線 tft.drawFastHLine(0, 39, 240, COLOR_GRAY); } /* ===== グラフ描画(フル活用) ===== */ void drawGraph() { const int graphX = 40; const int graphY = 235; const int graphW = 195; const int graphH = 190; // === min / max 自動算出 === float minT = 1000.0; float maxT = -1000.0; int valid = 0; for (int i = 0; i < HISTORY_SIZE; i++) { if (!isnan(tempHistory[i])) { minT = min(minT, tempHistory[i]); maxT = max(maxT, tempHistory[i]); valid++; } } if (valid < 2) { minT = 20; maxT = 30; } float margin = max(1.0, (maxT - minT) * 0.2); minT -= margin; maxT += margin; // グラフエリアクリア tft.fillRect(0, 40, 240, 200, ST77XX_BLACK); // 枠 tft.drawRect(graphX, graphY - graphH, graphW, graphH, ST77XX_WHITE); // === Y軸(温度)=== tft.setTextSize(1); for (int i = 0; i <= 6; i++) { float t = minT + (maxT - minT) * i / 6.0; int y = graphY - (graphH * i / 6); tft.drawFastHLine(graphX - 5, y, 5, ST77XX_WHITE); tft.drawFastHLine(graphX, y, graphW, COLOR_GRAY); tft.setCursor(2, y - 4); tft.print(t, 1); } // === データ描画 === int prevX = -1, prevY = -1; for (int i = 0; i < HISTORY_SIZE; i++) { int idx = (historyIndex + i) % HISTORY_SIZE; float temp = tempHistory[idx]; if (isnan(temp)) continue; int x = graphX + i * (graphW / (HISTORY_SIZE - 1)); int y = graphY - map(temp * 100, minT * 100, maxT * 100, 0, graphH); tft.fillCircle(x, y, 4, ST77XX_RED); if (prevX >= 0) { tft.drawLine(prevX, prevY, x, y, ST77XX_RED); } prevX = x; prevY = y; } } /* ===== 初期化 ===== */ void setup() { Serial.begin(115200); delay(500); dht.begin(); delay(2000); // ★ DHT安定待ち(超重要) tft.init(240, 240); tft.setRotation(3); // 横向き tft.fillScreen(ST77XX_BLACK); // 履歴初期化 for (int i = 0; i < HISTORY_SIZE; i++) { tempHistory[i] = NAN; } drawHeader(-99); drawGraph(); } /* ===== メインループ ===== */ void loop() { unsigned long now = millis(); if (now - lastMeasure < interval) return; lastMeasure = now; float temp = dht.readTemperature(); if (!isnan(temp)) { lastValidTemp = temp; } if (isnan(lastValidTemp)) return; Serial.print("Temp: "); Serial.print(lastValidTemp); Serial.println(" C"); tempHistory[historyIndex] = lastValidTemp; historyIndex = (historyIndex + 1) % HISTORY_SIZE; drawHeader(lastValidTemp); drawGraph(); } ``` 今回は5分おきに温度を計測していきます。 計測した値をディスプレイに表示しつつ、直近の温度変化をグラフとしても表示する仕様としました。 今回は液晶を使用しています。 tft.setRotation(3); // 横向き この関数で作画表示の向きを決定していきます。 ## 製作時の様子 マイコン、センサ本体などが入るように筐体を用意しました。  ↑ 内部に部品を入れたところ