「ESPr® Developer」カテゴリーアーカイブ

インターネット対応にラムネマシーンを改造

 

インターネット対応ラムネマシーン

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