swerveのアイコン画像
swerve 2021年02月10日作成 (2022年01月04日更新)
製作品 製作品 閲覧数 1548
swerve 2021年02月10日作成 (2022年01月04日更新) 製作品 製作品 閲覧数 1548

手を叩いて拝むとslackでおみくじの結果を教えてくれるIoT神社

作ったもの

3Dプリンタ製の小さな神社の前で手を叩いて拝むと、slackにおみくじの結果が送られてくるデバイスです。
キャプションを入力できます

ここに動画が表示されます

動機

作ったプログラムが動かない、設計したハードがうまく動かない、3Dプリントが何故か失敗しまくる、そんな神頼みでもいいから何とか動いて欲しいと思う時ってありますよね。

しかしPCや3Dプリンタに向かって祈るのは味気ない、ですよね?

そこでPCや3Dプリンターに乗せられるサイズの神社を作って、拝むと何かしら返答が返ってくるようにして楽しくしたい、と考えました。

システム構成

人感センサで接近を検知した際に、サウンドセンサが一定音量以上を認識するとESP-WROOM-02がWifiでGoogleApplicationScript(以下GAS)に送信します。GAS側でランダムに結果を出力して、slackに結果をメッセージとして送信します。
システム構成

回路構成

主な材料(入手先はあってるかは分かりませんが...)

部品名 個数 備考
ESP-WROOM-02 1 入手先:秋月
ESP-WROOM-02 ピッチ変換基板 コンパクト 1 入手先:千石
SB412A(焦電型赤外線人感センサー) 1 入手先:秋月
GROVE - 音センサ 1 入手先:Switch Science
三端子レギュレーターNJU7223DL1 1 入手先:秋月
ロープロファイルピンヘッダ 7.7㎜ 40P 1 秋月
タクトスイッチ 2 秋月で売ってるもの
チップ抵抗 2012サイズ 10kΩ 5
チップコンデンサ 2012サイズ 0.1μF 2
XHコネクタ(2,3,4ピン) 少々
PHコネクタ(2ピン) 少々
lipoバッテリー(1s 400mAh) 1 千石で売ってるもの
NJM2903D 2回路入りコンパレータ 1 秋月で売ってるもの
PchチップMOSFET IRLML6402(20V3.7A) 1 秋月で売ってるもの
抵抗各種(1k,2,2k,10k) 少々
ユニバーサル基板 1 72*47サイズ
基板用マイクロUSBコネクタ(電源専用) 1 秋月で売ってるもの
スライドスイッチ 1回路2接点 基板用 横向き 1 秋月で売ってるもの

神社の中にWifiモジュールを納めたかったため、
ESP32ではなく小型のESP-WROOM-02(ESP8266)を使用しています。

回路図
神社内部のWi-Fi部

神社裏の分圧回路など

ESP-WROOM-02のアナログ入力は0~1Vまでですが、音センサは3.3Vで動いているので1kと2.2kの抵抗で分圧しています。

電源はLipo、USBのどちらからでも取れるようにしています

またlipoバッテリーに保護回路が内蔵されているらしいですが、電圧が一定以下だったらESP側に通電しないように分圧回路とコンパレータで追加で回路を組んでいます。

ですがUSB給電をメインで使うのでこれあんまりいらなさそうなんですよね...

回路図を書きましたが、1枚しか作らないのでユニバーサル基板で製作しました

神社内部に入れる基板の写真
表

裏

はんだ付けが汚いですね()

ハードウェア

設計はSolidWorksで行いました。
キャプションを入力できます

神社本体は土台と屋根の2パーツ構成となっています。

キャプションを入力できます
神社をPCにつける為のマウントはこのようになっています。
PCのディスプレイに挟む形状にしています

キャプションを入力できます
また電池などを入れる回路箱は神社側にはめ込む形状にしました

設計が終わったら3Dプリンターで印刷

出来たものがこちらになります。
キャプションを入力できます

基板と組み合わせるとこのようになります。

キャプションを入力できます

焦電センサの固定用のパーツも製作
基板そのものが見えないように全て覆っています

ESP-WROOM-02側のソースコード

人を感知して一定音量以上を検知したら通信するコード

#include <ESP8266WiFi.h> #include "HTTPSRedirect.h" #include "DebugMacros.h" extern "C" { #include "user_interface.h" } const char* ssid = "*****";//繋ぎたいWifiのSSIDを入力 const char* password = "*****";//Wifiのパスワードを入力 String UniqueID = "1"; const char* KEY = "*****";//GAS側のコードを入力 const char* host = "script.google.com"; const char *GScriptId = "*****";//GAS側のコードを入力 const int httpsPort = 443; const char* fingerprint = ""; String url = String("/macros/s/") + GScriptId + "/exec?UniqueID=" + UniqueID +"&acc=" + 100; // Fetch Google Calendar events for 1 week ahead String url2 = String("/macros/s/") + GScriptId + "/exec?cal"; // Read from Google Spreadsheet String url3 = String("/macros/s/") + GScriptId + "/exec?read"; String payload_base = "{\"command\": \"appendRow\", \ \"sheet_name\": \"Sheet1\", \ \"values\": "; String payload = ""; HTTPSRedirect* client = nullptr; unsigned int free_heap_before = 0; unsigned int free_stack_before = 0; void setup() { Serial.begin(115200); Serial.flush(); free_heap_before = ESP.getFreeHeap(); free_stack_before = ESP.getFreeContStack(); Serial.printf("Free heap: %u\n", free_heap_before); Serial.printf("Free stack: %u\n", free_stack_before); Serial.println(); Serial.println(ssid); Serial.flush(); pinMode(12,INPUT); } void loop() { int detect = digitalRead(12); float accx = system_adc_read(); if(detect==1){//焦電センサの反応があるかどうか if(accx>250){//音センサの反応があるかどうか Serial.println(accx); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); // Use HTTPSRedirect class to create a new TLS connection client = new HTTPSRedirect(httpsPort); client->setInsecure(); client->setPrintResponseBody(true); client->setContentTypeHeader("application/json"); Serial.print("Connecting to "); Serial.println(host); bool flag = false; for (int i=0; i<5; i++){ int retval = client->connect(host, httpsPort); if (retval == 1) { flag = true; break; } else Serial.println("Connection failed. Retrying..."); } if (!flag){ Serial.print("Could not connect to server: "); Serial.println(host); Serial.println("Exiting..."); return; } String URL1 = "https://script.google.com/macros/s/"; URL1 += KEY; URL1 += "/exec?"; URL1 += "UniqueID="; URL1 += UniqueID; URL1 += "&accx="; URL1 += accx; client->GET(URL1, host); static int error_count = 0; static int connect_count = 0; const unsigned int MAX_CONNECT = 20; //static bool flag = false; } } }

Google Application Script側のソースコード

おみくじ結果出力

var postUrl = '*****';//slack側のURL var username = '*****'; // 通知時に表示されるユーザー名 var icon = ':*****:'; // 通知時に表示されるアイコン var message = '運勢'; // 投稿メッセージ function doGet(e) { let id = '*****'; //spreadsheet側のURL let sheetName = 'シート1'; //シートの名前 var result; // e.parameter has received GET parameters, i.e. temperature, humidity, illumination if (e.parameter == undefined) { result = 'Parameter undefined'; } else { var sheet = SpreadsheetApp.openById(id).getSheetByName(sheetName); var newRow = sheet.getLastRow() + 1; // get row number to be inserted var rowData = []; // get current time rowData[0] = new Date(); rowData[1] = e.parameter.UniqueID; rowData[2] = e.parameter.accx; var newRange = sheet.getRange(newRow, 1, 1, rowData.length);   var unsei = Math.floor(Math.random()*6)+3 var range = sheet.getRange(unsei,5);//セル情報取得(E列に書いた運勢) var value = range.getValue(); message= "   運勢    : "+value; var unseip = unsei-2 var ruckyitem = Math.floor(Math.random()*41)+3 var range2 = sheet.getRange(ruckyitem,6);//セル情報取得(F列) var value2 = range2.getValue(); message = message + "\n" + "ラッキーアイテム : " + value2; var debug = Math.floor(Math.random()*(unseip))+3 var range3 = sheet.getRange(debug,7);//セル情報取得(G列) var value3 = range3.getValue(); message = message + "\n" + "  デバッグ   : " + value3; var study = Math.floor(Math.random()*(unseip))+3 var range4 = sheet.getRange(study,8);//セル情報取得(H列) var value4 = range4.getValue(); message = message + "\n" + "   学問    : " + value4; var lost = Math.floor(Math.random()*(unseip))+3 var range5 = sheet.getRange(lost,9);//セル情報取得(I列) var value5 = range5.getValue(); message = message + "\n" + "   失物    : " + value5; var love = Math.floor(Math.random()*1)+3 var range6 = sheet.getRange(love,10);//セル情報取得(J列) var value6 = range6.getValue(); message = message + "\n" + "   恋愛    : " + value6; var home = Math.floor(Math.random()*(unseip))+3 var range7 = sheet.getRange(home,11);//セル情報取得(K列) var value7 = range7.getValue(); message = message + "\n" + "   転居    : " + value7; var business = Math.floor(Math.random()*(unseip))+3 var range8 = sheet.getRange(business,12);//セル情報取得(L列) var value8 = range8.getValue(); message = message + "\n" + "   商い    : " + value8; var travel = Math.floor(Math.random()*(unseip))+3 var range9 = sheet.getRange(travel,13);//セル情報取得(M列) var value9 = range9.getValue(); message = message + "\n" + "   旅行    : " + value9; var war = Math.floor(Math.random()*(unseip))+3 var range10 = sheet.getRange(war,14);//セル情報取得(N列) var value10 = range10.getValue(); message = message + "\n" + "   争い    : " + value10; var hitokoto = Math.floor(Math.random()*25)+3 var range11 = sheet.getRange(hitokoto,15);//セル情報取得(O列) var value11 = range11.getValue(); message = message + "\n" + " 一言コーナー  : " + value11; // 1 x rowData.length cells from (newRow, 1) cell are specified //var newRange = sheet.getRange(newRow, 1, 1, rowData.length); // insert data to the target cells newRange.setValues([rowData]); result = 'Ok'; } var jsonData = { "username" : username, "icon_emoji": icon, "text" : message }; var payload = JSON.stringify(jsonData); var options = { "method" : "post", "contentType" : "application/json", "payload" : payload }; UrlFetchApp.fetch(postUrl, options); return ContentService.createTextOutput(result); }

Googleスプレッドシート側の内容

キャプションを入力できます
上の画像にようにくじの選択肢を書き連ねています。

完成した後での改良点、感想

slackで返事が来るとはいえ、反応しているかがよくわからないのでLEDか何かしらを取り付けて光ると良い気がしました
あと、フィラメントの素材の色そのままで彩りがないので、神社本体の塗装も検討したいですね

おわりに

神社は石や鏡を祀るものが多いらしいですが、この神社のご神体は石(マイコン)と言えますね
Wi-Fiの安定でも祈願しましょう

ちなみにこのデバイスを作る時に神頼みしたくなる時が何度かありました

参考文献

https://qiita.com/marlex/items/3e24a2c56a00421a317a GASとデバイス側(ESP32)の接続の資料
https://qiita.com/chihiro/items/c7b11abc78f5d806c3a8 GASとslackの接続についての資料
https://tonari-it.com/gas-omikuji/ GASでおみくじを出力するときの資料

2
1
swerveのアイコン画像
東京の大学の学生です。3DCADと電子工作の勉強中です。
ログインしてコメントを投稿する