UDP通信を使って、ブラウザからESP32を操作する
ESP32を使っていると、スマホから操作できたらいいのにな〜と思うことはよくあります。
いくつか方法はありまして、
- ESP32をサーバーとして、ESP32からHTMLテンプレートをスマホ側に送信
- 出来合いのスマホアプリを使って、Bluetoothなどで操作
- LINE Messanger APIなどを使う
しかし、いずれの方法もUIを自分好みにはできません。
1の方法では、プログラミング初心者が作ったような、単純なボタンを配置する程度のUIが限界でしょう
そもそもArduinoエディタでHTMLを描くのは手間ですしナンセンスです
2でもできなくはないですが、UI面では一番制限されてしまいます。
アカウント作成など、余計な手間もあります。
3は地味にLINE Messanger APIの縛りが面倒ですし、素早い動作はできません
家のドアの開け閉めくらいが関の山です。
その他、Flutterなどでスマホアプリを作る方法もありますが、わざわざアプリをインストールするのも面倒ですし、銭ゲバApple社に高い登録料を払うのも癪です。
やはりUIは自分の好きに作りたいところ。
私はNuxt3を使えますので、Nuxtで自分好みのUIを作って、UDPで通信することにしました。
node.jsでUDP通信のテスト
まずはMacBookから信号を発信し、ESP32で受け取れるか確認します。
Nuxtを使うので、nodejsで実装します。
(ここではnodejsの実装方法は割愛します)
UDP通信をするにはdgramというライブラリを使います。
{
"dependencies": {
"dgram": "^1.0.1",
"ts-node": "^10.9.1"
}
}
import dgram from 'node:dgram';
import { Buffer } from 'node:buffer';
const HOST = '100.64.1.42' //ESP32のIPアドレスに合わせる
const PORT = 8086 // 何でも良い
const server = dgram.createSocket('udp4');
const message = Buffer.from('green'); // 'green'の文字列をバイナリ形式に変換
server.send(message, PORT, HOST, (err)=>{
console.log(err)
server.close()
})
何かしらのデータを送らなければならないので、'green'という文字列をバイナリに変換して送っています。
続いて、ESP32側です。
from usocket import socket, AF_INET, SOCK_DGRAM
from machine import Pin
SSID = "自宅wifiのssid"
PASSWORD = "自宅wifiのパスワード"
def do_connect():
import network
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
print('connecting to network...')
wlan.connect(SSID, PASSWORD)
while not wlan.isconnected():
pass
print('network config:', wlan.ifconfig())
do_connect()
HOST='100.64.1.42'
PORT=8086
s = socket(AF_INET,SOCK_DGRAM)
s.bind((HOST,PORT))
while 1:
print('waiting...')
bytes_data,address = s.recvfrom(1024)
data = bytes_data.decode()
print(data)
esp32のIPアドレスは、
print('network config:', wlan.ifconfig())
で表示されるアドレスの一番最初の項目です。
network config: ('100.64.1.42', '255.255.255.0', '100.64.1.1', '8.8.8.8')
この場合は、100.64.1.42です。
これをserver側、client側に設定しましょう。
ESP32を起動させた状態で、nodejsのコードを実行すると、'green'という文字がESP32に送られるはずです。
※補足
ESP32で反応をわかりやすくするために、21ピンと23ピンにLEDをつけてやりました
# main.py
from usocket import socket, AF_INET, SOCK_DGRAM
from machine import Pin
##### wifi接続の部分は省略 #####
HOST='100.64.1.42'
PORT=8086
green_led = Pin(21,Pin.OUT)
red_led = Pin(23,Pin.OUT)
s = socket(AF_INET,SOCK_DGRAM)
s.bind((HOST,PORT))
while 1:
print('waiting...')
bytes_data,address = s.recvfrom(1024)
data = bytes_data.decode()
print(data)
if data == 'red':
red_led.on()
green_led.off()
elif data == 'green':
red_led.off()
green_led.on()
else:
red_led.off()
green_led.off()
Nuxt3で実装
あとはNuxtで画面を作りましょう。
今回は最小の実装だけ載せています。
UI部分
<script setup lang="ts">
type Color = 'red'|'green'
const blink = async(color:Color)=>{
const {data} = await useFetch('api/esp32',{params:{color:color}})
console.log(data.value)
}
</script>
<template>
<div>
<h1>テスト</h1>
<button @click="blink('red')">赤を点灯</button>
<button @click="blink('green')">緑を点灯</button>
</div>
</template>
サーバー部分
import dgram from 'node:dgram';
import { Buffer } from 'node:buffer';
const HOST = '100.64.1.42' //ESP32のIPアドレスに合わせる
const PORT = 8086
const server = dgram.createSocket('udp4');
type Color = 'red'|'green'
export default defineEventHandler(async(e)=>{
const query = getQuery(e) as unknown as {color:Color}
console.log(query)
const message = Buffer.from(query.color);
server.send(message,PORT,HOST)
})
Discussion