Youtubeでも活動中

チャンネル登録してくれたら喜びます。

このボタンで簡単にチャンネル登録!

ESP32で無線ショートカットキーボードを作ってみる。【Bluetooth通信】

esp32-lolind32-bluetooth-keyboard-eyecatchArduino(マイコンボード)

今回は、ESP32搭載のLolinD32を使います。

ESP32と言えば、WiFiとBluetoothを使用して無線通信が可能です。
この無線通信を使えば、簡単に無線キーボードが作れます。

以前の記事で、
Arduino Pro Microを使ってキーボード入力しました。
この時は、ATMega32U4というチップが乗っていれば、
Leonardoでも何でもよかったのですが、
いかんせん有線という縛りがありました。

今回のESP32を使ってキーボードを作る方法だと、
ESP32自体をバッテリーで動かしてしまえば、
ホントに無線です。

Bluetooth通信の交信距離は大したことありませんが、
それでも2,3m離れたところから指令を出したり
結構使い道はありそうですよね。

それでは行ってみましょう!

自己紹介

東証一部上場企業でサラリーマンしてます。

主に工場(生産現場)で使用する検査装置のアプリケーション開発してます。

ヒトの作業を自動化して簡略化するアプリケーションを日々開発中。

転職に成功して現在は超大手企業でシステム系の開発をしています。

Youtubeチャンネルにさまざまな動画を上げています

↓↓↓こちらからYoutubeチャンネルにアクセス!! ↓↓↓

今回の記事を実践するとどんなことができる??【先に完成版を動画で。】

今回紹介している内容をそのまま実行すると、
こんな感じでAndroidタブレットのメモ帳に文字入力ができます。

LolinD32のスイッチ状態からタブレットに送る文字を決めてBluetoothで送信している。

ブレッドボードがリモコンみたいなイメージですね。
タブレット端末とマイコンは無線でしかつながっていません。
ほんとに市販のBluetooth接続のキーボードと扱いは全く一緒です。

ちゃんとタブレット端末から、このマイコンの名前が表示されて、
選択できます。市販の無線キーボードのセットアップと一緒ですよね。

各タクトスイッチにそれぞれ、「hobby」「happy」「blog」「エンターキー」
と打ち込むようにプログラミングしています。
通常のキーボードは1キー打つと1文字入力ですから、
これはショートカットというか、マクロ機能を搭載させている感じですかね。

必要な道具たち。

今回使用したのは以下のような道具とパーツです。
タブレット端末は、Bluetooth接続できるパソコンでも代用可能です。

  • Androidタブレット←bluetoothが使えればなんでもOK!
  • LolinD32
  • ブレッドボード
  • タクトスイッチx4
  • ジャンパーワイヤーx9

抵抗は、内部プルアップをかけるため不要です。
どうせやるならそろえる部品は少ないほうが良いですもんね!

一応どこで買えるかも情報を共有しておきますね。

Androidタブレット

これは、Androidタブレットだと必ずBluetooth通信可能だから選んでいます。
他にも最近だとノートPC、スマホはBluetooth通信可能ですよね。
デスクトップPCだとついてないことが多いイメージですね…

もちろん、みんな大好きAppleのiphoneだって使えます。
ただ、フルキーとして認識されるので、今回の使い方だと
deleteキーなどがソフトウェアキーボードで押せないので、
ほぼ使えないですね…ほかの文字が打てなくなってしまうので。

LolinD32

ESP32-lolinD32-connected-battery
本ブログでも何回か登場しているLolinD32。

LolinD32は、ESP32を搭載したマイコンボードです。
何回かこのブログでも登場していますが、
Amazonで気軽に購入できるマイコンボードということで、
わたしはいくつか購入しています。

※記事執筆(2023/3/12)時点でAmazonの在庫が切れてしまっています…

ただし、circuitpythonをやってみたいならこの子はオススメできません。
ESP32搭載のマイコンボードでcircuitpythonをやってみたいなら、
ESP32-S3がオススメです。
その時の記事はこちら↓↓

circuitpythonをESP32でやるならこれがオススメ。

ちなみに、circuitpythonの余談ですが、
ESP32でなくてcircuitpythonを使いたいなら、
手軽さで行くと、raspberryPiPicoがオススメですね。

こちらも以前記事にしているので参考にしてみてください。

RaspberryPiPicoでcircuitpythonを使っている記事。

ブレッドボード

次にブレッドボードです。
ブレッドボードは、安物はあまりオススメできません。

以前ご紹介しましたが、安いブレッドボードだと、
板自体が反ってしまって、マイコンボードのピンヘッダが
八の字になってしまいます。

amazonの安物ブレッドボードと比較したときの記事。

そのままはんだ付けするとわかりますが、
ほんとに八の字です。反っていないブレッドボードに
差し込もうとすると結構大変なんですよね…

タクトスイッチ

tact-switch
タクトスイッチたち。

タクトスイッチは、
ちょっとスイッチをつけたいときにすぐに必要になる割に、
そんなに数を持ってなかったりするんですよね…

最初の電子工作キットとかだと入っていてもせいぜい2~3個ですもんね。

今回のように4個使うとか、
他の装置でもう使っちゃっているよ…
なんてこと結構ありませんか?

わたしの場合はAmazonで解決しました。
小売してくれる電気パーツ屋さんのほうが安いんでしょうが、
Amazonのほうが配達が早かったり総量がかからなかったりなので、
まあAmazonが無難でしょうね。

これだけ入ってそんなに大した値段ではないので、
いったん買っておいたほうが後で楽だと思います。

ジャンパーワイヤー

jumper-wire
ジャンパーワイヤーと言えばこれですね。

ジャンパーワイヤーは、
今回は、LolinD32の各ピンと、タクトスイッチをつなぐ目的で使用します。

またまた余談ですが、
このジャンパーワイヤー好みの長さで自作できるってご存じでしたか??

以前自作したときの記事を貼っておきます。

ジャンパーワイヤーを自作したときの記事

具体的な配線方法については次の章で解説していきます。

配線・プログラミングについて。

配線方法と、LolinD32に書き込むプログラムについて
見ていきましょう。
そんなに複雑ではありませんが、1点注意点があります。

それが、外部ライブラリを用意する必要があるというところです。
まずは配線を完了させてからプログラムのところでまとめて解説しましょう。

配線方法

配線は、このような感じです。
使うピンは、16,17,18,19と連番にしています。

lolind32_keyboard
lolinD32とタクトスイッチ4個を接続。pullupはソフト側で行うので抵抗は不要。

また、タクトスイッチとピンを直接接続していますが、
これは後ほど指定するPullUPがありますので、
抵抗は不要です。

PullUPしない場合は抵抗を追加する必要がありますし、
配線するところが少々変更になりますので注意が必要です。

書き込むプログラムについて

さて、ハード的なセッティングは完了したので、
次はLolinD32に書き込むプログラムについて解説します。

まず、書き込むプログラムの内容を記載します。

#include <BleKeyboard.h>

BleKeyboard bleKeyboard("hobbyhappy"); // Can be renamed to any device name you like.

void setup() {
  bleKeyboard.begin();

  pinMode(16,INPUT_PULLUP);    // GPIO16(pullup)
  pinMode(17,INPUT_PULLUP);    // GPIO17(pullup)
  pinMode(18,INPUT_PULLUP);    // GPIO18(pullup)
  pinMode(19,INPUT_PULLUP);    // GPIO19(pullup)  
}

void loop() {
  if(bleKeyboard.isConnected()) {     // Check to see if they are connected.
    if(digitalRead(16) == LOW){       // If GPIO16 change to LOW
      bleKeyboard.print("hobby");     // Send hobby
      delay(200);
    }
    if(digitalRead(17) == LOW){       // If GPIO17 change to LOW
      bleKeyboard.print("happy");     // Send happy
      delay(200);
    }
    if(digitalRead(18) == LOW){       // If GPIO18 change to LOW
      bleKeyboard.print("blog");      // Send blog
      delay(200);
    }
    if(digitalRead(19) == LOW){       // If GPIO19 change to LOW
      bleKeyboard.write(KEY_MEDIA_VOLUME_UP);   // Push Enter-Key
      delay(200);
    }
  }
}

わかりにくいところについては少し解説をしていきます。

BleKeyboard.hとは??

この内容で一番難しいというかわかりづらいところが、
BleKeyboard.hという一番上に書いてあるものです。

これは、ライブラリと呼ばれるもので、
細かい内容を自分で書かなくても、代わりにやってくれる便利アイテムと
考えてもらえれば理解しやすいと思います。

具体的には、このライブラリを使うことで、
“hobby”と送信したいときにたった1行で記載可能になります。
bleKeyboard.print(“hobby”);
これがそれにあたります。

ライブラリを使用しないと、とんでもない行数を書く必要があり、
そもそも通信関係の様々な知識が無いと実現できませんから、
ここはありがたくライブラリを使用させてもらいましょう。

BleKeyboard.hはどこで手に入る?

さて、この便利なライブラリですが、
入手する方法は、Githubからダウンロードです。

ダウンロードが完了したら、ArduinoIDEで使えるように
ライブラリの読み込みをおこないます。

図が無いと少々わかりにくいので、実際の画像を使って解説していきましょう。

まず、こちらのリンク先に飛んでください。
※Githubに飛びます。

次に、緑色のCodeというボタンを押します。
すると、Download ZIPと出ますので、クリックしてダウンロードしましょう。

download-ble-keyboard-at-github
githubからble-keyboardをダウンロードする。

ArduinoIDEでble-keyboardを使えるようにする方法

ここまでいったらもうほとんど終わったようなものです。
先ほどダウンロードしたzipファイルをArduinoIDEに適用させます。

まずはArduinoIDEを開いて、
Sketch⇒Include Library⇒Add.ZIP Libraryを選択します。

Arduinoide-include-zip-library
ArduinoIDEでZIP形式のライブラリをインストールする手順。

あとは先ほどダウンロードしたble-keyboardのzipファイルを
選択したら#includeが通るようになります。

今回のまとめ。

今回は、ESP32を搭載したLolin D32を使って、
キーボードのように文字を打ったり、Enterキーを押す方法についてご紹介しました。

Arduino pro microでは、キーボードやマウスの操作が可能というお話を
以前記事にしていますが、
今回はESP32ということで、無線(bluetooth)が使えるというのがメリット大です。

少し前に出した記事で、LolinD32をバッテリーで動かす方法について紹介していますから、
この方法を使うと、ケーブルが不要の完全無線化が実現可能です。

ただ、ピンの数に限りがあるので、
ホントのフルのキーボードを自作しようと思うとちょっと工夫が必要ですね…

例えば、I2C通信で使えるMCP23017なんかを複数搭載することで、
とりあえずは端子の数はカバーできます。

あとは、それで通信速度が間に合うかですかね。
キーを押して1秒くらいラグなんてあるとちょっと実用性が無いですから…

そのうちMCP23017を使って自作できるかちょっと試してみましょうかね。
原理的にはできることはわかっているのですがね…

コメント

'}},vars:{discoveredBrushes:null,highlighters:{}},brushes:{},regexLib:{multiLineCComments:XRegExp("/\\*.*?\\*/","gs"),singleLineCComments:/\/\/.*$/gm,singleLinePerlComments:/#.*$/gm,doubleQuotedString:/"([^\\"\n]|\\.)*"/g,singleQuotedString:/'([^\\'\n]|\\.)*'/g,multiLineDoubleQuotedString:XRegExp('"([^\\\\"]|\\\\.)*"',"gs"),multiLineSingleQuotedString:XRegExp("'([^\\\\']|\\\\.)*'","gs"),xmlComments:XRegExp("(<|<)!--.*?--(>|>)","gs"),url:/https?:\/\/[\w-.\/?%&=:@;#]*/g,phpScriptTags:{left:/(<|<)\?(?:=|php)?/g,right:/\?(>|>)/g,eof:!0},aspScriptTags:{left:/(<|<)%=?/g,right:/%(>|>)/g},scriptScriptTags:{left:/(<|<)\s*script.*?(>|>)/gi,right:/(<|<)\/\s*script\s*(>|>)/gi}},toolbar:{getHtml:function(e){function t(e,t){return B.toolbar.getButtonHtml(e,t,B.config.strings[t])}for(var n='
',r=B.toolbar.items,i=r.list,a=0,l=i.length;l>a;a++)n+=(r[i[a]].getHtml||t)(e,i[a]);return n+=""},getButtonHtml:function(t,n,r){return n=e(n),''+e(r)+""},handler:function(e){function t(e){var t=RegExp(e+"_(\\w+)"),n=t.exec(r);return n?n[1]:null}var n=e.target,r=n.className||"",i=s(g(n,".syntaxhighlighter").id),a=t("command");i&&a&&B.toolbar.items[a].execute(i),e.preventDefault()},items:{list:["expandSource","help"],expandSource:{getHtml:function(e){if(1!=e.getParam("collapse"))return"";var t=e.getParam("title");return B.toolbar.getButtonHtml(e,"expandSource",t?t:B.config.strings.expandSource)},execute:function(e){var t=o(e.id);r(t,"collapsed")}},help:{execute:function(){var e=x("","_blank",500,250,"scrollbars=0"),t=e.document;t.write(B.config.strings.aboutDialog),t.close(),e.focus()}}}},findElements:function(e,t){var n=t?[t]:i(document.getElementsByTagName(B.config.tagName)),r=B.config,a=[];if(r.useScriptTags&&(n=n.concat(A())),0===n.length)return a;for(var l=0,s=n.length;s>l;l++){var o={target:n[l],params:p(e,E(n[l].className))};null!=o.params.brush&&a.push(o)}return a},highlight:function(e,t){var n=this.findElements(e,t),r="innerHTML",i=null,a=B.config;if(0!==n.length)for(var l=0,s=n.length;s>l;l++){var o,t=n[l],u=t.target,c=t.params,g=c.brush;if(null!=g){if("true"==c["html-script"]||1==B.defaults["html-script"])i=new B.HtmlScript(g),g="htmlscript";else{var h=b(g);if(!h)continue;i=new h}o=u[r],a.useScriptTags&&(o=M(o)),""!=(u.title||"")&&(c.title=u.title),c.brush=g,i.init(c),t=i.getDiv(o),""!=(u.id||"")&&(t.id=u.id),u.parentNode.replaceChild(t,u)}}},all:function(e){m(window,"load",function(){B.highlight(e)})}};return B.Match=function(e,t,n){this.value=e,this.index=t,this.length=e.length,this.css=n,this.brushName=null},B.Match.prototype.toString=function(){return this.value},B.HtmlScript=function(e){function t(e,t){for(var n=0,r=e.length;r>n;n++)e[n].index+=t}function n(e){for(var n,a=e.code,l=[],s=r.regexList,o=e.index+e.left.length,u=r.htmlScript,c=0,g=s.length;g>c;c++)n=L(a,s[c]),t(n,o),l=l.concat(n);null!=u.left&&null!=e.left&&(n=L(e.left,u.left),t(n,e.index),l=l.concat(n)),null!=u.right&&null!=e.right&&(n=L(e.right,u.right),t(n,e.index+e[0].lastIndexOf(e.right)),l=l.concat(n));for(var h=0,g=l.length;g>h;h++)l[h].brushName=i.brushName;return l}var r,i=b(e),a=new B.brushes.Xml,l=this,s="getDiv getHtml init".split(" ");if(null!=i){r=new i;for(var o=0,u=s.length;u>o;o++)(function(){var e=s[o];l[e]=function(){return a[e].apply(a,arguments)}})();return null==r.htmlScript?(v(B.config.strings.brushNotHtmlScript+e),void 0):(a.regexList.push({regex:r.htmlScript.code,func:n}),void 0)}},B.Highlighter=function(){},B.Highlighter.prototype={getParam:function(e,t){var n=this.params[e];return d(null==n?t:n)},create:function(e){return document.createElement(e)},findMatches:function(e,t){var n=[];if(null!=e)for(var r=0,i=e.length;i>r;r++)"object"==typeof e[r]&&(n=n.concat(L(t,e[r])));return this.removeNestedMatches(n.sort(k))},removeNestedMatches:function(e){for(var t=0,n=e.length;n>t;t++)if(null!==e[t])for(var r=e[t],i=r.index+r.length,a=t+1,n=e.length;n>a&&null!==e[t];a++){var l=e[a];if(null!==l){if(l.index>i)break;l.index==r.index&&l.length>r.length?e[t]=null:l.index>=r.index&&i>l.index&&(e[a]=null)}}return e},figureOutLineNumbers:function(e){var t=[],n=parseInt(this.getParam("first-line"));return y(e,function(e,r){t.push(r+n)}),t},isLineHighlighted:function(e){var t=this.getParam("highlight",[]);return"object"!=typeof t&&null==t.push&&(t=[t]),-1!=h(t,""+e)},getLineHtml:function(e,t,n){var r=["line","number"+t,"index"+e,"alt"+(""+(0==t%2?1:2))];return this.isLineHighlighted(t)&&r.push("highlighted"),0==t&&r.push("break"),'
'+n+""},getLineNumbersHtml:function(e,t){var n="",r=a(e).length,i=parseInt(this.getParam("first-line")),l=this.getParam("pad-line-numbers");1==l?l=(""+(i+r-1)).length:1==isNaN(l)&&(l=0);for(var s=0;r>s;s++){var o=t?t[s]:i+s,e=0==o?B.config.space:S(o,l);n+=this.getLineHtml(s,o,e)}return n},getCodeLinesHtml:function(e,t){e=C(e);for(var n=a(e),r=(this.getParam("pad-line-numbers"),parseInt(this.getParam("first-line"))),e="",i=this.getParam("brush"),l=0,s=n.length;s>l;l++){var o=n[l],u=/^( |\s)+/.exec(o),c=null,g=t?t[l]:r+l;null!=u&&(c=""+u[0],o=o.substr(c.length),c=c.replace(" ",B.config.space)),o=C(o),0==o.length&&(o=B.config.space),e+=this.getLineHtml(l,g,(null!=c?''+c+"":"")+o)}return e},getTitleHtml:function(t){return t?""+e(t)+"":""},getMatchesHtml:function(e,t){function n(e){var t=e?e.brushName||a:a;return t?t+" ":""}for(var r=0,i="",a=this.getParam("brush",""),l=0,s=t.length;s>l;l++){var o,u=t[l];null!==u&&0!==u.length&&(o=n(u),i+=N(e.substr(r,u.index-r),o+"plain")+N(u.value,o+u.css),r=u.index+u.length+(u.offset||0))}return i+=N(e.substr(r),n()+"plain")},getHtml:function(t){var n,r,i,a="",s=["syntaxhighlighter"];return 1==this.getParam("light")&&(this.params.toolbar=this.params.gutter=!1),className="syntaxhighlighter",1==this.getParam("collapse")&&s.push("collapsed"),0==(gutter=this.getParam("gutter"))&&s.push("nogutter"),s.push(this.getParam("class-name")),s.push(this.getParam("brush")),t=w(t).replace(/\r/g," "),n=this.getParam("tab-size"),t=1==this.getParam("smart-tabs")?R(t,n):H(t,n),this.getParam("unindent")&&(t=P(t)),gutter&&(i=this.figureOutLineNumbers(t)),r=this.findMatches(this.regexList,t),a=this.getMatchesHtml(t,r),a=this.getCodeLinesHtml(a,i),this.getParam("auto-links")&&(a=I(a)),"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.match(/MSIE/)&&s.push("ie"),a='
'+(this.getParam("toolbar")?B.toolbar.getHtml(this):"")+''+this.getTitleHtml(this.getParam("title"))+""+""+(gutter?'
'+this.getLineNumbersHtml(t)+"":"")+''+'
'+a+""+""+""+""+""+""},getDiv:function(e){null===e&&(e=""),this.code=e;var t=this.create("div");return t.innerHTML=this.getHtml(e),this.getParam("toolbar")&&m(c(t,".toolbar"),"click",B.toolbar.handler),this.getParam("quick-code")&&m(c(t,".code"),"dblclick",X),t},init:function(e){this.id=f(),u(this),this.params=p(B.defaults,e||{}),1==this.getParam("light")&&(this.params.toolbar=this.params.gutter=!1)},getKeywords:function(e){return e=e.replace(/^\s+|\s+$/g,"").replace(/\s+/g,"|"),"\\b(?:"+e+")\\b"},forHtmlScript:function(e){var t={end:e.right.source};e.eof&&(t.end="(?:(?:"+t.end+")|$)"),this.htmlScript={left:{regex:e.left,css:"script"},right:{regex:e.right,css:"script"},code:XRegExp("(?"+e.left.source+")"+"(?.*?)"+"(?"+t.end+")","sgi")}}},B}();"undefined"!=typeof exports?exports.SyntaxHighlighter=SyntaxHighlighter:null;
タイトルとURLをコピーしました