• モノづくりとアイデアで暮らしを豊かに。『STAGEA』

    ここでは、サンプルプログラムの動作原理について順を追って説明します。
    プログラミング初心者の方にとって、興味を持っていただくためのきっかけとなる内容になることはもちろんですが、ベテランの方であっても、専用設計されたハードウェアで動作確認済のサンプルコードがあることは非常に心強いはずです。
    判断に迷ったら参考にしてください。

    ■ベテランプログラマーの方へ
    本書は、右も左も分からない方でも、少し頑張れば感覚的に理解できることを目標に作成されています。
    見難かったり、回りくどいところもあるかもしれませんが、ご容赦いただければ幸いです。
    本書は、「プロトコル・ガイド」を補完するリファレンスとしてお役立てください。


    準備

    このサンプルプログラムは、Arduinoソフトウェアを使用します。
    こちらのページを参考にインストールし、こちらのページを参考にセットアップを行ってください。
    Arduinoでのプログラミングや、C言語を使用したハードウェア開発の経験がない方は、基本的な知識の習得が必要です。
    電気ポットの制御を例に、まとめたものを用意しましたので、こちらを参考に勉強をしてから次に進んでください。


    プログラム説明

    主MCUからのデジタルデータ受け取り(1)

    プロトコル・ガイドのページを振り返ってください。
    PCでご覧の方は別ウィンドウまたは新しいタブで開きます。
    【動作フロー(通常モード)】のコーナーで、「(1)1秒に1回、スイッチ類の状態を含む全てのセンサーの測定値を主マイコンから副マイコンに対して送信されます。」と書かれています。

    送信されてきているわけですから、何らかの方法で受信しなければなりません。

    主MCUと副MCUは、UARTというデジタル通信規格で接続されています。(詳しくは「UART」で検索して調べてください。)
    そして、データはあらかじめ決められたルールに従って送受信しています。
    そのルールが、プロトコル・ガイドページの末尾に書かれた「電文(パケット)のフォーマット」になります。
    電文のフォーマットを見ると、一度に送られてくるデータが0から始まり31で終わる計32バイトあることがわかると思います。
    それから、聞き慣れない言葉で「上位(下位)バイト」というのがあります。
    一つの測定データを上位と下位に分けなければいけない理由を説明します。
    主MCU-副MCU間の通信で使用するUARTというデジタル通信は、1秒間に数千〜数百万回、電気が流れたり止まったりを繰り返して情報伝達をしています。
    のろしやモールス信号をイメージしていただけるとわかりやすいと思います。
    この「流れたり止まったり」は、10回分を1セットとして扱っています。
    いきなりデータだけ送れば良いわけではなく、「これからデータを送るよ」「もう終わりです」という合図で各々1回分を使うため、残り8回分(8ビット)が、通信で使用できる回数となります。
    ON/OFFという2値の内容を8回送ることができる場合、送ることができる電信のパターンは「2の8乗通り」となるため、上限は255となります。
    一方、照度や二酸化炭素濃度などは最大で4000前後の値を扱いますので、上限を振り切ってしまいます。
    そのような時、1つの数を2バイト(先程、UARTは「8ビット=1バイト」と説明しました。2バイトの場合それが2つになります。)で表現します。
    この時、8ビット×2バイト=16ビットとなります。
    16ビットで表現できる数の上限は2の16乗なので、上限が一気に6万5535まで拡張されます。

    こうした理由で、上位バイトと下位バイトで分けています。

    電文フォーマットが理解できたところで、早速受信処理を行います。

    【動作フロー(通常モード)】の説明では、1秒に1回送られてくるデータを受信するところから全てが始まります。

    そのため、受信待機をしなければなりません。
    受信待機をする時は、以下のプログラムを書きます。

    if (Serial.available() >= 1) {
    }

    「Serial.available()」は、受信バッファに入っているデータの数を読み出す処理です。
    受信バッファとは、届いたデータを受け取るポストのことです。
    Serial.available()を実行すると、ポストを開いて届いたお手紙の数を数えます。
    このプログラムでは、「1通以上のお手紙が届いていること」をifの成立条件としています。

    主MCUからのデータ受け取りは、ずっと繰り返されるものなので、
    void loop()の中に書きます。

    void loop(){
        if(Serial.available()>=1){
        }
    }

    通信(UART)を有効にするため、void setup()内に以下のコードを書きます。

    void setup(){
        Serial.begin(9600);
    }
    void loop(){
        if(Serial.available()>=1){
        }
    }

    Serial.begin(9600)は、「デジタル通信(UART通信)を起動して待機しなさい」という命令です。
    この命令を最初に実行しないと、主MCUからデータが送られてきてもデータとして扱われず、何の意味も持たない高速な電気の点滅になってしまいます。
    括弧内の9600は、ON/OFFの速度指定です。

    「1秒間に9600ビットの速度でデータの送受信をしてください」という意味です。

    文書等で人に伝える時は、9600bps(9600ビット毎秒)と書き、言葉で伝える時は9600ビット・パー・セカンドと発音します。
    1秒間に9600回と聞けば、非常に高速でおどろくかもしれませんが、パソコンのUSBは1秒間に200億ビットで通信するため、決して高速ではありません。
    H10のような情報量の少ない機器や、おもちゃでも、マイコン間の通信は38400bpsか115200bpsが主流です。
    速度を上げれば転送にかかる時間を短縮できる一方で、MCUは常に高速動作をさせて待機させなければなりません。
    大規模災害で広域停電が発生しても、乾電池で1週間動くように設計しているため、ここまで速度を下げています。

    次に、if(Serial.available()>=1)の条件に合致する状態になった(つまり、主MCUから1バイトのデータが送られてきた)時の処理を記述します。
    受信バッファに届けられたデータの取り出しは、以下の命令で行います。

    Serial.read();

    Serial.read()を実行しても、受け取る準備がなければ話を右から左に流すのと同じことになるため、データとして扱えるようにメモリに記録します。

    RCV_VALUE00 = Serial.read();

    これで、RCV_VALUE00と命名されたメモリ領域にSerial.read();で受信したデータが入ります。

    Serial.read()で読み出したデータを保存するメモリ領域「RCV_VALUE00」も同じように領域確保する必要があります。
    受信データの数は32バイトなので、32個の領域を確保します。

    int RCV_BUFF00,RCV_BUFF01,RCV_BUFF02,RCV_BUFF03,RCV_BUFF04,RCV_BUFF05,RCV_BUFF06,RCV_BUFF07,RCV_BUFF08,RCV_BUFF09,
    RCV_BUFF10,RCV_BUFF11,RCV_BUFF12,RCV_BUFF13,RCV_BUFF14,RCV_BUFF15,RCV_BUFF16,RCV_BUFF17,RCV_BUFF18,RCV_BUFF19,
    RCV_BUFF20,RCV_BUFF21,RCV_BUFF22,RCV_BUFF23,RCV_BUFF24,RCV_BUFF25,RCV_BUFF26,RCV_BUFF27,RCV_BUFF28,RCV_BUFF29,
    RCV_BUFF30,RCV_BUFF31=0;
    void setup(){
        Serial.begin(9600);
    }
    void loop(){
        if(Serial.available()>=1){
        }
    }

    これで、1バイト分のデータが受信できるようになりました。
    でも、受信したいデータは32バイトです。
    データフォーマットに従った形(32バイト)でデータ受信をするには、以下のように記述します。

    if (Serial.available() >= 1) {
        RCV_BUFF00 = RCV_BUFF01;
        RCV_BUFF01 = RCV_BUFF02;
        RCV_BUFF02 = RCV_BUFF03;
        RCV_BUFF03 = RCV_BUFF04;
        RCV_BUFF04 = RCV_BUFF05;
        RCV_BUFF05 = RCV_BUFF06;
        RCV_BUFF06 = RCV_BUFF07;
        RCV_BUFF07 = RCV_BUFF08;
        RCV_BUFF08 = RCV_BUFF09;
        RCV_BUFF09 = RCV_BUFF10;
        RCV_BUFF10 = RCV_BUFF11;
        RCV_BUFF11 = RCV_BUFF12;
        RCV_BUFF12 = RCV_BUFF13;
        RCV_BUFF13 = RCV_BUFF14;
        RCV_BUFF14 = RCV_BUFF15;
        RCV_BUFF15 = RCV_BUFF16;
        RCV_BUFF16 = RCV_BUFF17;
        RCV_BUFF17 = RCV_BUFF18;
        RCV_BUFF18 = RCV_BUFF19;
        RCV_BUFF19 = RCV_BUFF20;
        RCV_BUFF20 = RCV_BUFF21;
        RCV_BUFF21 = RCV_BUFF22;
        RCV_BUFF22 = RCV_BUFF23;
        RCV_BUFF23 = RCV_BUFF24;
        RCV_BUFF24 = RCV_BUFF25;
        RCV_BUFF25 = RCV_BUFF26;
        RCV_BUFF26 = RCV_BUFF27;
        RCV_BUFF27 = RCV_BUFF28;
        RCV_BUFF28 = RCV_BUFF29;
        RCV_BUFF29 = RCV_BUFF30;
        RCV_BUFF30 = RCV_BUFF31;
        RCV_BUFF31 = Serial.read( );
    }

    このように書くことで、1バイト受信するたびに最初に受信したデータが順に若い番号に押し出されていくため、32バイト分のデータが全て揃います。

    次に、データを取り込む処理を作っていきます。
    32バイト受信できるようになったとはいえ、データの区切りがどこかわからないし、データ化けを起こしても検知することができず予期しない動作の原因となってしまいます。
    そこで、データ内容が正しいか判断しながら五月雨でデータ受信し、正しいデータが揃ったタイミングで別のメモリーにコピーを取る処理を作っていきます。

        if (Serial.available() >= 1) {
            RCV_BUFF00 = RCV_BUFF01;
            RCV_BUFF01 = RCV_BUFF02;
            RCV_BUFF02 = RCV_BUFF03;
            RCV_BUFF03 = RCV_BUFF04;
            RCV_BUFF04 = RCV_BUFF05;
            RCV_BUFF05 = RCV_BUFF06;
            RCV_BUFF06 = RCV_BUFF07;
            RCV_BUFF07 = RCV_BUFF08;
            RCV_BUFF08 = RCV_BUFF09;
            RCV_BUFF09 = RCV_BUFF10;
            RCV_BUFF10 = RCV_BUFF11;
            RCV_BUFF11 = RCV_BUFF12;
            RCV_BUFF12 = RCV_BUFF13;
            RCV_BUFF13 = RCV_BUFF14;
            RCV_BUFF14 = RCV_BUFF15;
            RCV_BUFF15 = RCV_BUFF16;
            RCV_BUFF16 = RCV_BUFF17;
            RCV_BUFF17 = RCV_BUFF18;
            RCV_BUFF18 = RCV_BUFF19;
            RCV_BUFF19 = RCV_BUFF20;
            RCV_BUFF20 = RCV_BUFF21;
            RCV_BUFF21 = RCV_BUFF22;
            RCV_BUFF22 = RCV_BUFF23;
            RCV_BUFF23 = RCV_BUFF24;
            RCV_BUFF24 = RCV_BUFF25;
            RCV_BUFF25 = RCV_BUFF26;
            RCV_BUFF26 = RCV_BUFF27;
            RCV_BUFF27 = RCV_BUFF28;
            RCV_BUFF28 = RCV_BUFF29;
            RCV_BUFF29 = RCV_BUFF30;
            RCV_BUFF30 = RCV_BUFF31;
            RCV_BUFF31 = Serial.read( );
            RX_CHKDIGIT=RCV_BUFF00+RCV_BUFF01+RCV_BUFF02+RCV_BUFF03+RCV_BUFF04+RCV_BUFF05+RCV_BUFF06+RCV_BUFF07+RCV_BUFF08+RCV_BUFF09+RCV_BUFF10+RCV_BUFF11+RCV_BUFF12+RCV_BUFF13+RCV_BUFF14+RCV_BUFF15+RCV_BUFF16+RCV_BUFF17+RCV_BUFF18+RCV_BUFF19+RCV_BUFF20+RCV_BUFF21+RCV_BUFF22+RCV_BUFF23+RCV_BUFF24+RCV_BUFF25+RCV_BUFF26+RCV_BUFF27+RCV_BUFF28+RCV_BUFF29+RCV_BUFF30;
            RX_CHKDIGIT=RX_CHKDIGIT%256;
            if((RCV_BUFF00==255)&&(RCV_BUFF01==255)&&((RCV_BUFF30==250)||(RCV_BUFF30==255))&&(RCV_BUFF31==RX_CHKDIGIT)){
                RCV_VALUE00 = RCV_BUFF00;
                RCV_VALUE01 = RCV_BUFF01;
                RCV_VALUE02 = RCV_BUFF02;
                RCV_VALUE03 = RCV_BUFF03;
                RCV_VALUE04 = RCV_BUFF04;
                RCV_VALUE05 = RCV_BUFF05;
                RCV_VALUE06 = RCV_BUFF06;
                RCV_VALUE07 = RCV_BUFF07;
                RCV_VALUE08 = RCV_BUFF08;
                RCV_VALUE09 = RCV_BUFF09;
                RCV_VALUE10 = RCV_BUFF10;
                RCV_VALUE11 = RCV_BUFF11;
                RCV_VALUE12 = RCV_BUFF12;
                RCV_VALUE13 = RCV_BUFF13;
                RCV_VALUE14 = RCV_BUFF14;
                RCV_VALUE15 = RCV_BUFF15;
                RCV_VALUE16 = RCV_BUFF16;
                RCV_VALUE17 = RCV_BUFF17;
                RCV_VALUE18 = RCV_BUFF18;
                RCV_VALUE19 = RCV_BUFF19;
                RCV_VALUE20 = RCV_BUFF20;
                RCV_VALUE21 = RCV_BUFF21;
                RCV_VALUE22 = RCV_BUFF22;
                RCV_VALUE23 = RCV_BUFF23;
                RCV_VALUE24 = RCV_BUFF24;
                RCV_VALUE25 = RCV_BUFF25;
                RCV_VALUE26 = RCV_BUFF26;
                RCV_VALUE27 = RCV_BUFF27;
                RCV_VALUE28 = RCV_BUFF28;
                RCV_VALUE29 = RCV_BUFF29;
                RCV_VALUE30 = RCV_BUFF30;
                RCV_VALUE31 = RCV_BUFF31;
                RxFlg=1;
            }else{
            }
        }else{
        }

    Serial.read()命令で受信が終わったら、

    if((RCV_BUFF00==255)&&(RCV_BUFF01==255)&&((RCV_BUFF30==250)||(RCV_BUFF30==255))&&(RCV_BUFF31==RX_CHKDIGIT)){

    この行で、受信データがデータフォーマット通りであるかどうかを判断します。
    「 || 」や「 && 」のような見慣れない記述があると思います。
    「A || B」は、AまたはBいずれか一方の条件が満たされるならば、を意味します。
    「A && B」は、AとBいずれの条件も満たされることが条件であることを意味します。
    つまり、
    ・0バイト目、1バイト目が各々255であること。
    ・30バイト目が250または255であること。
    それが、この行に記述されています。
    こちらのページの後半に記載しているデータフォーマットをご覧ください。正しいデータであることを満たす条件であることが確認できます。
    最後にある以下の記述は、さらに厳しくデータ内容を確認するために必要な条件となります。

    &&(RCV_BUFF31==RX_CHKDIGIT)

    誤ったデータであるにもかかわらず、0バイト目と1バイト目と30バイト目が偶然、255になったことで、正しいデータとして扱われることも考えられます。
    これを防ぐため、0〜30バイト目までの値を全て合計した値を256で割った余りの数が一致するかを3つ目の条件に加えています。
    これを、チェックデジットといいます。
    規則的な数ではないため、偶然が重なる可能性をさらに抑えることができます。
    チェックデジットの計算は、RX_CHKDIGITとして、以下の記述で計算されています。

    RX_CHKDIGIT=RCV_BUFF00+RCV_BUFF01+RCV_BUFF02+RCV_BUFF03+RCV_BUFF04+RCV_BUFF05+RCV_BUFF06+RCV_BUFF07+RCV_BUFF08+RCV_BUFF09+RCV_BUFF10+RCV_BUFF11+RCV_BUFF12+RCV_BUFF13+RCV_BUFF14+RCV_BUFF15+RCV_BUFF16+RCV_BUFF17+RCV_BUFF18+RCV_BUFF19+RCV_BUFF20+RCV_BUFF21+RCV_BUFF22+RCV_BUFF23+RCV_BUFF24+RCV_BUFF25+RCV_BUFF26+RCV_BUFF27+RCV_BUFF28+RCV_BUFF29+RCV_BUFF30;
    RX_CHKDIGIT=RX_CHKDIGIT%256;

    RX_CHKDIGITと256の間にある%は、割り算の余りを意味します。

    次に、正しいフォーマットで届いていることを検知したら、直ちに別の領域にデータをコピーしなければなりません。
    これをしないと、せっかく正しくデータが届いても、データが流れていってしまいます。
    そのため、

    if((RCV_BUFF00==255)&&(RCV_BUFF01==255)&&((RCV_BUFF30==250)||(RCV_BUFF30==255))&&(RCV_BUFF31==RX_CHKDIGIT)){

    この記述のカッコの中で、全ての受信バッファの内容をメモリにコピーする処理が書かれています。
    こうすることで、次にデータが流れてきても安全にバッファ内容を上書きすることができます。

    RCV_VALUE00 = RCV_BUFF00;
    RCV_VALUE01 = RCV_BUFF01;
    RCV_VALUE02 = RCV_BUFF02;
    RCV_VALUE03 = RCV_BUFF03;
    RCV_VALUE04 = RCV_BUFF04;
    RCV_VALUE05 = RCV_BUFF05;
    RCV_VALUE06 = RCV_BUFF06;
    RCV_VALUE07 = RCV_BUFF07;
    RCV_VALUE08 = RCV_BUFF08;
    RCV_VALUE09 = RCV_BUFF09;
    RCV_VALUE10 = RCV_BUFF10;
    RCV_VALUE11 = RCV_BUFF11;
    RCV_VALUE12 = RCV_BUFF12;
    RCV_VALUE13 = RCV_BUFF13;
    RCV_VALUE14 = RCV_BUFF14;
    RCV_VALUE15 = RCV_BUFF15;
    RCV_VALUE16 = RCV_BUFF16;
    RCV_VALUE17 = RCV_BUFF17;
    RCV_VALUE18 = RCV_BUFF18;
    RCV_VALUE19 = RCV_BUFF19;
    RCV_VALUE20 = RCV_BUFF20;
    RCV_VALUE21 = RCV_BUFF21;
    RCV_VALUE22 = RCV_BUFF22;
    RCV_VALUE23 = RCV_BUFF23;
    RCV_VALUE24 = RCV_BUFF24;
    RCV_VALUE25 = RCV_BUFF25;
    RCV_VALUE26 = RCV_BUFF26;
    RCV_VALUE27 = RCV_BUFF27;
    RCV_VALUE28 = RCV_BUFF28;
    RCV_VALUE29 = RCV_BUFF29;
    RCV_VALUE30 = RCV_BUFF30;
    RCV_VALUE31 = RCV_BUFF31;

    この処理の最後に、以下の1行を実行します。

    RxFlg=1;

    これは、データが受信できたことを他の処理ループから認識してもらうために必要なフラグです。
    このフラグが1になったことを検出したら、リレーのON/OFF処理が動くようにプログラミングします。

    これまでに作ったプログラムに、受信処理を追加すると以下のようになります。
    RCV_BUFFxxに加えて、RCV_VALUExxも領域を確保します。

    int RCV_BUFF00,RCV_BUFF01,RCV_BUFF02,RCV_BUFF03,RCV_BUFF04,RCV_BUFF05,RCV_BUFF06,RCV_BUFF07,RCV_BUFF08,RCV_BUFF09,
    RCV_BUFF10,RCV_BUFF11,RCV_BUFF12,RCV_BUFF13,RCV_BUFF14,RCV_BUFF15,RCV_BUFF16,RCV_BUFF17,RCV_BUFF18,RCV_BUFF19,
    RCV_BUFF20,RCV_BUFF21,RCV_BUFF22,RCV_BUFF23,RCV_BUFF24,RCV_BUFF25,RCV_BUFF26,RCV_BUFF27,RCV_BUFF28,RCV_BUFF29,
    RCV_BUFF30,RCV_BUFF31=0;
    int RCV_VALUE00,RCV_VALUE01,RCV_VALUE02,RCV_VALUE03,RCV_VALUE04,RCV_VALUE05,RCV_VALUE06,RCV_VALUE07,RCV_VALUE08,RCV_VALUE09,
    RCV_VALUE10,RCV_VALUE11,RCV_VALUE12,RCV_VALUE13,RCV_VALUE14,RCV_VALUE15,RCV_VALUE16,RCV_VALUE17,RCV_VALUE18,RCV_VALUE19,
    RCV_VALUE20,RCV_VALUE21,RCV_VALUE22,RCV_VALUE23,RCV_VALUE24,RCV_VALUE25,RCV_VALUE26,RCV_VALUE27,RCV_VALUE28,RCV_VALUE29,
    RCV_VALUE30,RCV_VALUE31=0;
    int RxFlg=0;
    int RX_CHKDIGIT=0;
    void setup(){
        Serial.begin(9600);
    }
    void loop(){
        if (Serial.available() >= 1) {
            RCV_BUFF00 = RCV_BUFF01;
            RCV_BUFF01 = RCV_BUFF02;
            RCV_BUFF02 = RCV_BUFF03;
            RCV_BUFF03 = RCV_BUFF04;
            RCV_BUFF04 = RCV_BUFF05;
            RCV_BUFF05 = RCV_BUFF06;
            RCV_BUFF06 = RCV_BUFF07;
            RCV_BUFF07 = RCV_BUFF08;
            RCV_BUFF08 = RCV_BUFF09;
            RCV_BUFF09 = RCV_BUFF10;
            RCV_BUFF10 = RCV_BUFF11;
            RCV_BUFF11 = RCV_BUFF12;
            RCV_BUFF12 = RCV_BUFF13;
            RCV_BUFF13 = RCV_BUFF14;
            RCV_BUFF14 = RCV_BUFF15;
            RCV_BUFF15 = RCV_BUFF16;
            RCV_BUFF16 = RCV_BUFF17;
            RCV_BUFF17 = RCV_BUFF18;
            RCV_BUFF18 = RCV_BUFF19;
            RCV_BUFF19 = RCV_BUFF20;
            RCV_BUFF20 = RCV_BUFF21;
            RCV_BUFF21 = RCV_BUFF22;
            RCV_BUFF22 = RCV_BUFF23;
            RCV_BUFF23 = RCV_BUFF24;
            RCV_BUFF24 = RCV_BUFF25;
            RCV_BUFF25 = RCV_BUFF26;
            RCV_BUFF26 = RCV_BUFF27;
            RCV_BUFF27 = RCV_BUFF28;
            RCV_BUFF28 = RCV_BUFF29;
            RCV_BUFF29 = RCV_BUFF30;
            RCV_BUFF30 = RCV_BUFF31;
            RCV_BUFF31 = Serial.read( );
            RX_CHKDIGIT=RCV_BUFF00+RCV_BUFF01+RCV_BUFF02+RCV_BUFF03+RCV_BUFF04+RCV_BUFF05+RCV_BUFF06+RCV_BUFF07+RCV_BUFF08+RCV_BUFF09+RCV_BUFF10+RCV_BUFF11+RCV_BUFF12+RCV_BUFF13+RCV_BUFF14+RCV_BUFF15+RCV_BUFF16+RCV_BUFF17+RCV_BUFF18+RCV_BUFF19+RCV_BUFF20+RCV_BUFF21+RCV_BUFF22+RCV_BUFF23+RCV_BUFF24+RCV_BUFF25+RCV_BUFF26+RCV_BUFF27+RCV_BUFF28+RCV_BUFF29+RCV_BUFF30;
            RX_CHKDIGIT=RX_CHKDIGIT%256;
            if((RCV_BUFF00==255)&&(RCV_BUFF01==255)&&((RCV_BUFF30==250)||(RCV_BUFF30==255))&&(RCV_BUFF31==RX_CHKDIGIT)){
                RCV_VALUE00 = RCV_BUFF00;
                RCV_VALUE01 = RCV_BUFF01;
                RCV_VALUE02 = RCV_BUFF02;
                RCV_VALUE03 = RCV_BUFF03;
                RCV_VALUE04 = RCV_BUFF04;
                RCV_VALUE05 = RCV_BUFF05;
                RCV_VALUE06 = RCV_BUFF06;
                RCV_VALUE07 = RCV_BUFF07;
                RCV_VALUE08 = RCV_BUFF08;
                RCV_VALUE09 = RCV_BUFF09;
                RCV_VALUE10 = RCV_BUFF10;
                RCV_VALUE11 = RCV_BUFF11;
                RCV_VALUE12 = RCV_BUFF12;
                RCV_VALUE13 = RCV_BUFF13;
                RCV_VALUE14 = RCV_BUFF14;
                RCV_VALUE15 = RCV_BUFF15;
                RCV_VALUE16 = RCV_BUFF16;
                RCV_VALUE17 = RCV_BUFF17;
                RCV_VALUE18 = RCV_BUFF18;
                RCV_VALUE19 = RCV_BUFF19;
                RCV_VALUE20 = RCV_BUFF20;
                RCV_VALUE21 = RCV_BUFF21;
                RCV_VALUE22 = RCV_BUFF22;
                RCV_VALUE23 = RCV_BUFF23;
                RCV_VALUE24 = RCV_BUFF24;
                RCV_VALUE25 = RCV_BUFF25;
                RCV_VALUE26 = RCV_BUFF26;
                RCV_VALUE27 = RCV_BUFF27;
                RCV_VALUE28 = RCV_BUFF28;
                RCV_VALUE29 = RCV_BUFF29;
                RCV_VALUE30 = RCV_BUFF30;
                RCV_VALUE31 = RCV_BUFF31;
                RxFlg=1;
            }else{
            }
        }else{
        }
    }

    これで、【電文(パケット)のフォーマット】に従った形での受信処理が完了しました。

    ページが見づらくなるため、以降、RCV_VALUEの初期化の行とSerial.read()からの取り出しの行を省略します。
    リレーを制御する

    ここでは、32個のデータが揃った時の処理を説明します。
    正しいデータフォーマットで受信完了した時、受信処理の中で以下の通り受信完了フラグを立てる処理をしました。

    RxFlg=1;

    RxFlgが1になった時に行う処理を追加するには、以下のプログラムを追加します。

    if(RxFlg==1){
    }

    この閉じ括弧 { } の中に、受信処理を記述していきます。

    「もしRxFlgが1ならば」の表現で、「if(RxFlg==1)」と書いています。
    イコールが2つ連続で書かれていますが、打ち間違いではありません。
    イコールを1つ(RxFlg=1)にした場合、RxFlgに1を入れる処理となります。
    イコールの数が1個の場合は代入、2つの場合は比較と決められています。
    詳しく知りたければ「C言語 比較演算子」で検索してみてください。
    受信データを実際の測定値に変換する

    32個の正しいデータが受信できたことを知らせるフラグが立ったら(RxFlg==1になったら)、受信したデータをプログラムで利用できる形に加工する必要があります。
    取得したデータは、そのままでは測定値として使用できません。
    副MCUが受け取る値は、温度センサーの値のように実際の測定値に近いものも中にはあるが、多くはセンサーが出力した電圧に比例した値になります。
    ここで、以降の理解をスムーズにするため脇道に逸れてADコンバータについて説明しようと思います。

    ADコンバータという言葉を聞いたことありますか?
    ミュージックプレイヤーなどの音響機器のカタログで見たことがあると思います。
    AはAnalogで DはDigitalを意味します。
    マイクは、声や楽器などが発する空気の振動を小刻みに変化する電圧変動の波に変換する装置です。
    ただ、SDカードのようなデジタル記憶装置に記録する場合、電圧変動の波の状態では記録ができないため、コンピュータで扱えるよう、数値(デジタルデータ)に変換しなくてはなりません。
    このような電圧変動をデータに変換する機器をADコンバータといいます。
    逆に、デジタルデータをアナログの電気信号(小刻みな電圧変動の波)に変換する機械をDAコンバータといいます。
    CDやSDカードの中には、音楽がデジタルデータで記録されています。
    一方、スピーカーやイヤホンの中にあるボイスコイルは、小刻みな電圧変化が加わることで磁石と反発する強さが小刻みに変わり、それが空気を震わせることで音が出るので、UART(デジタル通信)で256とか65535などのような数値データをボイスコイルに送っても音は出ません。(鳴っても雑音しか聴こえません。)
    そこで、デジタル値をアナログの電気信号に変えるため、DAコンバータが使用されます。
    ADコンバータは、MCUの電源電圧を基準に、どれくらいの割合の電圧かという値で出力しています。
    例えば、H10は主MCUの電源電圧を4096分割した比率で出力しています。
    なぜ4096かというと、H10の主MCUに搭載されているADコンバータが12ビット(2の12乗)のADコンバータだからです。
    0ボルトからMCUの電源電圧(約3.3ボルト)までの間を10個のON/OFFパターンを並べて表現できる値(2の12乗)でデジタル値にしています。
    H10に内蔵されている多くのセンサーは、環境の変化を電圧の変化で出力し、それを主MCUに入力させています。

    最大で4095までの測定値が送られてくるのに、UARTのデジタルデータは8ビットまでしか対応しないので、上位ビットと下位ビットに分けて送られてきます。
    この2つに分かれたデータを1つの測定値に戻す必要があります。
    上位バイトと下位バイトを合体させて元の数に戻す時は、上位バイトの値に256を掛けたものに下位バイトを足します。

    元々の数=(上位バイト×256)+下位バイト

    2進数は、0と1しかなく、2以上の数字が存在しないため、2の二乗ごとに桁が繰り上がります。
    上位バイトの最も小さい桁(一桁目)は、元々の数の9桁目にあたります。
    元々の数の1桁目は0か1かを表現するため9から1を引いた8・・・・2の8乗=256となるため、上位バイトの1桁目に256を掛けます。

    わかりやすいように10進数で説明します。
    10進数は、0に始まり9までが1桁目で次は桁が繰り上がる、みなさんが普段使っている数です。
    8桁表示の電卓のように8桁しか表現できない場合、表現できる数の上限は9999万9999までとなります。
    その次はどう頑張っても表現できません。
    電卓をもう一個並べて9桁目を表現した場合、その電卓の1桁目は億になります。
    こうして電卓を2台並べて数を表現する場合、
    表現したい数=(後から追加した電卓の値×1億)+最初に用意した電卓の値
    となると思います。
    これを2進数に置き換えて理解してください。

    このような方法で、2つに分かれたデータをAD変換値に戻していきます。
    AD変換値とはセンサーが出力した電圧値(に比例した4096を上限とした数)です。
    では、【プログラミング・リファレンス】の【電文(パケット)のフォーマット】のページを開きながら、2つに分かれているものを元々の数に戻していきます。

    SMELL=(RCV_VALUE02*256)+RCV_VALUE03;
    TEMP=(RCV_VALUE04*256)+RCV_VALUE05;
    CDS=(RCV_VALUE07*256)+RCV_VALUE08;
    NOISE=(RCV_VALUE09*256)+RCV_VALUE10;
    CO=(RCV_VALUE12*256)+RCV_VALUE13;
    CO2=(RCV_VALUE14*256)+RCV_VALUE15;

    温度センサーの値は、追加の処理が必要です。
    H10は、主MCUから温度データを送る時、10を掛けて送信しています。
    例えば現在25度ならば、250が送られてきます。
    H10は、小数点以下1位までの精度で温度測定ができます。
    しかし、小数点の取り扱いは少々奥が深く、初心者の方には難しいため、10を掛けています。
    そのため、温度(TEMP)は以下の通り修正します。

    TEMP=((RCV_VALUE04*256)+RCV_VALUE05)/10;

    人感センサは、反応があるたび1ずつ加算されていき、1分ごとにリセットされます。
    主MCUからデータが送られてくるのは1秒に1回のため、60以上になることはないため、1バイトで扱っています。
    湿度は小数点以下の測定をしていない(上限が100)ので、1バイトで納まります。
    DCINは、DCジャックから電源供給があれば1を、停電していたら0が送られてきます。
    押しボタンスイッチが押されていればD_IN1、D_IN2に1が送られてきます。
    全てのデータをまとめると以下の通りとなります。

    SMELL=(RCV_VALUE02*256)+RCV_VALUE03;
    TEMP=((RCV_VALUE04*256)+RCV_VALUE05)/10;
    HUMI=RCV_VALUE06;
    CDS=(RCV_VALUE07*256)+RCV_VALUE08;
    NOISE=(RCV_VALUE09*256)+RCV_VALUE10;
    HUMAN=RCV_VALUE11;
    CO=(RCV_VALUE12*256)+RCV_VALUE13;
    CO2=(RCV_VALUE14*256)+RCV_VALUE15;
    DCIN=RCV_VALUE16;
    D_IN1=RCV_VALUE17;
    D_IN2=RCV_VALUE18;
    CONTACT1=RCV_VALUE25;
    CONTACT2=RCV_VALUE26;

    ここで初めて出てきたSMELLやTEMPなどの値を保存する領域の確保も忘れずに行います。

    int SMELL,TEMP,CDS,NOISE,CO,CO2,HUMI,HUMAN,DCIN,D_IN1,D_IN2=0;

    ここではページが長くなるため全部つなげて書いていますが、以下のようにバラバラで書いても大丈夫です。
    intはいくつ出てきても大丈夫なので、目的ごとにまとめて初期化すると後から見やすくなります。

    int SMELL=0;
    int TEMP=0;
    int NOISE=0;   (以降、略)

    ここまでをまとめると以下の通りとなります。

    int RCV_BUFF00,RCV_BUFF01,RCV_BUFF02,RCV_BUFF03,RCV_BUFF04,RCV_BUFF05,RCV_BUFF06,RCV_BUFF07,RCV_BUFF08,RCV_BUFF09,
    RCV_BUFF10,RCV_BUFF11,RCV_BUFF12,RCV_BUFF13,RCV_BUFF14,RCV_BUFF15,RCV_BUFF16,RCV_BUFF17,RCV_BUFF18,RCV_BUFF19,
    RCV_BUFF20,RCV_BUFF21,RCV_BUFF22,RCV_BUFF23,RCV_BUFF24,RCV_BUFF25,RCV_BUFF26,RCV_BUFF27,RCV_BUFF28,RCV_BUFF29,
    RCV_BUFF30,RCV_BUFF31=0;
    int RCV_VALUE00,RCV_VALUE01,RCV_VALUE02,RCV_VALUE03,RCV_VALUE04,RCV_VALUE05,RCV_VALUE06,RCV_VALUE07,RCV_VALUE08,RCV_VALUE09,
    RCV_VALUE10,RCV_VALUE11,RCV_VALUE12,RCV_VALUE13,RCV_VALUE14,RCV_VALUE15,RCV_VALUE16,RCV_VALUE17,RCV_VALUE18,RCV_VALUE19,
    RCV_VALUE20,RCV_VALUE21,RCV_VALUE22,RCV_VALUE23,RCV_VALUE24,RCV_VALUE25,RCV_VALUE26,RCV_VALUE27,RCV_VALUE28,RCV_VALUE29,
    RCV_VALUE30,RCV_VALUE31=0;
    int SMELL,TEMP,CDS,NOISE,CO,CO2,HUMI,HUMAN,DCIN,D_IN1,D_IN2=0;
    int RxFlg=0;
    int RX_CHKDIGIT=0;
    void setup(){
        Serial.begin(9600);
    }
    void loop(){
        if (Serial.available() >= 1) {
            RCV_BUFF00 = RCV_BUFF01;
            RCV_BUFF01 = RCV_BUFF02;
            RCV_BUFF02 = RCV_BUFF03;
            RCV_BUFF03 = RCV_BUFF04;
            RCV_BUFF04 = RCV_BUFF05;
            RCV_BUFF05 = RCV_BUFF06;
            RCV_BUFF06 = RCV_BUFF07;
            RCV_BUFF07 = RCV_BUFF08;
            RCV_BUFF08 = RCV_BUFF09;
            RCV_BUFF09 = RCV_BUFF10;
            RCV_BUFF10 = RCV_BUFF11;
            RCV_BUFF11 = RCV_BUFF12;
            RCV_BUFF12 = RCV_BUFF13;
            RCV_BUFF13 = RCV_BUFF14;
            RCV_BUFF14 = RCV_BUFF15;
            RCV_BUFF15 = RCV_BUFF16;
            RCV_BUFF16 = RCV_BUFF17;
            RCV_BUFF17 = RCV_BUFF18;
            RCV_BUFF18 = RCV_BUFF19;
            RCV_BUFF19 = RCV_BUFF20;
            RCV_BUFF20 = RCV_BUFF21;
            RCV_BUFF21 = RCV_BUFF22;
            RCV_BUFF22 = RCV_BUFF23;
            RCV_BUFF23 = RCV_BUFF24;
            RCV_BUFF24 = RCV_BUFF25;
            RCV_BUFF25 = RCV_BUFF26;
            RCV_BUFF26 = RCV_BUFF27;
            RCV_BUFF27 = RCV_BUFF28;
            RCV_BUFF28 = RCV_BUFF29;
            RCV_BUFF29 = RCV_BUFF30;
            RCV_BUFF30 = RCV_BUFF31;
            RCV_BUFF31 = Serial.read( );
            RX_CHKDIGIT=RCV_BUFF00+RCV_BUFF01+RCV_BUFF02+RCV_BUFF03+RCV_BUFF04+RCV_BUFF05+RCV_BUFF06+RCV_BUFF07+RCV_BUFF08+RCV_BUFF09+RCV_BUFF10+RCV_BUFF11+RCV_BUFF12+RCV_BUFF13+RCV_BUFF14+RCV_BUFF15+RCV_BUFF16+RCV_BUFF17+RCV_BUFF18+RCV_BUFF19+RCV_BUFF20+RCV_BUFF21+RCV_BUFF22+RCV_BUFF23+RCV_BUFF24+RCV_BUFF25+RCV_BUFF26+RCV_BUFF27+RCV_BUFF28+RCV_BUFF29+RCV_BUFF30;
            RX_CHKDIGIT=RX_CHKDIGIT%256;
            if((RCV_BUFF00==255)&&(RCV_BUFF01==255)&&((RCV_BUFF30==250)||(RCV_BUFF30==255))&&(RCV_BUFF31==RX_CHKDIGIT)){
                RCV_VALUE00 = RCV_BUFF00;
                RCV_VALUE01 = RCV_BUFF01;
                RCV_VALUE02 = RCV_BUFF02;
                RCV_VALUE03 = RCV_BUFF03;
                RCV_VALUE04 = RCV_BUFF04;
                RCV_VALUE05 = RCV_BUFF05;
                RCV_VALUE06 = RCV_BUFF06;
                RCV_VALUE07 = RCV_BUFF07;
                RCV_VALUE08 = RCV_BUFF08;
                RCV_VALUE09 = RCV_BUFF09;
                RCV_VALUE10 = RCV_BUFF10;
                RCV_VALUE11 = RCV_BUFF11;
                RCV_VALUE12 = RCV_BUFF12;
                RCV_VALUE13 = RCV_BUFF13;
                RCV_VALUE14 = RCV_BUFF14;
                RCV_VALUE15 = RCV_BUFF15;
                RCV_VALUE16 = RCV_BUFF16;
                RCV_VALUE17 = RCV_BUFF17;
                RCV_VALUE18 = RCV_BUFF18;
                RCV_VALUE19 = RCV_BUFF19;
                RCV_VALUE20 = RCV_BUFF20;
                RCV_VALUE21 = RCV_BUFF21;
                RCV_VALUE22 = RCV_BUFF22;
                RCV_VALUE23 = RCV_BUFF23;
                RCV_VALUE24 = RCV_BUFF24;
                RCV_VALUE25 = RCV_BUFF25;
                RCV_VALUE26 = RCV_BUFF26;
                RCV_VALUE27 = RCV_BUFF27;
                RCV_VALUE28 = RCV_BUFF28;
                RCV_VALUE29 = RCV_BUFF29;
                RCV_VALUE30 = RCV_BUFF30;
                RCV_VALUE31 = RCV_BUFF31;
                RxFlg=1;
            }else{
            }
        }else{
        }
        if(RxFlg==1){
            RxFlg=0;
            SMELL=(RCV_VALUE02*256)+RCV_VALUE03;
            TEMP=((RCV_VALUE04*256)+RCV_VALUE05)/10;
            HUMI=RCV_VALUE06;
            CDS=(RCV_VALUE07*256)+RCV_VALUE08;
            NOISE=(RCV_VALUE09*256)+RCV_VALUE10;
            HUMAN=RCV_VALUE11;
            CO=(RCV_VALUE12*256)+RCV_VALUE13;
            CO2=(RCV_VALUE14*256)+RCV_VALUE15;
            DCIN=RCV_VALUE16;
            D_IN1=RCV_VALUE17;
            D_IN2=RCV_VALUE18;
            CONTACT1=RCV_VALUE25;
            CONTACT2=RCV_VALUE26;
        }else{
        }
    }
    条件分岐

    H10は、1秒ごとに主MCUから測定値データを送信していますが、副MCUから適切な返事をしないと、新たな測定は行いません。

    通常、マイコン同士でデジタルデータのやりとりを行う場合、受信側はデータがいつ届いても受信できるよう、待機していなければなりません。
    そして、データが届いたら、現在進めている処理を一旦中断して受信処理を行わないと、データを取りこぼしてしまいます。
    これを割り込み処理と言います。
    主MCU-副MCU間は、9600ビット毎秒という速度でデータ交換をしていますが、主MCUで使用しているデジタル通信はこれだけではありません。
    温度・湿度を測定するセンサーはデジタルデータで出力するタイプのセンサを使用していたり、小型の液晶表示装置を接続できるようにしたり、拡張性を持たせるための他の通信もしており、これらは秒間100000ビット毎秒(100kbps)というさらに速い速度で通信しています。
    その他、臭気(におい)センサーは温度測定の瞬間に電気を流して良い時間が厳密に決められており、これを超えて通電すると故障の原因となるため、割り込み受信が命取りになることもあります。
    H10は、見かけ以上に慌ただしくデータ処理をしています。
    これらの処理を行っている最中に9600bpsという低速データ通信を割り込ませてしまうと、他のセンサーとのデータ通信が邪魔されて失敗したり、臭気(におい)センサーを故障させる原因となります。
    主MCUのクロック速度を100MHz(メガヘルツ)、200MHzと上げれば全ての処理を両立させることが可能であるものの、クロック速度に比例して消費電力が増えてしまうため、電池で動かなくなってしまいます。
    H10は、大規模災害で停電が起きても、単三電池4本で1週間動作することを目標に設計しています。
    ここから逆算した消費電力で動作させることを最優先させた時、割り込み受信処理を断念することになりました。
    一方、割り込み処理を使用しない場合、副MCUは一体いつ送信すればいいのか迷うことになります。
    そこで、主MCUが時報を送信してから0.8秒以内(Webやクラウド上のフラグ確認など、時間の係る処理を行うなどで、副MCU側での処理が0.8秒以内に処理が終わらない場合は、2回目、3回目、さらにそれ以降の時報受信から0.8秒以内でも大丈夫)に返事をすれば良いというルールを設けることで、受信できるようにしています。
    ホームセキュリティシステムなど、類似のハードウェアと比較しても高機能で低消費電力を実現していますが、これ以外にも様々な省電力技術を搭載しています。

    このように、主MCUから時報を受け取ったら「測定して、結果を返信してください」という返事をしなければなりません。
    その方法について説明します。

    主MCUから受信するデータは、1秒ごとに受信する時報と、1分ごとに受信する時報の2通りあります。
    1秒ごとの時報は、主MCUと副MCUのデータの同期を取るための時報で、1分ごとの時報はアップロードタイミングを通知するために利用されることを想定して開発しています。
    そのため、必要な処理が異なるため、どちらの時報が届いたのかを判断しなければなりません。
    【プログラミング・リファレンス】の【電文(パケット)のフォーマット】の表をご覧ください。
    31バイト目に「種別」があると思います。
    1秒ごとの時報である場合、250が届きます。
    1分ごとの時報である場合、255が届きます。
    例えば、温度をずっと監視して30度を超えたら換気扇を回すためのリレーを動かす・・・などのような処理は、1秒ごとに行った方が使いやすいでしょう。
    ただ、クラウドへのアップロードは1分に1度で十分だと思いますので、255が届いた時にやればいいと思います。
    届いたデータの内容によって条件分岐する必要がありますが、ここではその方法について説明しようと思います。

    最初の方で、「もし1バイト以上のデータを受信したら」という処理を書いたと思います。
    思い出してみてください。

    if(Serial.available()>=1){
    受信処理
    }

    これです。
    やり方はこれと同じです。

    if(RCV_VALUE30==250){
     RCV_VALUE30が250だった時、つまり届いたデータが
     1秒ごとの時報だった場合に行う処理を書きます。
    }

    このようになります。
    それでは、主MCUに対して返事をする処理を{ }の中に書いていきましょう。
    データフォーマットに従った形式で送信されていれば、中身は何であっても構いません。
    送信フォーマットは、【プログラミング・リファレンス】の【電文(パケット)のフォーマット】をご覧ください。
    今まで見てきたところは「主MCU→副MCU」ですが、今度は逆ですので、もう少しスクロールさせて「副MCU→主MCU」のところを表示させます。
    0バイト目、1バイト目が固定値で255であることと、5バイト目にリレーON/OFFがある以外は中身がありませんが、この22バイト(0〜21バイト目までの22バイト)を送り返します。
    リレーON/OFFは、H10基板の右端にあるリレーという部品のON/OFFを決めます。
    例えばリレーに換気扇をつなぐことで、一定以上の室温になったら換気扇を回すとか、一定以下の照度になったら照明をつけるなどの処理を行う時に使います。

    送信は、以下の命令で行います。

    Serial.write(送信する値);

    リレーには、まだ何も接続していないはずですので5バイト目は0とします。
    他バイトも、「固定値(10進数)」の部分を参考に送信します。
    データフォーマットに添った形で送信するには、以下の通りとなります。

    Serial.write(255);
    Serial.write(255);
    Serial.write(0);
    Serial.write(0);
    Serial.write(0);
    Serial.write(0);
    Serial.write(0);
    Serial.write(0);
    Serial.write(0);
    Serial.write(0);
    Serial.write(0);
    Serial.write(0);
    Serial.write(0);
    Serial.write(0);
    Serial.write(0);
    Serial.write(0);
    Serial.write(0);
    Serial.write(0);
    Serial.write(0);
    Serial.write(0);
    Serial.write(0);
    Serial.write(254);

    ここまでで作成したプログラムにこの処理を追加すると以下の通りとなります。

    int RCV_BUFF00,RCV_BUFF01,RCV_BUFF02,RCV_BUFF03,RCV_BUFF04,RCV_BUFF05,RCV_BUFF06,RCV_BUFF07,RCV_BUFF08,RCV_BUFF09,
    RCV_BUFF10,RCV_BUFF11,RCV_BUFF12,RCV_BUFF13,RCV_BUFF14,RCV_BUFF15,RCV_BUFF16,RCV_BUFF17,RCV_BUFF18,RCV_BUFF19,
    RCV_BUFF20,RCV_BUFF21,RCV_BUFF22,RCV_BUFF23,RCV_BUFF24,RCV_BUFF25,RCV_BUFF26,RCV_BUFF27,RCV_BUFF28,RCV_BUFF29,
    RCV_BUFF30,RCV_BUFF31=0;
    int RCV_VALUE00,RCV_VALUE01,RCV_VALUE02,RCV_VALUE03,RCV_VALUE04,RCV_VALUE05,RCV_VALUE06,RCV_VALUE07,RCV_VALUE08,RCV_VALUE09,
    RCV_VALUE10,RCV_VALUE11,RCV_VALUE12,RCV_VALUE13,RCV_VALUE14,RCV_VALUE15,RCV_VALUE16,RCV_VALUE17,RCV_VALUE18,RCV_VALUE19,
    RCV_VALUE20,RCV_VALUE21,RCV_VALUE22,RCV_VALUE23,RCV_VALUE24,RCV_VALUE25,RCV_VALUE26,RCV_VALUE27,RCV_VALUE28,RCV_VALUE29,
    RCV_VALUE30,RCV_VALUE31=0;
    int SMELL,TEMP,CDS,NOISE,CO,CO2,HUMI,HUMAN,DCIN,D_IN1,D_IN2=0;
    int RxFlg=0;
    void setup(){
        Serial.begin(9600);
    }
    void loop(){
        if (Serial.available() >= 1) {
            RCV_BUFF00 = RCV_BUFF01;
            RCV_BUFF01 = RCV_BUFF02;
            RCV_BUFF02 = RCV_BUFF03;
            RCV_BUFF03 = RCV_BUFF04;
            RCV_BUFF04 = RCV_BUFF05;
            RCV_BUFF05 = RCV_BUFF06;
            RCV_BUFF06 = RCV_BUFF07;
            RCV_BUFF07 = RCV_BUFF08;
            RCV_BUFF08 = RCV_BUFF09;
            RCV_BUFF09 = RCV_BUFF10;
            RCV_BUFF10 = RCV_BUFF11;
            RCV_BUFF11 = RCV_BUFF12;
            RCV_BUFF12 = RCV_BUFF13;
            RCV_BUFF13 = RCV_BUFF14;
            RCV_BUFF14 = RCV_BUFF15;
            RCV_BUFF15 = RCV_BUFF16;
            RCV_BUFF16 = RCV_BUFF17;
            RCV_BUFF17 = RCV_BUFF18;
            RCV_BUFF18 = RCV_BUFF19;
            RCV_BUFF19 = RCV_BUFF20;
            RCV_BUFF20 = RCV_BUFF21;
            RCV_BUFF21 = RCV_BUFF22;
            RCV_BUFF22 = RCV_BUFF23;
            RCV_BUFF23 = RCV_BUFF24;
            RCV_BUFF24 = RCV_BUFF25;
            RCV_BUFF25 = RCV_BUFF26;
            RCV_BUFF26 = RCV_BUFF27;
            RCV_BUFF27 = RCV_BUFF28;
            RCV_BUFF28 = RCV_BUFF29;
            RCV_BUFF29 = RCV_BUFF30;
            RCV_BUFF30 = RCV_BUFF31;
            RCV_BUFF31 = Serial.read( );
            RX_CHKDIGIT=RCV_BUFF00+RCV_BUFF01+RCV_BUFF02+RCV_BUFF03+RCV_BUFF04+RCV_BUFF05+RCV_BUFF06+RCV_BUFF07+RCV_BUFF08+RCV_BUFF09+RCV_BUFF10+RCV_BUFF11+RCV_BUFF12+RCV_BUFF13+RCV_BUFF14+RCV_BUFF15+RCV_BUFF16+RCV_BUFF17+RCV_BUFF18+RCV_BUFF19+RCV_BUFF20+RCV_BUFF21+RCV_BUFF22+RCV_BUFF23+RCV_BUFF24+RCV_BUFF25+RCV_BUFF26+RCV_BUFF27+RCV_BUFF28+RCV_BUFF29+RCV_BUFF30;
            RX_CHKDIGIT=RX_CHKDIGIT%256;
            if((RCV_BUFF00==255)&&(RCV_BUFF01==255)&&((RCV_BUFF30==250)||(RCV_BUFF30==255))&&(RCV_BUFF31==RX_CHKDIGIT)){
                RCV_VALUE00 = RCV_BUFF00;
                RCV_VALUE01 = RCV_BUFF01;
                RCV_VALUE02 = RCV_BUFF02;
                RCV_VALUE03 = RCV_BUFF03;
                RCV_VALUE04 = RCV_BUFF04;
                RCV_VALUE05 = RCV_BUFF05;
                RCV_VALUE06 = RCV_BUFF06;
                RCV_VALUE07 = RCV_BUFF07;
                RCV_VALUE08 = RCV_BUFF08;
                RCV_VALUE09 = RCV_BUFF09;
                RCV_VALUE10 = RCV_BUFF10;
                RCV_VALUE11 = RCV_BUFF11;
                RCV_VALUE12 = RCV_BUFF12;
                RCV_VALUE13 = RCV_BUFF13;
                RCV_VALUE14 = RCV_BUFF14;
                RCV_VALUE15 = RCV_BUFF15;
                RCV_VALUE16 = RCV_BUFF16;
                RCV_VALUE17 = RCV_BUFF17;
                RCV_VALUE18 = RCV_BUFF18;
                RCV_VALUE19 = RCV_BUFF19;
                RCV_VALUE20 = RCV_BUFF20;
                RCV_VALUE21 = RCV_BUFF21;
                RCV_VALUE22 = RCV_BUFF22;
                RCV_VALUE23 = RCV_BUFF23;
                RCV_VALUE24 = RCV_BUFF24;
                RCV_VALUE25 = RCV_BUFF25;
                RCV_VALUE26 = RCV_BUFF26;
                RCV_VALUE27 = RCV_BUFF27;
                RCV_VALUE28 = RCV_BUFF28;
                RCV_VALUE29 = RCV_BUFF29;
                RCV_VALUE30 = RCV_BUFF30;
                RCV_VALUE31 = RCV_BUFF31;
                RxFlg=1;
            }else{
            }
        }else{
        }
        if(RxFlg==1){
            RxFlg=0;
            SMELL=(RCV_VALUE02*256)+RCV_VALUE03;
            TEMP=((RCV_VALUE04*256)+RCV_VALUE05)/10;
            HUMI=RCV_VALUE06;
            CDS=(RCV_VALUE07*256)+RCV_VALUE08;
            NOISE=(RCV_VALUE09*256)+RCV_VALUE10;
            HUMAN=RCV_VALUE11;
            CO=(RCV_VALUE12*256)+RCV_VALUE13;
            CO2=(RCV_VALUE14*256)+RCV_VALUE15;
            DCIN=RCV_VALUE16;
            D_IN1=RCV_VALUE17;
            D_IN2=RCV_VALUE18;
            CONTACT1=RCV_VALUE25;
            CONTACT2=RCV_VALUE26;
            if(RCV_VALUE30==250){
                Serial.write(255);
                Serial.write(255);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(254);   
            }else{
            }
        }else{
        }
    }

    「30バイト目が250ならば」の部分に、「if(RCV_VALUE30==250)」このようにイコールが2つ連続で書かれていますが、打ち間違いではありません。
    イコールを1つ(RCV_VALUE30=250)にした場合、RCV_VALUE30に250を入れる処理となります。
    比較する場合はイコールを2つ並べて「RCV_VALUE30==250」とします。

    送信する順番にSerial.writeを書いていきます。
    これで、主MCUからデータが送られてくる度に返事ができるようになりました。
    次に、1分ごとに送信されてくる「255」を受信したら温度をチェックして、リレースイッチのON/OFFを行う処理を記述していきます。

    室温の判定とリレー制御

    受け取ったデータから、温度のデータを抽出します。
    温度は、TEMPに代入されています。
    28度になったら換気扇がつながっているリレーをONにして、25度になったらOFFにする処理を追加します。
    先ほど触れた通り、温度は0.1度単位で計測しており、10を掛けた値が主MCUから送られてきているので、280を超えたらONにして、250を下回ったらOFFにする処理を記述します。

    主MCUから1分ごとに届く30バイト目が255の電文を受け取ったら、照度の判定とリレーの制御を行うので、以下の処理を追加します。

    if(RCV_VALUE30==255){
    }

    まずは、リレーをONにするために必要な条件『温度が280(28度の10倍)以上になったら』の部分を作っていきます。

    if(RCV_VALUE30==255){
        if(TEMP>=280){
        }else{
        }
    }

    次に、リレーをOFFにするための条件『温度が250以下になったら』の部分を追記します。

    if(RCV_VALUE30==255){
        if(TEMP>=280){
        }else{
        }
        if(TEMP<=250){
        }else{
        }
    }

    リレーを制御するためには、5バイト目を2(OFF)または1(ON)にします。
    その処理を追記すると、以下のような形となります。

    if(RCV_VALUE30==255){
        if(TEMP>=280){
            Serial.write(255);
            Serial.write(255);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(1);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(255);   
        }else{
        }
        if(CDS>=500){
            Serial.write(255);
            Serial.write(255);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(2);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);
            Serial.write(0);   
        }else{
        }
    }else{
    }

    31バイト目のチェックデジットの計算も忘れずに行います。
    ONにするときは、255+255+1を256で割った余りの255を、
    OFFにするときは、255+255+2を256で割った余りを0を31バイト目に記述します。

    これを、ここまでで作成したプログラムに追加すると、以下の通りとなります。

    int RCV_BUFF00,RCV_BUFF01,RCV_BUFF02,RCV_BUFF03,RCV_BUFF04,RCV_BUFF05,RCV_BUFF06,RCV_BUFF07,RCV_BUFF08,RCV_BUFF09,
    RCV_BUFF10,RCV_BUFF11,RCV_BUFF12,RCV_BUFF13,RCV_BUFF14,RCV_BUFF15,RCV_BUFF16,RCV_BUFF17,RCV_BUFF18,RCV_BUFF19,
    RCV_BUFF20,RCV_BUFF21,RCV_BUFF22,RCV_BUFF23,RCV_BUFF24,RCV_BUFF25,RCV_BUFF26,RCV_BUFF27,RCV_BUFF28,RCV_BUFF29,
    RCV_BUFF30,RCV_BUFF31=0;
    
    int RCV_VALUE00,RCV_VALUE01,RCV_VALUE02,RCV_VALUE03,RCV_VALUE04,RCV_VALUE05,RCV_VALUE06,RCV_VALUE07,RCV_VALUE08,RCV_VALUE09,
    RCV_VALUE10,RCV_VALUE11,RCV_VALUE12,RCV_VALUE13,RCV_VALUE14,RCV_VALUE15,RCV_VALUE16,RCV_VALUE17,RCV_VALUE18,RCV_VALUE19,
    RCV_VALUE20,RCV_VALUE21,RCV_VALUE22,RCV_VALUE23,RCV_VALUE24,RCV_VALUE25,RCV_VALUE26,RCV_VALUE27,RCV_VALUE28,RCV_VALUE29,
    RCV_VALUE30,RCV_VALUE31=0;
    
    int SMELL,TEMP,CDS,NOISE,CO,CO2,HUMI,HUMAN,DCIN,D_IN1,D_IN2,CONTACT1,CONTACT2=0;
    
    int RxFlg=0;
    int RX_CHKDIGIT=0;
    int senddata[22];
    
    void setup(){
        Serial.begin(9600);
        while(Serial.available() >= 1){
          RCV_VALUE31 = Serial.read( );
        }
    }
    void loop(){
        if (Serial.available() >= 1) {
            RCV_BUFF00 = RCV_BUFF01;
            RCV_BUFF01 = RCV_BUFF02;
            RCV_BUFF02 = RCV_BUFF03;
            RCV_BUFF03 = RCV_BUFF04;
            RCV_BUFF04 = RCV_BUFF05;
            RCV_BUFF05 = RCV_BUFF06;
            RCV_BUFF06 = RCV_BUFF07;
            RCV_BUFF07 = RCV_BUFF08;
            RCV_BUFF08 = RCV_BUFF09;
            RCV_BUFF09 = RCV_BUFF10;
            RCV_BUFF10 = RCV_BUFF11;
            RCV_BUFF11 = RCV_BUFF12;
            RCV_BUFF12 = RCV_BUFF13;
            RCV_BUFF13 = RCV_BUFF14;
            RCV_BUFF14 = RCV_BUFF15;
            RCV_BUFF15 = RCV_BUFF16;
            RCV_BUFF16 = RCV_BUFF17;
            RCV_BUFF17 = RCV_BUFF18;
            RCV_BUFF18 = RCV_BUFF19;
            RCV_BUFF19 = RCV_BUFF20;
            RCV_BUFF20 = RCV_BUFF21;
            RCV_BUFF21 = RCV_BUFF22;
            RCV_BUFF22 = RCV_BUFF23;
            RCV_BUFF23 = RCV_BUFF24;
            RCV_BUFF24 = RCV_BUFF25;
            RCV_BUFF25 = RCV_BUFF26;
            RCV_BUFF26 = RCV_BUFF27;
            RCV_BUFF27 = RCV_BUFF28;
            RCV_BUFF28 = RCV_BUFF29;
            RCV_BUFF29 = RCV_BUFF30;
            RCV_BUFF30 = RCV_BUFF31;
            RCV_BUFF31 = Serial.read( );
            RX_CHKDIGIT=RCV_BUFF00+RCV_BUFF01+RCV_BUFF02+RCV_BUFF03+RCV_BUFF04+RCV_BUFF05+RCV_BUFF06+RCV_BUFF07+RCV_BUFF08+RCV_BUFF09+RCV_BUFF10+RCV_BUFF11+RCV_BUFF12+RCV_BUFF13+RCV_BUFF14+RCV_BUFF15+RCV_BUFF16+RCV_BUFF17+RCV_BUFF18+RCV_BUFF19+RCV_BUFF20+RCV_BUFF21+RCV_BUFF22+RCV_BUFF23+RCV_BUFF24+RCV_BUFF25+RCV_BUFF26+RCV_BUFF27+RCV_BUFF28+RCV_BUFF29+RCV_BUFF30;
            RX_CHKDIGIT=RX_CHKDIGIT%256;
            if((RCV_BUFF00==255)&&(RCV_BUFF01==255)&&((RCV_BUFF30==250)||(RCV_BUFF30==255))&&(RCV_BUFF31==RX_CHKDIGIT)){
                RCV_VALUE00 = RCV_BUFF00;
                RCV_VALUE01 = RCV_BUFF01;
                RCV_VALUE02 = RCV_BUFF02;
                RCV_VALUE03 = RCV_BUFF03;
                RCV_VALUE04 = RCV_BUFF04;
                RCV_VALUE05 = RCV_BUFF05;
                RCV_VALUE06 = RCV_BUFF06;
                RCV_VALUE07 = RCV_BUFF07;
                RCV_VALUE08 = RCV_BUFF08;
                RCV_VALUE09 = RCV_BUFF09;
                RCV_VALUE10 = RCV_BUFF10;
                RCV_VALUE11 = RCV_BUFF11;
                RCV_VALUE12 = RCV_BUFF12;
                RCV_VALUE13 = RCV_BUFF13;
                RCV_VALUE14 = RCV_BUFF14;
                RCV_VALUE15 = RCV_BUFF15;
                RCV_VALUE16 = RCV_BUFF16;
                RCV_VALUE17 = RCV_BUFF17;
                RCV_VALUE18 = RCV_BUFF18;
                RCV_VALUE19 = RCV_BUFF19;
                RCV_VALUE20 = RCV_BUFF20;
                RCV_VALUE21 = RCV_BUFF21;
                RCV_VALUE22 = RCV_BUFF22;
                RCV_VALUE23 = RCV_BUFF23;
                RCV_VALUE24 = RCV_BUFF24;
                RCV_VALUE25 = RCV_BUFF25;
                RCV_VALUE26 = RCV_BUFF26;
                RCV_VALUE27 = RCV_BUFF27;
                RCV_VALUE28 = RCV_BUFF28;
                RCV_VALUE29 = RCV_BUFF29;
                RCV_VALUE30 = RCV_BUFF30;
                RCV_VALUE31 = RCV_BUFF31;
                RxFlg=1;
            }else{
            }
        }else{
        }
        if(RxFlg==1){
            RxFlg=0;
            SMELL=(RCV_VALUE02*256)+RCV_VALUE03;
            TEMP=((RCV_VALUE04*256)+RCV_VALUE05)/10;
            HUMI=RCV_VALUE06;
            CDS=(RCV_VALUE07*256)+RCV_VALUE08;
            NOISE=(RCV_VALUE09*256)+RCV_VALUE10;
            HUMAN=RCV_VALUE11;
            CO=(RCV_VALUE12*256)+RCV_VALUE13;
            CO2=(RCV_VALUE14*256)+RCV_VALUE15;
            DCIN=RCV_VALUE16;
            D_IN1=RCV_VALUE17;
            D_IN2=RCV_VALUE18;
            CONTACT1=RCV_VALUE25;
            CONTACT2=RCV_VALUE26;
         if(RCV_VALUE30==255){
                if(TEMP>=280){
                    Serial.write(255);
                    Serial.write(255);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(1);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(255);   
                }else{
                }
                if(TEMP<=250){
                    Serial.write(255);
                    Serial.write(255);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(2);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);
                    Serial.write(0);   
                }else{
                }
            }else{
            }
            if(RCV_VALUE30==250){
                Serial.write(255);
                Serial.write(255);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(0);
                Serial.write(254);   
            }else{
            }
        }else{
        }
    }