Fuka が 2026年01月31日22時09分39秒 に編集
体裁変更
メイン画像の変更
本文の変更
## はじめに
今回は Sony SPRESENSE を使って、温度センサー DHT11 の値を 1.54インチ 240×240 ピクセルのカラー液晶にリアルタイム表示する方法を紹介します。
1月も終わりに差し掛かってきたところではありますが、まだまだ寒い日が続きます。 温度を見ながら暖房をつけることが多いと思いますが、その際に現在の温度だけでなく、直前の温度変化がどのように変わっているのかも見れたらよいだろうと感じました。 そこで、今回は SPRESENSEを使って簡易温度モニターを作成してみました。 液晶を使用して温度の表示を行うだけでなく、直前の履歴をグラフ化することで温度の変化量を確認できるように工夫しました。
単純に温度をシリアルモニタに出すだけでなく、過去 5 回分の履歴をグラフ化して表示することで、より分かりやすく温度の変化を確認できます。 また、USB接続だけでなく、単体電源でも安定して表示できるように工夫しています。
## 使用するもの 今回の製作では以下の部品を使用しました。
Sony SPRESENSE メインボード
| 部品 | 数量 | |:---:|:---| | Spresense メインボード | 1 | | Spresense 拡張ボード | 1 | | 温度センサ(DHT11) | 1 | | 液晶モジュール | 1 | | 配線類 | 適宜 | | 筐体(自作) | 1 |
DHT11 温湿度センサ
Spresense メインボード、Spresense 拡張ボードについては機材提供をしていただきました。ありがとうございます。 その他、センサ、ディスプレイについては市販品のものを使用。 筐体については3Dをプリンタを使用して製作を行いました。
1.54インチ 240×240 SPI グラフィック液晶(ST7789)
## 配線と接続について SPRESENSEボードに対してディスプレイとセンサを接続していきます。 それぞれの接続先は以下のとおりです。
配線用ジャンパーワイヤ、ブレッドボード 配線と接続
### 液晶の接続
| 液晶ピン番号 | 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)
const unsigned long interval = 300000; // (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);
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; } }
/* ===== 初期化 ===== */
/* ===== init ===== */
void setup() { Serial.begin(115200); delay(500); dht.begin();
delay(2000); // ★ DHT安定待ち(超重要)
delay(2000); // DHT安定動作のおまじない
tft.init(240, 240);
tft.setRotation(3); // 横向き
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); // 横向き この関数で作画表示の向きを決定していきます。
↓筐体印刷時の画面 
## 製作時の様子
↓部品全体の様子 
マイコン、センサ本体などが入るように筐体を用意しました。
↓部品をすべて入れたところ 
 ↑ 内部に部品を入れたところ
## 出来上がったもの 最終的に出来上がったものは写真のとおりです。 現在の温度と過去の温度変化を赤線で示して可視化を行いました。 また、別途持っている温度計を比較してみたところ、測定温度は近似値であり普段使いでは問題ないことも確認ができました。  ## まとめ 今回はSpresenseを使用して”温度”という状態の可視化を行いました。 他にも様々なセンサを使用することで身の回りの情報を可視化、生活を豊かにできる工夫があると思うので、いろいろ試していきたいと感じました。