工作と競馬

電子工作、プログラミング、木工といった工作の記録記事、競馬に関する考察記事を掲載するブログ

タグ:Arduino

概要

ESP-WROOM-32のBLEを使って、iBeaconとして動かせるか試し、無事動かすことができた。

本内容は、記述から1年以上経っており、その間のarduino-esp32ライブラリ仕様変更により、もう動きません。(2018/07/25追記)。参考として、仕様変更後の2018/2/22頃にダウンロードしたarduino-esp32を使って新たに書き直したプログラムのリンクを付けます。ただし、私の手持ちのESP-WROOM-32 DevKitC開発ボードにて動作を確認したのみですので、その他の環境での動作は保証できません。


背景と目的

前回のESP-WROOM-32のLチカの後、ほったらかしだったので、BLEを使ってみることにした。手始めにiBeaconとして動かせるか試す。


詳細

1.方法

Web上で調べても、まとまった情報が見つからなかった、自力でどうにか考えることにした。

前回セットアップしたESP32 Dev Moduleのスケッチ例には、SimpleBLEというものがある。その例では、SimpleBLE.hというヘッダをインクルードしており、SimpleBLEというクラスが定義されている。iBeaconとして動かすには、BLEのアドバタイジングデータをiBeacon規格(参考)に則った内容にしてやればよいはずなので、SimpleBLEクラスの_ble_send_adv_dataというメソッドを少し変更してやればよさそうだ。ということで、方針は決まった。


2.実装

2.1 SimpleBLEライブラリをお手本にいじる

まず、ライブラリをそのままいじるのはよくないので、{ライブラリのインストール先}\espressif\esp32\libraries\SimpleBLEフォルダをコピーしてSimpleBeaconとし、SimpleBLE.hとSimpleBLE.cppをそれぞれ、SimpleBeacon.h、SimpleBeacon.cppと変更。(SimpleBLEクラスの派生クラスというのがまともなやり方だろうが、実験なのでどうでもよい)

2.2 SimpleBeacon.hの変更

次に、SimpleBeacon.hの中で、もともとSimpleBLEクラスが定義されているので、これもSimpleBeaconと変更。以下が31行目からの定義部を抜粋したモノ。

class SimpleBeacon {
    public:

        SimpleBeacon(void);
        ~SimpleBeacon(void);
:
2.3 SimpleBeacon.cppの変更

さらに、SimpleBeacon.cppで、SimpleBLEクラスのメソッドが実装されているので、名前を変える。(詳細は、省略。)

2.4 _ble_send_adv_dataメソッド

そして、いよいよ_ble_send_adv_dataメソッドを変更する。具体的には、以下のようにiBeacon規格に則った31バイトのデータを作成。16進を直接打ち込んでいる部分は、UUID="0000...65E3"、Major=0x7F、Minor=0x7Fという感じ。

void SimpleBeacon::_ble_send_adv_data(void)
{
	uint8_t adv_data_len = 31;
    uint8_t adv_data[HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA + 1] = {
    	adv_data_len, BT_DATA_FLAGS, BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED, 26, 0xFF, 0x4C, 0x00, 0x02, 0x15,
    	0x00, 0x00, 0x00, 0x00, 0x1A, 0xE9, 0x10, 0x01, 0xB0, 0x00, 0x00, 0x1C, 0x4D, 0x64, 0x65, 0xE3,
    	0x00, 0x7F,
    	0x00, 0x7F,
    	0xA0
    };
    adv_data[0] = adv_data_len - 1;
    ble_send_cmd(HCI_BLE_WRITE_ADV_DATA, (uint8_t *)adv_data, HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA + 1);
}

2.5 メインのスケッチ

メインのスケッチは、SimpleBLEのサンプルほぼそのままだが、はじめにSimpleBLE.hではなく、SimpleBeacon.hをインクルードし、SimpleBeaconクラスの変数を定義。他は変更しない。(デバイス名が変更されるサンプルなのだが、デバイス名をアドバタイズする部分を削ってしまったので、実際は動作しない)

#include "SimpleBeacon.h"
SimpleBeacon ble;


3.動作確認

確認は、手元のAndroidスマホでBeaconScannerというアプリを使用してスキャン。その結果、以下の通り、検出された。iBeaconとして動作しているようだ。成功。

Screenshot_20170502-230034

図3 スマホで検出された様子


まとめ

ESP-WROOM-32をiBeaconとして動かすことができた。

ESP-WROOM-32のスペックからすると、iBeaconとして動かすのはもちろん勿体なさすぎるが、アプリケーションの1つとしてできて損はないだろう。

概要

HiLetgo Attiny85 マイクロ USB 開発ボードの開発環境を整え、無事LED点滅することができた。

背景と目的

以前、Trinketという小さなArduino互換マイコンを遊んだ。しかし、200円程度と非常に安くて小さいHiLetgo Attiny85 マイクロ USB 開発ボードというものをAmazonで見つけた。そこで、このボードを動かしてみる。

IMG_3452

HiLetgo Attiny85 マイクロ USB 開発ボード

詳細

1.入手

Amazonから、注文したところ海外発送のため約10日で到着。

2.環境構築

Arduinoで開発できるように環境を構築するため、Web上でやり方を調べたら、既に先人がいろいろ試しているおかげでたくさん出てきた。参考としたものを以下に列挙。なお、私の環境は、

  • Arduino 1.8.2
  • Windows 10
2.0 参考になった情報源
2.1 ドライバのインストール

参考2のUsageを見ると、私の環境であるWindows10では、専用ドライバが必要とのこと。そこで、

こちらを丸ごとダウンロードし、

windows_driver_installerのzadig_2.1.2.exeを実行。

Device>Load Preset Deviceから、同フォルダのmicronucleus.cfgを選択。

Unknown Device #1を選択し、Install Driverボタンをクリック。

すると、デバイスマネージャーに以下のように表示される。これで準備は整ったはず。

なお、数秒おきにデバイスが認識されたり、解除されたりするのは仕様らしい。。。(参考1などの記述によれば)

20170331000657

図2.1 ドライバのインストールが終わったところ。

2.2 Arduino IDEのセットアップ

参考1や参考2の手順に従い、Arduino IDEにボード情報のインストールを行う。

http://digistump.com/package_digistump_index.json

をボードマネージャー参照先URLに追加し、ボードマネージャーからDigistump AVR Boardsというのをインストール。46MBくらいあってなかなかでかい。

これが終わると、ツール>ボードから、Digistump AVR Boardsが選択できる。今回使用するのは、Digispark (Default - 16.5mhz)なのでそれを選択しておく。

3.プログラムの作成と書き込み

3.1 作成したプログラム

作成したのは、スケッチ例>Digispark_Examples>Startほぼそのまま。

// the setup routine runs once when you press reset:
void setup() {                
  pinMode(1, OUTPUT); //LED on Model A  or Pro
}

// the loop routine runs over and over again forever:
void loop() {
  digitalWrite(1, HIGH);
  delay(500);               // wait for a second
  digitalWrite(1, LOW); 
  delay(500);               // wait for a second
}
3.2 書き込み

このデバイスは、書き込み方法が少しクセがある。

まず、デバイスはUSBに接続せず、書き込みボタンを押す

Plug in device now... (will timeout in 60 seconds)という表示が出るので、ここでUSBに接続する

しばらくすると、デバイスが認識され書き込み実行される

となる。以下が書き込まれた様子。

20170330235354

書き込まれた様子

4.動作確認

3.の書き込みが完了した直後、すぐにプログラムが走り、無事、LEDが点滅した。

まとめ

HiLetgo Attiny85 マイクロ USB 開発ボードの開発環境を整え、プログラムの動作確認ができた。

概要

ESP-WROOM-32をArduinoとして動かし、Lチカできることを確認した。


背景と目的

本日、ESP-WROOM-32の秋月版開発ボードを入手した。そこで、早速動かす。ESP-WROOM-02をArduino環境で動かしていたので、これも同様にやってみたい。


詳細

1.主な流れ

  • Arduino用開発ツールをインストール、セットアップ
  • Arduinoでコーディング

という感じで、サクサク行く。


2.Arduino用開発ツールをインストール、セットアップ

まず、秋月の商品ページに、Arduino-ESP32プロジェクトサイトというリンクがあったので、そこをざっと読んだところ、Using through Arduino IDEという項目があったので従う。(ただし、この開発プロジェクトでは2017/03/21現在、まだすべての機能について実装が完了していないらしい。)

20170321223817
図2.1 開発ステータス

 私のPCは、Windowsなのでこちらを参考にGitからクローニングした。クローニング先は、C:\Program Files (x86)\Arduino\hardware\espressif\esp32とした。

この場所は、Arduinoをアップデートしたときに削除対象になってしまうので良くない。他の場所にするか、アップデート前に切り取って、アップデート後に戻すととよい。(2017/03/27追記)

Arduino IDEのファイル>環境設定>スケッチブックの保存場所\hardware\espressif\esp32とした。この場所に置くことで、Arduino IDEのボードマネージャーに表示される。(2017/05/13追記)参考

20170321235916

図2.2 ボードマネージャー


3.コーディング

とりあえず、Lチカができればいいので、以下の簡単なコードを作成。LEDをIO12に接続し、1秒周期で点滅。

const int GPIO_LED = 12;

// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(GPIO_LED, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(GPIO_LED, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(500);                       // wait for a second
  digitalWrite(GPIO_LED, LOW);    // turn the LED off by making the voltage LOW
  delay(500);                       // wait for a second
}


4.動作確認

以下のように、ちゃんと点滅できた!


まとめ

ESP-WROOM-32の開発ボードのArduino開発環境が整った。


概要

 当初の目的であるエアコンの電源を出先のスマホから操作可能となり、システムが完成した。


背景と目的

 前回までで、AWS上のLambdaからRaspberry Piへトピックをパブリッシュしてエアコンの電源を操作することができた。今回は、いよいよスマートフォン等から操作可能にし、システムを完成させる。


詳細

1.方法

 システムを完成させるには、

  • AWSのAPI Gatewayにて、操作情報を受け取りとLambdaの駆動をするWebエンドポイントを用意する
  • スマホ側の操作インターフェースとして、電源ボタン操作インターフェースとWebエンドポイントへの操作情報を送信機能を持つWebアプリを作成する

 が必要。それぞれ順にやっていく。

2.Webエンドポイントの作成

 API Gatewayにて、POSTメソッドを用意し、統合タイプを前回作成したLambda関数呼び出しとする。パラメータは、何でもよいが、Webアプリ側の実装が簡単なJSONでの受け渡しになるようにした。

3.操作用Webアプリ

 図3.1、3.2のように、電源ボタンの図をタップすると、ON/OFFが切り替わり、それに応じて2で作成したエンドポイントへリクエストを送るようになっている。

IMG_3363

 図3.1 OFF状態

IMG_3368

 図3.2 ON状態

4.動作確認

 最後に、動作確認を行った。スマホで、操作用Webアプリを開き、電源ボタンを押したところ、1秒程度で無事エアコンの電源が入った!結構素早い動きで驚いた。


 以上で、エアコンの電源をインターネット経由で操作することができるようになった。作業に要したのはおよそ2日だったが、出来上がったときはさすがにちょっと感動した。作ってよかった。

概要

 Raspberry PiがAWSから操作情報を受け取るためのプログラムの実装と、LambdaからRaspberry PiでON/OFF操作情報を送信するテストを行った。


背景と目的

 前回までで、疑似赤外線リモコンが完成した。今回は、Rapsberry PiがAWSから操作情報を受け取って疑似赤外線リモコンへ送信指示を出せるようにする。


詳細

1.方法

 AWSとRaspberry Piを連携させるには、AWS IoTを使う。操作情報を含むトピックをRaspberry Piがサブスクライブすればよさそう。

2.AWS IoTのコンソールでデバイスの作成

 デバイス作成は、AWS IoT上のコンソールのウイザードに従ってやった。コンソールのconnectから、Configuring a deviceのGet startedをクリックし、プラットフォームとSDKを選択する。プラットフォームはRaspbianなのでLinuxを選択。SDKは、いつの間にやらPythonができているのでPythonを選択。

SDK選択
図2.2 プラットフォームとSDKの画面

 次に、Thingの名前を決めてNext Stepに進むと、Download connect kitというものダウンロードボタンが現れるので、ダウンロード。

connection_kit
図2.3 connect kitのダウンロード画面

 次に、コマンドの指示がでるので、ダウンロードしたzipファイルをFTPでRaspberry Piに転送し、コマンドを実行。Start.shがうまく動けば、以下のようにコンソールに表示が出る。同時に、図2.5のようにAWSのコンソールにも表示が出る。

test
図2.4 コマンドを実行させた様子

command2
図2.5 通信がうまくいってるときのコンソール

 ここまでで、Raspberry PiとAWS IoTの接続ができた。

3.Raspberry Piで、トピックをサブスクライブする

 以下のリンク等を参考に、トピックをサブスクライブするためのコードを作成。

 最初、END_POINTというパラメータは何を指定すればいいか分からなかったが、こちらを見て、InteractのHTTPSという欄にあるRest API EndpointでOKだということが分かった。

# Import SDK packages
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient

CLIENT_ID = "Thing名"
END_POINT = "xxx.iot.リージョン名.amazonaws.com"
PATH_CA = "配置先ディレクトリ/root-CA.crt"
PATH_KEY = "配置先ディレクトリ/Thing名.private.key"
PATH_CRT = "配置先ディレクトリ/Thing名.cert.pem"

# For certificate based connection
myMQTTClient = AWSIoTMQTTClient(CLIENT_ID)

# Configurations
myMQTTClient.configureEndpoint(END_POINT, 8883)
myMQTTClient.configureCredentials(PATH_CA, PATH_KEY, PATH_CRT)

# 接続
myMQTTClient.connect()

# サブスクライブ開始
myMQTTClient.subscribe("$aws/things/Thing名/shadow/update", 0, subscribe_callback)

# 受信待ち
while True:
    time.sleep(1)

 そして、subscribeメソッドの引数に渡したコールバック関数は、トピックが到着したときの処理を書けばよい。ここでは、詳細は省略するが、到着したペイロードの中に、ON/OFF操作情報があるとして、それに応じてGPIO18にパルスを送るようにしている。

def subscribe_callback(client, userdata, message):
    # データを受け取る
    payload = json.loads(message.payload)
    # payloadの内容に応じて、GPIO18にパルスを送る
    :
    :
4.Lambdaによる確認

 最後に、LambdaでAWS IoTのThing Shadowに、トピックをパブリッシュしてみた。実装は、boto3を使った。参考になったサイトは以下。payloadという辞書に、送信したいデータを入れる。

# coding: utf-8
import boto3
import json

iot = boto3.client("iot-data", リージョン名)

def lambda_handler(event, context):

    # 送信データを作成
    payload = {
        "state": {
            "desired": {
                キー : 値,
                :
            },
            "reported": {
                キー : 値,
                :
            }
        }
    }
    
    # パブリッシュする
    iot.publish(
        topic='$aws/things/Thing名/shadow/update',
        qos=0,
        payload=json.dumps(payload)
    )

    # TODO implement
    return {}

 この結果、正しくトピックが受信され、無事エアコンがON/OFFできた!

 というわけで、AWS IoTとRaspberry Piが接続され、操作情報を載せたトピックをパブリッシュすればエアコンの電源がON/OFFできるところまできた。次回は、最終段階であるAPI Gateway⇒Lambda部分を構築し、スマホの操作を完成させる。

概要

 疑似赤外線リモコンのソフトウェアコーディングと動作確認を行った。


背景と目的

 前回に続き、疑似赤外線リモコンのソフトウェアコーディングと動作確認を行う。


詳細

1.概要
  • TrinketというArduino互換マイコンを使い、GPIOから赤外LEDを駆動する。そのため、コーディングはArduino言語。Arduino開発環境セットアップは、以前行ったのでOK。
  • 私の家のエアコンのリモコンコードは、手持ちの赤外線リモコン受信モジュールとオシロスコープを使って調べたところ、家電協会フォーマットのようだ。(フォーマットは、こちらが詳しい。)
2.疑似赤外線リモコン ソフトウェアのコーディング

 まず、38kHzのサブキャリアとなるパルスを出力する関数。

// 38kHz, デューティ1/3のパルスを出力
// delayMicrosecondsの精度がいまいちなので26usになっていない
void output_pulse(){

  digitalWrite(PIN_DRIVE, HIGH);
  delayMicroseconds(4);
  digitalWrite(PIN_DRIVE, LOW);
  delayMicroseconds(7);
  
}

 次に、データ1ビット分を出力する関数。

// データビット1つを出力する
void output_databit(int high_or_low){

  int i;

  // HIGH期間
  for (i = 0; i < DATABIT_PULSE_COUNT; i++){
    output_pulse();
  }

  // LOW期間
  if(high_or_low){
    delayMicroseconds(DATABIT_PULSE_T * DATABIT_PULSE_COUNT * 3); // 3周期
  } else {
    delayMicroseconds(DATABIT_PULSE_T * DATABIT_PULSE_COUNT); // 1周期
  }
  
}

 リモコンコード先頭のリーダー部を出力する関数。

// リーダーを出力
void output_leader(){

  int i;
  
  // HIGH期間
  for (i = 0; i < DATABIT_PULSE_COUNT * 8; i++){
    output_pulse(); // 8周期 * 8T
  }

  // LOW期間
  delayMicroseconds(DATABIT_PULSE_T * DATABIT_PULSE_COUNT * 4); // 4T
}

 1バイト分のデータを出力する関数。

// データを出力
void output_data(byte databits, int bits_len){

  // 上位ビットからbits_len個を出力
  int i;
  for (i = 0; i < bits_len; i++){
    int x = (databits & B10000000) >> 7; // 最上位を取り出す
    output_databit(x);
    databits = databits << 1; // 1ビットシフト
  }
  
}

 リモコンコード1フレーム分を出力する関数。

// フレームを出力
// customercode = { 上位8bit, 下位8bit };
// parity = パリティ4bit + 0000;
// data_bytes = { data0, data1, ... };
void output_aeha_frame(byte customercode[], byte parity, byte data_bytes[], int data_len){

  int i, k;

  // リーダー
  output_leader();

  // カスタマーコード
  output_data(customercode[0], 8);
  output_data(customercode[1], 8);
  
  // パリティ
  output_data(parity, 4);

  // データ
  for(i = 0; i < data_len; i++){
    if (i == 0){
      k = 4;
    } else {
      k = 8;
    }
    output_data(data_bytes[i], k);
  }

  // ストップビット
  output_data(B10000000, 1);
  
}

 入力を検出する関数が以下。これは、Raspberry Piからの送信指示がGPIO#0に来たかを検出する。パルス幅5msecならON、パルス幅10msecならOFF、それ以外は無視という単純な送信指示パルスなので、ノイズによる誤動作もなくはないが、問題になったら改めるとして、とりあえずこれにする。

// 入力を検出
// 戻り値 : ONのときは1, OFFのときは0, 検出なしは-1
int detect_input(){

  int pw;
  int result;

  // いきなりHIGHのときは検出なし
  if (digitalRead(PIN_INPUT) == HIGH){
    return -1;
  }

  // 開始検出
  while (digitalRead(PIN_INPUT) == LOW) {
    delay(1);
  }

  // パルス幅をカウント
  int i;
  pw = 0;
  for(i = 0; i < 15; i++){
    if(digitalRead(PIN_INPUT) == HIGH) {
      pw += 1;  
    }
    delay(2);
  }
  if(4 <= pw && pw <= 6) {
    result = 1; // 5msecのときON
  } else if (9 <= pw && pw <= 11){
    result = 0; // 10msecのときOFF
  }
  
  return result;
  
}

 定数と初期設定たち。

const int DATABIT_PULSE_T = 26; // 26us
const int DATABIT_PULSE_COUNT = 15; // 26us*15周期
const int PIN_DRIVE = 1; // LED駆動
const int PIN_INPUT = 0; // 送信指示信号入力

// 送信データ
byte CC_FUJITSU[] = { B00101000, B11000110 }; // カスタマーコード
byte data_on[] = { B00000000, B00001000, B00001000, B01111111, B10010000, B00001100, B10000010, B00100000, B00000000, B00000000, B00000000, B00000000, B00000100, B11010110 };
byte data_off[] = { B00000000, B00001000, B00001000, B01000000, B10111111 };

boolean on_off = true;
 
// the setup routine runs once when you press reset:
void setup() {
  pinMode(PIN_DRIVE, OUTPUT);
  pinMode(PIN_INPUT, INPUT);
}

 メインのループ

// the loop routine runs over and over again forever:
void loop() {

  int input;

  // 入力検出
  input = detect_input();
  if (input == 1) {
    output_aeha_frame(CC_FUJITSU, B00000000, data_on, 14);
  } else if (input == 0){
    output_aeha_frame(CC_FUJITSU, B00000000, data_off, 5);
  } else {
    delay(100);
  }
  
}
3.動作確認

 動作確認は、Raspberry PiのGPIO18から送信指示のパルスを出力することで行った。
その結果、送信指示を受け取り正しいリモコンコードが発出され、エアコンの電源がON/OFFできた。

 ということで、リモコン部分は完成。次は、AWS側の設計・製作に入る。


概要

 エアコンの電源を出先のスマホから操作するためのシステム構想とハードウェア製作を行った。


背景と目的

 私の家のエアコンは、オンタイマーが30分刻みでしかセットできない。なので、何か解決案はないかといろいろ考えを巡らせるうちに、インターネット経由で電源をON/OFFできるようにしたくなった。そこで、まずシステム構想とハードウェア部の製作を行う。


詳細

1.システム構想

 今回は、手持ちのラズベリーパイと、アカウントがあるAmazon Web Service(以下、AWS)とを使ってシステムを構成した。ポイントを書き出すと、以下の感じ。

  • エアコンの操作は、疑似赤外線リモコンでリモコンコードを送信することで行う
  • ラズベリーパイでAWS側からエアコン操作情報を取得し、それに基づき疑似赤外線リモコンに送信指示を出す
  • AWSでは、Webエンドポイントを用意し、リクエストに応じてAWS IoTを経由してラズベリーパイに操作情報を与える
  • スマートフォン等にて、WebエンドポイントにアクセスできるようにWebサイトを用意しておく
システム図
図1 システム構成図

2.疑似赤外線リモコン ハードウェアの設計・製作

 疑似赤外線リモコンは、以前遊んだTrinket(Arduino互換マイコン)と赤外LEDを使って構成する。また、リモコンに対する送信指示をRaspberry Piから出すようにした。Trinketを併用する理由は、Raspberry PiのGPIOではリモコンコードの再現に必要な時間精度を出すのが難しいから。

回路
図2.1 回路図

 また、回路への実装パターンは図2.2のような感じ。

回路パターン
図2.2 回路パターン図

 製作した疑似赤外線リモコン部は以下。そこそこ小さく作れた。

IMG_3362
図2.3 疑似赤外線リモコン部

 というわけで、ハードウェアは完成。次は、Trinketで、リモコンコードを出力するためのソフトウェアを書く。


このページのトップヘ