温度計
UNO

概要

Arduinoに液晶ディスプレイをつないで、気温を表示させます。

温度センサには、ナショナルセミコンダクタ社製のLM60BIZを利用しました。液晶ディスプレイは、SUNLIKE社製のSC2004Cです。

作るもの

気温は、初回を除き、10回測定した結果を平均したものを出力します。表示する気温は、現在の気温、測定開始後の最高気温・最低気温とします。測定間隔は1秒とします。表示は以下の通り行います。

  • 液晶ディスプレイの(0、0)の位置に、「Thermometer」と表示します。
  • 液晶ディスプレイの(1、1)の位置に、「Current:」と表示し、1文字あけて、現在の気温を、表示します。
  • 液晶ディスプレイの(1, 2)の位置に、「Maximum:」と表示し、1文字あけて、今までの最高気温を表示します。
  • 液晶ディスプレイの(1, 3)の位置に、「Minimum:」と表示し、1文字あけて、今までの最低気温を表示します。

気温は、常に小数点1桁まで表示し、その後に、「°C」を表示します。また、気温は小数点を含めて最大5桁表示(利用する温度センサが-40℃から125℃まで測定できるので)とし、常に右詰めとします。

仕様としてはまだまだ曖昧さはありますが、あんまり書きすぎてもしかたがないので、とりあえずこのくらいで。イメージは以下の通りです。

用意するもの

以下のものを用意します。

  • Arduino Uno
  • 温度センサ(ナショナルセミコンダクタ社製LM60BIZ)
  • 液晶ディスプレイ(SUNLIKE社製SC2004C)
  • 可変抵抗10kΩ(液晶ディスプレイの輝度調整に使います)
  • (PC)
  • (USBケーブル)
  • (ブレッドボード)
  • (ジャンプワイヤ)

利用機器

今回利用するLM60BIZは、以下の特性を持っています。

測定範囲
-40℃~125℃
電圧(単位はmv)と温度との関係
温度 = (電圧 - 424) / 6.25

このため-40℃の時は174mV、+125℃のときは1205mVが出力されます。

基本的な考え方

状況把握

温度センサが出力する電圧をArduinoで読み取ります。その電圧を温度に変換します。

機器操作

Arduinoから送られてきた文字列を液晶ディスプレイに表示します。

処理決定

温度センサから読み取った温度が、今までの最高気温より高ければ最高気温を更新します。温度センサから読み取った温度が、今までの最低気温より低ければ最低気温を更新します。

機器操作

現在の気温、最高気温、最低気温を液晶ディスプレイに表示します。

設計

ハードウェアの設計

説明書によるとノイズの多い環境ではコンデンサを接続するようにとのことですが、とりあえず今回は何もしないことにします。

温度センサからの出力電圧を読み取るために、Arduinoのアナログピンと温度センサのV0とを接続します。

プログラムの設計

液晶ディスプレイについては、液晶ディスプレイのページを見てください。 ここでは、温度センサからの入力を温度に変換する部分を中心に記述します。

気温の取得

利用機器の節に書いた通り、気温を取得するためには、温度センサが出力する電圧を取得する必要があります。温度センサの出力電圧は、analogRead()という関数を使って読み取ることができます。この関数を使うと、0から1023までの数値が返ってくるので、参照電圧をもとに、実際の電圧を求めます。

参照電圧は、通常5Vですが、analogReference()という関数を使うことで、1.1Vにしたり、AREFピンに与えた電圧に変更することができます。

LM60BIZは、最大1205mvを出力しますが、1100mV(=1.1V)のときに、約108℃となることから、参照電圧としては1.1Vを使うことにします。参照電圧を1.1Vに設定するには、以下のようにします。

1
analogReference(INTERNAL);

上記の設定をした後、analogRead()で読み取った数値は、0の時に0V、1023のときに、1023/1024*1100mV(≒1099mv)となります。

電圧を気温に変換するには、温度 = (電圧 - 424) / 6.25 という式を用います。電圧の単位はmVです。

最高気温・最低気温

最高気温・最低気温は、それぞれ専用の変数に保持しておきます。初回の測定結果をそれぞれ、最高気温・最低気温に設定しておき、その後、最新の気温が最高気温を上回ったら最高気温を、最低気温を下回ったら最低気温を更新します。初回の気温は、setup()の中で測っておきます。

気温の表示

表示する文字列は、char の配列に書き込んだあと、その配列を液晶ディスプレイに表示します。文字列に書き込むために、sprintf()関数を利用します。

気温は、小数点一桁まで表示したいのですが、Arduinoのsprintf()は、浮動小数点型をうまく扱えません(%fを使うと?が表示されます)。このため、温度を10倍した値を整数型の変数に保持しておき、整数部と小数部とを別々に表示することにします。たとえば、実際の気温が12.3℃のとき、変数では、123という形でを保持しておき、表示するときは、123/10 と 123%10 とを別々に表示します。

ただし、このままでは、気温がマイナスの時、小数部の表示がおかしくなる(負の値になる)ので、小数部の値は気温の絶対値を取得した後、10で割った余りにします。

追記: 浮動小数点数を文字列に変換するdtostrf()という関数がありました。詳細はこちらをご覧ください。

気温の後には、「℃」を表示させます。液晶ディスプレイのデータシートを見ると、2進数で10111111というコードを表示させると「°」が表示されるようです。液晶ディスプレイのライブラリには、自分で文字を作成する機能(createChar())もありますが、今回は液晶ディスプレイが持っている文字を利用します。もしかしたら、利用する液晶ディスプレイにより、内蔵文字が異なるかもしれないので、注意してください。

以上を総合すると、以下のようにして、画面に表示する文字列を作成します。sは、charの配列、textは、Current等の文字列を入れる変数、temperatureは気温です。Arduinoソフトウェア(で使っているAVR-GCC)では、2進数を表現するための記法が拡張されており、「0b」の後に、二進数を記述することができます。2進数表現は、C++14からは、標準となっているようです。

1
sprintf(s, "%s %3d.%1d%cC", text, temperature / 10 , abs(temperature) % 10, 0b11011111);

ところが、上記の記述では、気温が-0.5℃等、0から-1の間だと、マイナス記号が表示されません(理由は考えてください)。しかたがないので、まずはマイナス記号なしで文字列を作成しておき、マイナス記号は別途表示(文字列の変更)することにします。

最初に、符号なしで、整数部3桁、小数点、小数部1桁を表示します。その後、必要に応じてマイナス記号を追加します。マイナス記号を表示するのは、整数部の長さにより変動します。

最初から、符号、整数部、小数部の3つを保持するのが早いかもしれませんが、最高気温・最低気温を更新しようとすると面倒なことになりそうです。

スケッチ

スケッチは以下の通りです。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
#include <LiquidCrystal.h>

/* Arduino との接続
 * Arduino    LCD
 * D2        14(DB7)
 * D3        13(DB6)
 * D4        12(DB5)
 * D5        11(DB4)
 * D10        6(E)
 * D11        5(R/W)
 * D12        4(RS)
 *            3(VLC)
 *            2(Vdd)
 *            1(Vss)
 * A5         温度センサのV0 -> INPUT_PINに対応
*/

#define COUNT 10                             // 測定回数
#define INTERVAL 1000                        // 測定間隔(ミリ秒)
#define INPUT_PIN 5                          // 温度センサを接続するアナログピンの番号
#define LCD_WIDTH 20                         // LCD表示幅

LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2);   // lcdオブジェクトの作成

int temperature_max, temperature_min;        // 最高気温、最低気温保持用大域変数

int get_temperature() {
  double vin, temperature;

  vin =1100.0 * analogRead(INPUT_PIN) / 1024; // alalogRead()で得られる値を1024で割って、1100を掛けると実際に電圧になる
  temperature = (vin - 424) / 6.25 * 10;      // 温度は10倍の値で保持しておく
  return (int)temperature;
}

void print_temperature_1(int x, int y, char *text, int temperature) {
  int minus = 0, minus_position = 0;
  int t;
  char s[LCD_WIDTH];
  
  /* 温度がマイナスのときは、マイナス記号を書きだす位置を別途計算しておく */
  if (temperature < 0) {
    temperature = -temperature;              // 符号を反転
    minus_position = strlen(text) + 3;       // デフォルトのマイナスの位置

    t = temperature / 10;                    // 温度の整数部を取得
    do {
      minus_position--;                      // -を表示する位置を左にずらす
      t /= 10;                               // 10で割って、0以上の間繰り返す
    } while (t > 0);
  }
  
  sprintf(s, "%s %3d.%1d%cC", text, temperature / 10 , temperature % 10, 0b11011111);  // 温度は10倍の値になっているので、整数と小数とを分けて表示する。0b11011111は、「°」
  
  if (minus_position) {
    s[minus_position] = '-';  // マイナス記号を表示す必要があるときは、先ほどの計算で求めた場所にマイナス記号を書く。
  }
  
  lcd.setCursor(x, y);
  lcd.print(s);
}

void print_temperature(long temperature_cur) {
  print_temperature_1(1, 1, "Current:", temperature_cur);
  print_temperature_1(1, 2, "Maximum:", temperature_max);
  print_temperature_1(1, 3, "Minimum:", temperature_min);
}

void setup()
{
  int temperature_cur;
  analogReference(INTERNAL); // Set the reference voltage to 1.1V.

  lcd.begin(20, 4);          // LCDの初期設定
  lcd.setCursor(0, 0);
  lcd.print("Thermometer");
  
  get_temperature();        // 1回目の測定はなぜか低めの値が出る。
  delay(1);                 // 単に待つだけではだめみたい。
  
  temperature_cur = temperature_max = temperature_min = get_temperature();  // 最高気温、最低気温の初期値を設定
  print_temperature(temperature_cur);
}

void loop () {
  int temperature_cur = 0;
  
  for (int i = 0; i < COUNT; i++) {                // 10回の平均をとるため、10回分の温度を加算
    temperature_cur += get_temperature();
    delay(INTERVAL);
  } 
  
  temperature_cur /= COUNT;                        // 10で割って平均値を求める
  
  if(temperature_max < temperature_cur) {          // 最高気温より高ければ
    temperature_max = temperature_cur;             // 最高気温を更新
  } else if (temperature_min > temperature_cur) {  // 最低気温より低ければ
    temperature_min = temperature_cur;             // 最低気温を更新
  }
  
  print_temperature(temperature_cur);
}

動作している様子

バージョン

Hardware:Arduino Uno
Software:Arduino 0022

最終更新日

November 1, 2022

inserted by FC2 system