MQTT にも一通り使い慣れた?ので、
送信側に m5stack、受信側にブラウザを使用した「ヤルキスイッチ」を作成しました。
役には立ちません。いわゆる「ネタアプリ?」でしょうか。
今までコマンドラインだけ、スイッチだけだったものが、連動して動くようになると本当に楽しいです。
とりあえず動くものを見てほしいよ
完成品を動かすと、以下の動画みたいになります。
それじゃ解説です。
目次
全体の構成
以下の構成でデータが流れます。
M5Stack(mqtt 送信側) ==> 公開サーバー(mqtt ブローカー) ==> ブラウザ(mqtt 受信側)
ブローカーは前回のMQTT を使いたい。3で作成したものをベースにしただけなので、割愛。
- M5Stack(mqtt 送信側)
- ブラウザ(mqtt 受信側)
以上二つを主に解説します。
M5Stack(mqtt 送信側)
先に準備として m5stack には 320*200 の大きさの jpg 画像をそれぞれ
- yaruki1logo.jpg
- yaruki2logo.jpg
- yaruki3logo.jpg
という名前で保存しておきます。
M5Stack の Lcd に、押したボタンに対応した画像を表示するためのものです。
以下を参考にして作成しました。
Arduino Client for MQTT
ESP32 を MQTT で Publish する
書き込んだソースファイルは以下の通り(簡単のため WIFI,MQTT の再接続は考慮しません)
yaruki-pub.ino1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| #include <WiFi.h> #include <M5Stack.h> #include <PubSubClient.h>
const char* SSID = "derutaASUS";
const char* PW = "sankaku3";
const char *mqttHost = "xxx.xxx.xxx.xxx";
const int mqttPort = 00000;
const char *clientid = "pubM5S";
const char *user = "pub_user_M5S";
const char *pw = "xdorytbseyrixdfgdsfgns";
const char *topic = "topicSW";
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
void setup() { M5.begin(); M5.Lcd.setBrightness(70);
WiFi.begin(SSID, PW); M5.Lcd.setTextSize(2); M5.Lcd.printf("Connecting"); while (WiFi.status() != WL_CONNECTED) { M5.Lcd.printf("."); delay(500); } M5.Lcd.clear(); M5.Lcd.setCursor(0, 0); M5.Lcd.printf("Connect!");
mqttClient.setServer(mqttHost, mqttPort); while (!mqttClient.connected()) { M5.Lcd.printf("Connecting to MQTT..."); if (mqttClient.connect(clientid, user, pw)) { M5.Lcd.printf("mqttconnected"); break; } delay(1000); randomSeed(micros()); } }
void loop() { M5.update(); if (M5.BtnA.wasPressed()) { mqttClient.publish(topic, "{\"data\":{\"button\":\"push-A\"}}"); M5.Lcd.clear(); M5.Lcd.drawJpgFile(SD, "/yaruki1logo.jpg", 0, 40, 320, 200); } if (M5.BtnB.wasPressed()) { mqttClient.publish(topic, "{\"data\":{\"button\":\"push-B\"}}"); M5.Lcd.clear(); M5.Lcd.drawJpgFile(SD, "/yaruki2logo.jpg", 0, 40, 320, 200); } if (M5.BtnC.wasPressed()) { mqttClient.publish(topic, "{\"data\":{\"button\":\"push-C\"}}"); M5.Lcd.clear(); M5.Lcd.drawJpgFile(SD, "/yaruki3logo.jpg", 0, 40, 320, 200); } mqttClient.loop(); }
|
Web(mqtt 受信側)
ブラウザをでの MQTT 送受信は以下を参考にして作成しました。
Node.js で MQTT ブローカーを立てて、ブラウザから確認する
今回は MQTT の受信の結果で動きを見せたいので、
pixi.jsを使用しました。
次などを参考にしています。
PixiJS — The HTML5 Creation Engine
learningPixi
css は skelton-css を使用してます。
以下を作成しました。
index.html1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>YARUKI SWITCH</title>
<link rel="stylesheet" href="./Skeleton-2.0.4/css/normalize.css"> <link rel="stylesheet" href="./Skeleton-2.0.4/css/skeleton.css"> <link rel="stylesheet" href="./a.css">
</head> <body> <div class="container"> <div class="row"> <h1>YARUKISWICH</h1> <div> <div class="row"> <div class="twelve columns cn"> <canvas id="cvtarget"></canvas> </div> </div> </div> </body> <script src="bundle.js"></script> </html>
|
index.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
| import * as PIXI from "pixi.js"; const mqtt = require("mqtt");
let files = [ { name: "0", pass: "images/yaruki1.png" }, { name: "1", pass: "images/yaruki2.png" }, { name: "2", pass: "images/yaruki3.png" }, { name: "3", pass: "images/yaruki4.png" }, { name: "back", pass: "images/back.png" }, ];
let logofiles = [ { name: "0", pass: "images/yaruki1logo.png" }, { name: "1", pass: "images/yaruki2logo.png" }, { name: "2", pass: "images/yaruki3logo.png" }, ];
let Textures = undefined; let Textureslogo = undefined;
let load = function (files) { let result = []; files.forEach((el) => { result.push({ Texture: PIXI.Texture.fromImage(el.pass), filename: el, name: el.name, }); }); return result; };
function name2tex(tex, name) { let result = undefined; tex.forEach((el) => { if (el.name == name) { result = el.Texture; } }); return result; }
let count = 0;
let texselect = 0;
function addsprite(select) { select = select == undefined ? texselect : select;
Sprites.push(new PIXI.Sprite(name2tex(Textures, `${select}`))); let len = Sprites.length - 1; let r = Math.random(); Sprites[len].sp = 6 + 3 * r; r += 0.5; let scale = app.screen.width / Sprites[len].texture.baseTexture.width / 3; Sprites[len].scale.x *= scale * r; Sprites[len].scale.y *= scale * r; Sprites[len].x = (app.screen.width / 4) * count * (r > 1 ? 1 : r); Sprites[len].y = (app.screen.height * 2) / 3; app.stage.addChild(Sprites[len]);
count++; texselect++; if (count > 3) { count = 0; } if (texselect > 2) { texselect = 0; } }
function addspritelogo(select) { console.log("addspritelogo"); select = select == undefined ? 1 : select;
Spriteslogo.push(new PIXI.Sprite(name2tex(Textureslogo, `${select}`))); let len = Spriteslogo.length - 1; let r = Math.random(); Spriteslogo[len].sp = 6 + 3 * r; r += 0.5; let scale = app.screen.width / Spriteslogo[len].texture.baseTexture.width / 3; Spriteslogo[len].scale.x *= scale * r; Spriteslogo[len].scale.y *= scale * r; Spriteslogo[len].x = (app.screen.width / 4) * count; Spriteslogo[len].y = (app.screen.height * 1) / 2; app.stage.addChild(Spriteslogo[len]); }
let Sprites = []; let Spriteslogo = [];
let lashcount = 0;
let app = undefined;
function start() { let width = document.getElementById("cvtarget").width; let height = width * 1.5; height = height > window.innerHeight ? window.innerHeight : height;
app = new PIXI.Application(width, height, { backgroundColor: 0x1099bb, view: document.body.querySelector("#cvtarget"), });
Textures = load(files); Textureslogo = load(logofiles);
var back = new PIXI.Sprite(name2tex(Textures, "back")); back.scale.x *= app.screen.width / back.texture.baseTexture.width; back.scale.y *= app.screen.height / back.texture.baseTexture.height; app.stage.addChild(back);
let loop = () => { for (let i = 0; i < Sprites.length; i++) { Sprites[i].y -= Sprites[i].sp; Sprites[i].sp += -0.18; if (Sprites[i].y > app.screen.height) { app.stage.removeChild(Sprites[i]); } } for (let j = 0; j < Spriteslogo.length; j++) { Spriteslogo[j].y -= Spriteslogo[j].sp; Spriteslogo[j].sp += -0.18; if (Sprites[j].y > app.screen.height) { app.stage.removeChild(Spriteslogo[j]); } } if (lashcount > 0) { if (lashcount % 5 == 0 && lashcount > 60) { addspritelogo(0); addsprite(); } if (lashcount == 5) { addspritelogo(1); addsprite(3); } lashcount--; } }; app.ticker.add(loop);
const client = mqtt.connect( "ws://[mqttブローカーサーバアドレス]:[mqttブローカサービスポート]", { clientId: "[クライアントID]", username: "[ユーザー名]", password: "[接続パスワード]", } ); client.subscribe("topicSW");
client.on("message", (topic, message) => { let data = JSON.parse(message.toString()).data; if (data.button == "push-A") { addspritelogo(0); addsprite(); } else if (data.button == "push-B") { addspritelogo(1); addsprite(3); } else if (data.button == "push-C") { lashcount += 200; } console.log(JSON.parse(message.toString())); }); }
start();
|
index.js は Webpack+babel でトランスパイルして使用します。
これらを動かすと冒頭の「ヤルキスイッチ」のフロントエンドが完成します。
この時の package.json は以下のようになります。
package.json1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| { "name": "yarukiswitch", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --watch --progress --mode development", "dev": "webpack-dev-server --watch --progress --mode development" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@babel/core": "^7.2.2", "babel": "^6.23.0", "babel-core": "^6.26.3", "babel-loader": "^8.0.4", "babel-preset-es2015-riot": "^1.1.0", "webpack": "^4.28.3", "webpack-cli": "^3.1.2", "webpack-dev-server": "^3.1.14" }, "dependencies": { "mqtt": "^2.18.8", "pixi.js": "^4.8.5" } }
|
だいたい 1 か月で、デバイスからインターネットを介して、ブラウザとやり取りができるようになりました。
まさに IoT な感じです。
ツイッターにアップしたテスト版は某プロレスラーをイメージしたいらすとやの画像を混ぜる悪ノリをしたけど、
記事にするにあたって、C ボタンは「ヤルキラッシュ」ということで雪崩のように画像が流れて、
最後に「モエツキ」がポンと一つだけポップするようにしました。
何気にその最後の挙動がお気に入りだったりします。
M5Stack とかを ArduinoIDE で開発をしていると、文字列の型の宣言にchar 変数名*
に書いたあたり、
昔 C を書いてた頃を思い出して少し懐かしい感じでした。
次回は MQTT で、デバイスは obniz を使用してドアセンサーを仕上げたいと思います。
間に何か(pixi.js と riot.js)挟むかも・・・。
ではでは