でんげき☆ Network Service

Raspberry Pi 4 で運用実験中 Connect checker

タグ「Arduino」を含む投稿6件]

(M5) ATOM Lite でまぁまぁスマートなリモコンを作ろう

202307171653523-admin.png
 
今年の初めに…かれこれ 18 年くらい住んでたボロアパートをぶっ壊すってんで追い出されて今度は高級マンションの最上階に引っ越してきました!(>_<)w そんな高級マンションには…ご入居前リフォームで 2022 年製のエアコンに交換されていたのはいいんだけどちょっと使いづらいんですよね
何が使いづらいかっていうと…リモコンでのタイマーセットが不便なんですよ 以前のボロアパートで使ってたエアコンはリモコンに RTC(時計) が実装されていたのでタイマーセットも絶対時間で行うことができていたんですよ それがご新居のエアコンのリモコンには RTC(時計) が付いてないもんだでタイマーセットしようとすると「7時間後」って感じの相対時間での設定になるんですよ 不便ですよね(-_-;)
そんな訳なんでもちっとタイマーセットしやすいリモコンをどうにかするっていうか…せっかくなんでスマホちゃん等と連動させて絶対時間でエアコンの On/Off を制御できるリモコンを作ってみることにしました

昨年に「安いじゃん!」って思って買っておいた (M5) ATOM Lite が手元にあるんでそれを使うことにしました 今買うなら (M5) ATOMS3 Lite  のほうがいいのかも知れないけど未確認です汗
それと CGI っていうか Perl が使える Web サーバが必要です 今回は Raspberry Pi OS にセットアップした Apache を使ってるけど…プロバイダなどで供されている CGI の使える Web サーバでも大丈夫だと思います

そうそう今回使う (M5) ATOM Lite には RTC が載っていません それ即ち時計が使えないって事なんでその辺をどうにかしなければなりません
そこで一定時間(今回は約30秒)ごとに Web サーバにアクセスして…アクセスされた Web サーバが現在時刻(とエアコン設定内容)を返しそれにより時刻の判定を行うようにしました

  :

それでは最初に (M5) ATOM Lite の方から初めていきましょう 開発環境は Linux っていうか Snap 版の Arduino IDE を使いました まぁ Arduino IDE であれば Windows 版とかでも同じく使えるとは思いますが未確認です汗 そんな Arduino IDE の設定は M5StickC Plus の開発環境を構築する が参考になると思います ボードを追加した後に「M5Stack-ATOM」を設定すれば ok です

お次は (M5) ATOM Lite 向けのライブラリのインストールですかね これは「ツール」メニューから「ライブラリを管理…」を選んで検索窓に必要なキーワードを入れて出てきたものをインストールってする感じですかね

20230717165352-admin.png
m5atom」と入れて出てきた「M5Atom」をインストールする

202307171653522-admin.png
irremoteesp8266」と入れて出てきた「IRremoteEsp8266」をインストールする

202307171653521-admin.png
heatpumpir」と入れて出てきた「HeatpumpIR」をインストールする

これでライブラリ類の準備も完了ですかね それではコードを見ていきましょう…っていうか手探りの「どうにか動く」テスト的なものだったのが意外と問題なく動いてしまって「あーなんか書き直すのもめんどくさいなー」って気分が昂ぶってきたんでそんなテスト的なものをそのまま載せています 動作未保証なのはもちろんとして…諸々をきちっと書き直して使ってください それだけが私の望みです汗

  :

----- Start of remcon.ino -----
#include <M5Atom.h> // Atom のヘッダファイルを準備
#include <WiFiMulti.h>
#include <HTTPClient.h>
#include <MitsubishiHeatpumpIR.h>

const char* url = "http://user:pass@hoge.orz/remocon/read.cgi"; // 現在時刻&設定内容の場所
const char* ssid = "WiFi_ssid";  // SSID
const char* password = "WiFi_pass";  // パスワード

const uint16_t kIrLed = 12; // 赤外線 LED 送信用のポート番号
IRSenderBitBang irSender(kIrLed); // 赤外線LEDが接続されているピン番号
HeatpumpIR *airCon = new MitsubishiFDHeatpumpIR();  // 三菱エアコンのインスタンス

// FastLED ライブラリの設定(CRGB構造体)
CRGB dispColor(uint8_t r, uint8_t g, uint8_t b) {
  return (CRGB)((r << 16) | (g << 8) | b);
}

WiFiMulti WiFiMulti;  // Wi-Fi を使いたい

void setup() {
  Serial.begin(115200);
  Serial.println("");

  M5.begin(true, false, true);  // 本体初期化(UART有効, I2C無効, LED有効)
  M5.dis.drawpix(0, dispColor(0, 0, 0));  // LED全消灯(赤, 緑, 青)

  WiFiMulti.addAP(ssid, password); // 「SSID」と「パスワード」を設定しておく
  Serial.print("Fi-Fi Connecting.");
}

void loop() {
  while (WiFiMulti.run() != WL_CONNECTED) {
    Serial.print(".");
    M5.dis.drawpix(0, dispColor(255, 0, 0));  //LED(赤)
    delay(500);
  }
  Serial.print("ADDR: ");
  Serial.println(WiFi.localIP());

  M5.dis.drawpix(0, dispColor(20, 20, 20)); // LED(白)

  HTTPClient http;
  http.begin(url);
  Serial.print("GET: ");
  int httpCode = http.GET();  // GET メソッドでファイルを取得
  Serial.println(httpCode);
//  Serial.println(http.errorToString(httpCode));

  if (httpCode > 0) {
    String htdoc = http.getString();  // 取得したファイルの内容
    Serial.println(htdoc);

//  YYYY/MM/DD HH:YY:MM(WDAY)\ts?,HH:MM e?,HH:MM m?
//  現在年/月/日 時:分:秒(曜日)\t開始フラグ,時:分 終了フラグ,時:分 モード
    String now_hour = htdoc.substring(11, 13);  // 現在時を取得
    String now_min = htdoc.substring(14, 16);   // 現在分を取得

    String st = htdoc.substring(25, 27);        // 開始フラグを取得
    String ed = htdoc.substring(34, 36);        // 終了フラグを取得

    String st_hour = htdoc.substring(28, 30);   // 開始時
    String st_min = htdoc.substring(31, 33);    // 開始分

    String ed_hour = htdoc.substring(37, 39);   // 終了時
    String ed_min = htdoc.substring(40, 42);    // 終了分

    String md = htdoc.substring(43, 45);        // モードを取得

    if (st == "s1" && now_hour == st_hour && now_min == st_min) {
      if (md == "m1") {
        Serial.println("Cool");
        M5.dis.drawpix(0, dispColor(0, 0, 255));  //LED(青)
        airCon->send(irSender, POWER_ON, MODE_COOL, FAN_1, 28, VDIR_DOWN, HDIR_AUTO); // 冷房
      } else if (md == "m2") {
        Serial.println("Hot");
        M5.dis.drawpix(0, dispColor(255, 255, 0));  //LED(黄)
        airCon->send(irSender, POWER_ON, MODE_HEAT, FAN_1, 16, VDIR_DOWN, HDIR_AUTO); // 暖房
      } else {
        Serial.println("Dry");
        M5.dis.drawpix(0, dispColor(0, 255, 0));  //LED(緑)
        airCon->send(irSender, POWER_ON, MODE_DRY, FAN_1, 28, VDIR_DOWN, HDIR_AUTO); // 除湿
      }
    }

    if (ed == "e1" && now_hour == ed_hour && now_min == ed_min) {
      Serial.println("--- Stop ---");
      M5.dis.drawpix(0, dispColor(255, 0, 255));  //LED(紫)
      airCon->send(irSender, POWER_OFF, MODE_COOL, FAN_1, 28, VDIR_DOWN, HDIR_AUTO); // 電源 Off
    }

  } else {  // ファイルの取得に失敗している
    Serial.println("Error");
  }

  delay(30000);
}
----- End of remcon.ino -----
※デバッグ用のシリアル通信のアレが残っているんで実稼働時には消したほうがいいかも知れないです

冒頭の #include <MitsubishiHeatpumpIR.h> は…まぁ見ての通り三菱社製のエアコン制御をする時に使うものです これが他社製であれば
#include <DaikinHeatpumpIR.h>  // ダイキン社製
#include <FujitsuHeatpumpIR.h>  // 富士通社製
#include <HitachiHeatpumpIR.h>  // 日立社製
#include <PanasonicHeatpumpIR.h>  // パナソニック社製
#include <ToshibaHeatpumpIR.h>  // 東芝社製
こんな感じになるんじゃないかと思います(未確認) 詳しくは Aruduino IDE の関連フォルダ Arduino/libraries/HeatpumpIR/ を眺めてみるといいかと思います

  :

データ(と時間)を取ってくる Web サーバの設定と… Wi-Fi 接続に関する設定を行います
const char* url = "http://user:pass@hoge.orz/remcon/read.cgi"; // 現在時刻&設定内容の場所
const char* ssid = "WiFi-ssid";  // SSID
const char* password = "WiFi-pass";  // パスワード

保安上っていうか恐らくベーシック認証なりをかけて運用すると思うのですが…その際に ID とパスワードの入力を自動化しておかねばなりません そんなときは http://user:pass@hoge.orz/remcon/read.cgi って感じで URL の前に「user:pass@」を付ければ自動で認証してくれます この書き方は一般的なブラウザでも(警告が出るかもだけど)使えます
その次に Wi-Fi の SSID とパスワードの設定を行います 上記のベーシック認証と併せて平文のパスワードを記すことになるので取り扱いには十分注意してください

  :

HTTPClient http;
http.begin(url);
int httpCode = http.GET();  // GET メソッドでファイルを取得

Wi-Fi 接続が確立したら目的のサーバからデータを取ってきます 後でも説明しますが…ここで得られるデータは次のようになっています

YYYY/MM/DD HH:YY:MM(WDAY)[タブ(\t)]s?,HH:MM e?,HH:MM m?
[タブ(\t)] の前半がアクセスした時点での日時
[タブ(\t)] の後半がエアコンの開始/終了の時刻とモードを指しています
例えば 12 時 34 分に作動させたい場合は s1,12:34 となります その次が e1,23:45 だったら 23 時 45 分に電源を切る動作となります これが s0 もしくは e0 だった場合は何も動作しないようにしています
行末の m? は動作モードを指しています m1 が冷房 m2 が暖房 m3 が除湿です 
http.GET(); した際のバッファサイズが不明だったんで…できるだけ切り詰めて必要最小限の内容にしました バッファに余裕があればもっと見やすい内容にするのもアリかもしれませんね

  :

そして実際に赤外線で信号を送信する部分です
airCon->send(irSender, POWER_ON, MODE_COOL, FAN_1, 28, VDIR_DOWN, HDIR_AUTO); // 冷房

各パラメータの意味は以下のようになっています
// airCon->send( irSender, POWER_**, MODE_**, FAN1, 温度設定, VDIR_** , HDIR_** ); // 設定項目 ※温度設定は「整数値」で
//  Arduino/libraries/HeatpumpIR/HeatpumpIR.h
//
// ・POWER (電源)
//    POWER_ON (on)
//    POWER_OFF (off)
// ・MODE (モード)
//    MODE_AUTO (自動)
//    MODE_COOL (冷房)
//    MODE_HEAT (暖房)
//    MODE_DRY (除湿)
//    MODE_ON (不明)
//    MODE_OFF (不明)
// ・FAN (風量)
//    FAN_AUTO
//    FAN_1 (弱)
//    FAN_2
//    FAN_3
//    FAN_4
//    FAN_5 (強)
// ・VDIR (風向高さ)
//    VDIR_AUTO
//    VDIR_UP
//    VDIR_MUP
//    VDIR_MIDDLE
//    VDIR_MDOWN
//    VDIR_DOWN
// ・HDIR (風向左右)
//    HDIR_AUTO
//    HDIR_LEFT
//    HDIR_MLEFT
//    HDIR_MIDDLE
//    HDIR_MRIGHT
//    HDIR_RIGHT

エアコンの設定温度は整数値で指定する必要があるようです 最近は 0.5℃ 間隔で設定できるエアコンが増えてる感じですが…そこまで細かい制御には対応できないっぽいです また今回のプログラムでは設定温度を即値の決め打ちしています 使い勝手を高めるのであればこの辺も外部から変更できるようにすべきですね

だいたいこんな感じですかね これを delay(30000); して約 30 秒間隔のループで回しています なにかの都合…例えば http.GET(); した際にサーバの返答が遅れたりするとループ間隔が長くなり指定された時間を通り越してしまうかも?って思ってこのくらいの間隔にしました まぁこの辺は各環境によりけりで程よい設定にしてもらえればなぁって思います

  :

さて…お次は UI っていうか Web サーバ側の云々ですかね 用意するのは 4 つのファイル(index.html, index.css, index.cgi, read.cgi)とスクリプトが生成する 1 つのファイル(data.txt)です

----- Start of index.html -----
<!DOCTYPE html>
<html lang="ja">
<head>
   <meta charset="UTF-8">
   <!-- ビューポートの設定 -->
   <meta name="viewport" content="width=device-width,initial-scale=1">
   <!-- スタイルシートの読み込み -->
   <link href="./index.css" rel="stylesheet">
   <title>M5 Atom Lite タイマー予約</title>
</head>
<body>

<center><div class="red"></div></center>
<center><form action="./index.cgi" method="get">
   <div>
      <label for="name"><a href="./index.cgi">M5 Atom Lite タイマー予約</a></label>
   </div>
   <div><hr class="double" width=80% noshade></div>
   <div>
      <label><input class="st" type="checkbox" name="st" value="1">「入り」を使う</label>
   </div>
   <div>
      <select name="st_hour">
         <option class="st_hour" value="00">00</option>
         <option class="st_hour" value="01">01</option>
         <option class="st_hour" value="02">02</option>
         <option class="st_hour" value="03">03</option>
         <option class="st_hour" value="04">04</option>
         <option class="st_hour" value="05">05</option>
         <option class="st_hour" value="06">06</option>
         <option class="st_hour" value="07">07</option>
         <option class="st_hour" value="08">08</option>
         <option class="st_hour" value="09">09</option>
         <option class="st_hour" value="10">10</option>
         <option class="st_hour" value="11">11</option>
         <option class="st_hour" value="12">12</option>
         <option class="st_hour" value="13">13</option>
         <option class="st_hour" value="14">14</option>
         <option class="st_hour" value="15">15</option>
         <option class="st_hour" value="16">16</option>
         <option class="st_hour" value="17">17</option>
         <option class="st_hour" value="18">18</option>
         <option class="st_hour" value="19">19</option>
         <option class="st_hour" value="20">20</option>
         <option class="st_hour" value="21">21</option>
         <option class="st_hour" value="22">22</option>
         <option class="st_hour" value="23">23</option>
      </select>時
      <select name="st_min">
         <option class="st_min" value="00">00</option>
         <option class="st_min" value="10">10</option>
         <option class="st_min" value="20">20</option>
         <option class="st_min" value="30">30</option>
         <option class="st_min" value="40">40</option>
         <option class="st_min" value="50">50</option>
      </select> 分
   </div>
   <div><hr class="double" width=80% noshade></div>
   <div>
      <label><input class="ed" type="checkbox" name="ed" value="1">「切り」を使う</label>
   </div>
   <div>
      <select name="ed_hour">
         <option class="ed_hour" value="00">00</option>
         <option class="ed_hour" value="01">01</option>
         <option class="ed_hour" value="02">02</option>
         <option class="ed_hour" value="03">03</option>
         <option class="ed_hour" value="04">04</option>
         <option class="ed_hour" value="05">05</option>
         <option class="ed_hour" value="06">06</option>
         <option class="ed_hour" value="07">07</option>
         <option class="ed_hour" value="08">08</option>
         <option class="ed_hour" value="09">09</option>
         <option class="ed_hour" value="10">10</option>
         <option class="ed_hour" value="11">11</option>
         <option class="ed_hour" value="12">12</option>
         <option class="ed_hour" value="13">13</option>
         <option class="ed_hour" value="14">14</option>
         <option class="ed_hour" value="15">15</option>
         <option class="ed_hour" value="16">16</option>
         <option class="ed_hour" value="17">17</option>
         <option class="ed_hour" value="18">18</option>
         <option class="ed_hour" value="19">19</option>
         <option class="ed_hour" value="20">20</option>
         <option class="ed_hour" value="21">21</option>
         <option class="ed_hour" value="22">22</option>
         <option class="ed_hour" value="23">23</option>
      </select>時
      <select name="ed_min">
         <option class="ed_min" value="00">00</option>
         <option class="ed_min" value="10">10</option>
         <option class="ed_min" value="20">20</option>
         <option class="ed_min" value="30">30</option>
         <option class="ed_min" value="40">40</option>
         <option class="ed_min" value="50">50</option>
      </select>分
   </div>
   <div><hr class="double" width=80% noshade></div>
    <div>
      <input class="md" type="radio" name="md" value="1">冷房
      <input class="md" type="radio" name="md" value="2">暖房
      <input class="md" type="radio" name="md" value="3">除湿
   </div>

   <div><hr class="hrline" width=80% noshade></div>
   <div>
      <input class="submit" type="submit" value="予約">
   </div>
</form></center>

</body>
</html>
----- End of index.html -----
index.html ってファイル名なんで…デフォルトで表示されるファイルっぽいですが実際にはそのような用途には使っていません デザインしやすいようにファイルを分割してあるだけで実際に表示は index.cgi が行います

----- Start of index.css -----
/* ボディ */
body {   /* 一般的な表示部 */
   margin: 0 ;
   padding: 0 ;
   font-size: 20px ;
   line-height: 3 ;
}

input {   /* チェックボックス */
   width:         25px;
   height:         25px;
   -moz-transform:      scale(1.4);
   -webkit-transform:   scale(1.4);
   transform:      scale(1.4);
}

select {   /* プルダウンメニュー */
   font-size: 30px ;
}

.hrline {   /* 罫線(?) */
   border: none;
   background-color: #fff;
   border-width: 1px 0 0 0;
   border-top: double;
   border-color: black;
}

.submit {   /* 送信ボタン */
   width:         100px;
   height:         40px;
   -moz-transform:      scale(1.4);
   -webkit-transform:   scale(1.4);
   transform:      scale(1.4);
}

.red {   /* 設定変更した */
   font-size: 40px ;
   color: #ff0000;
}

/* スマホだけに適用するCSS */
@media screen and ( max-width:479px ) {
   body {   /* 一般的な表示部 */
      font-size: 20px ;
   }

   input {   /* チェックボックス */
      width:         25px;
      height:         25px;
      -moz-transform:      scale(1.4);
      -webkit-transform:   scale(1.4);
      transform:      scale(1.4);
   }

   select {   /* プルダウンメニュー */
      font-size: 25px ;
   }

   .hrline {   /* 罫線(?) */
      border: none;
      background-color: #fff;
      border-width: 1px 0 0 0;
      border-top: double;
      border-color: black;
   }
   .submit {   /* 送信ボタン */
      width:         100px;
      height:         40px;
      -moz-transform:      scale(1.4);
      -webkit-transform:   scale(1.4);
      transform:      scale(1.4);
   }

   .red {   /* 設定変更した */
      font-size: 40px ;
      color: #ff0000;
   }
}
----- End of index.css -----
でっか文字な表示にしたくて見様見真似で用意しました 見様見真似の即興ってことで CSS について何一つ理解していません汗 おかしな所しかないと思うけど…まぁその辺は生温かい目で見守って頂けると幸いに思います(瀧汗

----- Start of index.cgi -----
#!/usr/bin/perl

$datafile = "./data.txt";   # ATOM Lite が読みにくるファイル
$htmlfile = "./index.html";   # ベースとなる HTML ファイル

if ($ENV{'REQUEST_METHOD'} eq 'POST') {   # POST メソッドで送られてきた場合
   read(STDIN, $query, $ENV{'CONTENT_LENGTH'});
} else {   # GET メソッドで送られてきた場合
   $query = $ENV{'QUERY_STRING'};
}

#### ATOM Lite が読みにくるデータファイルを作成する ####
open (DATA, $datafile);
$data = <DATA>;   # 既存のデータファイルを読み内容を取り出しておく
if ($data =~ /s(\d),(\d\d):(\d\d) e(\d),(\d\d):(\d\d) m(\d)/) {
   $in{'st'} = $1;
   $in{'st_hour'} = $2;
   $in{'st_min'} = $3;
   $in{'ed'} = $4;
   $in{'ed_hour'} = $5;
   $in{'ed_min'} = $6;
   $in{'md'} = $7;
}
close (DATA);

if ($query) {   # データを受信していた場合の処理
   open (DATA, ">", $datafile);   
   foreach $data (split(/&/, $query)) {   # 「&」で分割して全て処理する
      ($key, $value) = split(/=/, $data);   # 「name=value」のそれぞれに分割して連想配列を作っていく
      $value =~ s/\+/ /g;   # 以下ざっくりとした URL デコードする
      $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack('C', hex($1))/eg;
      $value =~ s/\t//g;
      $in{"$key"} = $value;   # value = $in{"$name"} の連想配列が出来上がる
   }
   # 送られてきた内容を適用してデータファイルを更新
   if (!($query =~ /st=/)) { $in{'st'} = '0'; }   # チェックボックスの挙動がアレなんで個別対応(汗
   if (!($query =~ /ed=/)) { $in{'ed'} = '0'; }   # チェックボックスの挙動がアレなんで個別対応(汗
   if ($in{'st'}) { print DATA "s1,"; } else { print DATA "s0,"; }
   if ($in{'st_hour'}) { printf DATA "%02d:", $in{'st_hour'}; } else { print DATA "00:"; }
   if ($in{'st_min'}) { printf DATA "%02d ", $in{'st_min'}; } else { print DATA "00 "; }
   if ($in{'ed'}) { print DATA "e1,"; } else { print DATA "e0,"; }
   if ($in{'ed_hour'}) { printf DATA "%02d:", $in{'ed_hour'}; } else { print DATA "00:"; }
   if ($in{'ed_min'}) { printf DATA "%02d ", $in{'ed_min'}; } else { print DATA "00 "; }
        if ($in{'md'} eq "1") { print DATA "m1\n"; } elsif ($in{'md'} eq "2") { print DATA "m2\n"; } else { print DATA "m3\n"; }
   close (DATA);
}

open (IN, $htmlfile);   # 以下ベースとなる HTML ファイルを読み込み設定時間などを反映させる

print "Content-type: text/html; charset=UTF-8\n\n";
@lines = <IN>;
foreach (@lines) {
   if ($query) { s/(<div class="red">)(<\/div>)/$1設定しました$2/; }

   if ($in{'st'} eq "1") { s/(class="st")/$1 checked/; }
   if ($in{'st_hour'}) { s/(class="st_hour" value=")($in{'st_hour'}")/$1$2 selected/; }
   if ($in{'st_min'}) { s/(class="st_min" value=")($in{'st_min'}")/$1$2 selected/; }
   if ($in{'ed'} eq "1") { s/(class="ed")/$1 checked/; }
   if ($in{'ed_hour'}) { s/(class="ed_hour" value=")($in{'ed_hour'}")/$1$2 selected/; }
   if ($in{'ed_min'}) { s/(class="ed_min" value=")($in{'ed_min'}")/$1$2 selected/; }
        if ($in{'md'}) { s/(name="md" value=")($in{'md'}")/$1$2 checked="checked"/; }

   print $_;
}
close (IN);
----- End of index.cgi -----
実際に表示を受け持つ index.cgi です 表示を受け持ちつつ…フォームで入力された値を解釈して data.txt を生成します そしてそのフォームで入力された値 index.html の内容を書き換えて表示するようになっています こうしておけば以前の設定内容を次回の設定に引き継げるようになるので便利かなーって思いまして
そんな訳なんで .htaccess で Web サーバの設定変更が可能な場合は DirectoryIndex index.cgi としてデフォルトで読み出されるファイルに設定しておくといいでしょう

あとこの index.cgi は GET メソッドでデータを受けれられるので…極端な話パラメータ付きの URL を直打ちすることでも設定が可能です 例えば 08 時 20 分冷房 で作動させ 19 時 30 分 に電源を切る場合だと
http://user:pass@hoge.orz/remocon/index.cgi?st=1&st_hour=08&st_min=20&ed=1&ed_hour=19&ed_min=30&md=1
こんな感じの URL になります それ即ち wget や curl などのコマンドでアクセスすることが可能となり…さらに cron などを使えば毎日の動作や毎週の動作といった感じで自動化することも可能になります いや実際に試してはないですけどたぶん大丈夫だと思います汗

----- Start of read.cgi -----
#!/usr/bin/perl

print "Content-type: text/plain; charset=UTF-8\n\n";

# 現在日時の表示
@wdays = ( "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
($sec, $min, $hour, $mday, $mon, $year, $wday) = localtime(time());
printf "%04d/%02d/%02d %02d:%02d:%02d(%s)\t", $year + 1900, $mon + 1, $mday, $hour, $min, $sec, $wdays[$wday];

$datafile = "./data.txt";   # ブラウザで設定した内容のファイル
open (DATA, $datafile);
print <DATA>;   # ファイルの 1 行目を読み取り表示する
close (DATA);
----- End of read.cgi -----
(M5) ATOM Lite がアクセスしてくるファイル(CGI)です 現在日時を整形して表示して…あとは index.cgi が生成した data.txt の内容をくっつけて送信してくれます

  :

ごちゃごちゃ長くなったんで以下にまとめときます 「名前を付けてリンク先を保存」とかすれば手間がなくていいと思いますたぶん
remcon.ino   (M5) ATOM Lite 用 Arduino Sketch ファイル
index.html   表示のベースとなる HTML ファイル
index.css   でっか文字にするためのアレ
index.cgi   ページ表示しつつデータファイルを作成する
read.cgi   (M5) ATOM Lite が読みに来る先のやつ
data.txt  index.cgi が作成するデータファイル

20230717165352-admin.jpg
そして後はいい感じに設置して完了ですかね (M5) ATOM Lite の赤外線はかなり飛距離が短いんで…エアコンにへばりつけるような設置になっちゃうのかな

あーあと余談っていうか注意事項ですが (M5) ATOM Lite が読みに来る read.cgi は数十秒に 1 回のペースで 24 時間 365 日延々とアクセスがあります そのままの Web サーバだと…そのアクセス毎にログファイルが蓄積されることになります もし可能なら Web サーバの設定を変更して…このアクセスに対してのみログの書き込みを抑制するなどしたほうがいいと思います ざっくり SetEnvIf nolog  なんて感じのキーワードでぐぐれば必要な情報に辿り着けるとイイナ!

  :

そんな感じで最後までご覧いただきありがとございました汗 ざっくりテスト用に作ったのをイジくりまわした代物なのでいろいろおかしな部分はありますが…手っ取り早く動作を体感してみたい!って時には役立つかもです それにしても 1,000円 ちょっとで買える (M5) ATOM Lite はホントすごいですよね きちんと筐体に収められていつつ… Wi-fi で接続を確立して HTTP でデータを取ってくることができる 赤外線 LED でリモコンのような動作もさせられる 欲を云えば RTC(時計) が欲しかったかなって感じではあるけど…それもどっかのサーバに接続して時刻を取得すればそれほど問題にはならないんでまぁいいかな♪ なんかそんなかんじで #Arduino #M5StickC

情報 <19935文字>

M5StickC Plus の開発環境を構築する

20220326153456-admin.jpg
 
elchika公式 @elchika_info さんがリツイートキャンペーンを行っていたのですが…

それに応募したら大当選しちゃいました!!

202203261507198-admin.jpg
そんな訳で M5StickC Plus を頂きました! 本当にありがとございます!!(>_<)w
こないだから Arduino Pro Micro をぼちぼちとイジり始めてマイコンへの興味が盛り上がってきた所なんで…さらに見識を深めることができそうで嬉しいです!(>_<)w

  :

そんな M5StickC Plus の外観をざっくり見てみましょう

202203261507197-admin.jpg 202203261507191-admin.jpg
裏面には I/O の説明が所狭しと書かれています 何かとよく使う(と思われる)ボタンや LED のピン番号なんかも書かれているんで…ド忘れした時とかでも困らないと思います

20220326150719-admin.jpg
USB は Type-C のようです

202203261507195-admin.jpg 202203261507194-admin.jpg
USB を挿して電源が供給されると起動するようです 何やらセンサーの状況が見れるようなものとかマイクのテストや赤外線の送信(?)のようなものを「M5」ボタンで切り替えながら楽しめるようです

202203261507193-admin.jpg
ちなみに電源の ON/OFF は側面のボタンを長押しすることにより操作できるようです 電源を ON する場合には 2 秒位の長押しで電源を OFF にする場合は 6 秒くらい長押しするといいようです

  :

そんな訳なんで動作チェックがてら開発環境を整えていきましょう M5StickC Plus (M5Stackシリーズ) の開発環境は Arduino IDE を使ってどうにかできるようなんで手軽っちゃぁ手軽ですよね
その辺の詳細な手順については 公式 に詳しく書かれていたんで…それを実際に試していきたいと思います

2022032615071914-admin.png
まずは Arduino IDE の「ファイル」→「環境設定」を開きます

2022032615071913-admin.png
その中の「追加ボードマネージャのURL」に https://m5stack.oss-cn-shenzhen.aliyuncs... を追加して「ok」します

2022032615071912-admin.png
お次に「ツール」→「ボード」→「ボードマネージャ」を開きます

2022032615071911-admin.png
検索欄に「m5stack」と入れて出てきた「M5Stack」をインストールする

2022032615071910-admin.png
これで「ツール」→「ボード」とした際に「M5Stick-C Plus」を選べるようになります

  :

使用ボードの設定が済んだらライブラリをインストールします

202203261507199-admin.png
「スケッチ」→「ライブラリをインクルード」→「ライブラリを管理」を開きます

202203261507198-admin.png
検索欄に「m5stickcplus」と入れて出てきた「M5StickCPlus」をインストールします

202203261507197-admin.png
なんか関連したライブラリ(?)も一緒にインストールするか聞かれるんだけど…よく判んなかったんで全部入れちゃいました(汗

202203261507196-admin.png
ちなみに…もし間違ったライブラリを入れちゃった時にそれを消したい場合は「ファイル」→「環境設定」で確認できる「スケッチブックの保存場所」で確認できるフォルダを開いて libraries フォルダを開いて

202203261507195-admin.png
該当するライブラリが入っているフォルダを削除して Arduino IDE を再起動すると消すことができるようです

202203261507194-admin.png 202203261507193-admin.png
まぁとにかくコレで M5StickC Plus のライブラリをインクルードできるようになりました

  :

それじゃ実際にコードを書いてコンパイルして M5StickC Plus に書き込んでみましょう まぁ本来なら LED を点滅させる…いわゆる「Lチカ」を試すべきトコロなんですが液晶ディスプレイが搭載されているって事なんでそれに文字を出力させてみましょう

----------

# include <M5StickCPlus.h>

void setup(){
  M5.begin(); // M5StickC Plus の初期化?
}

void loop() {
  M5.Lcd.setTextSize(3);  // フォントサイズ
  M5.Lcd.setRotation(3);  // 画面表示の向き
  M5.Lcd.print("Hello World");  // お決まりのやつ
}

----------

202203261507191-admin.png
ざっくりこんな感じですかね これをマイコンボードに書き込んでみましょう

202203261507192-admin.jpg
無事に「Hello World」と表示されて大成功です!

  :

ちなみにコンパイル時に…我が家の環境では以下のようなエラーが発生しました

Traceback (most recent call last):
  File "/home/nekoyama/.arduino15/packages/m5stack/tools/esptool_py/3.1.0/esptool.py", line 38, in <module>
    import serial
ModuleNotFoundError: No module named 'serial'
exit status 1
ボードM5Stick-C-Plusに対するコンパイル時にエラーが発生しました。


これは Python にシリアル通信するための pyserial モジュールが入ってない時に発生するらしいので…

202203261507192-admin.png
pip install pyserial としてインストールしておけばいいようですメモ

  :

20220326150719-admin.png
さらにちなみに初回起動時に動いていたセンサーの状況が見れるようなものとかマイクのテストや赤外線の送信(?)のようなものは「ファイル」→「スケッチ例」→「M5StickCPlus」→「FactoryTest」で再び入れ直せるようです 他にも参考になりそうなものが用意されているんで…ざっと目を通しておくといいかもです

  :

Arduino Pro Micro と M5StickC Plus を比べると…すぐに使える I/O が豊富でいろいろ興味を駆り立てられてしまいますね! 特に ESP32 ってやつですかね Wi-Fi でコネクションを確立したりその後に HTTP としてサーバからドキュメンをを取ってこれたりもできるようなんで Web サーバと連携させて M5StickC Plus をリモートで操作するようなものとか作れるかもしれんですね!
そんな感じで引き続きぼちぼちイジっていきたいと思います #Arduino #M5StickC

情報 <2732文字>

Arduino とマトリクスキーボード

202202201020325-admin.jpg
 
以前にどうにかしてたっていうか「Arduino Leonardo (Pro Micro) でマルチメディアキーボードを作ろう!」の後ぐらいにいろいろ調べてたら…なんかマトリクス・キーボードっていうの? 少ないピン数で多くのスイッチを読み取れる方法があるらしい??ってのを見つけて気になってたんで勉強がてらその辺を試してみることにしました

そんなマトリクスってのを日本語的に云うと…碁盤の目みたいなものって感じになるっぽい? それをキーボード的に実装すると…例えば横方向にマイコンから何本かの出力線を這わせておいてそれと何本かの入力線をスイッチを介してあたかも碁盤の目のように配置したもの?ってなるらしい??

202202201020324-admin.jpg 20211113072346-admin.jpg
そんな感じで Arduino VKLSVAN Pro Micro USB ATmega32U4 (※以下 Arduino と略す)を使って実際に作ってみました ちなみに Pro Micro のピン配列図はこんな感じです つーかブレッドボード(?)が狭くて碁盤の目になってなくてアレですよね(汗

202202201020325-admin.png
回路図的なものにするとこんな感じです つーか回路図なんて描いたことないんで正しくない図かもしれませんが…まぁ概念的な絵だと思って見てください(滝汗

202202201020324-admin.png
ここでは Arduino の 6 番ピンと 7 番ピンを出力として使い 14 番ピンと 15 番ピンを入力として使います
そして 6 番ピンのみに信号を出力します その状態で A もしくは B のスイッチが押されればそれぞれ対応した 14 番ピンか 15 番ピンで信号を読み取ることができます それにより A もしくは B のスイッチが押されたかどうかを特定できます
※なんか PULLUP 回路の都合上っていうの?
※普段は HIGH にしてあって…「出力する」としたものを LOW にすることで動作する仕組みです ちょっとややこしいですね(-_-;)

202202201020323-admin.png
引き続き今度は 7 番ピンにのみ信号を出力します その状態で C もしくは D のスイッチが押されればそれぞれ対応した 14 番ピンか 15 番ピンで信号を読み取ることができます それにより C もしくは D のスイッチが押されたかどうかを特定できます
この一連の動作を繰り返すことにより碁盤の目のように配置されたスイッチを特定していく…そんな構造のキーボードをマトリクスキーボードと呼ぶらしいです

それでは実際にスケッチを書いて動作を確認してみましょう

----------

#include "Keyboard.h"

void setup() {
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(14, INPUT_PULLUP);
  pinMode(15, INPUT_PULLUP);

  digitalWrite(6, HIGH);  // 6 番ピンを HIGH にしておく
  digitalWrite(7, HIGH);  // 7 番ピンを HIGH にしておく

  Serial.begin(9600);
  Keyboard.begin();
}

void loop() {
// -------------------------------- //
  digitalWrite(6, LOW); // 6 番ピンを LOW にしてスキャン開始
  if (digitalRead(14) == LOW) { // 14 番ピンは LOW になっている?
    Serial.print("A\n");  // LOW なら A のスイッチが押されている
    Keyboard.print("A\n");
  }
  if (digitalRead(15) == LOW) { // 15 番ピンは LOW になっている?
    Serial.print("B\n");  // LOW なら B のスイッチが押されている
    Keyboard.print("B\n");
  }
  digitalWrite(6, HIGH);  // 6 番ピンを HIGH にしてスキャン終了
// -------------------------------- //
  digitalWrite(7, LOW); // 7 番ピンを LOW にしてスキャン開始
  if (digitalRead(14) == LOW) { // 14 番ピンは LOW になっている?
    Serial.print("C\n");  // LOW なら C のスイッチが押されている
    Keyboard.print("C\n");
  }
  if (digitalRead(15) == LOW) { // 15 番ピンは LOW になっている?
    Serial.print("D\n");  // LOW なら D のスイッチが押されている
    Keyboard.print("D\n");
  }
  digitalWrite(7, HIGH);  // 7 番ピンを HIGH にしてスキャン終了
// -------------------------------- //
  delay(100); // 適当に待って繰り返し
}

----------

202202201020322-admin.png
シリアルモニタとテキストエディタを並べて動作テストしてみましょう スイッチに対応した文字が表示されれば成功です ちなみに今回はシリアルモニタとテキストエディタの両方に出力するようにしてますが…初期のテスト段階ではシリアルモニタだけを使ったほうがいいかもですね

  :

さてそんな感じでマトリクス・キーボードの大まかな動作を理解できた気になったんで…ぼちぼちマルチメディアキーボード的なやつを作るか!って思いたいトコロなんだけどスイッチ類を買い揃えて配線したりとかとかしょーみ面倒くさいなぁと思ってたんですよね そう思いつつ某Aマゾンを眺めてたら気になるものを見つけました

Ren He 5個セット 16キー 4*4 マトリックス フィルム ボタン キーボード マトリックススイッチ キーパッド メンブレン式 マトリックスキーボード Arduinoに対応

かなり怪しげな感じだけど 5 個入りなのに 600 円でお釣りがきちゃうお値段に釣られてポチっちゃいました 初期不良でいくつか動かなかったとしても十分にお値打ちっポイネ!!

202202201020323-admin.jpg
なんか適当なプチプチに包まれた荷姿で届くと思ってたのに…郵便受けをギリ通る厚みのプラケースに入れられて来ましたw やるじゃんww

202202201020321-admin.png
ちなみにデータシートの類は入ってなかったんで自力で配線を解析する必要があったけど…まぁわりと素直な作りだったんで簡単に判明してよかったです そして Arduino と接続したピン番号とかも

20220220102032-admin.jpg 202202201020321-admin.jpg 202202201020322-admin.jpg
実際に接続してみました まぁ毎度のごとく「接続例」って感じなんで…この辺は各人のお好みでどうにかしてもらえばいいかと思います

それでは早速スケッチを書いて動作を確認してみましょう

----------

const byte KEYOT[] = {4, 5, 6, 7};  // 出力ピンの設定
const byte KEYIN[] = {14, 15, 18, 19};  // 入力ピンの設定
const int WAIT = 100; // ちょっと待たせる

//  関数 keyscan() がキーの状態を読み取った値を入れておく配列
byte SW[sizeof(KEYOT)][sizeof(KEYIN)];

const byte ON = LOW;  // LOW と HIGH を…
const byte OF = HIGH; // ON と OF の別名で定義しておく

void setup() {
  for (byte i = 0; i < sizeof(KEYOT); i++) {
    pinMode(KEYOT[i], OUTPUT);  // 出力ピンのモード設定
    digitalWrite(KEYOT[i], HIGH); // 出力ピンを HIGH に設定
  }
  for (byte i = 0; i < sizeof(KEYIN); i++) {
    pinMode(KEYIN[i], INPUT_PULLUP);  // 入力ピンを PULLUP モードに設定
  }
  Serial.begin(9600); // シリアル通信を開始
}

void loop() {
  keyscan();  // キーの状態を読み取る
// -------------------------------- //
  if (SW[0][0] == ON) { Serial.println("1"); }
  if (SW[0][1] == ON) { Serial.println("2"); }
  if (SW[0][2] == ON) { Serial.println("3"); }
  if (SW[0][3] == ON) { Serial.println("A"); }
// -------------------------------- //
  if (SW[1][0] == ON) { Serial.println("4"); }
  if (SW[1][1] == ON) { Serial.println("5"); }
  if (SW[1][2] == ON) { Serial.println("6"); }
  if (SW[1][3] == ON) { Serial.println("B"); }
// -------------------------------- //
  if (SW[2][0] == ON) { Serial.println("7"); }
  if (SW[2][1] == ON) { Serial.println("8"); }
  if (SW[2][2] == ON) { Serial.println("9"); }
  if (SW[2][3] == ON) { Serial.println("C"); }
// -------------------------------- //
  if (SW[3][0] == ON) { Serial.println("*"); }
  if (SW[3][1] == ON) { Serial.println("0"); }
  if (SW[3][2] == ON) { Serial.println("#"); }
  if (SW[3][3] == ON) { Serial.println("D"); }
// -------------------------------- //
  delay(WAIT);
}

void keyscan() {  // キーの状態を読み取る関数
  for (byte i = 0; i < sizeof(KEYOT); i++) {
    digitalWrite(KEYOT[i], LOW);  // 特定の出力ピンを LOW にする
    for (byte j = 0; j < sizeof(KEYIN); j++) {
      if (digitalRead(KEYIN[j]) == ON) {  // 入力ピンの状態を読む
        SW[i][j] = ON;  // キーが押されている
      } else {
        SW[i][j] = OF;  // キーは離されている
      }
    }
    digitalWrite(KEYOT[i], HIGH); // LOW にした出力ピンを HIGH に戻す
  }
}

----------

冒頭にある

const byte KEYOT[] = {4, 5, 6, 7};  // 出力ピンの設定
const byte KEYIN[] = {14, 15, 18, 19};  // 入力ピンの設定
const int WAIT = 100; // ちょっと待たせる

この辺と…あとは loop() 内を書き換えるだけでいいように書いてみました 使用するピン数が増減しても概ね追従できると思います
あーあと setup() で使用したい機能を開始させるような辺りの書き換えも必要ですかね

それと…話がややこしくなるとアレなんで今回はシリアル通信のみで動作チェックしています

キーの状態を読み取る keyscan() 関数ってのがありますが…グローバル変数を使っているので引数も返り値もありません
読み取った値は SW[][] って二次元配列にセットしています

あと PULLUP の特性っていうか普段が HIGH となっていて…スイッチが押されると LOW になるって辺りが何となく直感的にイメージしにくいように思えたんで
const byte ON = LOW;  // LOW と HIGH を…
const byte OF = HIGH; // ON と OF の別名で定義しておく
って感じでスイッチが押されてる ON ってのとスイッチが離されてる OF って感じの別名で定義してあります

  :

そして loop() 内で keyscan() を呼んでキーの状態を読み取っておいて…あとは個々の SW[][] の状態に応じてお好みの文字(列)を出力させるなり何なりをすればいいでしょう
将来的にっていうかもし「1」が押されてたらキーボードとして文字を出力して「#」が押されてたらマウスの右クリックを…って感じで様々な処理を行えるよう少しばかり長ったらしく書いてあります

この辺の「やること」の種類が決まりきっているのであれば

void loop() {
  keyscan();
// -------------------------------- //
  const char* str[] = {
    "1\n", "2\n", "3\n", "A\n",
    "4\n", "5\n", "6\n", "B\n",
    "7\n", "8\n", "9\n", "C\n",
    "*\n", "0\n", "#\n", "D\n",
  };
  for (byte i = 0; i < sizeof(KEYOT); i++) {
    for (byte j = 0; j < sizeof(KEYIN); j++) {
      if (SW[i][j] == ON) {
        Serial.print(str[sizeof(KEYOT) * i + j]);
      }
    }
  }
// -------------------------------- //
  delay(WAIT);
}

こんなふうに for() で回すように書けばもうちょっとスッキリするかもしれんですね これはまぁ「やること」の内容によっていろいろ書き方があるよねって事で参考までに

  :

あーあと後半の中華クソ安 16 キーボードでの実験ではテキストエディタへの文字出力を行っていないんですが…これを実際に行うと不具合が出るものがあります

20220220102032-admin.png
キーボード上の「*」 なんですけどね シリアルモニタではちゃんと「*」が出力されているのに…テキストエディタ上では「(」になってしまいます
※別のスケッチでテストした際の画像です

これは #include "Keyboard.h" した際のキーボードレイアウトが KeyboardLayout_en_US になってて…まぁいわゆる US キーボードってやつですかね これが日本で一般的に使われている JIS キーボードとレイアウト(キーの配置)が違うから起こる現象のようです
アルファベットと数字に関しては問題ないのですが…記号はその多くが US と JIS とで配列が違うんで問題になります

202202201033061-admin.png
先ほどの「*」を例にすると… US キーボードでは「*」が「8」のキーに割り当てられています

20220220103306-admin.png
そのキーコードを JIS キーボードに当てはめると「8」のキーにあるのは「(」って事になるので正しく表示されないのです

困っちゃいますよね…そこそこ数が出回ってる(と思われる) JIS キーボードなんだで Keyboard.begin(KeyboardLayout_ja_JP); くらい通るようにしといてほしいのに!って思うんだけどそうはいきません(-_-;)

どうしたもんかなーって思いつつ調べてたら…何やら Keyboard.h を複製した後に JIS キーボード向けに書き換える手法を見つけました→ Arduino Leonardoで\記号を打つ:メガギガテラス:So-netブログ
実際に試してみたところ…これでバッチリ動作しました! まぁあくまで自己責任で!って感じになるんだけど…自作キーボードでいろいろしたい!って場合にはとても有用な情報だと思うんで参考にしてみてください

  :

キーボード自作って話になると耳にするマトリクスキーボードってやつですね 今までは何となく漠然と聞き流していたんですが…今回はその辺に踏み込んでその原理をほんのり理解できた気になれてよかったです マイコン側のピン数を減らしつつ…より多くのキーを読み取るテクニックは今後も色々と使えそうなんで今後もより掘り下げた勉強をしていきたいなって思いました

あと…マトリクスキーボードの構造的な問題っていうか特定条件の複数キーの同時押しをした際に正常に動きません 今回の中華クソ安 16 キーボードを使った実験を例にとると…縦軸に配されたキー(例えば「1と4」とか「AとBとC」とか)を同時押しすると「どれも押されてない」って判定されます まぁなんか読みたいキーとは別のスイッチを介して HIGH が逆流してくる(?)ことにより誤動作するらしく…そんな逆流を防ぐためにダイオードを入れるって事らしいんですが今回は既成品でその辺をどうにもできかねる感じなんでそのままにしてあります(汗

まぁその辺を踏まえた上で…この中華クソ安 16 キーボードでマルチメディアキーボード的なものを作るとしますかね 程々に硬い板に貼り付けて配線の取りまとめをすればいいだけって感じの見栄えよりお手軽さ!って感じのアレですが(瀧汗

そんなこなんで今回も長々とお疲れさまでした! 安いのに遊び甲斐のある Arduino をもっと色々と使い倒していきタイネ!(>_<)w #Arduino

情報 <7955文字>

Arduino の シリアル通信

20220130153543-admin.png
 
Arduino VKLSVAN Pro Micro USB ATmega32U4 (※以下 Arduino と略す)で開発してる際のデバッグとかで「変数がどうなってるのか」を見たくなることはありませんか? 私はあります いやむしろ見ないとやってられません!!(>_<)wって感じなんだけど Arduino にはモニタ出力がありません

20220130153543-admin.jpg
なんか LCD ディスプレイとやらを接続すれば文字なんかを表示させられるっぽいけど…接続とか面倒そうだし手が出ないなぁと思ってたけど何やら「シリアル通信」ってので PC と文字のやりとりができるらしいじゃん!? まじか!!

そんなシリアル通信は Arduino IDE から使えるようです

202201301535436-admin.png
[メニュー] → [ツール] → [シリアルモニタ] と操作するか…

202201301535435-admin.png
もしくはツールバーにある虫メガネみたいなアイコンを押下すると出てきます

202201301535434-admin.png
出てきた「シリアルモニタ」はこんな感じです
我が家の環境では「改行コード = LFのみ」で「通信速度 = 9600bps」が初期設定となっていました

通信速度は Arduino 側で設定した速度とシリアルモニタで設定する速度は同じものにしておく必要がある…らしいんですが軽くイジってみた感じでは設定値に関係なく動作しているっぽい感じなんで気にする必要はないのかもしれません

改行コードは Linux 系が「LF」で Mac 系(の昔のやつ?)が「CR」で… Windows 系が「CR+LF」と聞いたことがあるので機種ごとに適切なものに変更しておいたほうがいいの?
ざっくり使ってみた感じでは… Arduino からシリアルモニタに送られてくる場合の改行コードは設定の影響を受けないっぽいです シリアルモニタから Arduino に送信する際に「送信ボタンを押した」もしくは「エンターキーを押した」時に改行コードをどのようにするかの設定となるようです なおここでの解説は改行コードを「LFのみ」に設定してあるものとします

  :

それでは早速と言うか Arduino からシリアルモニタに文字を送信してみましょう シリアル通信を開始するには以下のように記述します
Serial.begin(speed) ってやつですね speed は BPS で… 300, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, 115200 なんかがよく使われがちですがそれ以外の値も指定できるようです ここでの解説では「9600」を使用するとします

その後は Serial.print() や Serial.println() を使ってシリアルモニタに数値や文字列を送信できます Serial.print() は C で云うトコロの sprintf() を意識した作りとなってるようですが…書式を全て解説するのもアレなんでその辺は手を抜いてざっくりサンプルを載せておきます

----------

void setup() {
  Serial.begin(9600); // シリアル通信を開始する
}

void loop() {
  unsigned long time = millis() / 1000; // 動作時間を秒の単位で取得
  String h = String(time / 3600); // 「時」を文字列にして取得
  String m = String((time % 3600) / 60); // 「分」を文字列にして取得
  String s = String(time % 60); // 「秒」を文字列にして取得

  Serial.println(String(h + ":" + m + ":" + s)); // 整形して送信
  delay(1000); // 1 秒待って繰り返し
}

----------

202201301535431-admin.png
これは Arduino が動作開始してからの時間を延々と表示し続けるってものです 動作開始してからの時間をミリ秒で取得できる millis() 関数を使い…そこで得られた変数の値を人の目で確認しやすい(と思われる)「時:分:秒」に整形して表示しています こんな感じで変数の内容を確認できるんでデバッグなどでは大いに助かりますね!

  :

シリアルモニタには Arduino に対して文字を送信する機能も備えられています それを Arduino 側から Serial.read() などを使って読み取るわけですが…基本的に「1文字づつ」送られてくる扱いになるようです
例えば「a10Z」と送信すれば「a」と「1」と「0」と「Z」に「設定した改行コード」が加えられて順番に送られてきます

あと文字は「文字コード」として送られてきます 「a」だと「97 (0x61)」となり「1」だと「49 (0x31)」となり「0」だと「48 (0x30)」となり「Z」だと「90 (0x5A)」で…最後に「改行コード(LF)」が「10 (0x0A)」って感じになります
デバッグ時に「ループ回数の変数をプログラム実行中に指定の値に変更したい」とかの場合だとシリアルモニタから送られてきた「文字」を「数値」に変換する必要があります

----------

void setup() {
  Serial.begin(9600); // シリアル通信を開始する
}
 
void loop() {
  if (Serial.available() > 0) // 文字(列)が送信されてきた?
  {
    byte data = (Serial.read()); // 送信されてきた文字を読み込む
    if (data >= '0' && data <= '9') { // 文字は数字か?
      data = data - '0'; // 文字コードを数値に変換
      Serial.print("数字が入力された [" + String(data) + "]\n");
    } else {
      if (data == 10) { // 改行コードは表示に不都合があるんで
        data = 0; // 何も表示しないようにしておく
      }
      Serial.print("数字以外 [" + String((char)data) + "]\n");
      data = 0; // 数字以外は「0」と扱うことにする
    }

    for (int i=1; i <= data; i++){ // 送信された数値の数だけループしてみる
      Serial.print(String(data) + " 回ループの " + String(i) + " 回目\n");
    }
  }
}

----------

202201301535433-admin.png
シリアルモニタに適当な適当な文字(列)を入力して「送信」します

202201301535432-admin.png
送信した文字に「数値」があればその数だけ for() のループ回数を変更します 「数値」以外の文字は処理しないようにしてあります
なお「1文字づつ」送られてくるってのが少々厄介で…「10以上の数値」を扱う場合にはひと工夫必要です どうしたらいいんだろう? 先に送られてきた数値を10倍してそこに新たに送られてきた数値を足すって流れになるのでしょうか ちょっと大掛かりになってくるっていうか面倒なんで今回はスルーしときますが概ねそんな流れになると思います(汗

そんなこんなな Arduino のシリアル通信なお話でした しょーみもっと早く知っとっけばよかった!って思いつつ書いてましたとさ(瀧汗 #Arduino

  :

※ 追記 ※
「いやいや Arduino でも sprintf() って使えるじゃん?」ってタレコミを頂いんたんでよくよく調べたらありました(汗 参照している Arduino リファレンス にはその辺が載ってなかったんで…無いものとばっか思ってました

他にも数値かどうか?を判定する isDigit() や改行の判定に使ってたものを isPrintable() に置き換えられるようなものもありました
上で説明してる数値を変数に代入して云々…で書いてるものもこんな感じでできそうです

    if (data >= '0' && data <= '9') { // 文字は数字か?
        ↓
    if (isDigit(data)) { // 文字は数字か?

----

      if (data == 10) { // 改行コードは表示に不都合があるんで
        ↓
      if (!isPrintable(data)) { // 改行コード等は表示に不都合があるんで

いやはや勉強になりました! 興味本位でなんとなく始めた Arduino ってことでまだまだ知らないことだらけなんで…こうして知識が広がっていくのは楽しいです どもありがとございました!(>_<)w

情報 <3825文字>

Arduino Leonardo (Pro Micro) でマルチメディアキーボードを作ろう!

20211203181404-admin.jpeg
 
前回に引き続き Arduino 的な諸々をひとつって事で…マルチメディア・キーボードって云うんですかね?
20211114113829-admin.png
+α的に実装されている音量調節などのキーとかノート PC でよく見かける Fn キーと併用して使える再生とか早送りなどのキーを ATmega32U4 を搭載した Arduino Leonardo (Pro Micro) でどうにかしてみるお話です

そんな今回の実験の様子
202111141138293-admin.jpeg
以前のマウスクリック超連射実験の回路がくっついたままなんで判りにくいですよね…

202111141138291-admin.png
申し訳程度に回路図を描いてみたけど…それでも判りづらいですよね(汗 まぁとにかく 2 番ピンと 3 番ピンを使用しました

今回のマルチメディア・キーボード(?)的な機能を簡単に実装するためにライブラリを追加してみました
202111141138292-admin.jpeg
Arduino IDE で [スケッチ] → [ライブラリをインクルード] → [ライブラリ管理] してライブラリ管理画面を出す

202111141138291-admin.jpeg
目的のライブラリを「hid」ってキーワードで検索して…出てきた「HID-Project」をインストールする ※画面は既にインストール済みだけどまぁそんな感じで(汗

後は Arduino IDE でこんな感じのを用意してそれをマイコンボードに書き込みます

----------

// include the HID library
#include "HID-Project.h" // 今回インストールしたやつをインクルードする

// definitions for each pin used
const int pinLed = LED_BUILTIN;
const int playButton = 2; // 今回使うピン番号
const int fwdButton = 3; // 今回使うピン番号
const int backButton = 4; // 以下今回は使わない(-_-;)
const int volUpButton = 5;
const int volDwnButton = 6;
const int muteButton = 7;


void setup() {
  // define the pin mode for each pin used
  pinMode(pinLed, OUTPUT);
  pinMode(playButton, INPUT_PULLUP); // 今回使うピン番号
  pinMode(fwdButton, INPUT_PULLUP); // 今回使うピン番号
  pinMode(backButton, INPUT_PULLUP); // 以下今回は使わない(-_-;)
  pinMode(volUpButton, INPUT_PULLUP);
  pinMode(volDwnButton, INPUT_PULLUP);
  pinMode(muteButton, INPUT_PULLUP);

  // begin HID connection
  Consumer.begin();
}

void loop() {
  if (!digitalRead(playButton)) {
    digitalWrite(pinLed, HIGH); // turn on LED
    Consumer.write(MEDIA_PLAY_PAUSE); // 一時停止・再生的なコマンド
    delay(500); // 動作確認用の LED 点滅
    digitalWrite(pinLed, LOW); // turn off LED
  }
  
  if (!digitalRead(fwdButton)) {
    digitalWrite(pinLed, HIGH);
    Consumer.write(MEDIA_NEXT); // 次の曲的なコマンド
    delay(500); // 動作確認用の LED 点滅
    digitalWrite(pinLed, LOW);
  }
}

----------

20211114113829-admin.jpeg
Ubuntu 20.04.3 LTS と Audacious 3.10.1 で動作チェックしたところ…「再生/一時停止」と「次の曲」の動作をタクトスイッチの押下にて実現できました やったね!

わりと入手しづらいイメージのマルチメディア・キーボード(?)の拡張部分を Arduino Leonardo (Pro Micro) でどうにかできる事が判明しました 後は程よいケースとスイッチを用意すれば完成なんだけど…それはまた後の話ってかんじで

  :

他にも機能があるっていうか…キーコードは Arduino/libraries/HID-Project/src/HID-APIs/ConsumerAPI.h に定義されています ※ファイルの場所は環境によって異なる場合があります

enum ConsumerKeycode : uint16_t {
  // Some keys might only work with linux
  CONSUMER_POWER = 0x30,
  CONSUMER_SLEEP = 0x32,

  MEDIA_RECORD = 0xB2,
  MEDIA_FAST_FORWARD = 0xB3,
  MEDIA_REWIND = 0xB4,
  MEDIA_NEXT = 0xB5,
  MEDIA_PREVIOUS = 0xB6,
  MEDIA_PREV = 0xB6, // Alias
  MEDIA_STOP = 0xB7,
  MEDIA_PLAY_PAUSE = 0xCD,
  MEDIA_PAUSE = 0xB0,

  MEDIA_VOLUME_MUTE = 0xE2,
  MEDIA_VOL_MUTE = 0xE2, // Alias
  MEDIA_VOLUME_UP = 0xE9,
  MEDIA_VOL_UP = 0xE9, // Alias
  MEDIA_VOLUME_DOWN = 0xEA,
  MEDIA_VOL_DOWN = 0xEA, // Alias

  CONSUMER_BRIGHTNESS_UP = 0x006F,
  CONSUMER_BRIGHTNESS_DOWN = 0x0070,

  CONSUMER_SCREENSAVER = 0x19e,

  CONSUMER_PROGRAMMABLE_BUTTON_CONFIGURATION = 0x182,
  CONSUMER_CONTROL_CONFIGURATION = 0x183,
  CONSUMER_EMAIL_READER = 0x18A,
  CONSUMER_CALCULATOR = 0x192,
  CONSUMER_EXPLORER = 0x194,

  CONSUMER_BROWSER_HOME = 0x223,
  CONSUMER_BROWSER_BACK = 0x224,
  CONSUMER_BROWSER_FORWARD = 0x225,
  CONSUMER_BROWSER_REFRESH = 0x227,
  CONSUMER_BROWSER_BOOKMARKS = 0x22A,

今回使った MEDIA_PLAY_PAUSEMEDIA_NEXT の他にもいろいろあります 例えば CONSUMER_BROWSER_REFRESH を超連打して…いわゆる F5 アタックを実装するのもいいかもしれません ※やめて!(>_<)q

  :

まぁざっくりこんな感じですかね これを用いてちょっと便利な補助キーボードを作れるとイイナ! #Arduino

情報 <3322文字>

Arduino Leonardo (Pro Micro) でマウスクリック連射装置を作ろう!

20211203180938-admin.jpg
 
ワンボードマイコンっていうか…ここんとこ Arduino が人気沸騰で気になりますよね!って事なんでモノは試しに安っすい VKLSVAN Pro Micro USB ATmega32U4 を買ってぼちぼちイジってみることにしました

202111130723461-admin.jpeg
まぁなんか Arduino にもいろいろあるようで…その中で ATmega32U4 を搭載した Arduino Leonardo と呼ばれるものが HID 機能を搭載しているって事で気になっていました そんな Arduino Leonardo の互換機が今回購入した Pro Micro って感じです
その HID 機能とはなんぞや?って話ですがまぁざっくり PC の標準的なキーボードやマウスを指すようです 要するに USB 接続した Arduino Leonardo (Pro Micro) がキーボードやマウスに化けるって事なんですね
そこで今回は連打系ブラウザゲーとして名高い CookieClicker を超連射できるボタンを実装してみることにしました

20211113072346-admin.jpg
そんな Pro Micro のピン配列図 なんかいろいろ書いてあるけど…単純な On/Off スイッチを付ける場合は  Arduino  と色付けされた 0 から 20 までのピンを使うようです

20211113072346-admin.png
それを今回はこんな感じで 10 番ピンに配線しました
そしてお次はそれを制御するプログラムで…

----------

#define RX_LED 17 // RX LED はポート 17 っぽい?
#define TX_LED 30 // TX LED はポート 30 っぽい?
#define SW 10 // スイッチ用
#include <Mouse.h> // マウス機能を使いたい

void setup() {
  pinMode(SW, INPUT_PULLUP);
  Mouse.begin();
}

void loop() {
  if(!digitalRead(SW)) {
    digitalWrite(TX_LED, LOW); // TX LED を点灯
    //Mouse.click();
    //delay(100);
    Mouse.press(MOUSE_LEFT);
    //delay(17); // 最速
    delay(20);
    Mouse.release(MOUSE_LEFT);
    //delay(9); // 最速
    delay(10);
    digitalWrite(TX_LED, HIGH); // TX LED 消灯
  } else {
    digitalWrite(RX_LED, LOW); // RX LED を点灯
    delay(40);
    digitalWrite(RX_LED, HIGH); // RX LED 消灯
    delay(20);
  }
}

----------

Arduino IDE でこんな感じのを用意してそれをマイコンボードに書き込みます 書き込まれた後に直ちに動作を開始します
スイッチが Off の時には基板上の LED が細かく点滅しています スイッチを On にするとマウスクリックの連射が始まります

マウスのクリックには Mouse.click(); って関数と Mouse.press(); Mouse.release(); とを選べるようです
Mouse.click(); は…いい感じに「押して離す」をまとめて実行できるようです
Mouse.press(); は…ドラッグ操作っていうんですかね この関数を実行するとマウスボタンが押されたままになり Mouse.release(); を実行するとマウスボタンが離されます

どちらの関数をどのようなタイミングで繰り返すかは個々の環境によって違うかと思いますが…
20211113072346-admin.jpeg
我が家の環境では Mouse.press(); Mouse.release(); を使って 1 秒間に 38 連射を記録しました → マウスクリック連射速度テスト
高橋名人もびっくりな超高速連射を実装できて満足しました!

Arduino Leonardo (Pro Micro) の HID 機能は手軽に有用な機能を実装できてなかなか楽しいです またぼちぼちイジっていきたいと思いました #Arduino

情報 <1926文字>

DASHBOARD

■全文検索:

複合検索窓に切り替える

■複合検索:

  • 投稿者名:
  • 投稿年月:
  • #タグ:
  • カテゴリ:
  • 出力順序:

■ハッシュタグ:

■カテゴリ:

■日付検索:

■機器状態:

Raspberry Pi 4 Status

編集

RSSフィード