Home > LEGO > LEGO : LEGO MINDSTORMS NXTとPCを連携(その2) - NintendoDS、Flashでコントロール

LEGO : LEGO MINDSTORMS NXTとPCを連携(その2) - NintendoDS、Flashでコントロール

  • Posted at: 2008年6月12日 11:32
  • Update: 2008年7月18日 12:08

080604_mindstorms1.gif

iCommandを使って、PCとか他のガジェットからコントロールできるRCカーとか作ってみまーす。構成は↑な感じ。鯖とリモコンはそれぞれソケット通信でデータをやりとりします。Bluetoothのデバイスとして認識されているNXTは、シリアルポートを通じてPCと通信をします。鯖とNXTは、Javaでシリアル通信をするためにRXTXライブラリを使い、NXT上のマイコンに命令を送るわけです。

NintendoDSとMINDSTORMSを連携させてみる。

FlashとDSの連携に使ったServerに、iCommandを組み込んでリモコンにします。DS側はボタンのダンエッジとか少し変更。鯖側は、データを受信してそれをクライント全員に返すだけだったので、JSONを扱えるようにします。DSからボタンごとのステータスが送られてくるので、対応させた動きをiCommandのAPIで追加していきます。

NXTと通信させる。

NXTCommand.open();    // Bluetoothの通信を接続
NXTCommand.close();    // Bluetoothの通信を切断

モーターを動かす。

Motor.A.forward();       // ポートAに接続したモーターを前に回転。
Motor.A.backward();    // ポートAに接続したモーターを後に回転。
Motor.A.stop();            // ポートAに接続したモーターを停止。

ビープ音を鳴らす。

Sound.playTone(n, w);    // トーン n を w ミリ秒間鳴らす。

できたもの

とりあえずスレッド部を修正。あとJSONオブジェクト用のクラスも用意。DS側も少し修正。

・Java:ChatServerThread.java

import icommand.nxt.comm.NXTCommand;
import icommand.nxt.Motor;
import icommand.nxt.Sound;
import net.sf.json.*;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.regex.*;


//チャットサーバスレッド
public class ChatServerThread extends Thread { 
  private static List<ChatServerThread> threads = new ArrayList<ChatServerThread>();
  private Socket socket; // ソケット
  
  public static final int DELAY_MS = 100;
  public static final int DIRECTION_FORWARDS = 1;
  public static final int DIRECTION_BACKWARDS = 2;
  public static int direction;
  
  // ビープ音を設定
  private static final short[] note1 = { 2349, 80, 0, 5, 1760, 45, 0, 35 };
  private static final short[] note2 = { 1760, 45, 0, 5, 2349, 80, 0, 35 };
  
  private Boolean PowerState = false;
  
  dsKey dsKeys = new dsKey();
  
  // コンストラクタ
  public ChatServerThread(Socket socket) {
    super();
    this.socket=socket;
    threads.add(this);
  }

  // ここから処理
  public void run() {
    InputStream in = null;
    String message;
    int size;
    
    Pattern p;
    Matcher m;

    byte[] w = new byte[10240];
    
    
    try {
      // ストリーム
      in =socket.getInputStream();
      while(true) {
        try {
          // 受信待ち
          size=in.read(w);

          // データのサイズが0なら切断
          if (size<=0) throw new IOException();

          // 読み込み
          message = new String(w,0,size,"UTF8");
          
          // 全員にメッセージ送信
          sendMessageAll(message);
          
          // JSON文字列→JSONObject→Beanクラス
          p = Pattern.compile("(¥"([a-z]+)¥":([0-9]+).)*");
          m = p.matcher(message);
          if (m.find()) {
            JSONObject jsonObject = JSONObject.fromObject(message);
            System.out.println(jsonObject.toString());
            dsKey dsKeys = (dsKey) JSONObject.toBean(jsonObject, dsKey.class);
            
            // NXTにコマンドを送信
            sendCommandNXT(dsKeys);

          }
          
          
        } catch (IOException e) {
          socket.close();
          threads.remove(this);
          return;
        }
      }
    } catch (IOException e) {
      System.err.println(e);
    }
  }

  // 全員にメッセージ送信
  public void sendMessageAll(String message) {
    ChatServerThread thread;
    for (int i=0;i<threads.size();i++) {
      thread=(ChatServerThread)threads.get(i);
      if (thread.isAlive()) thread.sendMessage(this,message);
    }
    System.out.println(message);
  }

  // メッセージ送信
  public void sendMessage(ChatServerThread talker,String message){
    try {
      OutputStream out=socket.getOutputStream();
      byte[] w=message.getBytes("UTF8");
      out.write(w);
      out.flush();
    } catch (IOException e) {
    }
  }
  
  // NXTにコマンドを送信
  public void sendCommandNXT(dsKey dsKeys) {
    Motor.A.setSpeed(300);
    Motor.C.setSpeed(300);
    
    // ↑キーなら両方のモーターを前進
    if (dsKeys.getAu() == 1) {
      System.out.println("au:"+dsKeys.getAu());
      direction = DIRECTION_FORWARDS;
      Motor.A.forward();
      Motor.C.forward();
    }
    // ↓キーなら両方のモーターを後進
    else if (dsKeys.getAd() == 1) {
      System.out.println("ad:"+dsKeys.getAu());
      direction = DIRECTION_BACKWARDS;
      Motor.A.backward();
      Motor.C.backward();
    }
    // ←キーなら右のモーターだけ前進/後進
    else if (dsKeys.getAl() == 1) {
      if (direction == DIRECTION_FORWARDS) {
        Motor.A.forward();
      } else {
        Motor.A.backward();
      }
      Motor.C.stop();
    }
    // →キーなら左のモーターだけ前進/後進
    else if (dsKeys.getAr() == 1) {
      if (direction == DIRECTION_FORWARDS) {
        Motor.C.forward();
      } else {
        Motor.C.backward();
      }
      Motor.A.stop();
    }
    // STARTボタン
    else if (dsKeys.getBs() == 1) {
      if (!PowerState) {
        
        // NXTに接続
        NXTCommand.open();
        
        // 起動ビープ音を鳴らす
        for (int i = 0; i < note1.length; i += 2) {
          final short ww = note1[i + 1];
          final int n = note1[i];
          if (n != 0)
            Sound.playTone(n, ww * 10);
          try {
            Thread.sleep(ww * 10);
          } catch (InterruptedException e) {
          }      
        }
        sendMessageAll("PowerOn");
        PowerState = true;
      }       
    }
    // SELECTボタン
    else if (dsKeys.getBe() == 1) {
      if (PowerState)  {
            // 停止ビープ音を鳴らす
            for (int i = 0; i < note2.length; i += 2) {
              final short ww = note2[i + 1];
              final int n = note2[i];
              if (n != 0)
                Sound.playTone(n, ww * 10);
              try {
                Thread.sleep(ww * 10);
              } catch (InterruptedException e) {
              }      
            }

            // NXTと切断
            NXTCommand.close();

            sendMessageAll("PowerDown");
            PowerState = false;
          }

    }
    // 入力がなければモーターを停止
    else {
      Motor.A.stop();
      Motor.C.stop();
    }
    
    // スレッドをDELAY_MSの間スリープ
    try {
      Thread.sleep(DELAY_MS);
    } catch (Exception e) {
      System.out.println(e);
      System.exit(1);
    }
  }
}

・Java:dsKey.java

public class dsKey {
  private int au;
  private int ad;
  private int al;
  private int ar;
  private int ba;
  private int bb;
  private int bx;
  private int by;
  private int bl;
  private int br;
  private int be;
  private int bs;
  private int pp;
  private int px;
  private int py;
  
  public dsKey() {
  }
  
  public int getAu() { return au; }
  public void setAu(int au) { this.au = au; }
  
  public int getAd() { return ad; }
  public void setAd(int ad) { this.ad = ad; }
  
  public int getAl() { return al; }
  public void setAl(int al) { this.al = al; }
  
  public int getAr() { return ar; }
  public void setAr(int ar) { this.ar = ar; }
  
  public int getBa() { return ba; }
  public void setBa(int ba) { this.ba = ba; }
  
  public int getBb() { return bb; }
  public void setBb(int bb) { this.bb = bb; }
  
  public int getBx() { return bx; }
  public void setBx(int bx) { this.bx = bx; }
  
  public int getBy() { return by; }
  public void setBy(int by) { this.by = by; }
  
  public int getBl() { return bl; }
  public void setBl(int bl) { this.bl = bl; }

  public int getBr() { return br; }
  public void setBr(int br) { this.br = br; }
  
  public int getBs() { return bs; }
  public void setBs(int bs) { this.bs = bs; }
  
  public int getBe() { return be; }
  public void setBe(int be) { this.be = be; }
  
  public int getPp() { return pp; }
  public void setPp(int pp) { this.pp = pp; }
  
  public int getPx() { return px; }
  public void setPx(int px) { this.px = px; }
  
  public int getPy() { return py; }
  public void setPy(int py) { this.py = py; }

}

・DS:arm9

#include <nds.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <dswifi9.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
//---------------------------------------------------------------------------------

#define VCOUNT (*((u16 volatile *) 0x04000006))
#define PEN_DOWN (~IPC->buttons & (1 << 6))

touchPosition touchXY;
int touch_was_down = 0;

int my_socket;

static int old_x = 0;
static int old_y = 0;
static int shape_x = 0;
static int shape_y = 0;
static int shape_width = 4;
static int shape_height = 4;
static char *sendBuf;


//---------------------------------------------------------------------------------
// Dswifi helper functions

// WiFi タイマー関数
// sgIPのアップデート
void Timer_50ms(void) {
  Wifi_Timer(50);
}

// ARM7へFIFOメッセージを通知する関数
void arm9_synctoarm7() { // FIFOメッセージを送る
  REG_IPC_FIFO_TX=0x87654321;
}

// 割り込みハンドラ
// ARM7からのFIFOメッセージを取得する
void arm9_fifo() { // FIFOメッセージの入力をチェック
  u32 value = REG_IPC_FIFO_RX;
  if(value == 0x87654321) Wifi_Sync();
}

// フレームバッファで短型を動かす
//---------------------------------------------------------------------------------
void draw(int x, int y, uint16* buf, uint16 c) {
//---------------------------------------------------------------------------------

  buf += y * SCREEN_WIDTH + x;
  int i;
  for(i = 0; i < shape_height; i++) {
    uint16* line = buf + SCREEN_WIDTH * i;
    int j;
    for(j = 0; j < shape_width; j++) {  
      *line++ = c;
    }
  }
}

// Vblank割り込み時の処理。
//---------------------------------------------------------------------------------
void VblankHandler() {
//---------------------------------------------------------------------------------

  sendBuf = (char *)malloc(128);
  int keys[16] = {0};
  
  scanKeys();
  touchXY = touchReadXY();
  int held = keysHeld();
  int down = keysDown();
  int up = keysUp();
  
  if(held & KEY_UP) {
    keys[0] = 1;
  }
  if(held & KEY_DOWN) {
    keys[1] = 1;
  }
  if(held & KEY_RIGHT) {
    keys[2] = 1;
  }
  if(held & KEY_LEFT) {
    keys[3] = 1;
  }
  if(held & KEY_A) {
    keys[4] = 1;
  }
  if(held & KEY_B) {
    keys[5] = 1;
  }
  if(held & KEY_X) {
    keys[6] = 1;
  }
  if(held & KEY_Y) {
    keys[7] = 1;
  }
  if(held & KEY_R) {
    keys[8] = 1;
  }
  if(held & KEY_L) {
    keys[9] = 1;
  }
  if(down & KEY_START) {
    keys[10] = 1;
  }
  if(down & KEY_SELECT) {
    keys[11] = 1;
  }
  
  if(!touch_was_down && PEN_DOWN) {
    touch_was_down = 1;
    keys[12] = 1;
    keys[13] = touchXY.px;
    keys[14] = touchXY.py;
    iprintf("\x1b[5;0H");
    iprintf("Touch x / y = %03d / %03d\n", touchXY.px, touchXY.py);
  }
  else if(touch_was_down && !PEN_DOWN) {
    touch_was_down = 0;
  }
  else if(touch_was_down && PEN_DOWN) {
    keys[12] = 2;
    keys[13] = touchXY.px;
    keys[14] = touchXY.py;
    iprintf("\x1b[5;0H");
    iprintf("Touch [ %03d / %03d ]\n", touchXY.px, touchXY.py);
  }
  
  old_x = shape_x;
  old_y = shape_y;
  shape_x = touchXY.px;
  shape_y = touchXY.py;

  draw(old_x, old_y, VRAM_A, RGB15(5, 5, 5));
  draw(shape_x, shape_y, VRAM_A, RGB15(0,31,0));
  
  if(held || down || up) {
    sprintf(sendBuf,"{\"au\":%d,\"ad\":%d,\"al\":%d,\"ar\":%d,\"ba\":%d,\"bb\":%d,\"bx\":%d,\"by\":%d,\"bl\":%d,\"br\":%d,\"bs\":%d,\"be\":%d,\"pp\":%d,\"px\":%d,\"py\":%d}\n", 
      keys[0], keys[1], keys[2], keys[3], keys[4], keys[5], keys[6], keys[7], keys[8], keys[9], keys[10], keys[11], keys[12], keys[13], keys[14] );
    send( my_socket, sendBuf, strlen(sendBuf), 0 );
  }
  
  iprintf("\x1b[6;0H");
  iprintf("Keys  [ U D L R A B X Y L R E S ]");
  
  free(sendBuf);
  
  VBLANK_INTR_WAIT_FLAGS |= IRQ_VBLANK;
}

//---------------------------------------------------------------------------------
int main(void) {
//---------------------------------------------------------------------------------

  powerON(POWER_ALL);
  
  videoSetMode(MODE_FB0);  //フレームバッファモード
  videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE);  //グラフィックエンジンのモードを指定。アクティブにして使用するBGも設定
  vramSetMainBanks(VRAM_A_LCD, VRAM_B_LCD, VRAM_C_SUB_BG, VRAM_D_LCD); //VRAMの使用領域設定
  
  SUB_BG0_CR = BG_MAP_BASE(31);
  
  BG_PALETTE_SUB[255] = RGB15(31,31,31);
  consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(31), (u16*)CHAR_BASE_BLOCK_SUB(0), 16);

  // ARM7 の WiFi 機能を初期化するためのFIFOメッセージを送る
  {
    REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_SEND_CLEAR; // FIFOの有効&クリア
    
    u32 Wifi_pass= Wifi_Init(WIFIINIT_OPTION_USELED);
    REG_IPC_FIFO_TX=0x12345678;
    REG_IPC_FIFO_TX=Wifi_pass;
       
    *((volatile u16 *)0x0400010E) = 0; // Timer3 無効
    
    irqInit();
    irqSet(IRQ_TIMER3, Timer_50ms); // Timer IRQ のセット
    irqEnable(IRQ_TIMER3);
    irqSet(IRQ_FIFO_NOT_EMPTY, arm9_fifo); // FIFO IRQ のセット
    irqEnable(IRQ_FIFO_NOT_EMPTY);
    irqEnable(IRQ_VBLANK); // Vblank IRQ のセット
     
    REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_RECV_IRQ; // FIFO IRQ 有効
     
    Wifi_SetSyncHandler(arm9_synctoarm7); // WiFi ライブラリに ARM7への通知用関数をセットする

    // Timer3 のセット
    *((volatile u16 *)0x0400010C) = -6553; // 6553.1 * 256 cycles = ~50ms;
    *((volatile u16 *)0x0400010E) = 0x00C2; // enable, irq, 1/256 clock
    
    while(Wifi_CheckInit()==0) { // ARM7 の初期化終了待ち
      while(VCOUNT>192); // VBlank 待ち
      while(VCOUNT<192);
    }
    
  }
  // WiFi 初期化終了 ここからWiFi ライブラリが使えるようになります
  
  // 単純な接続
  {
    int i;
    // Wifi_AutoConnect では、AOSS 機能は使えないらしいです。
    // WEPの設定をDS本体から読んできます。
    Wifi_AutoConnect(); // 接続要求
    while(1) {
      i=Wifi_AssocStatus(); // ステータスチェック
      if(i==ASSOCSTATUS_ASSOCIATED) {
        iprintf("Connected successfully!\n");
        break;
      }
      if(i==ASSOCSTATUS_CANNOTCONNECT) {
        iprintf("Could not connect!\n");
                while(1);
        break;
      }
    }
  }
  
  // 接続成功したら、ここからソケットインターフェイスを使ってインターネットに接続することができます。

  // gethostbynameでIPアドレスに返す。直接IPでも。
  struct hostent *myhost = gethostbyname( "10.0.2.1" );
  iprintf("Found IP Address!\n");

  // SOCK_STREAM 型のソケットを作る。TCPってこと。
  my_socket = socket( AF_INET, SOCK_STREAM, 0 );
  iprintf("Created Socket!\n");

  // 指定したポートにソケットが繋がるか尋ねてみる。
  struct sockaddr_in sain;
  sain.sin_family = AF_INET;
  sain.sin_port = htons(16000); // ポートを指定。なんとなく16000にしてみた。
  sain.sin_addr.s_addr= *( (unsigned long *)(myhost->h_addr_list[0]) );
  connect( my_socket,(struct sockaddr *)&sain, sizeof(sain) );
  iprintf("Connected to server!\n");

  // 接続確認のメッセージを送る。
  //char *sendBuf = "CONNECTED SUCCESS\n";
  //send( my_socket, sendBuf, strlen(sendBuf), 0 );


  /*
  必要ならこの辺で返ってきたメッセージを受信させるといいんじゃないかな。
  */


  while(1) {
  
    VblankHandler();
    swiWaitForVBlank();
  
  }
  
  //return 0;
}

動かしてみる。

動くと結構うれしいw。DSの通信周りが不安定なんですぐ落ちるのが難点。WiFiのタイマー周りだと思うんだけどなぁ。。

FlashとMINDSTORMSを連携させてみる。

もーあとはソケット通信できて、JSON形式で送れればなんでもいいわけで。Flashとも連携させましょう。まずリモコンのパネルとかこしらえます。通信しましょう。動きますw。

うごいたうごいた。でもこれFlashで動いてるかどうかわかんよな。。

いろいろ作りたいなぁ

流行りじゃないのか情報ソースが少ないのが問題ですかね。センサー使ってなんか動かしたい!Gainerとか触ってみたいけどー、電子工作はちょっと。。みたいな人にはおすすめ。

あ、データにJSON形式を使ってるのはiCommandの仕様じゃないです。都合のいいプロトコルを使えばMIDIだったり、MAX/MSPとかProcessingをリモコンにできるかと。

関連エントリ:

関連タグ: Bluetooth, iCommand, MINDSTORMS, NintendoDS

Comments:2

Comment Form
Saqoosha 2008年7月18日 12:23

TO-FU!!!!
ぼくももってる!!!!

hoehoe3 2008年7月18日 14:08

おお。400%TO-FUかわいいよw
全部大きいので出しなおしてほしいですよねー

Home > LEGO > LEGO : LEGO MINDSTORMS NXTとPCを連携(その2) - NintendoDS、Flashでコントロール

Auther
hoehoe3 : おおさか方面でWebとかやってますよ。
What am I doing...
    Search
    Feeds

    Return to page top