1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Arduinoを USB/HIDデバイス(仮想デバイス)として活用する第三回

Last updated at Posted at 2020-05-27

##本記事について
 Arduinoを USB/HIDデバイスとして活用する第三回です。第二回はここ。マイコンとブラウザーの間の双方向通信処理を作ってみました。BME280で測定値のうち、ブラウザーからの指示に基づいた項目を Browserに定時間毎に数値表示するアプリを紹介します。

##第三回)「Toneを利用し、ArduinoとBrowserとの双方向リアルタイムモニター」
 前回までで、Arduinoから USB/HID を使って文字列を出力することができました。逆に Arduinoへの入力の例として、 Tone(音)を使ってみました。 Toneは "音が鳴るか鳴らないか” の 1ビット信号として使っています。
マイコンは前回同様、Arduino互換 Adafruit Trinket M0を使用します。
##機能概要
TrinketM0で収集した測定値を文字列としてPCのブラウザーにHID/USB出力することによってブラウザーに表示する、リアルタイム・モニターを実現しました。測定値は温度/湿度などがあり、どれを表示するかはブラウザーから指定することができます。
・Setup処理
 ・SWの押下を待って処理開始。まず音量をUPして音が聞こえる状態にする。
 ・WebデータのURLをHID/USB出力し画面が表示された後、Loop処理に移行
・Loop処理
 ・定時間隔毎、処理を繰り返し実施
 ・ブラウザーからデータ種別を受信する
 ・I2Cデバイス(BME280)から気温/湿度などの測定値を取集
 ・定時間隔毎に指定データ種別の「ラベル文字列」「測定値文字列」「単位文字列」をHID/USB出力
##準備するもの
 PC (Windows10) ブラウザーは、Microsoft Edge,Chrome
 Adafruit Trinket M0
 開発環境は Windows版の Arduino IDE v1.8.9
 WebServer(ローカスPC上でも可)
##全体の仕組み
htmlをWebサーバーに置いておく。内部のローカルサーバーでもよい。Adafruit Trinket M0には、テスト用のSWがあり、温度・湿度などを測定するBME280をI2C接続する。PCのイヤフォンジャックと Adafruit Trinket M0を接続する。
image.png
WebServerは、WindowsPCのローカルにapacheを立てて確認しました。
##ハード構成
Adafruit Trinket M0 周りのハード構成を示す。
image.png
image.png
image.png
動作確認
image.png
image.png
##ソフトウエア
HTML内JavaSciptで音を鳴らす

example0301.htm
<script type="text/javascript">
//グローバルに定義
var audioCtx=new(window.AudioContext || window.webkitAudioContext)();
function mybeepOnce(pd){
 var oscillator=audioCtx.createOscillator();
 if(audioCtx.state=="suspended") audioCtx.resume();
 oscillator.connect(audioCtx.destination);
 oscillator.frequency.value = 1000; // 値はHz(ヘルツ)
 oscillator.start();
 oscillator.stop(audioCtx.currentTime + pd);
}
//
//ONbitを送りたい場合の呼び出し
 mybeepOnce(0.05);
//OFFbitを送りたい場合の呼び出し
 mybeepOnce(0.025);
</script>

1ビットの処理
 ・Enter押してから少したつと、Trinket M0側に音が聞こえる。
 ・ON:50msecの間音が鳴る。音が鳴り始めてから、35msec 後に再度、音がなっていたらONと判定する。
 ・OFF: 25msecの間音がなる。音が鳴り始めてから、35msec 後に音がなっていなかったらOFFと判定する。

データは最大8ビット
 ・ブラウザーに Inqu で問い合わせた後、
 ・"1" + Tab + Enter => M0 で音の着信待ち。ONO/FFを判定する。
 ・"2" , "3" ,,,,, "8" と繰り返し、8ビット分のON/OFFデータを受信する。

apl_usbhid_TrinketM0_03Wondows.ino
/*
 *公開第三回)双方向リアルタイムモニター
 */
// WindowsPC
#define USBHOST_WINPC
//環境に合わせて内容を確認または修正すること ーーここから -----------------------------
//BME280 I2Cアドレスの設定 (0x76 or 0x77 のどちらか)
#define BME280DEVADDR 0x76
//#define BME280DEVADDR 0x77
//Webserverに合わせて、URLを記述すること
//String g_url_string = "http'//192.168.xxx.xxx/xxxxxxxx.htm"; //内部WebServer
String g_url_string = "http'//localhost/example0301.htm"; //localhost
//③ハード設定
#define LED_PIN 13
#define SW_PIN  1
#define TONE_PIN  4
//環境に合わせて内容を確認または修正すること ーーここまで -----------------------------

#define WCS_DELAY_T1 150 //T1 ブラウザー応答時間150
#define WCS_DELAY_GAMEN 3000  //URLたたいてから画面が表示されるまで待つ
#define WCS_BITREAD_MAXCOUNT 5  //bit read のリトライ回数上限
#define WCS_SWWAIT_MAXCOUNT 30  //sw押下待ちのリトライ回数上限 30秒
#include <Wire.h>
#include <ZeroTimer.h>
#include <SparkFunBME280.h>
#include "lib_keyboard.h"
#include "ToneManager.h"
#include "VolumeControl.h"

BME280 mySensor;
ToneManager myToneM;
VolumeControl myVolumeC;

int16_t g_pass;  //HID出力したら後の待ち時間を制御する
boolean g_I2CNormal;
volatile int g_i; //timerカウント
volatile boolean g_high_spead;
int g_dataSyubetu = 1; //1-4。初期値は温度
int g_retry_count;
//
//
//
void setup(){  
  g_pass = 1;
  g_i = 0;
  g_high_spead = 0;
  g_retry_count = 0;
  pinMode(LED_PIN,OUTPUT);
  pinMode(SW_PIN, INPUT_PULLUP);
  myToneM.begin(TONE_PIN, 8);  //8bit

  Wire.begin();
  delay(100); //I2Cデバイスの初期化待ち
  mySensor.setI2CAddress(BME280DEVADDR);
  //BME280の初期化ができない場合、値ゼロで動かす
  if (mySensor.beginI2C() == false) g_I2CNormal = false;
  else                              g_I2CNormal = true;
  //
  sub_fw_Blink(LED_PIN, 3, 50); //動き始めたことを知らせる
  digitalWrite(LED_PIN, HIGH);  //明確に点灯
  //SWが押されるまで待つ 10msec x 100 = 1sec でエラーとする
  boolean sw_pushed = false;
  for (int j = 0; j< (WCS_SWWAIT_MAXCOUNT * 100); j++) {  
    if (sub_fw_SWcheck(SW_PIN) == 1) {
      sw_pushed = true;
      break;
    }
    delay(10);
  }
  if (sw_pushed == false )
    while(1) sub_fw_Blink(LED_PIN, 10, 50);  //LED点滅し続ける

  // 音量UPのため、HIDデバイスを定義する
  myVolumeC.begin();
#if defined USBHOST_WINPC
  myVolumeC.volumeUP(1);  //windows
#elif defined USBHOST_MAC
  myVolumeC.volumeUP(2);  //MAC
#else
  // 何もしない。後で音が拾えない可能性がある
#endif
  //
  delay(1000);  //USB/HID再定義のための待ち時間。
#if defined USBHOST_WINPC
  sub_kbd_begin(1);  //Windows用に初期化
#elif defined USBHOST_MAC
  sub_kbd_begin(2);  //Mac用に初期化
#else
  // 何もしない。後でKEYBOARDが動作しない可能性大
#endif
  delay(100); //HIDデバイスの初期化待ち
  //
  sub_fw_timerset();  //タイマー起動
}

環境に合わせて、以下の内容を確認または修正してください
①BME280 I2Cアドレスの設定 (0x76 or 0x77 のどちらか有効にすること)
 //#define BME280DEVADDR 0x76
 //#define BME280DEVADDR 0x77

②Webserverに合わせて、URLを記述すること
 //String g_url_string = "http'//192.168.xxx.xxx/xxxxxxxx.htm"; //内部WebServer用
 //String g_url_string = "http'//localhost/example0301.htm"; //localhost用

③ハード設定が間違いないか確認
 LED_PIN 13
 SW_PIN 1
 TONE_PIN 4

loop処理
//
//main loop
//
void loop(){
  // 40msec毎に処理を行う
  if (sub_fw_event(2)) sub_proc();
}
//
//USB/HID処理
//
void sub_proc() {
  static byte s_command = 0; //処理振り分け
  static byte s_first = 0;
  int j;
  
  //待ち時間がゼロになるまで何もしない。
  if (g_pass-- >= 0 ) return;
  //処理振り分け
  if (s_command == 0) { 
    s_command = 1;
    s_first = 1;
    sub_initurl(); //一回だけ実施
  } else if (s_command == 1) { 
    sub_out_kbd(1);  //start
    if (sub_check_tone()) s_command = 20;
    else s_command = 90;
  } else if (s_command == 10) {
    sub_out_kbd(10); //Data
    if (sub_check_tone()) s_command = 11;
    else s_command = 90;
  } else if (s_command == 11) {
    sub_out_kbd(11); //センサー値UP
    if (sub_check_tone()) s_command = 20;
    else s_command = 90;
  } else if (s_command == 20) {
    sub_out_kbd(20); //Inqu
    if (sub_check_tone()) s_command = 21;
    else s_command = 90;
    myToneM.clear();
  } else if ((s_command >= 21) && (s_command <= 28)) {
    //制御情報を1ビットつづ受けとる。8ビット
    sub_out_kbd(s_command); //Inq no1 to no8
    if (s_command == 21) {
    }
    g_high_spead = 1; //問い合わせは待ち時間なしで動かす。本当はS_command==21のみでいい
    if (myToneM.readBit(s_command - 20)) {
      //正常
      s_command = s_command+1;   
      //8bitは無駄なので、4bitで次へ
      if (s_command == 25 ) s_command = 29; 
    } else {
      //異常時は同じビットを再送要求する
      if (g_retry_count++ >= WCS_BITREAD_MAXCOUNT) s_command = 90; //リトライオーバーで異常へ
    }
  } else if (s_command == 29) { 
    g_high_spead = 0; //問い合わせは待ち時間なしで動かすモードを終了する
    //制御情報を1ビットつづ受けとる。8ビット
    sub_out_kbd(29); //Inqe end
    g_dataSyubetu = myToneM.getToneVal(); //受信した制御値を取り出す
    s_command = 10; //次のセンサーデータUPへ、
  } else if (s_command == 90) {
    //フォーカスがおかしくて音が拾えない場合にここにくる
    sub_out_kbd(90); //フォーカスをコマンドフィールドへ
    s_first = 1;
    s_command = 1;   //startコマンドから再開する     
  } else {
    //その他はエラー
    sub_fw_Blink(LED_PIN, 10, 40);
  }
  
  if (s_command == 1) {
    g_pass = 50; //40msec*50= 2sec
  } else if (s_command == 20) {
    if (s_first == 1) {
      g_pass = 50; //40msec*50= 2sec
      s_first = 0;
    } else {
      g_pass = 200; //40msec*200= 8sec
    }
  } else if ((s_command > 9) && (s_command < 12)) {
    g_pass = 25; //40msec*25= 1sec
  } else if ((s_command > 20) && (s_command < 30)) {
    g_pass = 1; //high speed
  } else if (s_command == 90) {
    g_pass = 25; //40msec*25= 1sec
  } else {
    //フォーカスおかしいときも、一旦ここにきて待つ
    g_pass = 200; //40msec*200= 8sec
  }
}
(以下、省略。ソースコードを参照ください)

 ソースコードは、GitHubから取り出すことができます。 Windows用とMac用にわかれています。各フォルダー内のすべてのファイルを同一フォルダに保存して、Arduino IDE でコンパイルしてください。
注意)
・BME280のI2Cアドレスが間違った場合は、測定値ゼロとします。
・ツール/ボードの設定は、 Adafruit Trinket M0 です。
・サンプルのHTMLファイルは、htmlフォルダにあります。
##実行
準備
・WebServerが使えて、PCと接続できていること。
 WindowsPCのapacheを立てることが簡単かと思います。(下記参考欄)
・既定のブラウザーは、Microsoft Edge または、Chromeになっていること。
・WindowsPCの入力モードは、英数字である必要があります。日本語モードになっていないことを確認してください。
1.PCに Adafruit Trinket M0 をUSB接続、イヤフォンジャック接続する。SW押下で動き出す

2.音量UPコマンドを数回出す。
3.Windowsキー + ‘r’ でcmd窓を開き、URL文字列を入力する
image.png
4.音出しを繰り返し、安定して音が受信できることを確認する
 ・‘Start’ + Tab + Enterを入力する
 ・Enter押下時に、Browser側で音を出す。M0側では音を拾う。
 ・音が聞こえれば次へ。聞こえなければ繰り返す
5.定時間隔で以下を繰り返す
・下りデータ受信
   ‘Inqu’ + Tab + Enterを入力する
   ‘1’ + Tab + Enterを入力すると、送信データ値にしたがって 音を出す。
   M0側で音を拾い、 ON/ OFFを判定する
  必要なビット数 (ここでは4回)繰り返す。データ種別が把握できる。
・上りデータ送信
   ‘Data’ + Tab + Enterを入力する
  ラベル + Tab + 測定値 + Tab + 単位 + Tab + Enter を入力する
  (送信すべきデータ種別にしたがって、ラベル /測定値 /単位を送信する )
image.png
##まとめ
Arduinoとブラウザーの連携において、USB/HIDの上りデータとToneの下りデータを使うことで、双方向の通信基盤が確認できた。これにより、マイコン制御の基本的な仕組みは実現できると考えます。Toneを使った下り通信は、安定性や情報量が少ないなどの課題があるため、次回は 下りデータをToneから発展させることに挑戦します。ESP8266/ESP32のWiFiをうまいこと使えないかと検討中です。
##参考
音量調整ボタンについて、以下を参照にしました。
 「ArduinoでUSB接続のPC音量調整ボタンを作る方法」
 https://qiita.com/kwbt/items/0c9f930a236ca989e402

WindowsPCにapacheを立てる場合の例は、以下を参考にしました。
 https://www.adminweb.jp/apache/install/

Trinket M0の開発環境については、以下を参考にしました。
 https://www.denshi.club/cookbook/arduino/trinketm0/trinket-m01arduino-ide.html

2つのライブラリが必要です。以下を参考にしました。
 ・ZeroTimer.h
   https://ehbtj.com/electronics/arduino-zero-timer-interrupt-library/
 ・SparkFunBME280.h
   BME280のライブラリーは豊富にでているので、GitHubなどで探してみてください。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?