Arduinoを使ってみよう
小学校でもプログラミングの授業がはじまりました。プログラミングを勉強すると、考え方や物事の進め方を学ぶことができるそうです。
でも、ただ勉強するだけでは面白くないよね。そう、高専では目的を持ってプログラムを学びます。今日は熱中症対策組込システムを作製するぞ!
袋の中身を確認しましょう。ちゃんと入っていますか?
入っているもの一覧
@マイコンボード
AUSB通信ケーブル
B温湿度センサー
CフルカラーLED
Dジャンパー線 メス−オス 7本
E袋
使うボードはこれだ! マイコンボードと呼ばれています。
真ん中にあって黒い四角いところから銀色の足が4方向に出ていて、合計で32本の銀色の端子があります。
これがマイコンボードの本体で、中にはコンピュータの計算を司るCPU(Central Processing Unit)と呼ばれるコア部分があります。
その他LEDを点けたりスイッチの入力を読み取ったりある程度の情報を覚えることができるメモリ等が詰め込まれているぞ!
パソコンのUSBポートとマイコンボードをUSB通信ケーブルで接続します。

接続すると、マイコンボード上の電源ランプが青色に点灯します。
電源ランプが点灯していない人はスタッフに声をかけてください。ケーブルの接続不良か、あるいはマイコンボードが壊れているかもしれません。

プログラミングにはArduinoIDE*という開発環境を使います。
緑色の顔のようなアイコンをダブルクリックします。
※デスクトップにアイコンが無い場合は、スタート→Arduinoの順にクリックしてください。
* 家のコンピュータでやってみたい人は、「ArduinoIDE インストール」と検索して、家のパソコンにインストールしてみてね。10分もあればできるよ!
起動するとこんな感じ(下図)です。

ウィンドウの上にファイル名が示され、メニューバーと呼ばれる場所には「ファイル」「編集」「スケッチ」「ツール」「ヘルプ」などのメニューコマンドがあります。
プログラムを書いたら「ファイル」メニューから「保存」か「名前を付けて保存」をすることで、プログラムを保存することができます。なお、Arduinoではプログラムのことをスケッチといいます。
よく使うのは「✅」マークのボタンで示された、プログラムの検証です。これは自分で書いたプログラムがちゃんとマイコン上で動くかを検証するためのボタンです。
次によく使うのは「⇒」マークで示された、プログラムをマイコンに転送するボタンです。マイコンにプログラムを転送するとマイコンが自動的に動き始めます。
マイコンが動き始めてからよく使うのが、「虫眼鏡」のマークで示された、シリアルモニタを起動するボタンです。これを使うと、マイコンからのメッセージを表示するなど、マイコンと通信することができます。
プログラミングに入る前に、開発環境の準備を行います。
メニューバーの「ツール」から「ボード」という場所が「Aruduino Uno」となっていることを確認してみてください。
もしなっていなかった場合は「ボード」から「Arduino AVR Boards」、表示される一覧から「Aruduino Uno」を選択してみてください。

最初は、マイコンに「 Hello!! 」と言わせるプログラムを作成してみましょう。
といっても、今回のマイコンには音を出すスピーカなどはついていません。ですので、マイコンに「 Hello!! 」という文字を出力させます。
ここで、文字を出力?と言われておかしいなと思った人もいるでしょう。そうです、マイコンにはディスプレイなど文字を表示する装置が付いていません。
そこでマイコンで出力した文字を、通信ケーブルを使ってパソコンのディスプレイに出力します。それを実現するのがシリアルモニタです。
シリアルモニタとは、マイコンとパソコンとを繋いでいる通信ケーブルでやり取りされている内容を表示してくれる便利なモニターなのです。
メニューバーの「ツール」から「シリアルポート」を選び、マイコンがUSBケーブルで接続されているシリアルポートを選びます。
下の図ではCOM3というのシリアルポートが表示されていますが、これはパソコンによって変わります。
もし、他のシリアルポートも表示されていて、次に行うシリアルポートの確認で失敗するようであれば、他のシリアルポートを選択してみてください。


メニューバーの「ツール」から「ボード情報を取得」をクリックします。
ウィンドウが現れ「ボード情報」が表示されれば、シリアルポートが正しく設定されています。
うまく設定できていないと、ボード情報ウィンドウは現れず、コンソールにエラーメッセージが表示されます。
その場合には、シリアルポートの番号を変更するか、USBケーブルを抜き差ししてマイコンとパソコンがしっかりと接続されているかを確認しましょう。

それでは下記のプログラムを打ってみましょう。マウスで枠内の文字を選択してコピー&ペーストすると楽です。
void setup() {
    // シリアル通信速度の設定
    Serial.begin(9600);
}
void loop() {
    // シリアルモニタにテキストを表示する
    Serial.println("  Hello!!  ");
    // 1秒間待機
    delay(1000);
}
プログラムを打ち終わったら「Ctrl」+「t」を同時に押してください。見やすいように自動的に整形してくれます。
まず、void setup()と呼ばれる関数のなかで、マイコンに必要な設定を行います。
具体的にはパソコンとマイコンの通信設定を行います。通信速度を9600bpsに設定します。
次に,void loop()と呼ばれる関数の中で、パソコンのシリアルモニタに「 Hello!! 」と表示します。
次に、1000ミリ秒、つまり1秒間停止します。
マイコンはvoid loop()という関数を繰り返し実行し続けるようにできているので、シリアルモニタには1秒おきに「 Hello!! 」という文字が表示されることになります。
*関数ってなに?
プログラム言語では処理の固まりに名前を付けますが、それを関数といいます。
上のプログラムでは、void setup () とvoid loop()という2つの関数があり、各々setup、loopという関数名が付いています。
関数名の後の中カッコ始まり「{ 」から中カッコ終わり「 }」までの間に、マイコンに行わせたい処理を書きます。

検証が終わると、コンソールに「コンパイルが完了しました。」と表示されます。
プログラムに間違いがあると、コンソールにエラーの内容が表示されます。
下記の例は、文字の打ち間違いと、日本語をプログラム中に入れてしまった場合の例です。

「ボードへの書き込みが完了しました」と出たら成功です。すでにマイコンは皆さんが書いたプログラムの内容を実行し始めています。次にマイコンの動作を確認してみましょう。


シリアルモニタが現れます。
8行目の「 Hello!! 」を他の文字に変えたり、9行目のdelayの値を変更したりして再度マイコンボードに転送すると、表示やタイミングの変更ができます。

3色の色を表示できるLEDをマイコンに接続していろいろな色を表示させてみましょう。LEDには銀色に伸びる端子が4本ついています。
短い端子はアノードと呼ばれる端子でここに電圧を加えます。いちばん長いのがカソードと呼ばれます。
各色のアノードとカソードとをそれぞれプラスとマイナスにつなぐと赤、緑、青色に点灯します。

#define RLED 2
#define GLED 3
#define BLED 4
void setup() {
    // シリアル通信速度の設定
    Serial.begin(9600);
    // フルカラーLEDの設定
    pinMode(RLED, OUTPUT);
    pinMode(GLED, OUTPUT);
    pinMode(BLED, OUTPUT);
}
void loop() {
    // フルカラーLEDを赤色に点灯させる
    Serial.println("赤 RED");
    digitalWrite(RLED, HIGH);
    delay(1000);
    digitalWrite(RLED, LOW);
    // フルカラーLEDを緑色に点灯させる
    Serial.println("緑 GREEN");
    digitalWrite(GLED, HIGH);
    delay(1000);
    digitalWrite(GLED, LOW);
    // フルカラーLEDを青色に点灯させる
    Serial.println("青 BLUE");
    digitalWrite(BLED, HIGH);
    delay(1000);
    digitalWrite(BLED, LOW);
}
setup関数内のpinModeという命令OUTPUTを用いることで、2番ピンを赤色の出力モード、3番ピンを緑色の出力モード、4番ピンを青色の出力モードとします。
INPUTと打つと入力モードとなり、スイッチを接続した場合に使います。
loop関数内ではdigitalWriteという命令で、光らせたい色の番号のピン(赤色は2番、緑色は3番、青色は4番)の電圧を+5Vとします。指定した番号のピンに関連する色にLEDが点灯します。
次にdelay命令で1秒間待った後、再度digitalWrite命令を使い、光らせた色の番号のピンの電圧を0Vとします。そうするとLEDが消灯します。
これを赤緑青の順番に行い、loop関数の最初に戻る、という動作を繰り返します。
なお、「//」で始まる行はコメント行と言ってプログラムを説明するためのもので、マイコンの動作には関係しません。
一般的にはここは英語で書いてあります。今回は分かりやすいようにあえて日本語で書いています。

温度と湿度を測ることができるセンサー(DHT11)をマイコンに接続し、温度と湿度を読み取りシリアルモニタを使って表示します。

上の図のように配線します。
赤色の電源ラインに5Vを差しセンサーに電源を供給します。
黄色は温度や湿度などの信号を送信するラインで、マイコンの12番ピンに接続します。プログラム中の「#define DHTPIN 12」が12番ピンに信号線が接続されていることを意味しています。
黒色のラインはGNDラインです。マイコンのGNDと書かれたところに接続します。
新規ファイルを作成し下記コードをコピー&ペーストしてマイコンボードに書き込みましょう。温度と湿度がシリアルモニタに表示されます。
#include "Adafruit_Sensor.h"
#include "DHT.h"
#define DHTPIN 12
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
void setup() {
    // シリアル通信速度の設定
    Serial.begin(9600);
    // 温湿度センサーのセットアップ
    dht.begin();
}
void loop() {
    delay(2000);
    
    // データの取得
    float temperature = dht.readTemperature();
    float humidity = dht.readHumidity();
    
    // データの表示
    Serial.println("------------------------------");
    Serial.println("温度" + (String)temperature + "℃");
    Serial.println("湿度" + (String)humidity + "%");
}
「フルカラーLED」と「温度と湿度を表示」を組み合わせて熱中症予防マシンを作成しましょう。
WBGT値とは温度と湿度から算出される熱中症予防指針です。
下表で熱中症への警戒レベルがわかります。同じ温度でも湿度が高い場合は熱中症の危険度が上がります。
| 
   薄ピンク  | 
  
   注意  | 
  
   黄色  | 
  
   警戒  | 
  
   ピンク  | 
  
   厳重警戒  | 
  
   赤  | 
  
   危険  | 
 

WBGT値をLEDで下記の4パターンで表示し、警告を行うプログラムをマイコンに書き込みます。
| 
   注意  | 
  
   緑の点灯  | 
 ||||||
| 
   警戒  | 
  
   黄色の点滅  | 
  
   
  | 
  
   
  | 
  
   
  | 
  
   
  | 
  
   
  | 
  
   
  | 
 
| 
   厳重警戒  | 
  
   赤の点滅  | 
  
   
  | 
  
   
  | 
  
   
  | 
  
   
  | 
  
   
  | 
  
   
  | 
 
| 
   危険  | 
  
   赤と黄色 早い点滅  | 
  
   
  | 
  
   
  | 
  
   
  | 
  
   
  | 
  
   
  | 
  
   
  | 
 

#include "Adafruit_Sensor.h"
#include "DHT.h"
#define DHTPIN 12
#define DHTTYPE DHT22
#define RLED 2
#define GLED 3
#define BLED 4
DHT dht(DHTPIN, DHTTYPE);
// 熱中症警報用マトリクス
// 0:注意、1:警戒、2:厳重警戒、3:危険
int wbgt[20][17] = {
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2},
    {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2},
    {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3},
    {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3},
    {0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3},
    {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3},
    {0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3},
    {0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3},
    {1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}
};
void setup() {
    // シリアル通信速度の設定
    Serial.begin(9600);
    // 温湿度センサーのセットアップ
    dht.begin();
    // フルカラーLEDの設定
    pinMode(RLED, OUTPUT);
    pinMode(GLED, OUTPUT);
    pinMode(BLED, OUTPUT);
    //フルカラーLEDの点灯確認
    LED_Initial(10);
    Serial.println("熱中症警告システム DHT22");
}
void loop() {
    // データの取得
    float tempareture = dht.readTemperature();
    float humidity = dht.readHumidity();
    // データの表示
    Serial.println("--------------------------------");
    Serial.println("温度" + (String)tempareture + "℃");
    Serial.println("湿度" + (String)humidity + "%");
    // 警報レベルの判定
    int necchu;
    if ((tempareture < 21) || (humidity < 20)) {
        necchu = 0;
        if (tempareture < 0)
            necchu = 4;
    } else {
        necchu =  wbgt[int (tempareture - 21)][int ((humidity / 5) - 4)];
    }
    // 警報レベルの表示
    String mess[5];
    mess[0] = "注意";
    mess[1] = "警戒";
    mess[2] = "厳重警戒";
    mess[3] = "危険";
    mess[4] = "零下警報";
    Serial.println("WBGT値 " + mess[necchu]);
    // LEDの点灯制御
    int delay_time = 2000;
    WARNNING(necchu, delay_time);
}
/****************************************************
  警報レベルによってLEDの色と点灯パターンを変化させる。
  0:注意       緑、点灯
  1:警戒      黄色、ゆっくり点滅
  2:厳重警戒   赤、はやめに点滅
  3:危険    赤と黄色が交互に素早く点滅
  4:零下警報  青、点灯
 ****************************************************/
int flag = 1;
void WARNNING(int necchu, int delay_time)
{
    // 0:注意、1:警戒、2:厳重警戒、3:危険 4:零下警報
    digitalWrite(RLED, LOW);
    digitalWrite(GLED, LOW);
    digitalWrite(BLED, LOW);
    // 警報レベルによるLEDの点灯制御
    switch (necchu) {
        case 0: //0:注意 緑
            digitalWrite(GLED, HIGH);
            for (int n = 0; n < (int)(delay_time/100); n++) {
                delay(100);
            }
            break;
        case 1: //1:警戒 黄色
            digitalWrite(RLED, flag);
            digitalWrite(GLED, flag);
            for (int n = 0; n < (int)(delay_time/400); n++) {
                flag = 1 - flag;
                digitalWrite(RLED, flag);
                digitalWrite(GLED, flag);
                delay(400);
            }
            break;
        case 2: //2:厳重警戒 赤
            digitalWrite(RLED, flag);
            for (int n = 0; n < (int)(delay_time/250); n++) {
                flag = 1 - flag;
                digitalWrite(RLED, flag);
                delay(250);
            }
            break;
        case 3: //3:危険 赤黄色の点滅
            digitalWrite(RLED, HIGH);
            for (int n = 0; n < (int)(delay_time/100); n++) {
                flag = 1 - flag;
                digitalWrite(GLED, flag);
                delay(100);
            }
            break;
        case 4: //4:寒すぎる 青点灯
            digitalWrite(BLED, HIGH);
            for (int n = 0; n < (int)(delay_time/100); n++) {
                delay(100);
            }
            break;
        default:
            digitalWrite(RLED, flag);
            digitalWrite(GLED, flag);
            digitalWrite(BLED, flag);
    }
}
/* LED を赤、青、緑、で点灯させる 初回動作 */
void LED_Initial(int num)
{
    int n;
    for (n = 0; n < num; n++) {
        Serial.println(" R");
        digitalWrite(RLED, HIGH);
        delay(100);
        digitalWrite(RLED, LOW);
        Serial.println(" B");
        digitalWrite(BLED, HIGH);
        delay(100);
        digitalWrite(BLED, LOW);
        Serial.println(" G");
        digitalWrite(GLED, HIGH);
        delay(100);
        digitalWrite(GLED, LOW);
    }
}
今回使ったLEDは赤(R)、緑(G)、青(B)の3色に光らせることができるものですが、HSV色空間を使うことで赤緑青以外の3色も表現することができます。以下のプログラムを実行すると、最初にLEDを光らせた時と色がどのように変わるのか見てみよう!
#define RLED 2
#define GLED 3
#define BLED 4
// Buling speed. You may change this value.
#define PWM_Factor 30 
int j, k;
float s = 1.0, v = 1.0, f;
long h;
int i, vi;
int p1, p2, p3;
int r, g, b;
void setup() {
  h = 0;
  pinMode(RLED, OUTPUT);
  pinMode(GLED, OUTPUT);
  pinMode(BLED, OUTPUT);
}
void loop() {
  //chage to HSV color model
  i = (int)(h / 60.0);
  f = h / 60.0f - i;
  p1 = (int)(v * (1 - s) * 255.0);
  p2 = (int)(v * (1 - s * f) * 255.0);
  p3 = (int)(v * (1 - s * (1 - f)) * 255.0);
  vi = (int)(v * 255);
  //Red -> Yellow
  if (i == 0) {
    r = vi ;
    g = p3 ;
    b = p1;
  }
  //Yellow -> Green
  if (i == 1) {
    r = p2 ;
    g = vi ;
    b = p1;
  }
  //Green -> Light blue
  if (i == 2) {
    r = p1 ;
    g = vi;
    b = p3;
  }
  //Light blue -> Blue
  if (i == 3) {
    r = p1 ;
    g = p2 ;
    b = vi;
  }
  //Blue -> Pink
  if (i == 4) {
    r = p3 ;
    g = p1 ;
    b = vi;
  }
  //Pink -> Red
  if (i == 5) {
    r = vi ;
    g = p1 ;
    b = p2;
  }
  //PWM loop
  for (j = 0; j < PWM_Factor ; j++) {
    digitalWrite(RLED, LOW);
    digitalWrite(GLED, LOW);
    digitalWrite(BLED, LOW);
    for (k = 255; k > 0; k--) {
      if (k == r)
        digitalWrite(RLED, HIGH);
      if (k == g)
        digitalWrite(GLED, HIGH);
      if (k == b)
        digitalWrite(BLED, HIGH);
    }
  }
  h = h + 1;
  if (h >= 360) h = 0;
}