ESP32を使って学習リモコンを作る(81) ソフトウェア 本機プロジェクトの勘所

リピート送信に対応しました。             [2026/09/29]
リピート送信対応前のバージョンはこちらから:ESP32を使って学習リモコンを作る(8) ソフトウェア 本機のプロジェクト

はじめに

前回 ESP32を使って学習リモコンを作る(71) ソフトウェア 本機のプロジェクト で説明したプロジェクトの勘所を説明します。

ソフトウェアの要素技術

ソフトウェアの要素技術についてはこれまでに説明してきた内容を参照ください。
ESP32を使って学習リモコンを作る(3) ソフトウェア 赤外線リモコン受信( irReceive )
ESP32を使って学習リモコンを作る(4) ソフトウェア 赤外線リモコン送信( irSend )
ESP32を使って学習リモコンを作る(5) ソフトウェア webserver
ESP32を使って学習リモコンを作る(6) ソフトウェア Flash 読書き(SPIFFS)

本項ではクライアント(Webブラウザ)とサーバー(ESP32 main.cpp)間の相互関係や関数とのデータ受け渡しおよび関数の有効、無効の制御方法を中心に説明をして Smart Remote Controller 全体のシステム動作の理解に役立てればと思います。

グローバル変数(データ)

データの受け渡しに使用するグローバル変数を表1 にまとめました。
グローバル変数にしなくてもデータの受け渡しはできると思いますが、あえてグローバル変数として受け渡しに使用することを意識するようにしました。

表1 グローバル変数(データ)
変数名 型 説明
status_information String main.cpp で発生した制御状態の変化を任意の文字列で表現してこの変数に代入することで、クライアント側に文字列情報が伝達されてブラウザ画面に表示されます。
wrfolder String SPIFFS を使用して Flash に書込む際のフォルダ名として書込み、読出しで使用します。
wrfile String SPIFFS を使用して Flash に書込む際のファイル名として書込み、読出しで使用します。
writeStr String 取込んだ赤外線リモコン信号(hex)を文字列として格納します。receive_IR_process()を実行すると戻り値 writeStr を返します。
信号登録が許可されると store_Flash() は writeStr を引数として Flash に書込みます。
readStr String  restore_IR_proicess() を実行すると赤外線リモコン信号が読出されて戻り値 readStr を返します。
send_IR_process() は readStr を引数として赤外線リモコン信号を送信します。
num_reg int  リモコン送信ボタンに登録する赤外線リモコン信号の登録数カウンタです。
bt_id String  赤外線リモコン信号を複数登録中にリモコン送信ボタンが変更されたかを識別するために使用します。
num_period int リピート周期(初期値 108ms)
hex_repeat String リピート信号(hex)

グローバル変数(フラグ)

制御のためにフラグとして使用する bool 型グローバル変数を表2 にまとめました。

表2 グローバル変数(フラグ)

変数名 型 説明
ena_send bool 赤外線リモコン信号送信関数 send_IR_process(); の有効/無効フラグとして使用します。
ena_store bool Flash への書込み関数 store_Flash(); の有効/無効フラグとして使用します。
ena_restore bool Flash からの読出し関数 restore_Flash(); の有効/無効フラグとして使用します。
ena_assign bool 信号登録の有効/無効フラグとして使用します。
is_triggered bool 周期実行関数をトリガーするフラグとして使用します。
is_cancelled bool 周期実行関数をキャンセルするフラグとして使用します。
stop_requested bool 周期実行関数を停止するフラグとして使用します。
Repeat_flag bool リピート信号を送信するフラグとして使用します。

主要関数とグローバル変数(フラグ)の関係

主要関数とグローバル変数(フラグ)の関係を表3 にまとめました。
この表から主要関数の有効/無効の条件および引数/戻り値を一覧できます。

表3 主要関数とグローバル変数(フラグ)の関係

機能 関数名 有効条件
(値)
無効条件
(値)
引数
(型)
引数
(型)
戻り値
(型)
赤外線
信号受信
receive_IR_process() 33PIN
(HIGH)
33PIN
(LOW)
writeStr
(String)
赤外線
信号送信
send_IR_process() ena_send
(true)
ena_send
(false)
readStr
(String)
Flash
書込み
store_Flash() ena_store
(true)
ena_store
(false)*
wrfile
(String)
writestr
(String)
Flash
読出し
restore_Flash ena_restore
(true)
ena_restore
(false)
wrfile
(String)
readStr
(String)

* ena_store のみ store_Flash 関数から抜ける時に false に設定されます。

表4 に周期実行関数 periodicLoop() とグローバル変数(フラグ)の関係をまとめました。

表4 周期実行関数とグローバル変数(フラグ)の関係

状態 is_triggered is_cancelled stop_requested
待受け false false false
キャンセル X true X
周期処理開始 true false false
周期処理終了 X true または true

X は Dont’care を示します。

ブラウザ画面に表示されるボタン

ボタンの動作を説明することでクライアント(Webブラウザ)とサーバー(ESP32)間の相互関係が明らかになります。ここでは Web ブラウザに表示されるボタンの定義を再掲します。(図1)

図1 Web ブラウザのボタン定義

モード切換ボタン(Smart Remote Controller)

ブラウザ画面下寄りの Smart Remote Controller の文字がモード切換ボタンで、モード切換が有効な場合は赤色、無効の場合は青色で表示されています。(図1 ③)

index.html

57    <!– モード切換ボタンの定義( ボタンの輪郭は消しているので文字だけに見える)–>
58    <divclass=“bt_mode”>
59        <input type=“checkbox” id=“bt_mode” name=“bt_mode” onclick=“bt_modeClick();”>
60        <label id=“bt_mode_label” for=“bt_mode”>Smart Remote Controller</label>
61    </div>
57~61 モード切換ボタンを定義しています。
    checkbox 属性を使用して id と name 両方に bt_mode を設定しています。

js.js

265    function bt_modeClick(){
266        var xhr = new XMLHttpRequest();
267        xhr.open(“GET”, “/mode”, true);
268        xhr.send(null);
269      }

265~269 モード切換ボタン bt_mode を押した時サーバーへメソッド GET、 URL /mode のリクエストを送信します。

main.cpp

301    server.on(“/mode”, HTTP_GET, [](AsyncWebServerRequest *request){
302        Serial.println(“モード切換ボタンが押されました。”); //ターミナルにメッセージ出力
303        status_information = “モード切換ボタンが押されました。”; //クライアント側で情報を表示する
304        request->send_P(200, “text/plain”, getMsg_information().c_str());
305      });

301~305 URL /mode によりモード切換ボタン bt_mode からの GET リクエストと認識されて処理が行われます。
302    ターミナルにメッセージを出力します。
303~304 内部変数 status_information にメッセージを設定してクライアント側に text/plane として送信してブラウザ画面に表示します。

js.js

上記処理とは別にモード切換ボタンにイベントリスナを付加してボタンの状態が true か false かによって異なる動作を定義しています。

16    control_bt_mode_status();

16 モード切換ボタンの動作を定義した関数 control_bt_mode_status(); を呼び出します。

19    setInterval(getCurTime, 500);
19 関数呼び出しは 500ms 周期で実行されます。
356    function control_bt_mode_status(){
357        const ev_SRC = document.getElementById(‘bt_mode’);
358        ev_SRC.addEventListener(‘click’,
359          function(event){
360            enable_all_of_TX_keys();
361            const btSRC = document.getElementById(‘bt_mode’);
362            if ( btSRC.checked ){ /** bt_mode が true の場合制御ボタンを有効にして画面上で見えるようにする */
                   .
                   .
372            } else { /** bt_mode が false の場合制御ボタンを無効にして画面上で見えなくする */
                  .
                  .
382            }
383          }
384        );
385      }

356~385 モード切換ボタンの動作を関数 function control_bt_mode_status(){} で定義します。

357     const ev_SRC = document.getElementById(‘bt_mode’);
358        ev_SRC.addEventListener(‘click’,
359        function(event){

357~358 モード切換ボタン bt_mode にイベントリスナを付加します。
359     クリックされたら関数 function(event){} を実行します。

346    function enable_all_of_TX_keys(){
347        /* 16個の「リモコン送信ボタン」bt_1 から bt_16 を有効にする */
348        for (let i = 1; i < 17; i++){
349          buf_btc2 = “bt_” + i ;
350          enable_button(buf_btc2);
351        }
352      }

346~352 bt_1 から bt_16 のリモコン送信ボタン 16個全てを有効にします。

337     async function enable_button(button_id){
338        try{
339            document.getElementById(button_id).removeAttribute(“disabled”);
340        } catch (err){
341            console.error(err);
342        }
343     }

337~343 button_id で指定したボタンの Atribute “disabled” を削除して有効にします。

361     const btSRC = document.getElementById(‘bt_mode’);

361    モード切換ボタン bt_mode をインスタンス化します。

bt_mode が true の場合(制御ボタン有効)

362    if ( btSRC.checked ){ /** bt_mode が true の場合制御ボタンを有効にして画面上で見えるようにする */
363          enable_button(“bt_c1”);
364          enable_button(“bt_c2”);
365          enable_button(“bt_c3”);
366          enable_button(“bt_c4”);
367          bt_standby(‘bt_c1_label’);
368          bt_standby(‘bt_c2_label’);
369          bt_standby(‘bt_c3’);
370          bt_standby(‘bt_c4_label’);
371          document.getElementById(“msg_information”).innerHTML = “赤外線リモコン信号取込機能が有効になりました。 “;

362~371 ボタン bt_c1 ~ bt_c4 を有効にします。
関数 enable_button(); が “disabled” Atribute を削除してボタンを有効にします。(337~343行目)
367~370 ボタン bt_c1 ~ bt_c4 が見えるようにします。
関数 bt_standby(); が文字、背景、境界を見える色に変更します。(396~401行目)
371 ブラウザの情報表示窓に表示するためのメッセージを設定します。

bt_mode が false の場合(制御ボタン無効)

372  } else { /** bt_mode が false の場合制御ボタンを無効にして画面上で見えなくする */
373          disabale_button(“bt_c1”);
374          disabale_button(“bt_c2”);
375          disabale_button(“bt_c3”);
376          disabale_button(“bt_c4”);
377          bt_hidden(‘bt_c1_label’);
378          bt_hidden(‘bt_c2_label’);
379          bt_hidden(‘bt_c3’);
380          bt_hidden(‘bt_c4_label’);
381          document.getElementById(“msg_information”).innerHTML = “赤外線リモコン信号取込機能を終了しました。\n通常の赤外線リモコン送信機として機能します。”;
382        }       

372~382 ボタン bt_c1 ~ bt_c4 を無効にします。
関数 disable_button(); が “disabled” Atribute を付加してボタンを無効にします。(328~334行目)
377~380 ボタン bt_c1 ~ bt_c4 が見えないようにします。
関数 bt_hidden(); が文字、背景、境界を見えない色に変更します。(403~408行目)
381 ブラウザの情報表示窓に表示するためのメッセージを設定します。

取込ボタン(bt_c1)

index.html

64    <div class=”bt_c1″>
65    <input type=”checkbox” id=”bt_c1″ name=”bt_c1″onclick=”bt_c1Click();”>
66    <label id= “bt_c1_label” for=”bt_c1″>取込</label>
67    </div>
64~67 取込ボタン bt_c1 を定義しています。checkbox 属性を使用して id と name 両方に bt_c1 を設定しています。

js.js
273   functionbt_c1Click(){
         .
         .
288    }
273~288 取込ボタン bt_c1 が押された時の処理を定義します。
274    var xhr = new XMLHttpRequest();
275    xhr.open(“GET”, “/capture”, true);
276    xhr.send(null);
274~276 取込ボタンが押された時サーバーへメソッド GET、URL /capture のリクエストを送信します。
277    const b11 = document.getElementById(‘bt_c1’);
278    const b12 = document.getElementById(‘bt_c2’);
279    b11.checked = true; /* 「取込」ボタンONを保持する */
280    b12.checked = false; /* 「登録」ボタンをOFFする */
281    bt_active(‘bt_c1_label’); /* 「取込」ボタンをアクティブ色に変更 */
282    bt_standby(‘bt_c2_label’); /* 「登録」ボタンをスタンバイ色に変更 */
277~278 取込ボタン bt_c1 、登録ボタン bt_c2 をインスタンス化します。
279~280 取込ボタン ON を保持し、登録ボタン OFF を保持します。
281 関数 bt_active(); がボタンの背景色をトマト色、文字色を白に変更します。(389~394行目)
282 関数 bt_standby(); がボタンの背景色を水色に、文字色を黒色に変更します。(396~401行目)

284    for (leti = 1; i < 17; i++){
285        buf_btc1 = “bt_” + i ;
286        disabale_button(buf_btc1);
287    }

284~287 リモコン送信ボタン bt_1~bt_16 全てを無効にします。

main.cpp

306    //capture 「取込」ボタン bt_c1 に対するレスポンス
307    server.on(“/capture”, HTTP_GET, [](AsyncWebServerRequest*request){
308        Serial.println(“\n「取込」ボタンが押されました。”); //ターミナルにメッセージ出力
309        digitalWrite(ena_receive_PIN, HIGH); //irReceive レベルシフト回路の動作を開始
310        status_information=“「取込」を開始します。リモコン送信機のボタンを押してください。”; //クライアント側で情報を表示する
311        request->send_P(200, “text/plain”, getMsg_information().c_str());
312    });

306~312 URL /capture により取込ボタン bt_c1 からの GET リクエストと認識されます。
308 ターミナルにメッセージを出力します。
309 ena_receive_PIN を HIGH レベルにして赤外線信号受信回路の動作を許可します。
310 内部変数 status_information にメッセージを設定します。
311 クライアント側に text/plane として送信してブラウザ画面に表示します。

437    String receive_IR_process() {
.
.
510    return writeStr;
511    }

437~511 赤外線リモコン信号の受信を処理する関数です。
この関数は Loop 内 357行目で呼び出されて常に動作状態になっています。
取込ボタン bt_c1 が押された時のレスポンスの 309行目で ena_receive_PIN を HIGH レベルに設定することで赤外線リモコン信号受信回路の動作が有効になります。

このとき被学習リモコンのボタンが押されると、赤外線リモコン信号を受信したリモコン受光 IC の出力信号が ESP32 モジュールに入力されることになります。
関数 String receive_IR_process(){} で処理されることにより文字列 writeStr が戻り値として得られます。

登録ボタン(bt_c2)

index.html

68    <divclass=“bt_c2”>
69    <inputtype=“checkbox”id=“bt_c2”name=“bt_c2”onclick=“bt_c2Click();”>
70    <labelid= “bt_c2_label”for=“bt_c2”>登録</label>
71    </div>
68~71 登録ボタン bt_c2 を定義しています。checkbox 属性を使用して id と name 両方に bt_c2 を設定しています。

js.js

197    functionbt_c2Click(){
198        varxhr = newXMLHttpRequest();
199        xhr.open(“GET”, “/memory”, true);
200        xhr.send(null);
201        constb11 = document.getElementById(‘bt_c1’);
202        constb12 = document.getElementById(‘bt_c2’);
203        b11.checked = false; /* 「取込」ボタンをOFFする */
204        b12.checked = true; /* 「登録」ボタンONを保持する */
205        bt_standby(‘bt_c1_label’); /* 「取込」ボタンをスタンバイ色に変更 */
206        bt_active(‘bt_c2_label’); /* 「登録」ボタンをアクティブ色に変更 */
207        enable_all_of_TX_keys(); /* 16個の「リモコン送信ボタン」をすべて許可する */
208    }
197~208 登録ボタン bt_c2 の動作を定義しています。

197~200 登録ボタン bt_c2 を押した時サーバーへメソッド GET、URL /memory のリクエストを送信します。
201~202 取込ボタン bt_c1 と登録ボタン bt_c2 をインスタンス化します。
203~204 取込ボタンを OFF に、登録ボタンを ON に保持します。
205~206 取込ボタンをスタンバイ色(水色)に、登録ボタンをアクティブ色(トマト色)に変更します。
207 16個のリモコン送信ボタンをすべて有効にします。

main.cpp

314    server.on(“/memory”, HTTP_GET, [](AsyncWebServerRequest*request){
315        Serial.println(“\n「登録」ボタンが押されました。”); //ターミナルにメッセージ出力
316        digitalWrite(ena_receive_PIN, LOW); //irReceive レベルシフト回路の動作を停止
317        ena_store = true;
318        num_reg = num_reg + 1; //「リモコン送信」ボタンに登録する登録数を加算する
319        status_information=“登録を開始します。\n登録する「任意」のボタンを押してください。”; //クライアント側で情報を表示する
320        request->send_P(200, “text/plain”, getMsg_information().c_str());
321    });

314~321 URL /memory により登録ボタン bt_c2 からの GET リクエストと認識されます。
315 ターミナルにメッセージを出力します。

316 ena_receive_PIN を LOW レベルにして赤外線リモコン信号受信回路の動作を停止します。
317 ena_store = true; 有効に設定して Flash への書込みを許可します。
318 リモコン送信ボタンに登録する赤外線リモコン信号の登録数を加算します。
319 内部変数 status_information にメッセージを設定します。
320 クライアント側に text/plane として送信してブラウザ画面に表示します。

終了ボタン(bt_c3)

index.html

72    <button class=‘bt_c3’ onclick=‘bt_c3Clicked(this)’ id=‘bt_c3’>終了</button>

72 終了ボタンを定義しています。終了ボタンは状態を保持する必要が無いので属性には checkbox は使わずに button を使用します。

js.js

305    functionbt_c3Clicked(element) {
306        varxhr = newXMLHttpRequest();
307        xhr.open(“GET”, “/finish”, true);
308        xhr.send(null);
309        constb11 = document.getElementById(‘bt_c1’);
310        constb12 = document.getElementById(‘bt_c2’);
311        b11.checked = false; /* 「取込」ボタンをOFFする */
312        b12.checked = false; /* 「登録」ボタンをOFFする */
313        bt_standby(‘bt_c1_label’); /* 「取込」ボタンをスタンバイ色に変更 */
314        bt_standby(‘bt_c2_label’); /* 「登録」ボタンをスタンバイ色に変更 */
315        enable_all_of_TX_keys(); /* 16個の「リモコン送信ボタン」をすべて許可する */
316    }

305~316 終了ボタン bt_c3 の動作を定義しています。
305~308 終了ボタン bt_c3 を押した時サーバーへメソッド GET、 URL /finish のリクエストを送信します。
309~310 取込ボタン bt_c1 と登録ボタン bt_c2 をインスタンス化します。
311~312 取込ボタンと登録ボタンを OFF にします。
313~314 取込ボタンと登録ボタンをスタンバイ色(水色)にします。
315 16個のリモコン送信ボタンすべてを有効にします。

main.cpp

323    server.on(“/finish”, HTTP_GET, [](AsyncWebServerRequest*request){
324        Serial.println(“\n「終了」ボタンが押されました。”); //ターミナルにメッセージ出力
325        digitalWrite(ena_receive_PIN, LOW); //irReceive レベルシフト回路の動作を停止。
326        ena_send = false; //irsend を禁止
327        ena_store = false; //Flash 書込みを禁止
328        ena_restore = false; //Flash 読出しを禁止
329        ena_assign = false; //信号登録禁止
330        status_information=“取込、登録を終了しました。”; //クライアント側で情報を表示する
331        num_reg = 0; //登録数カウンタをリセットする
332        request->send_P(200, “text/plain”, getMsg_information().c_str());
333    });

323 URL /FINISH により終了ボタン bt_c3 からの GET リクエストと認識されます。
324 ターミナルにメッセージを出力します。

325 ena_receive_PIN を LOW レベルに設定して赤外線リモコン信号受信回路の動作を停止します。
326~329 ena_send、ena_store、ena_restore、ena_assign の各フラグを false に設定して赤外線リモコン信号送信、Flash 書込み、Flash 読出し、登録を禁止します。
330 内部変数 status_information にメッセージを設定します。
331 登録数カウンタ num_reg = 0; をリセットします。
332 クライアント側に text/plane として送信してブラウザ画面に表示します。

リモコン送信ボタン(bt_1 ~ bt_16)

リモコン送信ボタン bt_1 ~ bt_16 の動作を説明します。
index.html

18<p>TV</p>
19 <div class=”inline_1″>
20 <button class=”bt_1″ onpointerdown=”bt_1Down(this)” onpointerup=”bt_1Up(this)” id=”bt_1″>電源</button>
         .
23 <button class=”bt_4″ onpointerdown=”bt_4Down(this)” onpointerup=”bt_4Up(this)” id=”bt_4″></button>
24 </div>
25 <br>
         .
39 <p>BOSE</p>
40 <div class=”inline_4″>
         .
45 </div>
46 <br>

18 ~ 46 リモコン送信ボタン bt_1 ~ bt_16 を定義しています。
18 、 39 TV と BOSE の2つのグループに分類するラベルを付けています。
19、26、33、40 ボタンを4個ずつ横並びにするので inline_1~inline_4 のクラス名を付けて分類しています。
各ボタンは button 属性を使って class と id に bt_1 ~ bt_16 を設定しています。
20 ボタン bt_1 を定義していま
onpointerdown=”bt_1Down(this)” によりボタンを押下した瞬間に js.js 38~43 行目で定義した bt1_Down() 関数を実行します。
onpointerup=”bt_1Up(this)” によりボタンを離した瞬間に js.js 44~49 行目で定義した bt1_Up() 関数を実行します。
ボタンが押下されると bt1_Down() によりリモコン信号の送信を開始します。そしてリモコン信号の送信が終了した後はボタンが離されて bt1_Up() までリピート送信を繰り返すことになります。

js.js

38    functionbt_1Down(element){
39        if (document.getElementById(‘bt_c1’).checked == false){
40        varxhr = newXMLHttpRequest();
41        xhr.open(“GET”, “/bt_1_on”, true);
42        xhr.send(null); }
43     }
44    functionbt_1Up(element){
45        if (document.getElementById(‘bt_c1’).checked == false){
46        varxhr = newXMLHttpRequest();
47        xhr.open(“GET”, “/bt_1_off”, true);
48        xhr.send(null); }
49    }


38~49 リモコン送信ボタン bt_1 が押されたときと離されたときの動作を定義しています。
39 取込ボタン bt_c1 が押されていない、つまりチェックが false の場合に以下を実行します。
40~42 リモコン送信ボタンが押された時サーバーへメソッド GET、URL /bt_1_on のリクエストを送信します。
45 取込ボタン bt_c1 が押されていない、つまりチェックが false の場合に以下を実行します。
46~48 リモコン送信ボタンが離された時サーバーへメソッド GET、URL /bt_1_off のリクエストを送信します。
リモコン送信ボタン bt_2_on~bt_16_on、bt_2_off~bt_16_off にはそれぞれ URL を設定しています。

main.cpp

160    server.on(“/bt_1_on”, HTTP_GET, [](AsyncWebServerRequest*request){
161         //タイマー開始、ボタンの番号とラベルを渡す。
162         startTimer(num_period,“1“,”TV電源“);
163    request->send(200, “text/plain”, getMsg_information().c_str());
164    server.on(“/bt_1_off”, HTTP_GET, [](AsyncWebServerRequest*request){
165         stopTimer() //タイマー停止
166    request->send(200, “text/plain”, “OFF”); });
160 URL /bt_1_on によりボタン bt_1 が押された時の GET リクエストと認識されます。
162 関数 startTimer( ); を実行します。
163 クライアント側に text/plane メッセージを送信してブラウザ画面に表示します。
164 URL /bt_1_off によりボタン bt_1 が離された時の GET リクエストと認識されます。
165 関数 stopTimer( ); を実行します。
166 クライアント側に text/plane メッセージを送信してブラウザ画面に表示します。
648    void bt_response(String bt_label, String bt_name){
649      wrfolder = “/bt_” + bt_label; //フォルダ名生成(bt_label の値は 1-16)
650      wrfile = “/bt_” + bt_label;   //ファイル名生成
    .
    .
672    }

648~672 リモコン送信ボタンが押された時の処理になります。
649~650 引数 bt_label を使ってフォルダ名とファイル名を生成します。
この関数は受信した赤外線リモコン信号を Flash に書込んで登録する場合と、登録した赤外線リモコン信号を Flash から読出して送信する場合の2種類の動作に分かれます。

リモコン送信ボタンに登録する場合
取込んだ赤外線リモコン信号を登録する場合は登録ボタン bt_c2 に対するレスポンス 317行目で ena_store = true; に設定されるので、次の内容が実行されます。
653    if (ena_store == true){     //「取込」、「登録」に続けて「リモコン送信」ボタンを押した場合、
654        if ( bt_id!=bt_label){ //複数「登録」で異なる「リモコン送信」ボタンを押した場合、
655        num_reg = 1;            //リモコン送信ボタンに登録するリモコン信号の数を 1 に戻しておく。
656        }  
657        ena_assign = true;        //信号登録許可
658        delay(1000);              //Flash 書込みを待ってから次の処理へ進める。
659       Stringstr_buf = dtostrf(num_reg,2,0,m_buf);  //整数を文字列に変換
660        //クライアント側で情報を表示する
661        status_information=bt_name+” ボタンに”+str_buf+” つめのリモコン信号を「登録」しました。”;
662        //複数登録中にリモコン送信ボタンが変更されたか識別用 bt_id に bt_label (1-16) を初期値として設定する。
663        bt_id=bt_label;
664      }
653~664 当該リモコン送信ボタンに登録する場合を定義しています。
654~656 複数登録で異なるリモコン送信ボタンを押した場合は登録数カウンタ num_reg を 1 に戻します。
657 信号登録許可フラグを有効 ena_assign = true; にします。Loop 内 361行目で ena_store == true、かつ ena_assign == true の条件が成立したので、Flash への書込み登録が開始されます。
658 Flash への書込みが終わるまで 1000ms 待ちます。
659~661 クライアント側で情報を表示するための文字列を生成します。
663 複数登録中にリモコン送信ボタンが変更されたかを識別するための変数 bt_id に引数で受け取った bt_label を設定します。

361    if ((ena_store == true) && (ena_assign == true)){ //Flash 書込み許可、信号登録許可 の場合
             .
368        wrfile=wrfolder+wrfolder+“_”+num_reg+“.txt”;
369        store_Flash( wrfile, writeStr ); //受信した信号を Flash に書込む
             .
374        wrfile=wrfolder+“/num_reg.txt”;
375        Stringstr_buf = dtostrf(num_reg,2,0,m_buf);  //整数を文字列に変換
              .
378        ena_store = true; //Flash 書込み許可
379        store_Flash( wrfile, str_buf ); //登録数カウンタ値を Flash に書込む
380        ena_assign = false; //信号登録禁止
381    }
361~381 登録ボタンに続けてリモコン送信ボタンが押された時の処理です。
368 649行目で生成したフォルダ名と登録数カウンタから赤外線リモコン信号を格納するファイル名を生成します。
369 生成したファイル名 wrfile と 493行目で取込んだ赤外線リモコン信号 writeStr を引数として Flash に書込みます。

374 649行目で生成したフォルダ名と登録数カウンタから登録数カウンタを格納するファイル名を生成します。
305 整数 num_reg を文字列に変換して Flash に書込む準備をします。
378 書込みフラグを有効にします。
379 374行目で生成したファイル名と375行目で変換した文字列を引数として Flash に書込みます。

例えばリモコン送信ボタン bt_1 に 2 つのリモコン信号を登録した場合に生成されるファイル名と Flash に書込まれる内容は次のようになります。
フォルダ名      ファイル名
/bt_1 —–|—–bt_1_1.txt      (リモコン信号1:hex文字列)
             |—–bt_1_2.txt      (リモコン信号2:hex文字列)
             |—–num_reg.txt   (2:文字列)

リモコン信号を読出して送信する場合
ena_store が false の時にリモコン送信ボタンが押されると以下の動作をします。

666    elseif ((ena_store == false)){
667        ena_restore = true;  //Flash 読出しを許可
668        ena_send = true;     //irSend リモコン信号送信を許可
669         //クライアント側で情報を表示する
670        status_information=bt_name+” リモコン信号を送信しました。”;
671    }
666~671 リモコン送信ボタンが押された時の処理になります。
667    Flash 読出し許可フラグを有効にします。
668    リモコン信号送信許可フラグを有効にします。
670    クライアント側で情報を表示するための文字列を生成します。
385    if ((ena_restore == true) && (ena_send == true)){ //Flash 読出し許可、irSend 許可 の場合
.
.
416    }

385~416
Loop 処理中に Flash 読出し許可フラグ、リモコン信号送信許可フラグともに有効となった場合の処理です。

387  //登録数カウンタ num.reg の値を Flash から読出す
388    wrfile = wrfolder + “/num_reg.txt”; //ファイル名生成 (例) wrfile = “/bt_1/num_reg.txt”
389    restore_Flash(wrfile);              //Flash から読出し
390    int count = atoi(readStr.c_str());  //for ループでカウントするために文字列を整数に変換する。
391    
392    //「リモコン送信」ボタンに何も登録されていなければ処理を終了する。
393    if ( count == 0){
394      ena_restore = false; //Flash 読出し禁止
395      ena_send = false;    //irSend 禁止
396      status_information = “このボタンには何も登録されていません。”; //クライアント側で情報を表示する
397      cancelPeriodicLoop() ; //タイマー停止
398      Serial.println(status_information); //ターミナル出力
399    }

387~399 Flash から読出す準備をします。
388    押されたボタンに対応した登録数カウンタ値を読取るためにファイル名を生成します。
389    Flash から登録数カウンタの値を読出して readStr に格納します。
390    読出した文字列 readStr を 整数 count に変換します。
393~399 count が 0 の場合は当該ボタンに何も登録されていないので、ena_send == false に設定してリモコン信号送信許可フラグを無効にします。そしてボタンに何も登録されていない旨のメッセージをクライアント側とターミナルに出力します。

400    //「リモコン送信」ボタンに登録された登録数 num_reg 回だけ Flash から読出して送信する。
401    for (inti =1 ; i <= count; i++){ // count は Flash から読出した登録数を整数化したもの
402    
403      //ファイル名生成 (例) wrfile = “bt_1/bt1_1.txt”
404      wrfile=wrfolder+wrfolder+“_”+i+“.txt”;
405      restore_Flash(wrfile); //FLAH からリモコン信号を読出す
406      //delay(100);
407      send_IR_process(readStr);  //リモコン信号送信
408    
409     //ループの最後でフラグを初期化しておく
410      if ( i == count ){
411        ena_restore = false; //Flash 書込み禁止
412        ena_send = false;    //irSend 禁止
413        triggerPeriodicLoop(); //待機中のタイマースレッド起動
414      }
415    }
401~415 リモコン送信ボタンに登録された登録数 count 回だけ Flash から読出してリモコン信号を送信します。
401    登録数 count 回 for ループを繰り返します。
404~407
当該ボタンに対応したフォルダ名とループ回数 i からファイル名を生成して、Flash から readStr を読出して赤外線リモコン信号を送信します。
410~414 for ループの最後に Flash 書込み許可フラグとリモコン信号送信許可フラグを無効化した上で、リピート送信のために待機中のタイマースレッドを起動します。

リピート送信する場合

420     if ( Repeat_flag == true){ // リピート信号開始フラグが true の場合
421         ena_send = true;                       //irSend 許可
422         send_IR_process(hex_repeat, 1); //リピート信号送信
423         //send_IR_process(readStr, 1);    //リモコン信号送信を繰り返すことも可能
424         Repeat_flag = false;
425     }
420~425 Frame 信号送出後もリモコン送信ボタンが押されている( Repaeat_flag == true)場合は、ボタンが離されるまでリピート信号を繰り返し送信します。
421   リモコン信号送信許可フラグが有効
422   第一引数を hex_repeat、第二引数を 1 にするとリピート信号が送信されます。
423   第一引数を readStr にすればリモコン信号(フレーム)を繰り返すこともできます。
424   繰り返しフラグを無効にします。

タイマースレッドを使って繰り返し送信する

動作概要

*図2 ボタンを押した時のタイミングチャート

ボタンを長く押した時は、ボタン押下と同時にタイマーを待機状態で立上げます。 赤外線リモコン信号(Frame)の送信が終わった段階でボタンが押されていると is_triggered = true となりタイマーのカウントがが開始されます。そしてボタンが離されたとき stop_requested = true によりタイマーが終了します。
ボタンを短く押した時は、ボタン押下と同時にタイマーを待機状態で立上げます。 赤外線リモコン信号(Frame)の送信が終わるまでにボタンが離されていると、stop_requested = true によりタイマーが終了します。

ボタンに何も登録されていないときは、赤外線リモコン信号(Frame)は送信されず、is_cancelled = true によりタイマーをキャンセルしています。

バックグラウンドで周期実行される関数

677   void periodicLoop(void *parameter){
678         uint32_t interval = (uint32_t)parameter;
679
680         // is_triggered または is_cancelled になるまで待つ
681         while (!is_triggered && !is_cancelled && !stop_requested) {
682         vTaskDelay(pdMS_TO_TICKS(1));
683         }
684
685         // キャンセル
686         if (is_cancelled) {
687         worker_task = NULL;
688         vTaskDelete(NULL);
689         return;
690         }
691        
692         // 周期処理開始
693         TickType_t lastWakeTime = xTaskGetTickCount();
694         while (!stop_requested && !is_cancelled) {
695         // ★ 周期的に実行する処理
696         Repeat_flag = true; // 繰り返しフラグを true にする
697         vTaskDelayUntil( &lastWakeTime, pdMS_TO_TICKS( interval ));
698         }
699
700         // 終了
701         worker_task = NULL;
702         vTaskDelete(NULL);
703  }
677~703 バックグラウンドで周期実行される関数 periodicLoop を定義します。 パラメータ *parameter の値で周期 [ms] を指定します、
678     ポインタを整数値に変換します。
681~683 タイマーの開始を is_triggered、is_cancelled、または stop_requested が true になるまで待ちます。
682 FreeRTOS が提供する関数で Ticks 数の間現在のタスクをウエイトします。 ミリ秒単位の時間を FreeRTOS システムの最小時間単位 Ticksに変換するマクロ pdMS_TO_TICKS(1) を引数としています。 1 ミリ秒だけこの関数を実行することでウォッチドックタイマーの作動による強制リブートを防ぐことができます。
686~690 キャンセル is_cancelled の場合タイマーを終了します。
693 Ticks 数を表す整数型 TickType_t に、システム起動から現在までの総 Ticks を取得する xTaskGetTickCount() を
最後にタスクが実行された時刻として保存します。
694~698 stop_requested = false かつ is_cancelled = false の場合周期処理を開始します。 ここでは Repeat_flag = true の処理を周期的に実行しています。loop 処理内の 420行目から始まるリピート信号送信処理が実行されます。
697 第 1 引数に基準とする前回起動時刻、第 2 引数に繰り返し周期を設定して正確な周期でタスクを実行します。

701~702 タイマー終了処理を行います。

タイマー開始

707   void startTimer(uint32_t interval, String name,String label){
708       if (worker_task != NULL) {
709           return;
710       }
711
712       is_triggered = false;
713       is_cancelled = false;
714       stop_requested = false;
715       bt_response (name.c_str(), label.c_str());
716
717       xTaskCreate( periodicLoop, “periodicLoop”, 4096, (void *)interval, 1, &worker_task );

718   }
707~718タイマーを開始する関数を定義します。

繰り返し周期 interval、ボタンの名前 name、ラベル label をパラメータとして指定します。

708~710 すでにタイマーが動作しているときは関数を抜けます。
712~714 is_triggered、is_cancelled、stop_requested 各フラグをリセットします。
715 ボタンの名前 name、ラベル label を引数としてボタンを押した時の処理を行う関数 bt_response を実行します。
717 FreeRTOSで新しいタスク(スレッド)を作成して起動する関数です。
第 1 引数 : 実行する関数名、第 2 引数 : タスク識別名、第 3 引数 : スタックメモリのサイズ、第 4 引数 : 関数に渡すポインタ型引数、第 5 引数 : タスクの優先度、第 6 引数 : タスクハンドルを受け取る引数。

タイマー終了

722   void stopTimer(){
723      stop_requested = true;
724  }
723 stop_requested = true により 694~698 行の周期処理を終了します。

待機中のタイマースレッドの起動トリガ

728   void triggerPeriodicLoop() {
729       is_triggered = true; // 起動トリガセット
730  }
729 待機中のタイマースレッドの起動トリガ is_triggered = true により 681~683行目の while ループを抜けて周期処理を開始します。

タイマースレッドのキャンセルトリガ

734   void cancelPeriodicLoop() {
735       is_cancelled = true; // キャンセルフラグセット
736   }
735 キャンセルフラグを is_cancelled = true に設定することにより 694~698 行目の周期処理ループを抜けてタイマーをキャンセルします。

おわりに

完全リタイヤ後に見よう見まねで始めたソフトウェア開発なので拙いところが多々あるかと思います。

開発中は試行錯誤が続くので途中で残したドキュメントは構想や目標と結果が入り乱れたものになりがちです。きちんと整理しておきたかったので本編をまとめてみました。

自分自身でレビューして再検証も多く相当時間を費やしましたが、その分理解が深まりました。次の開発へ向けて役立つものになりました。

リンク

ESP32を使って学習リモコンを作る(1) 学習リモコンでどんなことができるの?
ESP32を使って学習リモコンを作る(2) ハードウェア
ESP32を使って学習リモコンを作る(3) ソフトウェア 赤外線リモコン受信( irReceive )
ESP32を使って学習リモコンを作る(4) ソフトウェア 赤外線リモコン送信( irSend )
ESP32を使って学習リモコンを作る(5) ソフトウェア webserver
ESP32を使って学習リモコンを作る(6) ソフトウェア Flash 読書き(SPIFFS)
ESP32を使って学習リモコンを作る(71) ソフトウェア 本機のプロジェクト
ESP32を使って学習リモコンを作る(81) ソフトウェア 本機プロジェクトの勘所
ESP32を使って学習リモコンを作る(9) ハードウェア設計の勘所
ESP32を使って学習リモコンを作る(10) 学習リモコンを操作した時のターミナル出力
Smart_Remote_Controller プロジェクト GitHub

(YI)

コメント

error: Content is protected !!
タイトルとURLをコピーしました