ラベル シリアル通信 の投稿を表示しています。 すべての投稿を表示
ラベル シリアル通信 の投稿を表示しています。 すべての投稿を表示

2020年3月12日木曜日

Tic Tac Toe for M5Atom x9概要

M5Stack ATOM LITE(以下M5Atom)を使った〇×ゲーム(Tic Tac Toe)を作成した


ざっくり概要:

・9台のM5Atomを片方向シリアル接続しており、環状に通信可能
 上流から来た情報を下流に再送するイメージ
 この環状通信でボタンの押し下げとLED点灯命令をやりとりする

・9台のうち1台が親機で残りは子機
 〇×ゲームを処理するのは親機
 子機へのLED点灯命令発行、子機からのボタン押し下げイベント受理を行う
 子機は自分のボタン監視(イベント発行)とLED制御のみ

配線の様子:ジャンパ線の長さがまちまちなのは手製のため

テクニカルな話:

・M5Atomは背面5Vへの電源供給で動作
 実動作上は5V≒VCC端子
 回路図が公開されていないのでやや不安
 給電には5V2AのACアダプタ使用
 3/13追記:
  背面からの給電でアクセスポイントとして動作させた際の発熱が凄い
  USB給電の場合と比べてはっきりと発熱に差がある
  仕様がはっきりするまで給電に使うべきではないかも…

・1台にUSB給電するとほかの8台もドライブしてしまう
 定格電流やUSB給電の仕様が公開されてないので怖くて使っていない
 一台当たり50mA強はいけそうな気もするが…?
 今後の課題とする

・〇×ゲームにはミニマックス法を使ったため、プレーヤーは一生勝てない
 クソゲーである
 (※〇×ゲームは互いに最善手を打つ限り常に引き分けるゲーム)
 意図的に手加減する処理を入れないと病む

M5Atomは電源周りの情報がないのが不安

2015年7月29日水曜日

ESP-WROOM-02についてのメモ(2)

以下、ESP-WROOM-02にスケッチをArduinoIDEから書き込む際のメモ

・IDEの環境構築は省略
 githubのESP8266コミュニティから環境を頂戴してくる
 IDEのBoardsManagerからインストールすればボードの選択肢にESP8266が
  
・USB-シリアル変換(3.3Vレベル)が必要
例:FTDI USBシリアル変換アダプター ・シリアル変換機のTX/RXとESP-WROOM-02のRX/TXを接続
・シリアル変換機のGNDとESP-WROOM-02のGNDを接続
・シリアル変換機をArduinoIDEと同じPCにUSB接続

・GPIO0をGND(L)に
・GPIO2を3.3V(H)に
・GPIO15をGND(L)に

・ESP-WROOM-02の3V3に3.3V電源を接続
・ESP-WROOM-02のGNDに電源のGNDを接続

・ArduinoIDE(環境構築済)を起動
・ツール→ボードからGeneric ESP8266 Moduleを選択
 (選択肢が無い場合は環境構築に失敗している)
・ツール→ポートからシリアル変換機のポートを選択
 (ポートがわからなければデバイスドライバ等からCOM番号を確認)

・適当なスケッチを選んで書き込みボタン(→)を押す
・『マイコンボードへの書き込みが完了しました。』表示がでれば成功
・自動的にリセットがかかりスケッチが動作

・GPIO0を3.3V(H)にしてリセット
・スケッチが動作することを確認

注意点:
 GPIO0をGND(L)で起動すると書き込みモードで起動するためにスケッチは動作しない
 GPIO0をGND(L)でスケッチ動作するのは書き込み直後の自動的なリセット時のみ

※※追記:
 GPIO0,2,15をHにする場合はプルアップ抵抗があった方が安全


書き込み環境/マイクロテクニカのボードを使用

2mmジャンパによる書き込み時設定

2mmジャンパによる動作時設定

スケッチ書き込み成功時

2015年7月18日土曜日

ゲームの自動操作装置(3):USBキーボードスケッチ

ゲームの自動操作装置(2):USBキーボードシールド からの続き

以下は前述のUSBキーボードシールドのためのスケッチ

シリアルポートを監視してPCから打鍵情報(1byte)が来るのを待つ
打鍵情報が来たら対応するGPIOへの出力を行う

ピンの最大数は17、打鍵は3種類(ON/OFF/HIT(一瞬ONしたのちOFF))なので1byteで足りる
(スケッチ内では簡単化のために17ではなくキリのよい20で計算している)

また、暴走対策のためにすべてのキーをOFFにするALL_OFF命令を設けている
どのピンに何のキーが対応しているかはこのスケッチでは扱わない

シリアル出力へデバッグ出力を行っているがアプリケーション側はこれを無視してもかまわない


ゲームの自動操作装置(4):アプリ/シリアル通信部分 へ続く

  
#define  PIN_MAX  17
#define  KEY_BASE  20

#define  KEYTYPE_HIT  0
#define  KEYTYPE_ON  1
#define  KEYTYPE_OFF  2
#define  KEYTYPE_MAX  2

#define  ALL_OFF  0
#define  KEY_DELAY  100

char command = 0;
int pin;
int commandType;
int commandNum;

void setup()
{
  Serial.begin(9600);
  for(pin = 2; pin <= 17; pin++)
  {
    pinMode(pin, OUTPUT);
    digitalWrite(pin, HIGH);
  }
}

void loop()
{
  if (Serial.available() > 0) 
  {
    command = Serial.read();
    if(command == ALL_OFF)
    {
      Serial.print("command:");
      Serial.print(command, DEC);

      Serial.print("ALL OFF");
      for(pin = 2; pin <= 17; pin++)
      {
        digitalWrite(pin, HIGH);  
      }      
      delay(KEY_DELAY);
    }
    else
    {
      commandType = (int)(command / KEY_BASE);
      if(commandType <= KEYTYPE_MAX)
      {
        pin = (int)(command - (KEY_BASE * commandType));
        if(pin <= PIN_MAX)
        {
          Serial.print("command:");
          Serial.print(command, DEC);
          Serial.print(" commandType:");
          Serial.print(commandType);
          Serial.print(" pin:");
          Serial.println(pin);
           
          switch(commandType)
          {
            case KEYTYPE_HIT:
              Serial.print("HitKey");          
              digitalWrite(pin, LOW);
              delay(KEY_DELAY);
              digitalWrite(pin, HIGH);
              break;
            case KEYTYPE_ON:
              Serial.print("OnKey");          
              digitalWrite(pin, LOW);
              delay(KEY_DELAY);            
              break;
            case KEYTYPE_OFF:
              Serial.print("OffKey");          
              digitalWrite(pin, HIGH);
              delay(KEY_DELAY);
              break;
            default:
              Serial.println("UnknownType");
              break;         
          }
        } 
      }
    }
  }
}

ゲームの自動操作装置(1):全体の構成

過去にコンシューマTVゲームにのめり込んでいた時期があり
「他の作業中でもレベルアップできないか?」
とArduinoとWindowsアプリケーションで自動操作装置を作った
(止めた今となってはなぜそこまで傾倒していたのかがわからない・・・)

以下はその記録
なお、ネットワークゲームサービスなどで自動操作を用いると確実に処分対象になるため
使用はおススメしない


・全体構成
  
  ゲーム機からの画像情報をUSBキャプチャ/キャプチャソフトを用いてPCに表示させる
  これらはすべて市販のものでかまわない

  PC上の自動操作アプリケーションはPCに表示される画像を認識できる
  この情報を用いて状態を判断し、次にすべき操作をArduinoに出力する
  アプリケーションは自作

  Arduinoを使ったUSBキーボードプロジェクトをゲーム機にUSB接続する
  これはゲーム機側からはUSBキーボードとして認識される
  このプロジェクトも自作

  ArduinoはシリアルポートでPCとも接続する
  アプリから送られる操作情報をUSBキーボードの打鍵としてゲーム機に伝達する

  
  このプロジェクトではゲーム機への入力にUSBキーボード入力を用いたが
  ゲーム機のコントローラーとフォトリレー等を組み合わせればコントローラー入力も可能

 
システムの構成

2015年5月22日金曜日

単純シリアル出力装置のコード

以下コード表示テスト
以前に書いた単純シリアル出力装置プロジェクトのサンプルコード
EEPROMへのコマンド書き込みが可能

プロジェクトの作例


短いコードなので説明は割愛
どこかからのコピペが混じってると思う

  
#include <EEPROM.h>

const int buttonPin = A0;
const int ledPin1 = 13;

int eepWriteFlag = 0;
int eepAddr = 0;

int buttonState = 0;
int setupFlag = 0;

char str[64]; // 数字(文字列)の受信用配列  
char str2[64];

void setup(){
  int i;
  
  pinMode(ledPin1, OUTPUT);      
  pinMode(buttonPin, INPUT_PULLUP);    
  
  delay(100);
  
  //ボタン押しっぱなし起動→セットアップモード
  if(digitalRead(buttonPin) == LOW)
  {
    //セットアップモード:9600pps固定
    setupFlag = 1;
    Serial.begin(9600);
    Serial.println("Setup Mode");
  }
  else
  {
    //非セットアップモード
    //先頭に記述されている#pps XXXX よりシリアル接続
    eepAddr = 0;

    for(i = 0; i < 64; i++)
    {
      str[i] = (char)EEPROM.read(eepAddr);
      eepAddr++;
  
      if(str[i] == '\r')
        break;
    }
    if(strEq(str, "#pps"))
    {
      long ppsBuf;
      for(i = 0; i < 64; i++)
      {
        str2[i] = 0;
      }
      for(i = 0; i < 64; i++)
      {
        if(str[i+5] == '\r')
          break;

        str2[i] = str[i+5];      
      }
      ppsBuf = atol(str2);
      Serial.begin(ppsBuf);

      Serial.print("pps:");
      Serial.print(ppsBuf);
      Serial.println();        
    }
    else
    {
      Serial.begin(9600);
      Serial.println("pps readError");
    }
  }
}

void loop(){
  int i, j;
  if(setupFlag == 0)
  {
    buttonState = digitalRead(buttonPin);

    if (buttonState == LOW) {     
    // turn LED on:    
    digitalWrite(ledPin1, HIGH);
    
    serialOut(0);
    
    digitalWrite(ledPin1, LOW);
    }
  } 
  else
  {
    //ミソはバッファクリア―!
    for(i = 0; i < 64; i++)
     str[i] = 0;
     
    recvStr(str);  
    Serial.println(str);
    
    if(strEq(str, "#start"))
    {
      Serial.println("detect #start");
      eepWriteFlag = 1;
    }
    else if(strEq(str, "#end"))
    {
      Serial.println("detect #end");
      eepWriteFlag = 0;
  
      EEPROM.write(eepAddr, (byte)'\n');
      eepAddr = 0;
    }
    else if(strEq(str, "#read"))
    {
      Serial.println("detect #read");
      eepAddr = 0;
  
      for(j = 0; j < 512; j++)
      {
        for(i = 0; i < 64; i++)
        {
          str[i] = 0;
        }
        for(i = 0; i < 64; i++)
        {
          str[i] = (char)EEPROM.read(eepAddr);
          eepAddr++;
      
          if(str[i] == '\r')
            break;
        }
        Serial.println(str);
        if(str[0] == '\n')
           break;
      }
    }
    else if(strEq(str, "#clear"))
    {
      Serial.println("detect #clear");
      for(i = 0; i < 1024; i++)
      {
        eepWriteFlag = 0;
        eepAddr = 0;
    
        EEPROM.write(i, 0);
      }
    }
    else if(strEq(str, "#go"))
    {
      //#readとの違いはディレイやppsなど一部パースすること
      Serial.println("detect #go");
      eepAddr = 0;
      
      serialOut(1);
    }  
    else if(eepWriteFlag)
    {
      for(i = 0; i < 64; i++)
      {
        EEPROM.write(eepAddr, (byte)str[i]);
        eepAddr++;
        if(str[i] == '\r')
          break;
      }
    }
  }
}

//※文字列*baseStrの冒頭が*headStrと等しいかどうかをチェックする
//等しければ1、そうでなければ0
int strEq(char *checkStr, char *headStr)
{
  int size = sizeof(headStr) / sizeof(headStr[0]);
  int i;
  int ret = 1;
  
  for(i = 0; i < size; i++)
  {
    if(checkStr[i] != headStr[i])
    {
      ret = 0;
      break;
    }
  }
  
  return ret;
}

void recvStr(char *buf)
{
  int i = 0;
  char c;
  while (1) {
    if (Serial.available()) {
      c = Serial.read();
      buf[i] = c;
      if (c == '\r') break;
      i++;
    }
  }
  buf[i] = '\r';
} 

void serialOut(int debugFlag)
{
  int i,j;
  eepAddr = 0;

  for(j = 0; j < 16; j++)
  {
    for(i = 0; i < 64; i++)
    {
       str[i] = 0;
    }

    for(i = 0; i < 64; i++)
    {
      str[i] = (char)EEPROM.read(eepAddr);
      eepAddr++;
  
      if(str[i] == '\r')
      {
        break;
      }
    }
    
    if(strEq(str, "#delay"))
    {
      int delayBuf;
      for(i = 0; i < 64; i++)
      {
        str2[i] = 0;
      }
      for(i = 0; i < 64; i++)
      {
        if(str[i+7] == '\r')
          break;

        str2[i] = str[i+7];      
      }
      delayBuf = atoi(str2);
      
      if(debugFlag)
      {
        Serial.print("delay:");
        Serial.print(delayBuf);
        Serial.println();
      }
      else
      {
        delay(delayBuf);
      }
    }
    else if(strEq(str, "#pps"))
    {
      if(debugFlag)
      {
        Serial.println("pps Skip");        
      }
    }
    else
    {
      Serial.println(str);
    }
    if(str[0] == '\n')
       break;
  }
}



2015年5月19日火曜日

ArduinoのTIPS

・外部スイッチとのミニマムな構成
 入力ピンをプルアップ設定する
 スイッチの片側をGNDに、もう一方を入力ピンに接続する
  スイッチを押す:L
  スイッチを離す:H
 として検知できる
 抵抗は不要


・外部LEDとのミニマムな構成
 PinD13のみ内部抵抗が存在するため、LEDを直挿ししても正常動作する
  追記:Uno R3(Revision 3)から構造が異なるため、直挿しはやめた方がよい


Uno R2(Revision 2)の回路図より抜粋:D13ピンに抵抗とLEDを直付け

Uno R3の回路図より抜粋:D13ピン(SCK)からオペアンプ経由で抵抗とLEDを接続



・テスト環境のDIP化
 テスト環境/プロトタイプ構築のためにブレッドボードが用いられる
 各種コネクタには大抵DIP化キットが存在するため準備しておくとよい


・PCとの通信
 シリアル通信で行うのが一番楽
 PCとArduinoをUSB接続してやると自動的にシリアルポートが生成される
 あとはテキスト形式で任意フォーマットの入出力を行えばよい


・外部スイッチのON/OFFについて
 リレーを用いることで、外部機器のスイッチを制御することができる
 スイッチ間の信号が3.3V、5V程度ならば光学リレーフォトカプラ/フォトリレー)が安価かつ静かでよい
 ただし制御信号に抵抗を挟むのを忘れると簡単に壊れる(内部のLEDが焼切れる)ため注意
 ソケットを付けて交換可能にしておくとよい


・外部機器のキーボード/ジョイパッド操作
 USBキーボードエミュレーションなど様々な方法があるが、市販のUSBキーボード/GPIO変換モジュールを使うのが一番楽
  ビットトレードワン/REVIVE USB
 12本のピンに任意キーを設定することができる
 REVIVE USBのピンに対してArduinoのデジタルピンを接続してやるだけでよい

REVIVE USB/市販品を使うのが一番楽


Arduinoシリアル出力について

・シリアル入出力について
 Arduinoのシリアル入出力は内部で二分岐され
  ・TX/RXピン
  ・内部のUSB-シリアル変換機(Atmega16U2)
 に接続される

 排他処理はされておらず単純な二分岐
 ArduinoIDEでUSB経由のファームアップ時、TX/RXを接続しているとエラーが起こる場合がある
 一時的にTX/RXの接続を切ること


・RS-232C接続について
 TX/RXに市販のレベルコンバータを用いればOK
  RS-232Cのレベルはだいたい15V程度

秋月電子のレベルコンバータAE-ADM3202/D-Sub9ピンコネクタに接続した例


・RS-232Cケーブルについて
 ストレートケーブルとクロス(リバース)ケーブルが存在するので注意
  TX/RX端子をそのまま接続するのがストレートケーブル
  TX/RX端子をクロスして接続するのがクロスケーブル
 外観ではわかり辛いので製番で検索したりテスターを使おう


・Bluetoothとの接続について
 市販のBluetoothモジュールを用いることで、シリアル入出力を簡単に無線化できる
 TX/RX端子をモジュールに接続し、SPP(シリアルポートプロトコル)変換を行う
 適宜レベル変換を入れること
 技適証明の無い海外モジュールを使うと電波法に抵触するので避けること