おうちハック
ESP32

電子錠を作ろう ハードウェア編

もうすぐクリスマスですね、クリスマスといえばサンタクロース。サンタクロースといえばピッキングです。皆さんのお家の鍵は大丈夫でしょうか、私のおうちはやばいです。なので、電子錠を作って内側からのみ開けられる防犯性の高い玄関にしようと思います。
市販品で似たようなものは売っていますがお高い、、、安くて2万円はちょっとな。。。ということで今回作ることにいたしました。作成期間は2週間でした。

今回はハードウェア編です(3Dプリンタと電子工作の布教活動でもあります)
部品の購入先やモデリングソフトや回路に軽く触れつつ、作り方を紹介します。
※モデリングや電子回路に関しては素人なので変なところがあったら指摘していただけるとありがたいです。
※私個人が使用することを考えて作ってるので汎用的ではないかもしれません
※2017年12月22日追記 この回路構成では電源周りが弱いためESP32が動かなくなる恐れがあります。

大まかな手順はこんな感じです
1.完成品のイメージを固める
2.部品の調達
3.モデリング&3Dプリント
4.回路設計
5.開発環境&テスト

順番に説明していきます
その前に

完成品

作り方なんて興味ないよ作ったもの見せろよという人はこちらをどうぞ
こんなもの作りました


完成品のイメージを固める

かっこよく言うと要件定義ですね、今回欲しい機能は
1.外から鍵を使わず解錠する
2.停電時も動いてほしい
3.なるべく楽したい
4.安く作る(重要)
5.内側の錠前からかぶせるタイプのもの
6.手動でも動く

なので
1,3.Bluetoothで特定のデバイスが近づいたら開くようにする
2.予備電源
4.原価を抑える、3Dプリンタを使う
5,6.機構などで解決

といった具合に作るイメージが固まりました。
そうするとマイコンには***を使おう、ギアモーターが必要だなとかいろいろ決まってきます。
必要そうなものをリストアップしたら次は必要な部品を調達です。

部品の調達

完成品のイメージから必要なものを購入していきます。
原価を抑えるための重要ポイントです。
以下部品リストです

部品名 個数 価格 合計 入手先
ESP32開発基板 1 785 785 aliexpress
ギアモーター 1 280 280 aliexpress
モータードライバ 1 104 104 マルツパーツ
マイクロスイッチ(4個入り) 1 160 160 秋月電子
PchパワーMOSFET 2 40 80 秋月電子
トランジスタ(NPN) 2 10 20 秋月電子
ベアリング 2 90 180 モノタロウ
ねじM3皿 15 8 120 ホームセンター
ねじM2(10個入り) 1 102 102 ホームセンター
ナットM3 10 7 70 ホームセンター
ワッシャーM4 7 7 49 ホームセンター
磁石(8個セット) 1 108 108 セリア
ブレッドボード 1 97 97 aliexpress
プリント基板 1 30 30
抵抗、ダイオード、ワイヤー等 1 100 100
バッテリー 2 500 1000 ヤフーショップ
3285

抵抗やワイヤーなどはすでに購入していたものがあったのでまとめて大体100円くらいと見積もってます。
接着剤や3Dプリンタの原料は含んでいません。
振り返ってみるとバッテリーやマイクロスイッチをaliexpressで買っておけばもう少し抑えられそうでした。ESP32も開発基板じゃなく単品で購入するともう少し安くなります(PCと接続するためにuart to USBモジュールが必要になります)。
バッテリですが、安いモバイルバッテリーを使用しています。が、少し曲者なので、似たような構成で作りたい人は別なバッテリを考えたほうが良いかもしれません。

使用するマイコン

今回はESP32を使います。ESP32は素晴らしい。Bluetooth、Wifiを内蔵。豊富なIO。デュアルコア。単体で500円を切る低価格。豊富な開発手段。非常に魅力的です、詳しくはWiki

購入先について

購入先にaliexpressとありますが中国のECショップで中国越境アマゾンみたいなものです。ここで電子部品を買うと非常にお安い(配送に2週間近くかかる)。arduinoや3Dプリンタ等とても低価格で販売してるので購入を検討されている方は見てみると良いかもしれません。たまに全く同じ製品がAmazonで倍以上の値段で売られてたりして笑える
パーツはお店の系統?によって価格が変わるのでいろいろ見て回るといい感じです。

モデリング&3Dプリント

とっても楽しいモデリングと3Dプリントの時間です。この時が一番楽しかった。
モデリングソフトにはFusion360を使用しています。個人向けや小規模な企業であれば無料で全ての機能を使える素晴らしいソフトウェアです。AutoDeskありがとう。
Fusion360の使い方はチュートリアルが充実してるので割愛して、機構について軽く説明します。

機構

機構はシンプルです
1.ギアモーターで歯車を回す
2.手動で回したときにギアモーターに力が加わらないよう途中で逃がす
3.スイッチを使い鍵が閉まったときに止まるようにする

2については磁石とユニクロ(鉄製)のねじを使って作ります。磁石の吸着力を使って一定以上のちからが加わったときに空転するようにします。適当に考えたので名前は不明です。下の3Dデータを使用して説明します。


歯車にある8つのくぼみに磁石をはめて、ツマミ部品の円錐状のくぼみにねじをはめて歯車と重ねます。ねじと磁石が吸着するため連動して動くけれど、歯車とつまみに磁石の吸着力以上の力が加わると空転します。

3は写真を見て理解していただければ(写真忘れたのでそのうち載せます)

3Dデータ

そんなこんなで出来た3Dデータがこちらです。
全てのパーツを並べるとこのような感じです。


組み立てイメージはこんな感じです。


3Dプリンターでプリントする

作成した3Dデータをプリントしていきます。全て印刷するのには大体12時間くらいかかりそうです。その間KF2プレイしてる時間が一番楽しい
注意点ですが、3dプリンターは少し大きめに出力されるのである程度の誤差を考慮してデータを作らないと「あれ?はまらない...」ということがしょっちゅうあります。
それも3Dプリンターによっても個体差があるので3Dデータを調整するか、3Dプリンターのパラメータを弄るかするとなんとかなったりします。たまにパラメータを弄りすぎてひどいことにもなりますが...

余談

たまにどんな3Dプリンターが良いか聞かれるんですが、直交型で固そうでレビューが良いものを買っておけば多分大丈夫です。それからまた迷うようなら目を瞑って適当にクリックしたものを買えばいいと思います。
私は形や動きががかっこよさげだなと思ってでデルタ型を買いました。動きがかっこいいだけのロマン武器みたいなやつです。精度は微妙で少し後悔しました

回路設計

回路設計です、以下の機能をもった回路を作ります
1.電源切替(USB供給なので切替部分のみ)
2.モーター制御部分
3.モータードライバとのレベル変換

回路のことは詳しくないのでググって作って動いてOK!といった感じで以下のものを作りました。
電子錠回路図.jpg
左上:電源切り替え回路
左下:ESP32
右上:モータードライバとレベル変換回路
右下: スイッチ

 課題

・電源が貧弱すぎる!モーターが弱々しくて悲しいです。もっとギャンギャン動いてもらうためにも電源の改善が必要です。
・省電力化が必要です。バッテリ駆動にするにはもっと消費電力を抑えたい。いっその事USB充電器から給電してバッテリは停電時用の予備電力にしてもいいかもしれません。
・そもそも今回使用したモバイルバッテリの動作が微妙。このモバイルバッテリは消費電力が低いと電力供給を停止します。今後省電力化をすすめると問題が起きそうなので電源を考え直さないと。。。

開発環境&テスト

開発環境はたくさんありますが、Bluetoothを使用するので公式フレームワークのESP-IDFを使用します。
他の開発環境についてはQiitaでまとめてる記事があるのでそちらを参照ください。
インストールはここを見ながらやりました。WindowsよりLinuxのほうが圧倒的に導入しやすかったです。Windowsは諦めた。

ハードウェアのテストコード

一番最初の動画で使ったテストコードです
5秒間隔でモーターの回転方向を切り替え、スイッチの入力を割り込みに設定してスイッチが押されたらモーターが停止するような内容です。
esp-idfのサンプルコードを書き換えました。

gpio_example_main.c
/* GPIO Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"

/**
 * Brief:
 * This test code shows how to configure gpio and how to use gpio interrupt.
 *
 *
 */

#define GPIO_OUTPUT_IO_0    32
#define GPIO_OUTPUT_IO_1    33
#define GPIO_OUTPUT_PIN_SEL  (((uint64_t)1<<GPIO_OUTPUT_IO_0) | ((uint64_t)1<<GPIO_OUTPUT_IO_1))
#define GPIO_INPUT_IO_0     34
#define GPIO_INPUT_IO_1     35
#define GPIO_INPUT_PIN_SEL  (((uint64_t)1<<GPIO_INPUT_IO_0) | ((uint64_t)1<<GPIO_INPUT_IO_1))
#define ESP_INTR_FLAG_DEFAULT 0

static xQueueHandle gpio_evt_queue = NULL;

static void IRAM_ATTR gpio_isr_handler(void* arg)
{
    uint32_t gpio_num = (uint32_t) arg;
    xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}

static void gpio_task_example(void* arg)
{
    uint32_t io_num;
    for(;;) {
        if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
            printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num));
                        if (gpio_get_level(io_num) == 0){
                            gpio_set_level(io_num == 34 ? GPIO_OUTPUT_IO_0 : GPIO_OUTPUT_IO_1 , 0);
                        }
        }
    }
}

void app_main()
{
    gpio_config_t io_conf;
    //disable interrupt
    io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
    //set as output mode
    io_conf.mode = GPIO_MODE_OUTPUT;
    //bit mask of the pins that you want to set,e.g.GPIO18/19
    io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
    //disable pull-down mode
    io_conf.pull_down_en = 0;
    //disable pull-up mode
    io_conf.pull_up_en = 0;
    //configure GPIO with the given settings
    gpio_config(&io_conf);

    //interrupt of rising edge
    io_conf.intr_type = GPIO_PIN_INTR_POSEDGE;
    //bit mask of the pins, use GPIO4/5 here
    io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;
    //set as input mode    
    io_conf.mode = GPIO_MODE_INPUT;
    //enable pull-up mode
    io_conf.pull_up_en = 1;
    gpio_config(&io_conf);

    //change gpio intrrupt type for one pin
    gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_ANYEDGE);

    //create a queue to handle gpio event from isr
    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
    //start gpio task
    xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);

    //install gpio isr service
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    //hook isr handler for specific gpio pin
    gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);
    //hook isr handler for specific gpio pin
    gpio_isr_handler_add(GPIO_INPUT_IO_1, gpio_isr_handler, (void*) GPIO_INPUT_IO_1);

    //remove isr handler for gpio number.
    gpio_isr_handler_remove(GPIO_INPUT_IO_0);
    //hook isr handler for specific gpio pin again
    gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);

    int cnt = 0;
    while(1) {
        printf("cnt: %d\n", cnt++);
        vTaskDelay(5000 / portTICK_RATE_MS);
        gpio_set_level(GPIO_OUTPUT_IO_0, ( cnt + 1 ) % 2);
        gpio_set_level(GPIO_OUTPUT_IO_1, cnt % 2);
        printf("on: %d : %s\n", cnt % 2, cnt % 2 ? "open" : "close" );
    }
}

作ってみた感想とかまとめ

機構は良いが回路は微妙だった。
記事を読み返してみて説明不足なところが多いと思いました。よくわからなかったり知りたいことがあれば質問ください記事を書くのが一番つらかった
本当はファームウェアまでやる予定だったけどRTOS使ってたりもっとちゃんと読みたくなったので記事を分割しました。
クリスマスまでにはファームウェア編も書きます

2017年12月22日追記

ESP32は↓のサイトを見る限り電源周りが弱いと壊れることがある。
http://nemuisan.blog.bai.ne.jp/?eid=220875
今回使用したESP32開発基板および作成した回路はモーターが回ると電圧がガクンと落ちるので
あまり時間をかけずに壊してしまいそうだし早く直したい。
取り急ぎ搭載されてるレギュレータをADP3338に修正します。
それとAliexpressでDc-Dc電源を幾つか購入したのでそちらも含めて問題の再現とか購入したDc-Dcコンバータが使えるかテストできればいいな。出来たら記事にしよう。。。