| インターネット対応ラムネマシーン |
いよいよラムネマシーンをインターネット対応に改造します。ということで、出来たものは次の動画で確認してみて下さい。スマートフォンでスロットゲームをして、“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円~)