投稿者「wpepea」のアーカイブ

ZPH02を触ってみた

先日入手したWinsenのダストセンサーZPH02を触ってみた。検索でv1.2のデータシートがかかったけど公式からデータシートにリンクが張られているV1.0のほうだった。(公式の写真だとピンヘッダーが1*4だけど1*5のが届いたので微妙に違うものかも)なお、ピンヘッダーについてはデータシートにアサイン等記載なかった。少なくともコネクタと並び順は違う。

コネクタ(EH2.54-5P terminal socket)に関してはケーブルが付属していなかった。とりあえずジャンパーのメスと同じサイズなのでそれで引きさした。

出力は粒子数直接ではなくて粒子数に応じて出力LowのDuty比がかわる。データシート参照。

UART接続

環境

  • ArduinoNano互換機
  • Arduino IDE1.8.13(Windows10)
  • ブレッドボードとジャンパーケーブル少々

配線

NanoとPCはUSB接続

PHZ02Nano
#1 Control pinGND
#2 Output OUT2/RXD未接続
#3 Power positive (VCC)5V
#4 Output OUT1/TXDD2(スケッチで指定)
#5 GNDGND

#1をGNDにするか未接続かでPHZ02の出力がUARTになるかPWMになるかが切り替えられる。GNDだとUARTモードになる。UARTだと#4がTXになるのでそれをNanoのRXを割り当てたGPIOで受け取る。もともとのハードウェアなRX端子はPCとのやりとりで使いたいのでSoftwareSerialにて割り当てた。

ソース

#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3);

#define LED_PIN 13

#define BUFFER_SIZE 9
unsigned char inBuffer[BUFFER_SIZE];


void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);
}

void loop() {
  bool readFlg = false;
  int bufIndx = 0;
  
  while (mySerial.available() > 0) {
    unsigned char readValue =  mySerial.read();
    if (bufIndx > BUFFER_SIZE) {
      continue;
    }
    inBuffer[bufIndx] = readValue;
    bufIndx++;

    readFlg = true;
  }

  delay(10); // 少し待ってあげないと配列がちゃんと読み込めない

  if (readFlg && checkSum()) {
    writeData();
  }
  delay(100);
}

bool checkSum() {
  unsigned char tempq = 0;
  for ( int i = 1; i < 8 ; i++) {
    tempq += inBuffer[i];
  }
  tempq = (~tempq) + 1;
  
  if(!(tempq == inBuffer[8] )){
    Serial.println("チェックサムエラー");
  }
  return (tempq == inBuffer[8] );
}

void writeData() {
  for ( int i = 0; i < BUFFER_SIZE; i++ ) {
    Serial.print(inBuffer[i], HEX);
    Serial.print(",");
  }
  Serial.print("[");
  Serial.print(inBuffer[3]);
  Serial.print(".");
  Serial.print(inBuffer[4]);
  Serial.print("]");

  Serial.println("");
}


ZPH02の通信速度(Baud Rate)は9600なのでソフトシリアルで指定。コンストラクタでD2を指定。

コメント入れてあるdelay(10)がないと配列ちゃんと読み込めなかった。

チェックサムは~7バイト。スタートバイトはいらない。

for ( int i = 1; i < 8 ; i++) {

なにか、チェックサムがおかしな値しか返らなくなって再起動しなくてはいけない事象があった。(まだ詳しく見られていない。)

精度はパルス幅0.1%までの模様。値は結構上下するみたいなのでそのまま使うと上下に振れすぎて読みにくいかも。

スタートビットちゃんとみてデータ取得していない拾っていない途中まで読み込んでいるのは次回捨てている。

PWM接続

環境

  • ArduinoNano互換機(電源用)
  • Arduino IDE1.8.13(Windows10)
  • ブレッドボードとジャンパーケーブル少々

配線

PHZ02Nano
#1 Control pin未接続
#2 Output OUT2/RXDPWM出力(計測用にオシロの+に)
#3 Power positive (VCC)5V
#4 Output OUT1/TXD未接続
#5 GNDGND(計測用にオシロの-に)

#1をGNDにするか未接続かでPHZ02の出力がUARTになるかPWMになるかが切り替えられる。未接続だとPWMモードになる。

出力

1秒サイクルでLow(0V)の割合が濃度。

Web HID APIを触ってみた

メジャーなCO2センサー(ZGm053U)の値を拾おうとしたらHIDで通信しているということでPC版のChrome・Edge(あとOperaも?)なら使用できるWeb HID APIを使ってみた。なお、新しめの読み込みが公開されているタイプのみ対応。(古いタイプだと値が暗号化されて読み込み機能が公開されているわけではないので自重。読み方はネットに落ちてはいる)

日本だとCO2miniとして売られている大人気のモデル。

読み込み用のページはこちら

人気CO2センサーZGm053Uの値をブラウザ(PC版のChrome/Edge限定)で見れる便利ページ暫定公開しました。

使う人多そうだったら適宜バージョンアップします

使い方はpcにusb繋げて値取得開始ボタン押すだけ。 終了はおもむろに閉じてください。

コード的にはシンプルで、

if (“hid” in navigator) にてWebHIDが使用できるか確認。

const [device] = await navigator.hid.requestDevice({ filters }); にて対象デバイスを取得。CO2センサーのvendorId: 0x04d9,productId: 0xa052は少し上で指定。Windowsだとデバイスマネージャーのヒューマンインターフェースマネージャーにそれっぽいのがいるはず。

await device.open().then(() => { の中で接続時の処理を指定。

中でやっているのはdevice.addEventListener(“inputreport”, handleInputReport);とdevice.sendFeatureReport(0, new Uint8Array([0,0,0,0,0,0,0,0]));

sendFeatureReportはしないとセンサーが動かない。このCO2センサーに必要な送信の起動条件だと思う。

The sendFeatureReport() method of the HIDDevice interface sends a feature report to the HID device. Feature reports are a way for HID devices and applications to exchange non-standardized HID data.

https://developer.mozilla.org/en-US/docs/Web/API/HIDDevice/sendFeatureReport

addEventListenerで登録したfunction内で受信したReport(HIDの通信単位らしい)を適宜処理。ZGm053Uの場合は、0バイト目にデータの種類、received[1] * 256 + received[2]で値。チェックサムとかも入っているけどちゃんと見てはいない。

<script>
    const filters = [
      {
        vendorId: 0x04d9,
        productId: 0xa052
      },
    ];

    function handleInputReport(e) {
      let received = new Uint8Array(e.data.buffer);
      console.log(received);
      if(received[0]==80){ // ID 80 がCO2
        let resultValue =  received[1] * 256 + received[2]; // 値をそのまま信じる(チェックサム等は未確認)
        document.getElementById("co2value").innerText = resultValue;
        document.getElementById("ppm").innerText = "ppm";
      }
    }

    async function checkHID() {
      if ("hid" in navigator) {
        const [device] = await navigator.hid.requestDevice({ filters });
        await device.open().then(() => {
          device.addEventListener("inputreport", handleInputReport);
          device.sendFeatureReport(0, new Uint8Array([0,0,0,0,0,0,0,0])); // 値取得コマンド送信が必要な模様
        })
      } else {
        alert("USB接続を確認してください");
      }
    }
  </script>

データ送信はしていないけどこんな感じらしい。

const enableVibrationData = [1, 0, 1, 64, 64, 0, 1, 64, 64, 0x48, 0x01];
await device.sendReport(0x01, new Uint8Array(enableVibrationData));

// Then, send a command to make the Joy-Con device rumble.
// Actual bytes are available in the sample below.
const rumbleData = [ /* … */ ];
await device.sendReport(0x10, new Uint8Array(rumbleData));

あとクローズも

await device.close();

そのまま切っても大丈夫のものが多いと思うけど行儀は悪いのでちゃんとクローズしたほうが良い。(けど作ったソースではやっていない)

ちなみに、ブラウザでのボタン操作等必須。自動(onloadとか)で接続しようとすると以下のように怒られる。

zyco2.html:52 Uncaught (in promise) DOMException: Failed to execute 'requestDevice' on 'HID': Must be handling a user gesture to show a permission request.

ダストセンサー2種類届いた

とPM1.0/ PM10/ PM2.5ダストセンサー ZH06とPM2.5ダストセンサーZPH02が届いた。AliのWinsen公式で買って13日。

ZH06はケーブルついていた。ZPH02は付属ケーブル無し。

使い方はこれから調べる

ZH06

データシートはこちら

出力はPWMとUART。コネクタは1.25t-8a、入手しにくいから公式で一緒に売ってくれないかな。データシートのFig9あたりに置き方書いてあるけど通気口ふさいだらよくないし置く確度とかも意識しろと。この間かったDSM501Aにも書いてあったけど通気関係は結構シビアっぽい。

湿度は80%まで。ちょいちょい引っかかりそうね。

想定寿命は10000時間以上。連続稼働だと1年ちょいなので短めは短めね。モジュール本体はケーブル接続なので交換部品だけ買えるようにしておけばよいか。 そして油/煙のある環境はあかんと。

数値の計算式”Please refer the document of ZH06 I-IV Series Communication Protocol.”とか書いているけど、そのドキュメントどこにあるんだ?どりあえず旧バージョンのデータシートにそれっぽいのがのっているのできっとこれでよいだろう。あとでサポートにきかなくては。あいかわらずWinsenのドキュメント類はうち並みの整備状況っぽい。

ZPH02

データシートはこちら(V1.0で公式の製品情報からリンクがある物)こちら(V1.2でGoogle検索で出ててきたもの)が見つかった届いたものは、UARTのレスポンスbyte[6]が1でbyte[7]が0なのでどちらのデータシートとも微妙に違う。サポートに確認しようと思ったけど、ドキュメント大体なのでbyte[7]が0というのが現在の動作なのだろう。(売るならちゃんと聞く) 公式からリンクが張られている方だった。よくみたらVOCセンサーがない場合はbyte6が0x00と補足に書いてあった。

PIN5をグランドにするかどうかでPWMモードかUARTモードかを切り替えるとのこと。

UARTは5Vで1秒ごとの受信のみ。

コネクター以外にピンヘッダー付きそうな端子があるけどデータシートに記載なし。同じものが引き出されていそうなきはするけど、少なくとも並び順はコネクターと違う。後で調べる。

使い方

こちら

PM2.5ダストセンサーDSM501Aが届いた

Aliで買ったダストセンサー(ほこりセンサー)が届いた。(こちら

発注から到着は11日。最後ヤマトさんが持ってきた。(購入時800円中500円送料)

商品のページだと可変抵抗器2こついているけど届いたのは一つしかない。公式にはもうデータシートなさそう。ネットに残っているデータシート見ると触るなと書いてあるし基盤の形も色々なのでフェイク品ではなく色々バージョンがあると信じて触ってみよう。

端子は銀色を左上において左から

#1Control Vout 1 control
#2Vout 2Vout 2 output
#3Vcc Positive power supply(5V)
#4Vout 1 Vout 1 output
#5GND Ground

附属品のコネクターケーブルの色はVCCとかと関係ない。

粒子の数が283mlあたりという微妙な単位で出てくるけど1立方フィートの1/1000ぽい。

リンクのドキュメントには置き方書いていない。ただ、ヒーターで上昇気流を発生させているから云々と書いてあってどこか他のサイトに縦置きして出入口をふさぐなというようなことが書いてあった。

レンズは汚れたら綿棒で拭けと。

配線等はこちらほぼそのまま参考に数値はとれた。あっているのか?

// DSM501A Pin3 → Arduino 5V
// DSM501A Pin5 → Arduino GND
// PM1.0以上を計測したい場合:DSM501A Pin2 → Arduino D8
// PM2.5以上を計測したい場合:DSM501A Pin4 → Arduino D8
// Wind Abaft Co., Ltd.

#include<string.h>
byte buff[2];
int pin = 8;//DSM501A input D8
unsigned long duration;
unsigned long starttime;
unsigned long endtime;
unsigned long sampletime_ms = 30000; // データシートの計測方法が30秒のため
unsigned long lowpulseoccupancy = 0;
float ratio = 0;
float concentration = 0;
 
int i=0;
void setup()
{
  Serial.begin(9600);
  pinMode(8,INPUT);
  starttime = millis(); 
  Serial.println("Start");
}
void loop()
{
  duration = pulseIn(pin, LOW);
  lowpulseoccupancy += duration;
  endtime = millis();
  if ((endtime-starttime) > sampletime_ms)
  {
    ratio = (lowpulseoccupancy-endtime+starttime + sampletime_ms)/(sampletime_ms*10.0);  // Integer percentage 0=>100
    concentration = 1.1*pow(ratio,3)-3.8*pow(ratio,2)+520*ratio+0.62; // データシートのグラフ参照
    Serial.print("lowパルス幅(30秒間):");
    Serial.print(lowpulseoccupancy);
    Serial.print("    Lowパルス幅の占有率[%]:");
    Serial.print(ratio);
    Serial.print("    DSM501A計測粒子数[pcs/283ml]:");
    Serial.println(concentration);
    Serial.print("    DSM501A計測粒子数[pcs/ml]:");// ここだけ追加
    Serial.println(concentration/283);
    lowpulseoccupancy = 0;
    starttime = millis();
  } 
}

「I/chatty: identical 1 line」とかいってログ省略される

Androidで繰り返し処理の中で同じ値になるログ(Logcat)を出そうとしたら、うまく出ていなかった。その時出ていたログによくみると下のようなメッセージがでていた。(タグがchattyとなっているので表示対象を絞っていると見えない)

2022-07-06 10:54:19.786 9170-9170/jp.co.epea.CO2ViewerMini I/chatty: uid=10181(jp.co.epea.CO2ViewerMini) identical 1 line

なにやら「似たようなメッセージ何度も出てたから捨てといたよ♪」 ってことらしい。余計なことしやがって。参照

初Kotlinでこんな感じだろとバイト配列のイテレータ回したらいきなり件数が合わないように見えてはまった。

ATTiny85でI2CしたデータをUARTシリアル通信

概要

ATTiny85にてスレーブからI2Cで取得(readのみwriteしてない)したデータをPCにUARTで連携してみたメモ。

流れとしては「Masterとして使うATTinyの準備/設定」「Slaveとして使うArduinoNanoの設定」「動作確認」。

全体ソース

環境

開発環境

Windows11(64bit)

ArduinoIDE 1.8.19 (多分Microsoft Storeからインストール)

Arduino UNO ( 後で出てくるArduino as ISPとして使う。使えればほかでもOK)

試験用ハード

PC

開発環境と同じWindows

ATTiny85-20PU

秋月で買った純正品 これ

USBシリアル変換モジュール

CH340が搭載されているやつを使用。(リンクこれから準備)

最近のWindowsだったら元からドライバーは入っているはず。

Arduino Nano 互換機

I2Cのスレーブとして。手持ちのI2Cなセンサーとかでもよいけど動かない時の切り分けが大変。

その他

  • ジャンパー必要数
  • コンデンサー 計100uFほど(書き込み時のリセット操作用なのでなければジャンパー手操作でも)
  • LEDと1Kほどの抵抗(動作しているかチェック用。なければないで)

Masterとして使うATTinyの準備/設定

ATTinyのブートローダー書き込み及びプログラム書き込み方法

このままこちら参照

書き込むプログラム

UART周り

ATTiny85にはハードウェアシリアルがないのでソフトウェアシリアルを使う必要がある。

pinout(参照)のPBnnで使うピンを指定するけれど(PB)0,(PB)2はI2Cで使うので利用できるのは残り。とりあえずRXを(PB)3,TXを(PB)4に指定する。

なお、(PB)5はプログラム書き込みの時に使うので他用途で使うと試験中は面倒。

ざっくり雰囲気は以下。

#include <SoftwareSerial.h>


#define RX_PIN 3 // PB3はUSBシリアル変換モジュールのTXにつなぐ
#define TX_PIN 4 // PB4はUSBシリアル変換モジュールのRXにつなぐ

SoftwareSerial softSerial =  SoftwareSerial(RX_PIN, TX_PIN); 

void setup() {
  softSerial.begin(9600);
  softSerial.println("Master start");

}

注意事項

  • TX,RXを入れ子でつなぐの間違えがち。
  • softSerialなので通信速度はあんまり頑張れない(まだまだいけるだろうけど必要なければ無理はしない)
  • 間違えてSerial.print~としてもエラーは出ない。(softSerial.println)

I2C周り

よく使うWireは使えないらしい。マイコンごとに実装分かれていて実装されていないとかどっかで見た。(ソース1分斜め読みで見つからなかったけどまぁ信じる)

かわりにArduinoIDEのライブラリ管理から入れられるTinyWireMが使える。

先ほど見たpinoutにあるようにPB0がSDA,PB2がSCL。

ざっくりプログラムは以下

#include <TinyWireM.h>

#define SLAVE_ADDR   0x08 // Slaveのアドレス。

void setup() {
  TinyWireM.begin(); // 開始
}

void loop() {
  int code = TinyWireM.requestFrom(SLAVE_ADDR, 1); // スレーブのアドレスから1バイト(第二引数)読み込み。
   if ( code == 0){
    softSerial.println(TinyWireM.read()); // 読み込んだものを一括取得(そして書き出し)

   } else {
// エラーコードはここにしかなさそうhttps://github.com/adafruit/TinyWireM/blob/master/USI_TWI_Master.h
    softSerial.printf("error with code[%d]\n",code);
   }
  delay(500);
}

注意事項としてTinyWireM.requestFromの戻り値は要確認。エラーがあった場合チェックしないでreadを呼ぶと初期値の0がそのまま取得される。(ライブラリのExampleはチェックしていないけど大事)

Slaveとして使うArduinoNanoの設定

呼ばれたら返すだけ。アドレスは0x08に設定している。(予約アドレス以外任意可のはず)

#include <Wire.h>

byte b=0;

void setup() {
    Serial.begin(9600);
  Serial.println("slave START");
  Wire.begin(0x08);// Slave ID #8
  Wire.onRequest(requestEvent);
}

void loop() {
}

void requestEvent() {
  Serial.print("event call current data[");
   Serial.print(b);
   Serial.println("]");
  Wire.write(b++);
}

動作確認

結線

ATTinyの番号はピン番号(PBnのnじゃない)

役割USBシリアル変換ATTinyNano
5v5Vのピン85v
GNDGNDのピン4GND
(Attinyからみて)TXRXのピン3
(Attinyからみて)RXTXのピン2
SCL7A5
SDA5A4

(全体ソースでは動作確認用のLEDがありATTinyの6番から1K抵抗経由でグランドにつながっている。)

動作ログ

同時につなげるとNanoの準備ができるまで見つからない旨のエラーが返るけど準備できたら取得できるようになる。

Loop [1]
by available
error with code[3]
Loop [2]
by available
error with code[3]
Loop [3]
by available
0
Loop [4]
by available
1
Loop [5]
by available
2
Loop [6]
by available
3

インドにガンダムという地名が多い理由

ツイッターに疑問が出ていたのでちらっと調べてみた。

昔の言葉で湧き水かな? https://en.wikipedia.org/wiki/Ramagundam

多分、印欧祖語の *wed- (今残っている単語で似たものだとwet)

ラテン語のunda(湧き水、水から派生して波)が同じ雰囲気残っているかな。

ちなみに、サンスクリット語のudaが水で、井戸の昔の音wi(ウィスキーのイ)や、袖ケ浦の浦(ura)につながったという説もあるらしい。(参考

関連して、earthやteraの語源は印欧祖語の *ters- らしい。

インドラとundoちょっと似ていない?と思ったけど最初の「イ」の部分が語源かもぐらいの温度感かな?(参考:5.2. ヤースカの解釈)

ヒドラとかは同じところからきているらしい。(参考

おばけくるましーんのメモ

寝ない幼児を脅すために、リモコン操作で扉とかをガタガタさせたい。

通信はそこそこ距離が届きそうなESPNow使う。(ESPNow調べた時の記事)

揺らす道具は130モーターに何かつけて自作した振動モーター+ESP32-DevKit。DevKit高いけどとりあえず。

リモコンはM5StickC。こいつも高いけどどんな機能必要になるかわからないので手軽にいじれるやつで。

電池と手持ちの部品で動かすには突入電流が大きい(参考)のでコンデンサが足りなかった。コンデンサ100uFぐらいだとパチモンのDCDCでは2.7vぐらいまで、パチモン及び正規品レギュレーターでも2.8vぐらいまでおちる。レギュレーターだと半々の確率でESP起動まではいけることが多いけどモーターを回すとまあだめ。とりあえずコンデンサ大き目ぽちって待機。

今のプログラムはこれ

ハードは

電源の3.3VからESPの3.3vピン、電源グランドからGND

GPIO2-抵抗 – 動作確認用のLED -GND

GPIO15-抵抗 – 2SK4017のBase -GND

電源ライン – モーター – 2SK4017

あとはコンデンサやら帰還ダイオードやらざっくり

USBコンセント電源から5vでとったら動作するので追加コンデンサでも厳しかったら揺れるとこ(モーター)だけ扉につけるように半分離するとよいかも。(電池動作の動作確認機を兼ねているので極力電池で頑張る)

ESP-Nowの公式サンプルが動かなかったので調査

環境

  • ESP32-DevKitC V4 × 2個(MasterとSlave)
  • Arduino IDE (Windows Store 1.8.57.0)
  • ボード ESP32 Dev Module(ESP32 Espressif Systems v 1.0.6)

使用サンプル

ESP32 Dev Moduleの「スケッチ例」->「ESP32」->「ESPNow」->「Basic」のMaster/Slaveのセット

\ArduinoData\packages\esp32\hardware\esp32\1.0.6\libraries\ESP32\examples\ESPNow\Basic の MasterとSlave

発生エラー

Slaveのpeerにデータを送るタイミング(esp_now_send)で「違うチャンネルには送れないよ!」と怒られる。

E (838684) ESPNOW: Peer channel is not equal to the home channel, send fail!
Send Status: Invalid Argument

原因

文字通り違うチャンネルには送れないみたい。公式サイトになんとなくそれを前提としていそうなことは書いてあるけど明示してある箇所は見つけていない。(メッセージがそのままなのでまぁ間違いはないんだろう。)ここらみると2020年の冬ぐらいの1.0.5-rc2で変わったのかな?

For example, the destination device doesn’t exist; the channels of the devices are not the same

Masterは自分がDefaultの1チャンネルになっていて送信想定が3チャンネルになっているので引っかかっている。(それはそれとしてサンプルのSlaveはチャンネルが1になっているのにMasterのソースでは3を指定しているのでそこでも不整合が生じている)

対処

チャンネルを合わせればよい。

1chで上げる場合

Slaveは1チャンネルで上がっているので、送信想定を1にしてやればよい

define CHANNEL 1

slave.channel = CHANNEL;

その他チャンネルで上げる場合

Master/Slaveのチャンネルを任意(使用可能は1~13)に指定してやればよい。

Slave(APモード)のチャンネル指定

Slaveは(違和感あるけど)APモードになっていてSoftAP立ち上げの際に指定できる。

define CHANNEL 1 // からの
 bool result = WiFi.softAP(SSID, "Slave_1_Password", CHANNEL, 0);

Master(STAモード)のチャンネル指定

MasterはSTAモードになってる。ここをみるとSTAモードのときはsoft-APもstationモードと一致するらしい。(で。ドキュメントで確認できていないけどESPNowもsoft-APの設定使って動いているみたい。Espressif だとソース見れないみたいだけどM5Stackの方では派生したもの?みれるようなのでそっち追えばわかるかも)

3. ESP32 is limited to only one channel, so when in the soft-AP+station mode, the soft-AP will adjust its channel automatically to be the same as the channel of the ESP32 station.

指定方法の正しい作法はわからなかった。動いたのは以下の操作。

#include <esp_wifi.h>// して

void setup() {
 //の
  WiFi.mode(WIFI_STA);
 // した後に
  esp_wifi_set_channel(CHANNEL, WIFI_SECOND_CHAN_NONE); // チャンネルを指定してあげる

その他

チャンネルについて

2.4 GHz 帯の中で微妙に周波数変えて周波数ごとにデータ送るやつ。こことかわかりやすい。日本だと1-13チャンネル使えてESPも1-13指定できる。(多分同じ奴だと思うけど調べてない)チャンネル近いと干渉するので、既存の機器と重なったりしたら変えてみると幸せかもしれない。

少し上のWIFI_SECOND_CHAN_NONEについて

「チャンネル2つ使うとデータが倍の勢いで送受信できる」ってやつを使うかどうかの選択パラメータで使わない(HT20)を指定している。使う(HT40)指定のWIFI_SECOND_CHAN_ABOVEとWIFI_SECOND_CHAN_BELOWもあるけどESPNowには多分反映されないでWifiの方だけにかかわると思う。(とりあえず速さいらんと思うのでちゃんと調べてはいない)

仕事をさぼりやすいように人が来たら画面をコマンドプロンプトに切り替えてみる

やりたいこと

仕事さぼっているのがばれないように、人が近づいたらゲーム画面をコマンドプロンプト等(なんとなく黒くてそれっぽい画面)に切り替えられるようにする

必要要素

  • 人が近づいたことを検知する
  • コマンドプロンプト画面でゲーム画面を隠す

実装方針

人が近づいたことを検知する

何かのセンサーで人が近づいたことを検知すればOK。手持ちセンサーでそこそこの距離を測れる超音波距離センサー(HC-SR04)があったのでそいつを利用。角度は15度とほぼ正面しか測れないけどとりあえずこいつで。

コマンドプロンプト画面でゲーム画面を隠す

キーボード入力をマイコン(Raspberry Pi PICO)で自動化する。ライブラリを使ったら簡単にできるらしいできた。

実装技術

Circuit PythonだとAdafruitのライブラリで超音波距離センサーとキーボード入力もそろっていたのでCircuit Pythonを使った。

開発環境(エディタ?)

最初はVS Codeで開発しようとしたけどPICO開発用のPICO-goが新しいVSCodeだと使えないのでメジャーどころのThonnyにした。

なお、こちらにあるようにVSCodeのバージョンを1.65.2に落としたら動くはず。(いったんうごいていたけど油断していたらVSCodeのバージョンアップ走っていて使えなくなっていたので面倒になった。)

また、こちらにあるようにForkされたPico-goをビルドしてもよい模様。

実装

Circuit Pythonのインストール

公式からダウンロードしてインストール。(参考

コマンドプロンプト画面でゲーム画面を隠す

(ライブラリのインストール説明の都合でコマンドプロントから説明)

こちらを参考にHIDのライブラリをインストール。

Windowsでコマンドプロンプトを全画面起動するキー操作は以下。ついでになにかそれっぽい情報を出すためにDir結果も表示(実際に打つとわかりやすい)

  1. Windowsボタン(プログラムの検索画面を開く)
  2. “cmd”キー入力後 Enter (コマンドプロンプト起動)
  3. “dir” キー入力後Enter(Dir実行。ぱっとみそれっぽい情報を表示するため。)
  4. Alt押しながらEnter(コマンドプロンプト全画面か)

Windowsの反応時間を見なくてはいけないかもしれないのでちょいちょいスリープを入れているけど待ちが必要かはわからない。

なお、キーボードの種類指定できるけど日本語はなさげなので必要なら自分で書くか探さないといけない。

人が近づいたことを検知する

こちらからadafruit_hcsr04ライブラリをインストール。HIDの時と同じようにPICOのlibディレクトリにいれればよい。(adafruit_hcsr04.pyで最低限動くはず)

ハードとしてはPICOとhcsr04をジャンパーでつなぐ。

GP5とGP6はプログラムでGPIOピンなら任意指定に変更可能。ピンの役割はこちら

PICOHCSR04
VBUSvcc
GNDgnd
GP5trig
GP6echo

以下で値はとれる

import adafruit_hcsr04
sonar = adafruit_hcsr04.HCSR04(trigger_pin=board.GP5, echo_pin=board.GP6)
distance = sonar.distance

取り急ぎプログラムべたばり

何個もコマンドプロンプト立ち上げたり荒いけど使いながら調整する。プログラムもおもむろに書き直す。(HIDの試験用だったのでこのまま解体されるかもかも)

import microcontroller

import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS

from adafruit_hid.keycode import Keycode

import time
import board
import adafruit_hcsr04

keyboard = Keyboard(usb_hid.devices)
layout = KeyboardLayoutUS(keyboard)

sonar = adafruit_hcsr04.HCSR04(trigger_pin=board.GP5, echo_pin=board.GP6)
time.sleep(1)

while True:
    try:
        distance = sonar.distance
        if distance < 150:
            print('かくせ')
            
            keyboard.send(Keycode.WINDOWS)
            time.sleep(0.2)
            
            layout.write('cmd')
            time.sleep(0.1)
            keyboard.send(Keycode.ENTER)
            time.sleep(0.2)
            
            layout.write('dir\n')
            
            time.sleep(0.2)
            keyboard.send(Keycode.ALT,Keycode.ENTER)
            
        else:
            print("OK")
        
    except RuntimeError:
        print("Retrying!")
    time.sleep(2)