Qステア用自作iアプリ

| コメント(16)
赤外線の解析結果をもとに、 Qステア操縦用のiアプリを自作してみました。
私にとっては、純正のiアプリより操作し易いです。
ソースコードは、以下のとおりです。

////////////////////////////////////////////////////////////////////////////
// 
//  Q STEER リモコン abetuyo version
//  Tsuyoshi ABE 2007/05/26 09:14
// 
////////////////////////////////////////////////////////////////////////////

import com.nttdocomo.ui.*;
import com.nttdocomo.device.*;

public class QsteerController extends IApplication {
  public void start() {
    Display.setCurrent((Frame)new MainPanel());
  }
}

class MainPanel extends Panel implements SoftKeyListener, KeyListener{

  static final int  FREQ  = 40; // kHz
  static final byte F     =  1; // Forward
  static final byte B     =  2; // Backward
  static final byte L     =  3; // Left
  static final byte R     =  4; // Right
  static final byte FT    =  5; // Forward Turbo
  static final byte FL    =  6; // Forward Left
  static final byte FR    =  7; // Forward Right
  static final byte FLT   =  8; // Forward Left Turbo
  static final byte FRT   =  9; // Forward Right Turbo
  static final byte BL    = 10; // Backward Left
  static final byte BR    = 11; // Backward Right
  static final byte BT    = 12; // Backward Turbo
  static final byte BLT   = 13; // Backward Left Turbo
  static final byte BRT   = 14; // Backward Right Turbo
  static final byte BRAKE = 15;

  private      byte CH    =  3; // D ch

  private IrRemoteControl irRC = IrRemoteControl.getIrRemoteControl();
  private IrRemoteControlFrame[] CMD_FRAMES = new IrRemoteControlFrame[16];
  private IrRemoteControlFrame[] sendFrame  = new IrRemoteControlFrame[1];

  private int accel = 0; // Forward Turbo : 2
                         // Forward       : 1
                         // Stop          : 0
                         // Backward      :-1
                         // Backward Turbo:-2
  private int steer = 0; // Left:-1 Neutral:0 Right:1
  
  MainPanel() {
    setTitle("Q STEER リモコン abetuyo version");
    setSoftLabel(SOFT_KEY_1, "END");
    setSoftLabel(SOFT_KEY_2, "Dch");
    setSoftKeyListener((SoftKeyListener)this);
    setKeyListener((KeyListener)this);
    irRC.setCarrier(5000/FREQ, 5000/FREQ);
    irRC.setCode0(IrRemoteControl.PATTERN_LH, 434, 414);
    irRC.setCode1(IrRemoteControl.PATTERN_LH, 900, 414);
    setFrames();
    TextBox textBox =
      new TextBox("\n"+
	          " ターボ前進1\n"+
                  "    前進4  左折5  右折6\n"+
                  "    後進7\n"+
                  " ターボ後進*\n", 36, 6, TextBox.DISPLAY_ANY);
    textBox.setEditable(false);
    add(textBox);
  }

  private void setFrames() {
    for (byte i=1; i<16; i++) {
      CMD_FRAMES[i] = getFrame(i);
    }
  }

  private IrRemoteControlFrame getFrame(byte command) {
    IrRemoteControlFrame frame = new IrRemoteControlFrame();
    byte[] data = new byte[1];
    data[0] = (byte)((CH << 4 | command) << 2);
    frame.setFrameData(data, 6);
    frame.setFrameDuration(791); // x0.1 msec
    frame.setStartHighDuration(1730); // usec
    frame.setStartLowDuration(0);
    frame.setStopHighDuration(0);
    frame.setRepeatCount(1);
    return frame;
  }

  public void softKeyPressed(int softKey) {
  }
    
  public void softKeyReleased(int softKey) {
    if (softKey == SOFT_KEY_1) {
      IApplication.getCurrentApp().terminate();
    }
    if (softKey == SOFT_KEY_2) {
      irRC.stop();
      accel = 0;
      steer = 0;
      CH++;
      if (CH==4) CH=0;
      setFrames();
      switch (CH) {
      case 0:
	setSoftLabel(SOFT_KEY_2, "Ach");
	break;
      case 1:
	setSoftLabel(SOFT_KEY_2, "Bch");
	break;
      case 2:
	setSoftLabel(SOFT_KEY_2, "Cch");
	break;
      case 3:
	setSoftLabel(SOFT_KEY_2, "Dch");
	break;
      default:
	break;
      }
    }
  }
    
  public void keyPressed(Panel panel, int key) {
    irRC.stop();
    switch (key) {
    case Display.KEY_1:
      accel = 2;
      switch (steer) {
      case -1:
	sendFrame[0] = CMD_FRAMES[FLT];
	break;
      case  0:
	sendFrame[0] = CMD_FRAMES[FT];
	break;
      case  1:
	sendFrame[0] = CMD_FRAMES[FRT];
	break;
      default:
	return;
      }
      break;
    case Display.KEY_4:
      accel = 1;
      switch (steer) {
      case -1:
	sendFrame[0] = CMD_FRAMES[FL];
	break;
      case  0:
	sendFrame[0] = CMD_FRAMES[F];
	break;
      case  1:
	sendFrame[0] = CMD_FRAMES[FR];
	break;
      default:
	return;
      }
      break;
    case Display.KEY_7:
      accel = -1;
      switch (steer) {
      case -1:
	sendFrame[0] = CMD_FRAMES[BL];
	break;
      case  0:
	sendFrame[0] = CMD_FRAMES[B];
	break;
      case  1:
	sendFrame[0] = CMD_FRAMES[BR];
	break;
      default:
	return;
      }
      break;
    case Display.KEY_ASTERISK:
      accel = -2;
      switch (steer) {
      case -1:
	sendFrame[0] = CMD_FRAMES[BLT];
	break;
      case  0:
	sendFrame[0] = CMD_FRAMES[BT];
	break;
      case  1:
	sendFrame[0] = CMD_FRAMES[BRT];
	break;
      default:
	return;
      }
      break;
    case Display.KEY_5:
      steer = -1;
      switch (accel) {
      case -2:
	sendFrame[0] = CMD_FRAMES[BLT];
	break;
      case -1:
	sendFrame[0] = CMD_FRAMES[BL];
	break;
      case  0:
	sendFrame[0] = CMD_FRAMES[L];
	break;
      case  1:
	sendFrame[0] = CMD_FRAMES[FL];
	break;
      case  2:
	sendFrame[0] = CMD_FRAMES[FLT];
	break;
      default:
	return;
      }
      break;
    case Display.KEY_6:
      steer = 1;
      switch (accel) {
      case -2:
	sendFrame[0] = CMD_FRAMES[BRT];
	break;
      case -1:
	sendFrame[0] = CMD_FRAMES[BR];
	break;
      case  0:
	sendFrame[0] = CMD_FRAMES[R];
	break;
      case  1:
	sendFrame[0] = CMD_FRAMES[FR];
	break;
      case  2:
	sendFrame[0] = CMD_FRAMES[FRT];
	break;
      default:
	return;
      }
      break;
    default:
      return;
    }
    irRC.send(1, sendFrame, 60);
  }
    
  public void keyReleased(Panel panel, int key) {
    irRC.stop();
    switch (key) {
    case Display.KEY_1:
    case Display.KEY_4:
    case Display.KEY_7:
    case Display.KEY_ASTERISK:
      accel = 0;
      switch (steer) {
      case -1:
	sendFrame[0] = CMD_FRAMES[L];
	break;
      case  0:
	return;
      case  1:
	sendFrame[0] = CMD_FRAMES[R];
	break;
      default:
	return;
      }
      break;
    case Display.KEY_5:
    case Display.KEY_6:
      steer = 0;
      switch (accel) {
      case -2:
	sendFrame[0] = CMD_FRAMES[BT];
	break;
      case -1:
	sendFrame[0] = CMD_FRAMES[B];
	break;
      case  0:
	return;
      case  1:
	sendFrame[0] = CMD_FRAMES[F];
	break;
      case  2:
	sendFrame[0] = CMD_FRAMES[FT];
	break;
      default:
	return;
      }
      break;
    default:
      return;
    }
    irRC.send(1, sendFrame, 60);
  }
}

コメント(16)

すげー!すげーよ、abechan!TVとか複数のオーディオのリモコンとかを学習して1台のリモコンにまとえてくれるやつがあるけど、あんなことを簡単にできちゃうツールがあるんだね。iアプリってことは、携帯電話で操縦するのかな?

しかもルパンⅢ世のFIATT500じゃない。R32GT-Rもあるんだね。ラジコンもこの小ささだし、すごい時代になったもんだ。

QステアとデジQってなにがちがうんだっけ?

こんど見せてよ。

k2hikoさん、コメントありがとう。
そうなんです。LIRCを使うと赤外線が見えるのですよ!

デジQは、左右の後輪それぞれを独立したモーターで駆動し、その回転数の差でクルマの向きが変わりました。プロポのスロットルとステアリングの操作量に応じて、左右のモーターの回転数が制御されて低速から高速、小回りから大回りと、クルマの動きを変化させることが出来ました。

Qステアは、1個のモーターで後輪を駆動し、ステアリングはマグネット・アクチュエータで作動します。モーターもステアリングもただのON/OFF制御(スピードは2段階にだけ変わる)なので、運転自体はあまり面白くありません。なので、モーターと電磁石に流す電流をPWM制御して、スピードとステアリング切れ角を滑らかに変化させられるよう改造中です!

Qステアにもそんな改造ができるんだね!すごいいいい!

マグネットでon/offを切り替えているだけならステアリングの切れ角は変えられないんだね。切れるスピードを変えるのかな?

大きさ(スケール)はデジQとQステアで違うのかな?

マグネットもピタっとくっつくのではなく、磁力で引き寄せられる仕組みなので、電磁石の強さを変化させることで、切れ角にも変化をつけられないか画策中です!

大きさは、デジQよりQステアの方が小さいです。
QステアはチョロQと全く同じ大きさですよ。

あとQステアはボディが良くできていて、ウィンカーなど細かいところまで彩色されています。ルパンⅢ世のチンクエチェントも開発担当者が「会心の出来映え」と言うだけあってとても良い雰囲気だよ。

R32GT-Rも、僕の持っているチョロQとQステアのサイトの写真を見比べてみたら、Qステアの方がR32らしい形をしている。デザインをリファインしたのかもね。おひとついかが!?

磁石で引き寄せられる量をコントロールするのって難しそうだね。やりがいがあるね。バネでダンピングさせるとかができればよさそうだけど、スペースが足りないかな。

R32GT-RのQステア欲しいな。

磁力がかかっていない時にステアを中立に保つやわらかいバネが組み込まれているので、磁力と弾力のバランスで制御できないかなーと思っています。やりがいあります!

なるほど!そうだよね。出来上がったらブログにアップしてください。

Qステアのiアプリを探していて辿り着きました。
新しい携帯にしたら通常のQステアiアプリが使えなかったので困っていたら、このblogを発見し何とかabetuyoさんのソースコードで使えるようになりました!
ありがとうございました。

それから、ちょっとお伺いしたいのですが、車をAchで使っているのでデフォルトでAchにしたいのですがソースコードの何処を変えれば良いのでしょうか?デフォルトでDchに設定されているようですから色々と思い当る所を変えてみたのですが上手くいきません。

もうひとつは、少々背景が殺風景なので背景画像を入れたいのですがどのように変更すればよいでしょうか?
機種はドコモのN02-A、Doja5.1 SDK
例えば
480×854の"back.jpg"画像を作成
resフォルダに入れる

ソースコードはどのよう書換えれば良いでしょうか?
サイズ設定も変えなければなりませんか?

プログラムとか初心者で四苦八苦ですが、アドバイスよろしくお願いします!

ken2さん
「Q STEER リモコン abetuyo version」をお試し頂きありがとうございました!
ご質問のAchをデフォルトにする方法は、

private byte CH = 3; // D ch
の部分を、
private byte CH = 0; // A ch
に変更し、

setSoftLabel(SOFT_KEY_2, "Dch");
の部分を、
setSoftLabel(SOFT_KEY_2, "Ach");
に変更してみてください。

あと、背景画像の入れ方は、下記のURLのドコモのチュートリアルがご参考になるかと思います。
http://www.docomocup.com/e_learning/making/step05.html

それでは!

abetuyoさんへ

ありがとうございました。
Achをデフォルト設定に出来てスッキリしました。
おかげで新しい携帯でもスムーズに動作できました。

背景画像に関しては
チュートリアルを参考に自分なりにやってみたのですが・・・

http://www.dotup.org/uploda/www.dotup.org69541.jpg
(リッチテキスト形式のソース)
http://www.dotup.org/uploda/www.dotup.org69539.rtf.html

サイトを見ながら四苦八苦してみましたがどうも上手くいきません。
プログラムに無知な相手にして面倒かもしれませんが
どうかアドバイスお願いします。

ken2さん

Achデフォルト設定になって良かったです。
背景画像は、週末に試してみますね。
少々、お待ちください。

ken2さん

背景画像を入れてみました。ソースコードは以下です。
http://abetuyo.net/i/QsteerController.java
お試しください!

abetuyoさん
背景画像入りソースコードありがとうございました。
おかげさまで背景を付ける事が出来ました。
ありがとうございました!

ドライバー視点のラジコンカーも面白かったです!
今後も面白い工作やプログラムを期待しています。

ken2さん

背景画像無事に入って良かったです。
ドライバー視点のラジコンも見てくれてありがとうございます!
これからも工作等に精進していきたいと思います。

Qステアのアプリを探してたどり着きました。
すごいですね。眠っていたQステアを久々に操作しました。
ありがとうございました。

ガラケーもそろそろ発売されなくなり、androidでも動くようにできないのでしょうかと夢見ています。。。

peruriさん
本アプリを活用して頂けて良かったです。
androidで操作するのも面白そうですね。