6
4

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初心者がESP32でWebSocketラジコン作った話

Last updated at Posted at 2021-08-10

Arduino歴3ヶ月の初心者が安価で高機能なマイコンESP32を使ってスマホから操作できるラジコンを作った時に躓いた点などを備忘録兼共有用に時系列順で書き残す

経緯

とある所属しているロボコン団体のメンバー募集兼遊びとしてラジコンを作る企画が浮上した
そのときはESP32っていうwifi飛ばせるマイコンがあるらしいよという話だけを聞いのだが個人的にも興味が湧いたので企画に参加することにした

初期の最終目標

  • スマホで操作
  • すぐ動かせる
  • 電源は1つ 電池
  • 簡単に組み立てられる
  • 予算は一機¥3000

ハード

一般的なホビー用DCモーターx2の二輪駆動のラジコン

チームにいた有識者はESPはノイズに弱いと繰り返して別のマイコンを使うか電源をモーターとESPで分けることを薦めてきた
ブラシ付きモーターは回る時に接点の切り替えからノイズが発生する
なお私はなんらかのフィルターをつけてるもしくは何かしらで絶縁して対処すればいいと考えていたのでその程度で引き返すことは考えていなかった

最初の目標はESPはUSBから電源を供給した状態で別電源を使ってモーターを回すこと

「とりあえず動く」までに購入した部品

ESP32-DevKitC-32E

主役

ブレッドボード 301 (没)

チームの手元にあった
27列

モーター タミヤダブルギアボックス (没)

チームの手元にあった
調べたところ¥780
モーター電圧は3V以下

二輪ロボットプラットフォーム (高すぎ没)

高すぎ

  • 二輪ロボットプラットフォーム ¥1480 秋月電子

モータードライバー TB67H450 (没)

チームで最初に買いに行ったモータードライバーがこれ

一般的な1回路のモータードライバーだ
選んだ理由はよく分かっていない

端子は8本
IN端子x2
OUT端子x2
モーター電源 VM
モーター電流設定 Vref
モーター電流検出 RS
GND

IN端子2本はESPに繋ぎ電流関連は使わないので適宜GND等に接続した
回転速度はIN端子から直接PWMで制御すれば良い

私はタミヤのギアボックスとESP32Eで試作した
電源を3Vに設定して使用

結果動かなかった
モータードライバーのIN端子を直接ESPの電源に繋げるなどして確かめたが動かない
故障等疑ってもう一つ買ってきたのだがここで原因に気づいた

「・モータ電源電圧(VM):4.5V~44V」

完全に敗北した
そもそもこのドライバーではタミヤのギアボックスのモーターを直接制御することなどできなかったのである

チームの別のメンバーはこのドライバーで二輪ロボットプラットフォームのモーターを動かすことに成功していた
定格4.5VのモーターだったようでUSBの電源をそのまま流してやったところ元気に回ったそうだ

モータードライバー DRV8832 (没)

モーター電圧範囲が3Vを含むようなモータードライバーを個人で探して見つけたのがこれ

  • DRV8832 ¥230 秋月電子
    今度こそ行けるだろ&前のより¥90安いと作る前から勝手に舞い上がっていたわけだがこのドライバーが割と曲者だった

端子は10本
IN端子x2
OUT端子x2
モーター電源 VCC
モーター電圧設定 Vset
モーター電圧設定用定電圧出力 Vref
モーター電流設定 ISENSE
障害通知 FAULTn

IN端子があって何故モーターの電圧設定の端子があるのか
それはこのドライバーがIN端子からのPWM入力に対応していないからである
どうやら中にPWM生成のロジックが入っているようでVsetを参照してパルスを生成するとのこと
このVsetもPWMに対応していないと書いてあったがこれはESPのアナログ出力でなんとかできるだろう
テスターで確認したところVrefからは常に1.28Vほどが出ていた
これを抵抗等で分圧してVsetに入れるとその4倍の電圧がOUT端子から出てくるということだそう
乾電池直接繋げて即動かすと言う使い方には適していそうだが少なくとも今回の目的には合わない

かなり謎な仕様だなと思いつつ安いしこれでどうにかしたいところ
電流設定等いらない端子は適宜GND等にVsetをそのままVrefに繋げて
IN端子からESPのIO出力3.3Vをそのまま流したところモーターがぴくっと動いた
障害通知端子を確認したところGNDと通電していた

動いたのは大きかったが結局原因は分からず私のやる気は一度ここで尽きた

モータードライバー DRV8835 (可)

その後のチームの集まりで買い出しに行ったときにメンバーがノリで手に取った一品
ホームページで確認したところどうやら2回路入でモーター電圧等も用途に合っている
変な仕様のドライバー2個使うより安いしとりあえず一個買ってみるかと買った理由もほぼノリ

端子は12本
IN端子x2x2
OUT端子x2x2
ドライバー電源 Vcc
モーター電源 VM
モード設定 MODE
GND

モード設定というのはこのドライバーはDCモーターモードとステッピングモーターモードがあって
ステッピングモーターモードにすると位相制御が直でできるようだ

一番最初のドライバーで作った回路を少し変えてESPのプログラムもそのまま試したところなんとあっさり動いてしまった
二輪ロボットプラットフォームのモーターも動いた

ブレッドボード 801

新しく購入したドライバーDIPであるためブレッドボードの中央の溝を跨ぐ形でしか配置できず手元の27列ブレッドボード1枚では足りない
同じ¥200で購入できる30列のブレッドボードがあったので購入した

モーター ROB-13302

DRV8835を買いに行った足で隣の千石電商に寄ったところ
二輪ロボットプラットフォームのギアボックス付きモーターに似たものが2個セットで¥600で売っていた
タミヤのダブルギアボックスが¥780だったのでこれは安いということで購入した
定格4.5V

電池BOX

モータードライバー TC78H653FTG

これは私が秋月電子のページをぶらぶらしていたところ見つけたもの
DRV8835でもいいのだが2回路入りで驚きの¥200
即買った

端子は16本
IN端子x2x2
OUT端子x2x2
モーター電源 VM x2
モード設定 LARGE MODE STBY
GND x3

ドライバの電源はモーター電源から供給される
LARGEは電流を多く消費するモーターを使う場合にIN,OUT2組を並列にして使う場合の設定
MODEはDRV8835と同じくステッピングモーターのためのもの
STBYはGNDに落とすことでドライバーをスリープ状態にすることができる

これだけ詰め込んで¥200である
今までのドライバーを買った金はなんだったのだろうか

モード設定の端子を適宜落とすなり上げるなりして
他はDRV8835からほぼ配線を変更することなく置き換えることができた

ただしこのドライバー基盤の上の方に大きめのチップコンデンサが載っていて
ESPにギリギリまで近づけて配置した場合ESPのアンテナにコンデンサが干渉してしまう
さらに端子の識別文字が裏面に印刷されていて一度ブレッドボードに刺すと非常に配線が分かりにくくなる
この二つの問題点は基盤に足をつける際に表裏返すことで解決した
ピンヘッダーのプラスチック部の高さがチップコンデンサよりも高くブレッドボードに刺さりきらないこともないうえ識別文字も見えるため配線がわかりやすい
ランド部はスルーホール加工のため表面よりもハンダ付けの難易度は多少上がるが十分に機能する

モータードライバーはこれで確定

電源の統一

この時点でモーターのノイズが原因でESPが不安定になることは確認できなかったので電源の統一に取り組んだ

ESPの電源供給は3通り

  • USB給電
  • 5V端子から5+α~12V給電
  • 3.3V端子からきっかり3.3V給電

USB給電は今回電池BOXを用意してしまったうえコストカットの面からもよろしくないので除外する

電池切れの定義が電池一本の電圧が1Vになった時点として電池BOXからの電圧は3.0~4.5V
5V給電にせよ3.3V給電にせよ電源を昇圧もしくは昇降圧しなければならない
5V端子については個人で確認したところ給電してもESPの電源LEDが光らず動作しているのかわからなかった
よって3.3V給電にすることにした

ESPの3.3Vでの消費電流が調べたところ様々な情報があったが平均したところ最高で500mAほどだった
選んだDCDCコンバーターがこちら

入力5Vで最大900mAまで供給できるらしい
ただこれは基盤に直に半田付けした際の数値でありピンヘッダーを使うと精々500mAというところ
要件は満たしているのでこれでなんとかなりそうだ

部品確定

以上ESPとモーターは別電源でモーターを動かすところまでたどり着いた

計¥2670で収まった
ブレッドボードの配線はこんな感じ
左の電源ラインを4.5Vに接続すると動く
E7B713EB-AB14-40C8-B1AB-D8DD518C7B7D.jpeg

ソフト

メンバーにもコードを書いていた人がいるようだが個人で完成まで漕ぎ着けたのでそれを説明する

モーター速度制御

ledcWriteで普通にPWMする

クライアントと通信

ESP32は無線インターフェイスとしてWifiとBluetoothを備えている
ESPとクライアントで通信する一般的な方法は2通り
Blynkというアプリを使う方法とESPとクライアントをWifiで接続してブラウザを利用して情報をやり取りする方法である
Blynkはアプリのインストールやトークンの取得などその場ですぐ使うことは難しいので今回はWifi接続を選択する
Wifi接続もまた2通り
ESPとクライアントを同じWifiに接続して通信する方法とESPをサーバーにして直接Wifiを飛ばしそれにクライアントが接続する方法(softAP)である
すぐ使えるという観点から後者の方が今回の目的に合っているので後者のsoftAPを選択する

下記リンクを参考にsoftAPとhttpRequestでラジコンを作った

これでも遊んでいて十分楽しいのだがリアルタイムで制御ができない
折角だからもっと操作性の良いものを作りたい

WebSocket

さてここからが本題である
WebSocketを使ってリアルタイム通信すればだいぶ良くなるのではないか

ESP側

有志による優秀なライブラリがあるのでそれを使わせていただく

Async Web Serverと名前がついているがWebSocketを扱うプラグインが同梱されている
まずはこれとその依存ライブラリ(AsyncTCP)をインストールする

ライブラリの使用方法はドキュメントに記載されている

WebSocketサンプル
コンソールからWebSocket飛ばすとつながる

const IPAddress ip(192,168,1,1);
const IPAddress subnet(255,255,255,0);
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len){
	switch(type){
		case WS_EVT_CONNECT:
			Serial.printf("ws[%u] connect: %s\n", client->id(), client->remoteIP().toString().c_str());
			client->printf("id: %u\nip: %s\n", client->id(), client->remoteIP().toString().c_str());
			client->ping();
			break;
		case WS_EVT_DISCONNECT:
			Serial.printf("ws[%u] disconnect\n", client->id());
			break;
		case WS_EVT_ERROR:
			Serial.printf("ws[%u] error(%u): %s\n", client->id(), *((uint16_t*)arg), (char*)data);
			break;
		case WS_EVT_PONG:
			Serial.printf("ws[%u] pong\n", client->id());
			break;
		case WS_EVT_DATA:
			{
				AwsFrameInfo *info=(AwsFrameInfo*)arg;
				if(info->final && info->index==0 && info->len==len && info->opcode==WS_TEXT){
					data[len]=0;
					String str=(char*)data;
					Serial.printf("ws[%u] text-msg[%llu]: %s\n", client->id(), info->len, str.c_str());
					ws.printfAll("%s\n",str.c_str());
				}		
			}
			break;
	}
}
void setup(){
	Serial.begin(115200);
	delay(1000);

	WiFi.mode(WIFI_AP_STA);
	WiFi.softAP(ssid,pass);
	delay(100);
	WiFi.softAPConfig(ip,ip,subnet);
	Serial.printf("SSID: %s\nPASS: %s\nAPIP: %s\n",ssid,pass,WiFi.softAPIP().toString().c_str());

	ws.onEvent(onEvent);
	server.addHandler(&ws);
	server.begin();
	Serial.println("server started");
}
void loop(){
	ws.cleanupClients();
}

クライアント用HTML

こっちは一般的なWebSocket

<!DOCTYPE html>
<html lang="en" dir="ltr">
	<head>
		<meta charset="utf-8">
	</head>
	<body>
		<pre id="log">Connecting…</pre>
		<script>
			'use strict';
			let ws,ws_send=()=>console.log('uninitialized'),recieved={};
			const ws_init=()=>{
				console.log('ws_init');
				ws=new WebSocket(`ws://${window.location.hostname}/ws`);
				ws.onopen=e=>{
					log.textContent='Opened :)';console.log('opened');
					ws.send('Hello');
				};
				ws.onclose=e=>{
					log.textContent='Closed :(';console.log('closed');
					setTimeout(ws_init,2000);
				};
				ws.onmessage=e=>{
					log.textContent+=`${e.data}\n`;
				};
				ws.onerror=e=>{
					console.log(e);
				};
			};
			window.onload=ws_init;
		</script>
	</body>
</html>

ジョイスティック操作にした
こちらを参考にさせていただいた

/*
stick=HTMLElement
stick.children[0]=HTMLElement
stick_stat=pressed?true:false
*/

window.onpointermove=e=>{
	if(!stick_stat)return;
	const stick_style=stick.getBoundingClientRect();
	let pos=[
		(e.clientX-stick_style.left-stick_style.width*.5)||0,
		(e.clientY-stick_style.top-stick_style.height*.5)||0
	],l=Math.sqrt(pos[0]*pos[0]+pos[1]*pos[1]);
	pos=pos.map(x=>x*Math.min(stick_style.width*.5,l)/l);
	stick.children[0].style.transform=`translate(-50%,-50%)translate(${pos.join('px,')}px)`;
	if(timer)return;
	//https://openrtm.org/openrtm/sites/default/files/6357/171108-04.pdf
	pos=[Math.atan2(...pos)-Math.PI*.75,Math.min(1,l/stick_style.width*2)];
	ws_send([Math.cos(pos[0])*pos[1],Math.sin(pos[0])*pos[1]]);
	timer=setTimeout(()=>timer=0,100);
};

その他

ESPのString.toInt()が負の数に対応していないらしく送信時はオフセットをつけた
文字列で送信しているため切り出しを楽にするため0paddingをした

課題

websocketの実装を始めたのが8/9ジョイスティックまで完成したのが8/11なので動作確認があまりできていない
電池電圧が3.6Vあたりまで低下してくるとESPの電源供給が怪しくなってくる
ESPが発振してDCDCコンバーターが熱くなる
やはりコンバーターの許容電流が足りないのかもしれない

8/13追記
ESPの起動時の大食いが原因であることがわかった
電池と並列に100μFの電解コンデンサを挟んだところだいぶマシになった
補助電源としての役割の方が大きいので単純に容量が大きい方がいいとは思う
今手元に100μF以外ないので今後検証する
→470μFで安定した

おわり

今回のプロジェクトでできたものはここに置いてある

今後も改良を続けたい

6
4
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
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?