インターネット対応ラムネマシーン |
いよいよラムネマシーンをインターネット対応に改造します。ということで、出来たものは次の動画で確認してみて下さい。スマートフォンでスロットゲームをして、“777” が横,もしくは斜めに揃えば、ラムネマシーンからラムネが払い出される様になっています。動画の中でのスマホの操作はbluetoothマウスを使用し少し離れて行っています。尚、接続方法は下記の過去投稿と基本的には同じですので、参考にしてみて下さい。
“777” が揃ってから払い出しが始まるまでの遅れが、少し気になりますが、とりあえず想定していたものは出来ました。
プログラム概略説明 |
基本的には元のラムネマシーンのプログラムにWiFi接続機能を追加したものです。先ず、行番1,行番4~15をWiFi接続に関連する定数、変数を設定しています。行番4,5,10の内容は各WiFi接続環境、プログラムの配置などによって異なります。
次に行番79~94で、起動直後にWiFi接続しています。
最後にメインループ処理ですが、元のプログラムではトグルスィッチ(SW2)の信号入力で手動で反転動作を行うことが出来ましたが、今回はSW2入力時はWiFi接続モードとして、レンタルサーバー上のデータを取得する様にします。行番129でSW2の入力判定を行い、入力状態のとき行番130~183を実行します。行番130~173は過去投稿「ESPR DEVELOPERでインターネット接続する」のプログラムをほぼコピーしていますので説明は省略します。行番175では、行番130~173でレンタルサーバー上のPHPプログラムを起動し、戻ってきた内容を判定します。内容が “OK” (“777” が揃った時の値)であれば、行番177,178で LED 点灯用の変数を設定し、行番179でモード変更します。 “OK” 確認後、自動払い出しが完了するまで、WiFiに接続する必要はないので、DRV_STS=“INT” に変更後は行番185~189が実行される様になります。
実際のモーターの駆動は、元のプログラムでプッシュスィッチを入力した時、1個づつ払い出す処理をそのまま使えるように、行番213の判定条件を変更しています。一連の払い出し動作が完了するとモードがリセットされますので、再びWiFi接続によってレンタルサーバー上のデータ取得を開始します。
#include <ESP8266WiFi.h> #include <Wire.h> const char* ssid = "接続AP-SSID "; // 接続アクセスポイントのSSID const char* password = "接続AP-PASS"; // 接続アクセスポイントのパスワード const int httpPort = 80 ; const char* host = "kats-eye.net"; // 接続サーバードメイン名 // 呼び出すプログラム名 const char* path = "プログラムパス"; const int s_p = 4; String body = "OK"; String rtn = "" ; int loop_cnt = 0 ; // ★ IO-EXPANDER(MCP23017)定数・変数 #define DVC_MCP23017 0x20 // IO-EXPANDER_00 アドレス(外部端子設定) uint8_t U_PA = 0 ; // I/O 読込変数 // 共通 uint8_t r_MODE ; String DRV_STS = "" ; String STS_BEF = "" ; String ATO_STS = "" ; int chk_cnt ; // ◆ DRV8830 I2Cアドレス(モータ制御基板) #define ADR_DVC 0x64 // I2Cデバイスアドレス(DRV8830) #define CTR_ADR 0x00 // CONTROL_レジスタ #define FLT_ADR 0x01 // FAULT___レジスタ // ◆ 駆動制御 #define D_RDY 0x00 // スタンバイ #define D_REV 0x01 // 逆転 #define D_STD 0x02 // 正転 #define D_BRK 0x03 // ブレーキ // ◆ 電圧設定 byte SET_V[]={ 0x00 , 0x12 }; // ◆ 制御コマンド送信 void w_rg_v(byte reg , byte v_set , byte cont_d){ int reg_data = v_set << 2 | cont_d ; wrt_reg( ADR_DVC , reg , reg_data); } // ★ 指定レジスタにデータ書き込み void wrt_reg(byte dvc_adrs , uint8_t reg , uint8_t value) { Wire.beginTransmission(dvc_adrs); // デバイス指定、通信開始 Wire.write((uint8_t)reg); // レジスタ指定 Wire.write((uint8_t)(value)); // データ書込 Wire.endTransmission(); // 送信完了 } // ★ 指定レジスタからデータ読み出し uint8_t read_reg(byte dvc_adrs , uint8_t reg) { Wire.beginTransmission(dvc_adrs); // 送信処理開始 Wire.write(reg); // レジスタ指定 Wire.endTransmission(false); // 送信完了(コネクション維持) Wire.requestFrom(dvc_adrs , 1); // 1byteデータに要求 if (! Wire.available()) return -1; // データ有無判定 return (Wire.read()); // 1byteデータ } // モータブレーキ(停止) void drv_brake(){ w_rg_v(CTR_ADR , SET_V[ 0 ] , D_BRK); DRV_STS = ""; STS_BEF = "" ; ATO_STS = "" ; delay(600); } // 起動後、初回処理 void setup() { Serial.begin(115200); // シリアルモニタ出力速度設定 Serial.println(""); // シリアルモニタ改行 Serial.println() ; Serial.println() ; Serial.print("Connecting to ") ; Serial.println(ssid); WiFi.mode(WIFI_STA); // STAモード設定 WiFi.begin(ssid, password); // SSID,パスワードを指定してアクセスポイント接続 // Wi-Fi接続確認 while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFiに接続しました。"); Wire.begin(12,14); // スタンバイ w_rg_v(CTR_ADR , SET_V[0] , D_RDY); Serial.println("DRV8830 STAND BY"); // DVC_MCP23017(DVCアドレス:0x20) wrt_reg( DVC_MCP23017 , 0x00 , 0xFF ); // I/O-PortA 入力設定 wrt_reg( DVC_MCP23017 , 0x0C , 0xFF ); // I/O-PortA 入力プルアップ設定 wrt_reg( DVC_MCP23017 , 0x01 , 0x00 ); // I/O-PortB 出力設定 wrt_reg( DVC_MCP23017 , 0x13 , 0x00 ); // I/O-PortB 全OFF delay(1000); } // 繰り返し処理 void loop() { U_PA = read_reg( DVC_MCP23017 , 0x12 ); // デバイス・レジスタ指定しデータ読取 uint8_t P_PA = 255 - U_PA ; // 補数(High/Low反転処理) // LED出力変数初期化 byte OUT_LED[]={ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }; uint8_t LMT_SW1 = P_PA & 64 ; // リードSW1入力 uint8_t LMT_SW2 = P_PA & 128 ; // リードSW2入力 if (LMT_SW1 == 64){ OUT_LED[3]=8 ; } // LED出力設定(リードSW1) if (LMT_SW2 == 128){OUT_LED[4]=16; } // LED出力設定(リードSW2) if ((P_PA & 1 ) == 1 ){ OUT_LED[0] = 1 ; r_MODE = D_STD ; DRV_STS = "STD" ; // トグルSW1入力(正転) }else if ((P_PA & 2 ) == 2 && DRV_STS != "INT"){ OUT_LED[1] = 2 ; //r_MODE = D_REV ; DRV_STS = "RVS" ; // トグルSW2入力(逆転) r_MODE = D_BRK ; DRV_STS = "" ; // トグルSW未入力(停止) WiFiClient client; // TCP接続用にWiFiClient classを使用する // TCP接続確認 if (!client.connect(host, httpPort)) { // 外部サーバーに接続 Serial.println("接続に失敗しました。"); return; } // サーバにリクエストを送信(POST) client.print(String("POST ") + path + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Content-Length:" + String(body.length())+ "\r\n" + "Connection: close\r\n\r\n" + body); // リクエスト結果受信待ち unsigned long timeout = millis(); while (client.available() == 0) { if (millis() - timeout > 5000) { Serial.println(">>> タイムアウトしました。"); client.stop(); return; } } // HTTPヘッダを含むデータの読み取り(1文字毎) String raw = ""; while (client.available()) { char c = client.read(); raw = raw +c; } // HTTPヘッダの削除、 int index = raw.indexOf("\r\n\r\n"); // 改行が2回続く箇所検索(\r\n(='CR+LF')) rtn = raw.substring(index+s_p,index+s_p+2); // ( index + s_p + 1 )から2文字切り出す loop_cnt = loop_cnt + 1 ; Serial.println("----------------------------------------"); Serial.print(loop_cnt); Serial.print(" スロット結果:") ; Serial.println( rtn ) ; Serial.println("") ; if(rtn == "OK"){ // インターネット自動運転開始 OUT_LED[1] = 2 ; OUT_LED[6] = 64; // インターネット自動運転LED出力(自動処理終了まで継続) r_MODE = D_STD ; DRV_STS = "INT" ; // トグルSW2入力時、スロットOK確認 }else{ delay(1200); } }else if (DRV_STS == "INT"){ // インターネット自動運転中 OUT_LED[1] = 2 ; OUT_LED[6] = 64; r_MODE = D_STD ; DRV_STS = "INT" ; }else if ((P_PA & 4 ) == 4 || DRV_STS == "ATO"){ OUT_LED[5] = 32; // 自動運転LED出力(自動処理終了まで継続) r_MODE = D_STD ; DRV_STS = "ATO" ; // トグルSW未入力時、押しボタンSW入力 } else { OUT_LED[2] = 4 ; r_MODE = D_BRK ; DRV_STS = "" ; // トグルSW未入力(停止) } byte L_OUT_ALL=OUT_LED[0]+OUT_LED[1]+OUT_LED[2]+OUT_LED[3]+OUT_LED[4]+OUT_LED[5]+OUT_LED[6]; wrt_reg( DVC_MCP23017 , 0x13 , L_OUT_ALL ); // DVC00 I/O-PortB 出力設定(LED点灯) // 運転モード設定によるモータ駆動 if (r_MODE != D_BRK && DRV_STS != "ATO" && DRV_STS != "INT"){ // トグルSWによる手動モータ駆動 if(STS_BEF==""){ w_rg_v(CTR_ADR , SET_V[1] , r_MODE); STS_BEF = DRV_STS ; } else if (DRV_STS != STS_BEF) { drv_brake() ; // ブレーキ } } else if (r_MODE != D_BRK && (DRV_STS == "ATO" || DRV_STS == "INT")){ // 自動運転(手動SW入力、又は自動運転完了まで継続駆動) if (ATO_STS == ""){ w_rg_v(CTR_ADR , SET_V[1] , r_MODE); ATO_STS = "STARTED" ; chk_cnt = 0 ; } if ((ATO_STS == "STARTED") && (LMT_SW1 != 64) && (LMT_SW2 != 128)){ chk_cnt = chk_cnt + 1 ; if (chk_cnt > 3) { ATO_STS = "CHK_LMT_SW" ; chk_cnt = 0 ; } } if ((ATO_STS == "CHK_LMT_SW") && ((LMT_SW1 == 64) || (LMT_SW2 == 128))){ chk_cnt = chk_cnt + 1 ; if (chk_cnt > 3) { drv_brake() ; // ブレーキ } } } else { // ブレーキ if(STS_BEF != ""){ drv_brake() ; // ブレーキ } } delay(100); }
次のプログラムはサーバー側のPHPです。ラムネマシーンのESPr DEVELOPERから起動し、行番3でPOST送信されたデータを受け取ります。この受け取ったデータは今回特に利用していませんので、実質的な意味はありません。
行番6でテキストファイルからスロットゲームの結果(“OK” or “NG”)を読み出し、行番9でラムネマシーンのESPr DEVELOPERに読み出した結果をそのまま送ります。行番13は、次回ESPr DEVELOPER がファイルの中のデータを読んだときに、“OK” のままだと連続してラムネを払い出してしまうので、一旦 “NG” に書きかえています。
とりあえずシンプルですが、スロット側とラムネマシーン側から同時に書き込もうとするケースなどもあると思われますので、もう少し検討した方が良いかもしれません。
<?php // post送信のBodyすべて取得 $entireBody = file_get_contents("php://input"); // 'slot_result.txt'の内容を読み込む $contents = file_get_contents("slot_result.txt"); // ファイル読込結果をそのまま返す echo $contents; // 'slot_result.txt'に“NG”を書き込む //(“OK”のままだと引き続き払い出しする為) file_put_contents("slot_result.txt","NG"); ?>
まとめ |
今回を含め4回に分け、ESPr DEVELOPERからモーターを動かすおもちゃを製作しました。下記は関連する投稿リンクです。
★関連する前の投稿★ ※リンクしています。
◆ESPR DEVELOPERでモータを動かす◆
◆ESPR DEVELOPER でラムネマシーンを作る◆
◆JAVASCRIPTでスロットゲームを作る◆
※サーバー環境:レンタルサーバー(ロリポップ)
ライトプラン(月々250円~)