Arduinoでパチスロ(実機)のデータ収集 

記事内に広告を含みます

まとめ記事のご案内 [2024.11.20]

こちらの記事に前編と後編の記事内容をまとめましたので、こちらをおすすめします。

私、昔はクソみたいなスロカス学生でして、大学に行かずパチスロばかり行っておりました。

おかげで(そのせいで)留年もしております。(⁠๑⁠•⁠﹏⁠•⁠)

その当時、私を泥沼に嵌めてくれた思い入れのある機種(実機)を先日手に入れました。

パチスロ 新世紀エヴァンゲリオン〜まごころを、君に〜』です。

しかもアスカパネルVer.です。(⁠◔⁠‿⁠◔⁠)

日頃、暇なときにちょこちょこ打ってたんですが、やっぱり『データカウンター』がないと「今何ゲームなのか」「どのくらい出ているのか」がわかりません……。

ちなみに、データカウンターというのはパチンコホールでよく見かけるゲーム履歴を表示している機器のことです。

出典:大一電器産業株式会社 デー太郎NOTE

なので、今回はデータカウンターを作ってみます。ただ、今回はその前段階として、実機データのPCへの取り込みを検証していきます。

金欠ぱとろん
金欠ぱとろん

これが実は意外と簡単な仕組みなんですよね

データカウンターの仕組み

データカウンターには、ビッグボーナス(BB)/レギュラーボーナス(RB)の回数スランプグラフが表示されているのが普通ですが、これはどのようにして実現しているのでしょうか。

その仕組みを覗いてみましょう。

パチスロ実機の内部には以下のような基板があります。

パチンコホールでは、この基板とお店のデータカウンターが接続されています。

そして、この基板上のリレー(黒いやつ)を電気信号でON/OFFさせることで、データカウンターへボーナス当選やコインのIN/OUT枚数を伝えています

要は、パチスロの実機からはスイッチによってHigh(電源電圧)かLow(GND)の電気信号をデータカウンターに出しているだけなんです。

市販のデータカウンターはこの信号を検出してスランプグラフを出したり、ボーナス回数を表示したりしているのです。

動作解説

この電気信号について、もう少し詳しく解説します。

例えば、レバーオンした際にはメダル入力を表すINのリレーが「カチッ、カチッ、カチッ」と3回ONし、Arduinoへの入力電圧が下図のように+5V→0V→+5V→0V→+5V→0V→+5Vとなります。(メダル3枚がけですから。)

そして、(ベルなどの)入賞時には払い出しメダル枚数だけOUTのリレーがカチカチします。

その他にボーナスに当選した場合は、ボーナス図柄を揃えたときにリレーがONします。

構成

一般的なデータカウンターの仕組みが分かったところで、次はデータカウンターを自作するにあたっての構成を考えていきます。

単純な電気信号のHigh、Lowを検出してカウントできればOKなので、今回は実機のデータ送信基板Arduinoを接続し、Arduino経由でPCにデータを取り込もうと思います。

なお、Arduinoは実機内部に入れるので、なるべく小さいやつということで『Arduino nano every』を購入しました。

ちなみに、ウチの実機に付いているデータ送信基板のピンヘッダは以下のような配置になっています。

データ送信基板のピンヘッダ配置

ただ、この基板、実は単に電磁リレーが付いているだけなので、外部のプルアップ回路から電源を入力してあげる必要があります

そのようなことから、構成図はこんな感じになりました。

実物はこんな感じになります。

なお、Arduino nano every側とデータ送信基板側のピン対応は以下の通りです。

ピン配置対応表
Arduino nano every側ピンNo.データ送信基板側ピンNo.
D624IN1
D523OUT2
D422RB3
D321BB4

ソフトウェア開発

データ送信基板からデータをもらう準備ができましたので、Arduinoでデータを収集するためのプログラムを作っていきます。

なお、Visual Studio CodeでのArduino開発環境の構築の仕方は以下の記事をご参照ください。

フォルダ構成

フォルダ構成は、incフォルダに全てのヘッダファイル(.hファイル)、直下にソースファイル(.inoファイルと.cppファイル)となっています。

ご注意ください

Arduinoのメインファイルは「.ino」という拡張子のファイルになっています。

それ以外にC言語のソースファイルを追加したい場合は拡張子を「.cpp」にする必要があります。

これらファイルの詳細は以下の通りです。

ファイル一覧表
フォルダファイル名内容
.vscodearduino.jsonArduinoボードなどの設定を記述したファイル
c_cpp_properties.jsonC++の拡張機能が自動生成するファイル
settings.jsonVSCodeの設定ファイル
incDataManager.hデータ管理機能のヘッダファイル
Interrupt.h割り込み機能のヘッダファイル
PinDefine.hピン定義のヘッダファイル
Port.hポート設定のヘッダファイル
Serial_Com.hシリアル通信設定のヘッダファイル
vtype.h型定義のヘッダファイル
ー(フォルダ直下)DataManager.cppデータ管理機能のソースファイル
Interrupt.cpp割り込み機能のソースファイル
Pachislot_DataGet.inoArduinoのメインファイル
Port.cppポート設定のソースファイル
Serial_Com.cppシリアル通信設定のソースファイル

ソースコード

C言語でいうmain()的な役割を行うのが「.inoファイル」です。Arduinoのプログラムは.inoファイルから始まります。

.inoファイルに書いた初期化を行う関数『setup()』と、無限ループで延々繰り返し処理を行う関数『loop()』で基本的な処理が行われます。

ただ、今回はArduinoへの入力信号が変わったときに『IN枚数を更新したり』、『ボーナス回数を増やしたり』したいので、入力信号を外部割込みとして機能させて割込み発生時に処理を行いたいと思います。

それではソースコードを具体的に見ていきましょう。

Pachislot_DataGet.ino [メインファイル]

解説

Pachislot_DataGet.inoでは、setup()に各機能の初期化を行う関数をそれぞれ呼び出しています。

一方のloop()では何もしません。

Port.cpp

解説

Port_Init()では、ArduinoのライブラリにあるpinMode()関数を使ってポートの設定をしています。

ここで、第1引数にはデジタル入力の番号を設定するのですが、INの入力はデジタル入力のD6なので、IN_PINを『6』と定義のうえ設定しています。

同様にOUT_PINなら『5』になります。

そして、第2引数には入力モード出力モードかを設定するのですが、今回は外部割込みの入力となるので『INPUT』と設定しています。

Serial_Com.cpp

解説

Serial_Init()

Serial_Init()ではシリアル通信の初期化を行います。

ここでは、Serialクラスのbegin()メソッドを使用してボーレートを『9600bps』に設定しています。

Serial_Write()

Serial_Write()では、Serialクラスのprint()メソッドとprintln()メソッドを使用してPCへシリアル通信で文字列を送信しています。

print()で”IN枚数:”を送り、次にprintln()で”1”を送ります。

println()は最後に改行を入れてくれるので、以下のような表示になります。

Interrupt.cpp

解説

Intr_Init()

Intr_Init()では割り込み機能の初期化を行います。

ここでは、attachInterrupt()関数で外部入力による割り込みを登録します。

attachInterrupt()の第1引数には割り込み番号が必要なので、digitalPinToInterrupt()関数でデジタル入力番号を割り込み番号に変換しています。

また、第2引数には割り込み発生時に呼び出す関数(コールバック関数)を設定します。

最後に、第3引数には割り込みのきっかけを設定しますので、立下がりエッジを表す『FALLING』を設定します。

in_intr_occur()

このコールバック関数は、IN枚数が1枚増えたときの外部割込みによって呼び出されます。

あまり他の割込みと同時に発生する(多重割込み)ことはないと思いますが、一応noInterrupts()で割込み禁止にします。

次に、DataManager.cppのGet_IN_Coin()で現在のIN枚数を取得します。

そして、これを+1したうえでSet_IN_Coin()でIN枚数を更新します。

あとはSerial_Com.cppのSerial_Write()でPCへデータを送信します。

最後にinterrupts()で割込みを許可に戻します。

※他のout_intr_occur()などについてもほぼ同じなので省略します。

DataManager.cpp

解説

静的変数

DataManager.cppで管理しておくデータを宣言しています。

C言語はオブジェクト指向の言語ではないものの、DataManager.cpp外から好き勝手にこの変数にアクセスして欲しくないので、『static』で宣言することで隠蔽しています。

そのため、例えばmIN_CoinにアクセスするときはGet_IN_Coin()Set_IN_Coin()を介する必要があります。(いわゆるgetter、setterというやつです。)

Data_Init()

Data_Init()ではデータの初期化を行います。なので、すべての変数がゼロリセットされます。

Get_IN_Coin()

Get_IN_Coin()はいわゆるgetterです。単純に現在のmIN_Coinの値を返すだけです。

Set_IN_Coin()

Set_IN_Coin()はいわゆるsetterです。ここでは一度0以上かどうかを確認しています。しかし、符号なし型の変数なので正直あまり意味はないです。ただ、このようなsetterの形をとることで、プログラムによっては不正な値への更新やバグの作り込みの防止が期待できます。

※他のGet_OUT_Coin()などについてもほぼ同じなので省略します。

動作確認

作ったプログラムを動かしてみます。

金欠ぱとろん
金欠ぱとろん

カウントしすぎてます・・・おかしいな・・・

3枚掛けなのでレバーオン時にはまず『IN_COIN:3』までログが出るはずが、『IN_COIN:9』までカウントしてますね…。

もしやチャタリングを起こしているのではと思い、Arduinoの入力波形を測定してみました。

ぱっと見は問題なさそうですが…。

拡大してみると…

ほらね・・・。(´;ω;`)

電磁リレーと言えど、機械的なリレーではよく起こることです。

やはりチャタリングによって、余計な外部割込みが発生してカウントし過ぎていました。

ソフトウェア修正

電磁リレーのチャタリングを無視する必要があるので、一度割込みを発生させたら一定時間以内に同じ割込みが発生してもカウンティングを行わないようにします

解説

ローカル変数

前回外部割込みが発生した時の時間記録用の変数を追加しました。

Intr_Init()

時間記録用の変数の0リセットを追加しました。

in_intr_occur()

前回の割込みからの経過時間を計算し、時間がまだ経過していなければ何も処理しないように修正しました。

このときのINTR_WAIT[ms]は80[ms]に設定しています。

理由としては、信号の立下がった後の戻るときにもチャタリングをしてしまうからです。

したがって、次の信号立下がりが発生する直前(80[ms]くらい)を狙っています。

また、時間記録用の変数は、割込みが入ってちゃんとカウントされたときにのみ更新されるようにしています。

本記事に掲載したソースコードは一部を抜粋したものになっていますので、全ソースコードが見たい方は、以下のGitHubのリポジトリをご参照ください。

動作確認(再)

レバーオンしたときにIN枚数が、小役入賞時にOUT枚数が増えています。特に問題なさそうですね。(^_-)-☆
(あと、ボーナスもちゃんとカウントしてくれていました。)

ということで、とりあえず自作データカウンターの前哨戦は完了。

次回は、PC側のデータカウンターアプリケーションを作っていきます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です