marunomaruno-memo

marunomaruno-memo

Lego Mindstomes NXT - NXC プログラミング (7) - コマンドライン

2008年01月29日 | Weblog
開発環境の BricxCC を使うだけではなく、ちょっと目先を変えて、コマンドラインベー
スで使ってみましょう。

BricxCC のエディッタには多少の不満があるので、コマンドラインベースででも開発でき
ると便利。

本当は、Eclipse で開発できると便利なんだけど、まだ、leJOS に対するものしかなさそ
う。(★)

LDT - leJOS Development Toolset
http://www.eclipseplugincentral.com/Web_Links-index-req-viewlink-cid-306.html

これも、機会があれば、そのうち試してみたい。


■ 環境変数 PATH の設定

まず、BricxCC のパスをシステムの環境変数 PATH に追加します。

デフォルトのままインストールしたのであれば、「C:\Program Files\BricxCC」にインス
トールされているので、これを環境変数 PATH の最後にセミコロン(;)をつけて、その後
につけます。


■ コマンド nbc

NXC プログラムをコンパイルしたり、ダウンロードするのは、nbc コマンドを用います。

オプション -help で、どのようなコマンドのシンタックスを見てみましょう。

---
> nbc -help
Next Byte Codes Compiler version 1.0.1.b34 (1.0.1.34, built Sun Nov 25 20:14:23 CST 2007)
     Copyright (c) 2006, John Hansen
Syntax: nbc [options] filename [options]

   -S=<portname>: specify port name (COMn or usb), resource name, or alias
   -BT: use bluetooth
   -d: download program
   -b: treat input file as a binary file (don't compile it)
   -q: quiet
   -n:  prevent the system file from being included
   -D=<sym>[=<value>]: define macro <sym>
   -x: decompile program
   -Z[1|2]: turn on compiler optimizations
   -O=<outfile> : specify output file
   -E=<filename> : write compiler errors to <filename>
   -I=<path>: search <path> for include files
   -nbc=<filename> : save NXC intermediate NBC code to <filename>
   -L=<filename> : generate code listing to <filename>
   -Y=<filename> : generate symbol table to <filename>
   -w[-|+] : warnings off or on [default is off]
   -EF : enhanced firmware
   -safecall: NXC will wrap all function calls in Acquire/Release
   -api: dump the API to stdout
   -help : display command line options
---



■ コンパイルと NXT へのダウンロード

プログラム Bumper01.nxc をコンパイルしてダウンロードしてみます。

---
C:\_docs\Lego\Bricx_NXC>nbc -S=usb -d Bumper02.nxc
---


コンパイルエラーのときは、以下のように表示されます。今回は、19 行目の
Off(OUT_BC)
文のセミコロンを抜かしました。

---
C:\_docs\Lego\Bricx_NXC>nbc -S=usb -d Bumper02.nxc
# Error: ';' expected
File "C:\_docs\Lego\Bricx_NXC\Bumper02.nxc" ; line 20
#    unsigned
#----------------------------------------------------------
---



■ 目的ファイルの生成

目的ファイルをファイルとして残す場合は、-O オプションを使います。このとき、この
実行ファイルの拡張子は「.rxe」を指定しておきます。
---
> nbc -O=Bumper02.rxe Bumper02.nxc
---

そうすると、Bumper02.rxe というファイルができます。これが、コンパイル後の目的フ
ァイルです。-O オプションで目的ファイル名を指定しないと、どうやら NULL に出力さ
れるようです。(★)


■ 逆コンパイル

この目的ファイルを逆コンパイルするには、-x オプションを使います。
逆コンパイル結果は、標準出力に出ます。

---
C:\_docs\Lego\Bricx_NXC>nbc -x Bumper02.rxe
---

逆コンパイルした結果は、NBC のコードです。
NBC のコードについては、BricxCC をインストールしたフォルダーのサブフォルダーに
Documentation フォルダーがあり、そのなかにガイドとチュートリアルがありますので、
参考にしてください。
C:\Program Files\BricxCC\Documentation


■ NBC ファイルの生成

わざわざ、逆コンパイルしなくても、NBC ファイルを生成するオプションもあります。
NBC ファイルを生成するには、-nbc オプションを使います。

逆コンパイルした結果と、-nbc オプションで生成したファイルを比較しても面白いかも
しれません。

---
C:\_docs\Lego\Bricx_NXC>nbc -nbc=Bumper02.nbc Bumper02.nxc
---


■ NeXTTool - USB & Bluetooth NXT communication utility

NXT と通信をするユーティリティ。

これで取得できる情報や、操作も、ほとんどは BricxCC の [Tools] メニューから取得で
きるので、あまり使うことはないかも。

まずは、オンラインヘルプ。

---
C:\_docs\Lego\Bricx_NXC>nexttool -help
NeXTTool version 1.0 (1.0.10.1, built Mon Nov 5 13:15:27 CST 2007)
     Copyright (c) 2006 John Hansen
Usage: NeXTTool [options] [actions]
Options:
   /COM=port: specify port name (COMn, usb, resource string, or alias)
   /BT[=name]: use bluetooth (selects the first brick found or the named brick)
   /HEX: use hexidecimal for numeric output
   /Duration=<n>: specify the tone duration for the playtone action
   /Inbox=<n>: use inbox number n when sending or reading a message
   /Loop: loop when playing sound files
   /Relative: reset output position relative
   /Empty: empty mailbox when reading
   /Bin[=filename]: dump data output as binary to a file (nxt.bin)
Actions:
   -init : initialize nxt.dat file
   -listbricks : list resource names of all found NXT bricks
   -clear : erase all items on the brick
   -battery : return the battery level
   -input=<N> : read input N (0-3)
   -output=<N> : read the status of output N (0-2)
   -mute : stop playing sounds
   -playtone=<frequency> : play a tone for the specified duration
   -run=<filename> : run the specified program
   -runningprogram : return the name of the currently running program
   -stop : stop the currently running program
   -playfile=<filename> : play the specified sound file
   -firmware=<filename> : download firmware
   -download=<filename> : download the specified file to the NXT
   -upload[=<pattern>] : upload the specified file(s) from the NXT (or *.*)
   -listfiles[=<pattern>] : list the files matching the pattern (or *.*)
   -listmodules[=<pattern>] : list the modules matching the pattern (or *.*)
   -delete=<filename> : delete the specified file from the NXT
   -datalog | -datalog_full: upload datalog (_full == verbose)
   -eeprom=<n> | -eeprom_full: upload eeprom block (_full == all blocks)
   -memory=<n> | -memory_full: upload 128 bytes of memory (_full == all memory)
   -map: upload memory map
   -keepalive : return the current sleep time limit
   -sleep=<timeout> : set NXT sleep timeout (in minutes)
   -msg=<string> : send the specified message to the NXT
   -readmsg=<box> : read the message from the specified box
   -resetoutputposition=<port> : reset the position for the specified port
   -resetinputsv=<port> : reset the input scaled value for the specified port
   -setname=<new_name> : set the name of the NXT
   -getname : return the name of the NXT
   -versions : return the NXT firmware and protocol versions
   -deviceinfo : return all NXT device information
   -freemem : return the amount of free memory
   -lsstatus=<port> : return the low speed status for the specified port
   -boot : reset the NXT into SAMBA mode (usb only)
   -btreset : reset the NXT bluetooth to factory settings (usb only)
   -defrag : defragment the NXT filesystem
General:
   -help : display command line options
---



/COM オプションで接続ポートを指定する。

メモリーに入っているファイルのリストを表示するのは、 -listfiles アクション。= の
後は、バイト数(と思われる★)
また、この出方からいうと、プログラム名は 15 文字以内かな。(★)

---
C:\_docs\Lego\Bricx_NXC>nexttool /COM=usb -listfiles
Test01.rxe=748
Trybot02.rxe=1744
Bumper02.rxe=714
Bumper01.rxe=676
Sensor01.rxe=1796
HelloWorld01.rxe=542
Trybot01.rxe=1642
TouchSensor01.rxe=444
UltrasonicSenso.rxe=1664
TouchSensor02.rxe=710
Random02.rxe=1366
TrunRight05.rxe=528
Random01.rxe=710
Spiral01.rxe=508
TrunRight04.rxe=516
TrunRight03.rxe=524
TrunRight02.rxe=482
TurnRight01.rxe=468
ForwardAndBackw.rxe=6040
LineTracer01.rxe=11522
NVConfig.sys=1
RPGReader.sys=14346
Demo.rxe=9436
Try-Touch.rtm=3788
Try-Light.rtm=4456
Try-Sound.rtm=6864
Try-Ultrasonic.rtm=3756
Try-Motor.rtm=2630
Woops.rso=4699
faceopen.ric=316
faceclosed.ric=316
! Startup.rso=8161
! Click.rso=451
! Attention.rso=1755
---



バッテリーレベルなどの情報の表示。

---
C:\_docs\Lego\Bricx_NXC>nexttool /COM=usb -battery -versions -deviceinfo
7076
Protocol version = 1.124
Firmware version = 1.03
Brick name = NXT
Bluetooth Address = 00:12:AB:34:CD:56
Bluetooth signal strength = 0,0,0,0
Free memory = 34000
---


バッテリーレベルの単位は mV のようだ。(★)


以上

Lego Mindstoms NXT - NXC プログラミング (6) タスク

2008年01月22日 | LEGO
今まで作っていた NXT のプログラムはすべて main タスクだけで
した。関数をいくつか作りましたが、それらもタスクとしてはすべ
て main タスクの配下にあります。

今回は、main タスクとは別のタスクを作ってみましょう。

NXC では、タスクは最大で 255 個まで作れます。


■ 動きながら音を鳴らす

これは、「動く」タスクと、「音を鳴らす」タスクの 2 つを作り、
これらのタスクを main タスクから起動します。

これらの動きを、main タスクだけのシングルタスクで実現するの
は無理があります。
まあ、モーター自体はタスクがスリープしている間は動き続けるの
でいいとして、トーンを鳴らすスリープとタイミングを計るのは無
理があります。
これらは、別タスクとして動かすことで、スムーズに無理なく 2
つの動作を実現できるのです。

今回は、それぞれのタスクを無限ループするようにしているので、
止めるときは NTX の停止ボタンで止めます。


---
/**
 * MovingAndPlaying01.nxc
 * 「トライボット」使用
 * 1秒間前進し、その後1秒間停止、1秒間後退する。
 * その間に音を鳴らす。
 * @author maruno
 * @version 1.0, 2008-01-12
 * @since 1.0
 */
#include "NXCDefs.h"
#include "Music01.nxc"  // 音楽関係のマクロ  // (1)

/**
 * 音楽再生タスク
 */
task playing()  // (2)
{
    while (true) {
        PLAY_TONE(FR_C6,  NOTE_MINIM,    2, false, INTERVAL);
        PLAY_TONE(FR_C5,  NOTE_QUAVER,   2, false, INTERVAL);
        PLAY_TONE(FR_C4,  NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_C5,  NOTE_CROTCHET, 2, false, INTERVAL);

        PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL + NOTE_CROTCHET);

        PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL + NOTE_CROTCHET);
    }
}

/**
 * 移動タスク
 */
task moving()   // (3)
{
    while (true) {
        OnFwd(OUT_BC, 75);
        Wait(500);
        Off(OUT_BC);
        Wait(500);
        OnRev(OUT_BC, 75);
        Wait(500);
        Off(OUT_BC);
    }
}

/**
 * メインタスク
 */
task main()
{
    Precedes(playing, moving);  // (4)
}
---


□ #include "Music01.nxc" // 音楽関係のマクロ // (1)

前回の周波数等はマクロの定数としてヘッダーファイルに入れてお
きます。


□ task playing() // (2)

タスク playing の定義です。task キーワードを使います。関数と
同じように、使う前に定義しておく必要があります。


□ task moving() // (3)

タスク moving の定義です。


□ Precedes(playing, moving); // (4)

playing, moving タスクを起動します。


□ Precedes 関数
---
Precedes(task1, task2, ..., taskN) Function

指定されたタスクの実行をスケジュールします。

例:
Precedes(moving, drawing, playing);
---


---
#ifndef MUSIC01_H_
#define MUSIC01_H_
/**
 * Music01.ncx
 * 音楽関係のマクロ定義
 * @author maruno
 * @version 1.0, 2008-01-12
 * @since 1.0
 */

/*
 * 音符を演奏する時間(ミリ秒)
 */
#define NOTE_SEMIBREVE 1200                 // 全音符
#define NOTE_MINIM     (NOTE_SEMIBREVE / 2) // 二分音符
#define NOTE_CROTCHET  (NOTE_MINIM     / 2) // 四分音符
#define NOTE_QUAVER    (NOTE_CROTCHET  / 2) // 八分音符

/*
 * 周波数
 */
#define FR_B3  247
#define FR_B4  494
#define FR_B5  988
#define FR_B6  1976
#define FR_B7  3951
#define FR_B8  7902

#define FR_AS3 233
#define FR_AS4 466
#define FR_AS5 932
#define FR_AS6 1865
#define FR_AS7 3729
#define FR_AS8 7458

#define FR_A3  220
#define FR_A4  440
#define FR_A5  880
#define FR_A6  1760
#define FR_A7  3520
#define FR_A8  7040
#define FR_A9  14080

#define FR_GS4 415
#define FR_GS5 831
#define FR_GS6 1661
#define FR_GS7 3322
#define FR_GS8 6644
#define FR_GS9 13288

#define FR_G4  392
#define FR_G5  784
#define FR_G6  1568
#define FR_G7  3136
#define FR_G8  6272
#define FR_G9  12544

#define FR_FS4 370
#define FR_FS5 740
#define FR_FS6 1480
#define FR_FS7 2960
#define FR_FS8 5920
#define FR_FS9 11840

#define FR_F4  349
#define FR_F5  698
#define FR_F6  1397
#define FR_F7  2794
#define FR_F8  5588
#define FR_F9  11176

#define FR_E4  330
#define FR_E5  659
#define FR_E6  1319
#define FR_E7  2637
#define FR_E8  5274
#define FR_E9  10548

#define FR_DS4 311
#define FR_DS5 622
#define FR_DS6 1245
#define FR_DS7 2489
#define FR_DS8 4978
#define FR_DS9 9956

#define FR_D4  294
#define FR_D5  587
#define FR_D6  1175
#define FR_D7  2349
#define FR_D8  4699
#define FR_D9  9398

#define FR_CS4 277
#define FR_CS5 554
#define FR_CS6 1109
#define FR_CS7 2217
#define FR_CS8 4435
#define FR_CS9 8870

#define FR_C4  262
#define FR_C5  523
#define FR_C6  1047
#define FR_C7  2093
#define FR_C8  4186
#define FR_C9  8372

/*
 * 音符間の間隔(ミリ秒)
 */
#define INTERVAL 100

/**
 * トーンを鳴らすマクロ
 * @param frequency 周波数
 * @param duration  持続時間(ミリ秒)
 * @param volume    音量
 * @param bLoop     繰り返すか
 * @param interval  間隔時間(ミリ秒)
 */
#define PLAY_TONE(frequency, duration, volume, bLoop, interval) 
    do { 
        PlayToneEx((frequency), (duration), (volume), (bLoop)); 
        Wait((duration)+(interval)); 
    } while(false)

#endif
---




■ タッチセンサーで、演奏したり止めたりする

つぎに、タッチセンサーをスイッチ代わりにして、タッチするたび
に演奏したり止めたりするようにしてみましょう。

ただし、これだと、NBC の stopthread 文でエラーになる。なぜ?


---
# Error: Invalid opcode: stopthread
File "C:_docsLegoBricx_NXCMovingAndPlaying02.nxc" ; line
2482
# stopthread playing
---


---
/**
 * MovingAndPlaying02.nxc
 * 「トライボット」使用
 * 1秒間前進し、その後1秒間停止、1秒間後退する。
 * その間に演奏する。このとき、タッチセンサーが押されたら、演奏したり、止めたりする。
 * @author maruno
 * @version 1.0, 2008-01-12
 * @since 1.0
 */
#include "NXCDefs.h"
#include "Music01.nxc"  // 音楽関係のマクロ
#include "SensorFuncs02.nxc"  // センサー関係

bool isPlay;  // 演奏中かどうか  // (1)

/**
 * 音楽再生タスク
 */
task playing()
{
    while (true) {
        PLAY_TONE(FR_C6,  NOTE_MINIM,    2, false, INTERVAL);
        PLAY_TONE(FR_C5,  NOTE_QUAVER,   2, false, INTERVAL);
        PLAY_TONE(FR_C4,  NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_C5,  NOTE_CROTCHET, 2, false, INTERVAL);

        PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL + NOTE_CROTCHET);

        PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL + NOTE_CROTCHET);
    }
}

/**
 * 移動タスク
 */
task moving()
{
    while (true) {
        OnFwd(OUT_BC, 75);
        Wait(500);
        Off(OUT_BC);
        Wait(500);
        OnRev(OUT_BC, 75);
        Wait(500);
        Off(OUT_BC);
    }
}

/**
 * タッチセンサータスク
 */
task touching()   // (2)
{
    while (true) {
        if (SENSOR_1 == 1) {
            if (isPlay) {  //(3)
                StopTask(playing);  // (4)
                isPlay = false;
            } else {
                StartTask(playing);  // (5)
                isPlay = true;
            }
        }
    }
}

/**
 * メインタスク
 */
task main()
{
    // センサーの設定
    SetSensors(MASK_S1);

    // タスクの開始
    Precedes(moving, touching);
}
---



□ bool isPlay; // 演奏中かどうか // (1)

演奏中かどうかを表すフラグをグローバル変数として定義する。


□ task touching() // (2)

タッチセンサーのタスク。
演奏中のときにタッチされれば演奏を止め、演奏していないときに
タッチされると演奏を開始する。


□ if (isPlay) { //(3)

演奏中かどうかを判断。


□ StopTask(playing); // (4)
□ StartTask(playing); // (5)

playing タスクの終了、開始。


□ StartTask 関数
---
StartTask(task) Function

指定されたタスクを開始する。

例:
StartTask(sound); // start the sound task
---

□ StopTask 関数
---
StopTask(task) Function

指定されたタスクを停止する。

例:
StopTask(sound); // stop the sound task
---


■ しょうがないので、少し仕様を変えて、タッチセンサーが押さ
れたら、演奏を開始するようにした。

---
/**
 * MovingAndPlaying03.nxc
 * 「トライボット」使用
 * 1秒間前進し、その後1秒間停止、1秒間後退する。
 * その間に演奏する。このとき、タッチセンサーが押されたら、演奏を開始する。
 * @author maruno
 * @version 1.0, 2008-01-14
 * @since 1.0
 */
#include "NXCDefs.h"
#include "Music01.nxc"  // 音楽関係のマクロ
#include "SensorFuncs02.nxc"  // センサー関係

/**
 * 音楽再生タスク
 */
task playing()
{
    PLAY_TONE(FR_C6,  NOTE_MINIM,    2, false, INTERVAL);
    PLAY_TONE(FR_C5,  NOTE_QUAVER,   2, false, INTERVAL);
    PLAY_TONE(FR_C4,  NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_C5,  NOTE_CROTCHET, 2, false, INTERVAL);

    PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL + NOTE_CROTCHET);

    PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL + NOTE_CROTCHET);
}

/**
 * 移動タスク
 */
task moving()
{
    while (true) {
        OnFwd(OUT_BC, 75);
        Wait(500);
        Off(OUT_BC);
        Wait(500);
        OnRev(OUT_BC, 75);
        Wait(500);
        Off(OUT_BC);
    }
}

/**
 * タッチセンサータスク
 * タッチされたら、演奏タスクを開始
 */
task touching()
{
    while (true) {
        if (SENSOR_1 == 1) {
            StartTask(playing);
        }
    }
}

/**
 * メインタスク
 */
task main()
{
    // センサーの設定
    SetSensors(MASK_S1);

    // タスクの開始
    Precedes(moving, touching);
}
---



以上


Lego Mindstomes NXT - NXC プログラミング (5) ミュージック

2008年01月15日 | Weblog
■ rso ファイル

2 つのサウンドファイル(.rso)を鳴らします。
最初は、NTX の起動音を 2 秒間。つぎに、"Good Job" を 2 秒間
繰り返します(実際には、2回と少し)。

---
/**
 * PlayingFile01.nxc
 * 「トライボット」使用
 * rso ファイルを鳴らす。
 * @author maruno
 * @version 1.0, 2008-01-12
 * @since 1.0
 */
#include "NXCDefs.h"

task main()
{
    PlayFileEx("! Startup.rso", 2, false); // (1)
    Wait(2000);

    PlayFileEx("Good Job.rso", 2, true);   // (2)
    Wait(2000);
}
---


□ PlayFileEx("! Startup.rso", 2, false); // (1)

指定された rso ファイルを鳴らすのは、PlayFileEx 関数を使いま
す。
このとき、つぎに Wait 関数を指定しないと、音は鳴りません。


□ PlayFileEx 関数
---
PlayFileEx(filename, volume, bLoop) Function

指定されたサウンドファイル(.rso)またはメロディファイル(.rmd)
を鳴らす。このとき、音量は、0 (無音) から 4 (最大)まで指定で
きます。また、繰り返すかどうかを bool 型の bLoop で指定しま
す。true のときは繰り返します。

例:
PlayFileEx("startup.rso", 3, true);
---

□ PlayFileEx("Good Job.rso", 2, true); // (2)

"Good Job" を繰り返します。

ただし、これはもしかしたら鳴らないかもしれません。これは、指
定されたサウンドファイル "Good Job.rso" が NXT の中に入って
いないからです。

ちなみに、ファームウェア v1.03 をダウンロードした直後だと、
つぎの rso ファイルがあります。
! Attention.rso
! Click.rso
! Startup.rso
Woops.rso


□ ファイルのダウンロード

[Tools] メニュー から、 [NeXT Explorer] 項目をクリックして、
NeXT Explorer を開いてください。左側のペインが、NXT のファイ
ルのリスト。右側のペインがローカルのファイルシステムです。

NXT のファイルリストの中に、指定された "Good Job.rso" がなけ
れば、ローカルからそのファイルをドラッグしてダウンロードしま
す。

サウンドファイルは、Lego ソフトウェアをインストールしたディ
レクトリのサブディレクトリにいろいろと入っています。標準の場
所にインストールしたのなら、つぎのディレクトリになります。
C:\Program Files\LEGO Software\LEGO MINDSTORMS NXT\engine\So
unds

NeXT Explorer の右側のペインを操作して、目的のファイルを左側
のペインにドラッグすれば、ファイルのダウンロードは終わりです。

ダウンロードできたら、もう 1 度実行しましょう。今度は、"Good
Job" を繰り返します。



■ 音を鳴らす

トーンを使って、音を鳴らすことができます。これには、周波数、
持続時間、音量を指定する PlayToneEx 関数を使います。また、こ
の関数も PlayFileEx 関数と同じように Wait 関数を使わないと、
実際には音は鳴りません。

---
/**
 * PlayingMusic01.nxc
 * 「トライボット」使用
 * 音を鳴らす。
 * @author maruno
 * @version 1.0, 2008-01-12
 * @since 1.0
 */
#include "NXCDefs.h"

task main()
{
    // 音階6 を B から C まで
    PlayToneEx(1976, 400, 2, false); Wait(500);
    PlayToneEx(1865, 400, 2, false); Wait(500);
    PlayToneEx(1760, 400, 2, false); Wait(500);
    PlayToneEx(1661, 400, 2, false); Wait(500);
    PlayToneEx(1568, 400, 2, false); Wait(500);
    PlayToneEx(1480, 400, 2, false); Wait(500);
    PlayToneEx(1397, 400, 2, false); Wait(500);
    PlayToneEx(1319, 400, 2, false); Wait(500);
    PlayToneEx(1245, 400, 2, false); Wait(500);
    PlayToneEx(1175, 400, 2, false); Wait(500);
    PlayToneEx(1109, 400, 2, false); Wait(500);
    PlayToneEx(1047, 400, 2, false); Wait(500);
}
---


□ PlayToneEx(1976, 400, 2, false); Wait(500); // (1)

周波数 1976、持続時間 400 ミリ秒、音量 2 でトーンを鳴らしま
す。
ただし、この関数も、すぐにつぎの命令を実行してしまうので、
Wait 関数を使ってスリープ状態を作ってあげなければなりません。
多少、持続時間に余裕を持たせてあげた方がいいでしょう。


□ PlayToneEx 関数
---
PlayToneEx(frequency, duration, volume, bLoop) Function

トーンを、周波数、持続時間、音量を指定して鳴らす。周波数は
Hz、持続時間はミリ秒、音量は 0 (なし) から 4 (最大)を指定す
る。

例:
PlayToneEx(440, 500, 2, false);
---


□ 周波数はつぎの表によります。

---
Sound  3   4   5   6    7    8    9
B     247 494 988 1976 3951 7902
A#    233 466 932 1865 3729 7458
A     220 440 880 1760 3520 7040 14080
G#        415 831 1661 3322 6644 13288
G         392 784 1568 3136 6272 12544
F#        370 740 1480 2960 5920 11840
F         349 698 1397 2794 5588 11176
E         330 659 1319 2637 5274 10548
D#        311 622 1245 2489 4978 9956
D         294 587 1175 2349 4699 9398
C#        277 554 1109 2217 4435 8870
C         262 523 1047 2093 4186 8372
---




■ トーンを鳴らすマクロ

PlayingMusic01 と同じであるが、PlayToneEx 関数と Wait 関数を
使ったマクロ PLAY_TONE を作る。引数は PlayToneEx に加えて、
間隔時間として interval を指定する。duration + interval で、
Wait 関数の時間とする。
ついでに、音階 A を 3 から 9 まで鳴らしてみる。

---
/**
 * PlayingMusic02.nxc
 * 「トライボット」使用
 * 音を鳴らす。
 * @author maruno
 * @version 1.0, 2008-01-12
 * @since 1.0
 */
#include "NXCDefs.h"

#define PLAY_TONE(frequency, duration, volume, bLoop, interval) \
    do { \
        PlayToneEx((frequency), (duration), (volume), (bLoop)); \
        Wait((duration)+(interval)); \
    } while(false)

task main()
{
    // 音階6 を B から C まで
    PLAY_TONE(1976, 400, 2, false, 100);
    PLAY_TONE(1865, 400, 2, false, 100);
    PLAY_TONE(1760, 400, 2, false, 100);
    PLAY_TONE(1661, 400, 2, false, 100);
    PLAY_TONE(1568, 400, 2, false, 100);
    PLAY_TONE(1480, 400, 2, false, 100);
    PLAY_TONE(1397, 400, 2, false, 100);
    PLAY_TONE(1319, 400, 2, false, 100);
    PLAY_TONE(1245, 400, 2, false, 100);
    PLAY_TONE(1175, 400, 2, false, 100);
    PLAY_TONE(1109, 400, 2, false, 100);
    PLAY_TONE(1047, 400, 2, false, 100);

    Wait(500);
    
    // 音階A を 3 から 9 まで
    PLAY_TONE(220, 400, 2, false, 100);
    PLAY_TONE(440, 400, 2, false, 100);
    PLAY_TONE(880, 400, 2, false, 100);
    PLAY_TONE(1760, 400, 2, false, 100);
    PLAY_TONE(3520, 400, 2, false, 100);
    PLAY_TONE(7040, 400, 2, false, 100);
    PLAY_TONE(14080, 400, 2, false, 100);
}
---



■ 実際の音楽っぽく

つぎは、ある曲を実装したものです。
楽譜を基に実装したのだが、まったく違うものらしい。
わたしの楽譜の読み方が違うのか、周波数のトーンが間違っている
のか?
(中学の音楽は 1 がついたこともあったくらいなので...)

---
/**
 * PlayingMusic03.nxc
 * 「トライボット」使用
 * 音を鳴らす。
 * @author maruno
 * @version 1.0, 2008-01-12
 * @since 1.0
 */
#include "NXCDefs.h"

#define FR_B3  247
#define FR_B4  494
#define FR_B5  988
#define FR_B6  1976
#define FR_B7  3951
#define FR_B8  7902

#define FR_AS3 233
#define FR_AS4 466
#define FR_AS5 932
#define FR_AS6 1865
#define FR_AS7 3729
#define FR_AS8 7458

#define FR_A3  220
#define FR_A4  440
#define FR_A5  880
#define FR_A6  1760
#define FR_A7  3520
#define FR_A8  7040
#define FR_A9  14080

#define FR_GS4 415
#define FR_GS5 831
#define FR_GS6 1661
#define FR_GS7 3322
#define FR_GS8 6644
#define FR_GS9 13288

#define FR_G4  392
#define FR_G5  784
#define FR_G6  1568
#define FR_G7  3136
#define FR_G8  6272
#define FR_G9  12544

#define FR_FS4 370
#define FR_FS5 740
#define FR_FS6 1480
#define FR_FS7 2960
#define FR_FS8 5920
#define FR_FS9 11840

#define FR_F4  349
#define FR_F5  698
#define FR_F6  1397
#define FR_F7  2794
#define FR_F8  5588
#define FR_F9  11176

#define FR_E4  330
#define FR_E5  659
#define FR_E6  1319
#define FR_E7  2637
#define FR_E8  5274
#define FR_E9  10548

#define FR_DS4 311
#define FR_DS5 622
#define FR_DS6 1245
#define FR_DS7 2489
#define FR_DS8 4978
#define FR_DS9 9956

#define FR_D4  294
#define FR_D5  587
#define FR_D6  1175
#define FR_D7  2349
#define FR_D8  4699
#define FR_D9  9398

#define FR_CS4 277
#define FR_CS5 554
#define FR_CS6 1109
#define FR_CS7 2217
#define FR_CS8 4435
#define FR_CS9 8870

#define FR_C4  262
#define FR_C5  523
#define FR_C6  1047
#define FR_C7  2093
#define FR_C8  4186
#define FR_C9  8372

#define NOTE_SEMIBREVE 1200                 // 全音符
#define NOTE_MINIM     (NOTE_SEMIBREVE / 2) // 二分音符
#define NOTE_CROTCHET  (NOTE_MINIM     / 2) // 四分音符
#define NOTE_QUAVER    (NOTE_CROTCHET  / 2) // 八分音符

#define PLAY_TONE(frequency, duration, volume, bLoop, interval) \
    do { \
        PlayToneEx((frequency), (duration), (volume), (bLoop)); \
        Wait((duration)+(interval)); \
    } while(false)

#define INTERVAL 80

task main()
{
    PLAY_TONE(FR_C6,  NOTE_MINIM,    2, false, INTERVAL);
    PLAY_TONE(FR_C5,  NOTE_QUAVER,   2, false, INTERVAL);
    PLAY_TONE(FR_C4,  NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_C5,  NOTE_CROTCHET, 2, false, INTERVAL);

    PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL + NOTE_CROTCHET);

    PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL + NOTE_CROTCHET);

    PLAY_TONE(FR_G6,  NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_G8,  NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_G8,  NOTE_CROTCHET, 2, false, INTERVAL + NOTE_CROTCHET);

    PLAY_TONE(FR_C6,  NOTE_MINIM,    2, false, INTERVAL);
    PLAY_TONE(FR_C5,  NOTE_QUAVER,   2, false, INTERVAL);
    PLAY_TONE(FR_C4,  NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_C5,  NOTE_CROTCHET, 2, false, INTERVAL);

    PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL + NOTE_CROTCHET);

    PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_DS6, NOTE_MINIM,    2, false, INTERVAL);
    PLAY_TONE(FR_DS5, NOTE_QUAVER,   2, false, INTERVAL);

    PLAY_TONE(FR_C4, NOTE_SEMIBREVE, 2, false, INTERVAL);
}
---


周波数をあらわすマクロなどもまとめて、ヘッダーファイルにしておくといいでしょう。

以上


Lego Mindstoms NXT - NXC プログラミング (4) 関数

2008年01月13日 | LEGO
■ 関数

関数は C 言語とほぼ同じです。ただ、inline キーワードを使って、関数をインライン展開できます。ただし、インライン関数をネストさせることはできません。
また、safecall キーワードを使って、この関数の実行を排他的に行うことができます(★)。

---
[safecall] [inline] 戻り値型 名前(引数リスト)
{
// 関数のコード
}
---

ただし、関数のプロトタイプ宣言はできないので、その関数を使う前に関数を定義しておかなければなりません。ちょっと残念な仕様です。
ただし、関数が使えることで、main タスクの見通しはかなりよくなりました。


■ Trybod01 の各機能を関数にする

Trybod01 では、タッチするまで前進する、暗くなるまで前進する、などがそれぞれコーディングされています。これらを、関数を使ってコーディングしてみます。

---
/**
 * Trybot02.nxc
 * 「トライボット」使用
 * 前進し、そこでタッチしたものをつかみ、後退、回転し、
 * 黒線があるところまで前進し、黒線のところでつかんだものを離す。
 * @author maruno
 * @version 1.0, 2008-01-08
 * @since 1.0
 */
#include "NXCDefs.h"

#define PUT_THRESHOLD 51 // 物を置くしきい値 (cm)
#define SOUND_THRESHOLD 50 // 音のしきい値 (dB)
#define LIGHT_THRESHOLD 40 // 光のしきい値

/**
 * タッチされるまで前進する。
 */
inline void ForwardUntilTouch()
{
    OnFwd(OUT_BC, 75);
    until (SENSOR_1 == 1);
    Off(OUT_BC);
}

/**
 * 暗くなるまで前進する。
 * @param threshold 暗くなるしきい値
 */
inline void ForwardUntilDark(long threshold)
{
    OnFwd(OUT_BC, 75);
    until (SENSOR_3 <threshold); threshold);
}

/**
 * main タスク。
 */
task main()
{
    // センサーの設定
    SetSensorTouch(S1);     // タッチセンサーの設定
    SetSensorSound(S2);     // 音センサーの設定
    SetSensorLight(S3);     // 光センサーの設定
    SetSensorLowspeed(S4);  // 超音波センサーの設定

    // 物が置かれるまで停止
    StopUntilPut(PUT_THRESHOLD);

    // タッチするまで前進
    ForwardUntilTouch();

    // 音がするまで停止
    StopUntilAnySound(SOUND_THRESHOLD);

    // アームを閉じる
    CloseArm(500);

    // 後退
    ReverseMilliSeconds(500);

    // 回転
    RotetoLeftMilliSeconds(500);

    // 黒線のところまで前進して停止
    ForwardUntilDark(LIGHT_THRESHOLD);

    // アームを開く
    OpenArm(500);
}
---



■ 関数部分を別ソースに書く

Trybot02 の関数部分を別ソースとして書いてみます。
そうすることで、自分で作った関数を別プログラムでも使いまわすことができます。

関数は、Trybot02 で作ったモーター関係の関数と、センサー関係の関数として、別ソース "MotorFuncs01.nxc"、"SensorFuncs01.nxc" として作ります。これらを #include を使ってインクルードします。

ただ、これらの関数のソースの編集で、BricxCC を使う場合、エディタでキーワードを強調されるので、拡張子は "nxc" にしておいた方が便利でしょう。

---
/**
 * Trybot03.nxc
 * 「トライボット」使用
 * 前進し、そこでタッチしたものをつかみ、後退、回転し、
 * 黒線があるところまで前進し、黒線のところでつかんだものを離す。
 * @author maruno
 * @version 1.0, 2008-01-11
 * @since 1.0
 */
#include "NXCDefs.h"
#include "MotorFuncs01.nxc"   // モーター関係 (1)
#include "SensorFuncs01.nxc"  // センサー関係 (2)

#define PUT_THRESHOLD 51 // 物を置くしきい値 (cm)
#define SOUND_THRESHOLD 50 // 音のしきい値 (dB)
#define LIGHT_THRESHOLD 40 // 光のしきい値

/**
 * main タスク。
 */
task main()
{
    // センサーの設定
    SetSensors(MASK_ALL_SENSORS); // (3)

    // 物が置かれるまで停止
    StopUntilPut(PUT_THRESHOLD);

    // タッチするまで前進
    ForwardUntilTouch();

    // 音がするまで停止
    StopUntilAnySound(SOUND_THRESHOLD);

    // アームを閉じる
    CloseArm(500);

    // 後退
    ReverseMilliSeconds(500);
    
    // 回転
    RotetoLeftMilliSeconds(500);
    
    // 黒線のところまで前進して停止
    ForwardUntilDark(LIGHT_THRESHOLD);

    // アームを開く
    OpenArm(500);
}
---


□ #include "MotorFunctions01.nxc" // モーター関係 (1)
□ #include "SensorFunctions01.nxc" // センサー関係 (2)

それぞれの関数のソースをインクルードする。
こうすることで、ソースを分割して記述できるのでモジュール化することがしやすくなる。
ただし、MotorFunctions01.nxc などのソースのコンパイルエラーが、main タスクでコンパイルエラーとして出る場合もあるようだ。(★)


□ SetSensors(MASK_ALL_SENSORS); // (3)

4 つのセンサーを使う場合のマクロ定数。定義は、 "SensorFuncs01.nxc" で行っている。


---
#ifndef MOTOR_FUNCS01_H_  // (4)
#define MOTOR_FUNCS01_H_  // (4)
/**
 * MotorFuncs01.nxc
 * モーターを動かす関数
 * @author maruno
 * @version 1.0, 2008-01-11
 * @since 1.0
 */

/**
 * タッチされるまで前進する。
 */
inline void ForwardUntilTouch()
{
    OnFwd(OUT_BC, 75);
    until (SENSOR_1 == 1);
    Off(OUT_BC);
}

/**
 * 暗くなるまで前進する。
 * @param threshold 暗くなるしきい値
 */
inline void ForwardUntilDark(long threshold)
{
    OnFwd(OUT_BC, 75);
    until (SENSOR_3 <threshold); threshold);
}

#endif // (4)
---


□ #ifndef MOTOR_FUNCS01_H_ // (4)
□ #define MOTOR_FUNCS01_H_ // (4)
□ #endif // (4)

ヘッダーの2重インクルードの防止。


---
#ifndef SENSOR_FUNCS01_H_
#define SENSOR_FUNCS01_H_
/**
 * SensorFunctions01.nxc
 * センサー関連の関数
 * @author maruno
 * @version 1.0, 2008-01-11
 * @since 1.0
 */

#define MASK_S1 (0x01 << S1) // (5)
#define MASK_S2 (0x01 << S2) // (5)
#define MASK_S3 (0x01 << S3) // (5)
#define MASK_S4 (0x01 << S4) // (5)
#define MASK_ALL_SENSORS (MASK_S1+MASK_S2+MASK_S3+MASK_S4) // 4つのセンサーすべて // (6) /** * センサーを設定する。 * @param mask 使うセンサーのマスク * 76543210 * -------1 タッチセンサー * ------1- 音センサー * -----1-- 光センサー * ----1--- 超音波センサー */ inline void SetSensors(int mask) { if (mask & MASK_S1) SetSensorTouch(S1); // タッチセンサーの設定 if (mask & MASK_S2) SetSensorSound(S2); // 音センサーの設定 if (mask & MASK_S3) SetSensorLight(S3); // 光センサーの設定 if (mask & MASK_S4) SetSensorLowspeed(S4); // 超音波センサーの設定 } #endif ---


□ #define MASK_S1 (0x01 << S1) // (5)
□ #define MASK_S2 (0x01 << S2) // (5)
□ #define MASK_S3 (0x01 << S3) // (5)
□ #define MASK_S4 (0x01 << S4) // (5)

それぞれのセンサーに対応したビット位置を ON にするようなマスクを作る。


□ #define MASK_ALL_SENSORS (MASK_S1+MASK_S2+MASK_S3+MASK_S4) // 4つのセンサーすべて // (6)

4 つのセンサーに対応するビットをすべて ON にしたマスクをつくる。


■ センサーの値を表示する関数

つぎのように、センサーの値を表示する関数を作り、使います。

---
/**
 * Sensor02.nxc
 * 「トライボット」使用
 * センサーの値を読み取り、表示する。
 * @author maruno
 * @version 1.0, 2008-01-08
 * @since 1.0
 */
#include "NXCDefs.h"

/**
 * センサー名を格納した文字列定数の配列
 */
const string SENSOR_NAME[] = {
        "Touch",
        "Sound",
        "Light",
        "US",
};  // (1)

/**
 * センサーポートを指定し、それに対するセンサー名を取得する。
 * @param sensor センサーのポート番号
 */
string getSensorName(int sensor)
{
    string name;
    switch (sensor) {
    case S1:
        name = "Touch";
        break;
    case S2:
        name = "Sound";
        break;
    case S3:
        name = "Light";
        break;
    case S4:
        name = "US";
        break;
    default:
        name = "undef";
        break;
    }
    return name;
}

/**
 * ディスプレイの指定された行に指定されたセンサーの値を表示する。
 * @param line 表示する行
 * @param sensor センサーのポート番号
 */
void OutSensorValue(int line, int sensor)  // (2)
{

    int num = ((sensor != S4) ? Sensor(sensor) : SensorUS(S4));  // (3)
    string value = NumToStr(num);
    string name = getSensorName(sensor); // SENSOR_NAME[sensor];  // (4)
    string msg = StrCat(name, " = ", value);
    TextOut(0, line, msg);
}

/**
 * main タスク。
 */
task main()
{
    // センサーの設定
    SetSensorTouch(S1);     // タッチセンサーの設定
    SetSensorSound(S2);     // 音センサーの設定
    SetSensorLight(S3);     // 光センサーの設定
    SetSensorLowspeed(S4);  // 超音波センサーの設定

    while (true) {
        // タッチセンサーの値の表示
        OutSensorValue(LCD_LINE1, S1);

        // 音センサーの値の表示
        OutSensorValue(LCD_LINE2, S2);

        // 光センサーの値の表示
        OutSensorValue(LCD_LINE3, S3);

        // 超音波センサーの値の表示
        OutSensorValue(LCD_LINE4, S4);

    }
}
---



□ const string SENSOR_NAME[] = {"Touch", ..."US", }; // (1)

文字列定数の配列を定義します。


□ void OutSensorValue(int line, int sensor) // (2)

関数の定義。この関数は OutSensorValue 関数から呼ばれるので、inline キーワードはつけない。NBC では、インライン関数のネストはできないことに注意。


□ int num = ((sensor != S4) ? Sensor(sensor) : SensorUS(S4)); // (3)

超音波センサーと他のセンサーとは取得方法に違いがあることに注意。


□ string name = getSensorName(sensor); // SENSOR_NAME[sensor]; // (4)

配列を使って取得すると、なぜか実行時エラーになる。メッセージは「file error!」。なぜ?★



■ サブルーチン

C 言語には関数しかありませんが、NXC には、「サブルーチン」という機能があります。NXC では、inline でない関数で、戻り値がないものです。もっとも、これは NXC ではなく、これの前提になっている NQC の文法を引き継いでのことと考えられますので、とくに使わなくてもいいでしょう。(★)


以上

Lego Mindstoms NXT - NXC プログラミング (3) センサー

2008年01月10日 | LEGO
それでは、センサーを使ってみましょう。

NXT には、つぎのセンサーがあります。

・タッチセンサー (ポート 1)
・音センサー (ポート 2)
・光センサー + ライト (ポート 3)
・超音波センサー (ポート 4)
・回転センサー (モーターA、B、C)


■ タッチセンサー

まずは、タッチセンサーを確認するプログラムを作ります。

---
/**
 * TouchSensor01.nxc
 * 「トライボット」使用
 * タッチセンサーを確認する。
 * @author maruno
 * @version 1.0, 2008-01-08
 * @since 1.0
 */
#include "NXCDefs.h"

task main()
{
    SetSensorTouch(S1);  // (1)
    OnFwd(OUT_BC, 75);
    until (SENSOR_1 == 1);  // (2)
    Off(OUT_BC);
}

---

□ SetSensorTouch(S1); // (1)

ポート 1 をタッチセンサーとして設定します。
SENSOR_TOUCH とすることで、タッチセンサーで、その結果はブール値 (0 または 1) で取得できるようになります。


□ SetSensorTouch 関数
---
SetSensorTouch(port) Function

指定されたポート port をタッチセンサーとして構成します。

例:
SetSensorTouch(S1);
---

センサーのポートは、0, 1, 2, 3 の4つあります。これらは、次のように定義されています。Sn または IN_n のどちらを使っても同じです。(★)

IN_1, IN_2, IN_3, IN_4
S1, S2, S3, S4

センサーから取得された値は、つぎの変数に入ります。

SENSOR_1, SENSOR_2, SENSOR_3, SENSOR_4


□ until (SENSOR_1 == 1); // (2)

ポート S1 のセンサーから取得した値は SENSOR_1 に入るので、この値が 1 のときにタッチされたことが検出されます。したがって、ここでは until ループで、1 になるまでループさせます。当然、この間はモーターが動き続けることになります。


■ トライボットの「バンパー」のプログラム

これは、トライボットの 2 番目の工程で作った「バンパー (タッチセンサー)」のプログラムです。ただし、後退する時間は回転数ではなく、0.5 秒という時間で行っています。

---
/**
 * Bumper01.nxc
 * 「トライボット」使用
 * 前進し、そこでタッチしたものをつかみ、後退する。
 * @author maruno
 * @version 1.0, 2008-01-08
 * @since 1.0
 */
#include "NXCDefs.h"

task main()
{
    SetSensorTouch(S1);  // タッチセンサーの設定

    // タッチするまで前進
    OnFwd(OUT_BC, 75);
    until (SENSOR_1 == 1);
    Off(OUT_BC);

    // アームを閉じる
    OnRev(OUT_A, 100);
    Wait(500);
    Off(OUT_A);

    // 0.5秒後退
    OnRev(OUT_BC, 75);
    Wait(500);
    Off(OUT_BC);

}

---



■ 前進した分だけ後退する

今度は、前進した分だけ後退するようにします。
これには、前進した分の時間を計測して、これを後退するときの待ち時間に設定すればいいです。前進した分の時間は、
停止した時刻 - 前進を始めた時刻
で取得できます。この、時刻を取得する関数が、CurrentTick 関数です。

---
/**
 * Bumper02.nxc
 * 「トライボット」使用
 * 前進し、そこでタッチしたものをつかみ、後退する。
 * @author maruno
 * @version 1.0, 2008-01-08
 * @since 1.0
 */
#include "NXCDefs.h"

task main()
{
    SetSensorTouch(S1);  // タッチセンサーの設定

    // タッチするまで前進
    unsigned long start_time = CurrentTick();  // (1)
    OnFwd(OUT_BC, 75);
    until (SENSOR_1 == 1);
    Off(OUT_BC);
    unsigned long forwarding_time = 
                           CurrentTick() - start_time;

    // アームを閉じる
    OnRev(OUT_A, 75);
    Wait(500);
    Off(OUT_A);

    // 前進した時間分後退
    OnRev(OUT_BC, 75);
    Wait(forwarding_time);
    Off(OUT_BC);

}

---

□ unsigned long start_time = CurrentTick(); // (1)

モーターを動かし始めた時刻を取得します。


□ CurrentTick 関数
---
CurrentTick() Value

現在時刻( tick とよぶ)をミリ秒単位の unsigned 32-bit 値として取得します。

例:
x = CurrentTick();
---



■ 各センサーの値を読み取る

---
/**
 * Sensor01.nxc
 * 「トライボット」使用
 * センサーの値を読み取り、表示する。
 * @author maruno
 * @version 1.0, 2008-01-08
 * @since 1.0
 */
#include "NXCDefs.h"

task main()
{
//    SetSensor(S1, SENSOR_TOUCH);  // タッチセンサーの設定 (1)
    SetSensorTouch(S1);
    SetSensorSound(S2);             // 音センサーの設定 (2)
//    SetSensor(S3, SENSOR_LIGHT);  // 光センサーの設定 (3)
    SetSensorLight(S3);
    SetSensorLowspeed(S4);      // 超音波センサーの設定 (4)

    while (true) {
        string num;
        string msg;

        // タッチセンサーの値の表示
        num = NumToStr(SENSOR_1);
        msg = StrCat("Touch    = ", num);
        TextOut(0, LCD_LINE1, msg);

        // 音センサーの値の表示
        num = NumToStr(SENSOR_2);
        msg = StrCat("Sound    = ", num);
        TextOut(0, LCD_LINE2, msg);

        // 光センサーの値の表示
        num = NumToStr(SENSOR_3);
        msg = StrCat("Light    = ", num);
        TextOut(0, LCD_LINE3, msg);

        // 超音波センサーの値の表示
        num = NumToStr(SensorUS(S4));  // (5)
        msg = StrCat("US       = ", num);
        TextOut(0, LCD_LINE4, msg);
        
        // モーター回転速度センサーの値の表示
        num = NumToStr(MotorBlockTachoCount(OUT_A));  // (6)
        msg = StrCat("Tacho    = ", num);
        TextOut(0, LCD_LINE5, msg);

        // モーター回転センサーの値の表示
        num = NumToStr(MotorRotationCount(OUT_A));  // (7)
        msg = StrCat("Rotation = ", num);
        TextOut(0, LCD_LINE6, msg);

    }
}

---

□ SetSensorTouch(S1); // タッチセンサーの設定 (1)

タッチセンサーをポート 1 に設定します。
センサーから取得した値は、SENSOR_1 変数に入ります。


□ SetSensorSound(S2); // 音センサーの設定 (2)

音センサーをポート 2 に設定します。
センサーから取得した値は、SENSOR_2 変数 (0 ~ 100) に入ります。


□ SetSensorSound 関数
---
SetSensorSound(port) Function

指定されたポート port を音センサー (dB 単位)として構成します。

例:
SetSensorSound(S1);
---


□ SetSensorLight(S3); // 光センサーの設定 (3)

光センサーをポート 3 に設定します。
センサーから取得した値は、SENSOR_3 変数に入ります。


□ SetSensorLight 関数
---
SetSensorLight(port) Function

指定されたポート port を光センサーとして構成します。

例:
SetSensorLight(S1);
---


□ SetSensorLowspeed(S4); // 超音波センサーの設定 (4)

超音波センサーをポート 4 に設定します。
センサーから取得した値は、SensorUS 関数を使って取得します。


□ SetSensorLowspeed 関数
---
SetSensorLowspeed(port) Function

指定されたポート port を超音波(I2C デジタル、9V パワー)センサーとして構成します。

例:
SetSensorLowspeed(S1);
---


□ num = NumToStr(SensorUS(S4)); // (5)

超音波センサーの値を取得し、文字列化します。
超音波センサーの値は、直接 SENSOR_n 変数を使って取得するのではなく、SensorUS 関数を使って取得する必要があります。


□ SensorUS 関数
---
SensorUS(n) Value

指定されたポート n に設定されている超音波センサー(I2C デジタル)の値を取得します。

例:
x = SensorUS(S4); // read sensor 4
---


□ num = NumToStr(MotorBlockTachoCount(OUT_A)); // (6)

モーター A の回転速度を取得します。このプログラムでは、自分でアームを開いたり、閉じたりして表示値を確認してみてください。前進が正数、後退が負数になるので、アームを開いているときは正数、閉じているときは負数になっているはずです。



□ MotorBlockTachoCount 関数
---
MotorBlockTachoCount(output) Value

指定されたモーターのブロックに関連した位置カウンターを取得します。
tacho なので、回転速度と思われます。(★)

例:
x = MotorBlockTachoCount(OUT_A);
---


□ num = NumToStr(MotorRotationCount(OUT_A)); // (7)

モーター A の回転角度を取得します。回転速度の取得と同じように、自分でアームをを開いたり、閉じたりして表示値を確認してみてください。前進が正数、後退が負数になるので、アームを開いているときは正数、閉じているときは負数になっているはずです。

ただ、ディスプレイで表示を見ると、MotorBlockTachoCount の値も、MotorRotationCount の値も同じになっています。これらは、別に回転速度と角度ではなく、どの程度動いたか? というものかもしれません。これらについて調べるのは別の機会にしたいと思います。★


□ MotorRotationCount 関数
---
MotorRotationCount(output) Value

指定されたモーターのプログラムに関連した位置カウンターを取得します。
rotation なので、回転角度と思われます。(★)

例:
x = MotorRotationCount(OUT_A);
---


■ トライポッドの動きを NXC プログラムで再現する

---
/**
 * Trybot01.nxc
 * 「トライボット」使用
 * 前進し、そこでタッチしたものをつかみ、後退、回転し、
 * 黒線があるところまで前進し、黒線のところでつかんだものを離す。
 * @author maruno
 * @version 1.0, 2008-01-08
 * @since 1.0
 */
#include "NXCDefs.h"

#define PUT_THRESHOLD 51 // 物を置くしきい値
#define SOUND_THRESHOLD 50 // 音のしきい値
#define LIGHT_THRESHOLD 40 // 光のしきい値
#define TOUCHED 1 // タッチしたときの値

task main()
{
    // センサーの設定
    SetSensorTouch(S1);     // タッチセンサーの設定
    SetSensorSound(S2);     // 音センサーの設定
    SetSensorLight(S3);     // 光センサーの設定
    SetSensorLowspeed(S4);  // 超音波センサーの設定

    // 物が置かれるまで停止
    until (SensorUS(S4) <PUT_THRESHOLD); SOUND_THRESHOLD);

    // アームを閉じる
    OnRev(OUT_A, 75);
    Wait(500);
    Off(OUT_A);

    // 後退
    OnRev(OUT_BC, 75);
    Wait(500);
    Off(OUT_BC);

    // 回転
    OnRev(OUT_C, 75);
    OnFwd(OUT_B, 75);
    Wait(500);
    Off(OUT_BC);

    // 黒線のところまで前進して停止
    OnFwd(OUT_BC, 75);
    until (SENSOR_3 <LIGHT_THRESHOLD);
---

以上


Lego Mindstoms NXT - NXC プログラミング (2) 制御文、スクリーン表示

2008年01月08日 | LEGO
実際に、いくつかプログラムを作って、NXC の制御文や変数の使い
方をまとめてみる。
また、スクリーンへの文字列の表示も行う。

なお、前回と同じように★は、推測で記している箇所なので、注意
されたい。ご存知の方は、よろしければご教授ください。

「Lego Mindstomes NXT - NXC プログラミング (1)」に記したが、
制御文のところだけ再掲する。


■ 制御文

□ if 文

if (条件式) 条件式の結果が true のとき実行するボディ
if (条件式) 条件式の結果が true のとき実行するボディ else そ
うでないときに実行するボディ


□ switch 文

switch (式) ボディ

case 定数式 :
default :


□ while 文

while (条件式) ボディ


□ do while 文

do ボディ while (条件式)


□ for 文

for (初期値化式 ; 条件式 ; 更新式) ボディ


□ repeat 文

repeat (式) ボディ

C 言語にはない制御文です。式の結果の回数分だけ繰り返します。
なお、式の結果が 0 以下の場合、ボディを実行することはありま
せん。


---
repeat (10) {
// 繰り返すコード
}
---


□ until マクロ

つぎのように定義されています。センサーを使うときによく使いま
す。

#define until(c) while(!(c))


したがって、while キーワードの代わりに、

until (条件式) ボディ
do ボディ until (条件式)

と書くことができます。このとき、条件式の意味は、「~まで」と
いうことになるので、「条件式が true になったらループ終了」と
いうことです。


■ くり返し制御文のサンプル

それでは、TrunRight01 プログラムをくり返しの制御文を使ってそ
れぞれ作ってみましょう。繰り返す回数は 3 回です。

----------- -----------
プログラム 使用制御文
----------- -----------
TrunRight02 repeat
TrunRight03 for
TrunRight04 while
TrunRight05 do - until
----------- -----------

---
/**
 * TrunRight02.nxc
 * 「トライボット」使用
 * 500ミリ秒間前進し、その後右に180度曲がる。これを3回繰り返
す。
 * @author maruno
 * @version 1.0, 2008-01-05
 * @since 1.0
 */
#include "NXCDefs.h"

#define MOVE_TIME 500
#define TURN_TIME 360

task main()
{
    repeat (3) {
        OnFwd(OUT_BC, 75);
        Wait(MOVE_TIME);
        OnRev(OUT_C, 75);
        Wait(TURN_TIME);
    }
    Off(OUT_BC);
}

---


---
/**
 * TrunRight03.nxc
 * 「トライボット」使用
 * 500ミリ秒間前進し、その後右に180度曲がる。これを3回繰り返
す。
 * @author maruno
 * @version 1.0, 2008-01-05
 * @since 1.0
 */
#include "NXCDefs.h"

#define MOVE_TIME 500
#define TURN_TIME 360

task main()
{
    for (int i = 0; i <3; i++) {
---

for 文を使っています。C 言語と違い、初期値化式でローカル変数
を宣言することもできます。


---
/**
 * TrunRight04.nxc
 * 「トライボット」使用
 * 500ミリ秒間前進し、その後右に180度曲がる。これを3回繰り返
す。
 * @author maruno
 * @version 1.0, 2008-01-05
 * @since 1.0
 */
#include "NXCDefs.h"

#define MOVE_TIME 500
#define TURN_TIME 360

task main()
{
    int i = 0;
    while (i <3) {
---


---
/**
 * TrunRight05.nxc
 * 「トライボット」使用
 * 500ミリ秒間前進し、その後右に180度曲がる。これを3回繰り返
す。
 * @author maruno
 * @version 1.0, 2008-01-05
 * @since 1.0
 */
#include "NXCDefs.h"

#define MOVE_TIME 500
#define TURN_TIME 360

task main()
{
    int i = 0;
    do {
        OnFwd(OUT_BC, 75);
        Wait(MOVE_TIME);
        OnRev(OUT_C, 75);
        Wait(TURN_TIME);
        i++;
    } until (i >= 3);

    Off(OUT_BC);
}

---

until マクロを使っています。マクロ展開後は、

do {
...
} while (!(i >= 3));

になります。



■ 分岐制御文を使ったサンプル

ランダムにトライボットが 10 秒間動きます。
ランダムな時間値やパワー値の取得は、Random 関数を使っていま
す。

---
/**
 * Random01.nxc
 * 「トライボット」使用
 * ランダムに10秒間動く。
 * @author maruno
 * @version 1.0, 2008-01-05
 * @since 1.0
 */
#include "NXCDefs.h"

#define MOVE_LIMIT_TIME 1000 * 10
#define TURN_TIME 180

task main()
{
    int move_total_time = 0;
    do {
        int outputs;
        switch (Random(3)) {  // (1)
        case 0:
            outputs = OUT_B;
            break;
        case 1:
            outputs = OUT_C;
            break;
        default:
            outputs = OUT_BC;
            break;
        }

        int move_time = Random(500);
        OnFwd(outputs, Random(201) - 100);  // (2)
        Wait(move_time);
        move_total_time += move_time;

        Off(OUT_BC);  // (3)
    } while (move_total_time <MOVE_LIMIT_TIME);
---

□ switch (Random(3)) { // (1)

モーター B、C、BC を動かすので、どのモーターを動かすかを乱数
(0, 1, 2)を使って決めます。


□ Random 関数

Random 関数は、オーバーロードされています。上限を引数で渡し
て、「0 以上 上限未満」の値を返す上の関数の方が使い勝手はい
いでしょう。

---
Random(n) Value
16 ビットの符号なしの乱数値(0 以上 n 未満)を返します。

例:
x = Random(10); // return a value of 0..9
---

---
Random() Value
16 ビットの符号つきの乱数値を返します。

例:
x = Random();
---


□ OnFwd(outputs, Random(201) - 100); // (2)

乱数で決まったモーターを、また、乱数値で指定されたパワーで動
かします。
OnFwd 関数のパワーは、-100 ~ 100 です。負数の場合は、後退す
る OnRev 関数と同じ意味になります。


□ Off(OUT_BC); // (3)

今回はループの中に入れています。一度、モーターを停止させない
と、停止させられるまで動き続けるからです。すなわち、モーター
B を動かしてからつぎに モーター C を動かす場合、モーター B
も停止しないと、つぎのモーター C では、モーター BC が動くこ
とになります。(これはこれでおもしろい動きになるかもしれませ
んが)



■ 情報をスクリーンに表示する

C 言語や C++ 言語の最初に学習するのは、標準出力に "Hello Wor
ld!" を表示する、というものでしょう。CUI のアプリケーション
では、何かしらの形で表示しないと動きがわかりません。でも、ロ
ボットを使ったプログラムでは、モーターなど、標準出力に表示し
なくても動きがわかったりします。
ただ、センサーから入力した値などは、表示しないとわかりません。
ここでは、スクリーンに文字列を表示してみましょう。
描画や画像を表示することもできますが、これは別の機会に。


□ "Hello World!" の表示

まずは、定番の "Hello World!" を表示してみます。

---
/**
 * HelloWorld01.nxc
 * 「トライボット」使用
 * スクリーンに "Hello World!" を示する。
 * @author maruno
 * @version 1.0, 2008-01-05
 * @since 1.0
 */
#include "NXCDefs.h"
task main(){
     TextOut(0, LCD_LINE3, "Hello World!");  // (1)
     TextOut(0, LCD_LINE6, "12345678901234567890");  // (2)
     Wait(5000);
}

---

このプログラムで、スクリーンの 3 行目に "Hello World!" が、
また、6 行目に "1234567890123456" が 5 秒間表示されたと思い
ます。

文字列の表示には、TextOut 関数を使います。


□ TextOut
---
TextOut(x, y, msg, clear = false) Function

文字列をスクリーンの座標 (x ,y) から書きます。y にはつぎの値
を指定します。
LCD_LINE1、LCD_LINE2、...、LCD_LINE8

引数 clear は、この文字列を表示する前にスクリーンをクリアす
るかどうかを指定します。指定がなければ、クリアしません(デフ
ォルト値が false です)

例:
TextOut(0, LCD_LINE3, "Hello World!");
---

なお、引数の最後に「clear = false」とあるのは、この引数は省
略してもいいし、省略しなくてもいい。省略した場合は、デフォル
ト値として、「false」を指定したのと同じことになる、というこ
とです。これも、C++ ではおなじみの「デフォルト引数」というも
のですね。


□ TextOut(0, LCD_LINE3, "Hello World!"); // (1)

TextOut 関数によって、指定された文字列を 3 行目に表示します。


□ TextOut(0, LCD_LINE6, "12345678901234567890"); // (2)

20 文字分指定されていますが、実際に表示されるのは最初の 16
文字だけです。
スクリーンは、一行 16 文字分だけ表示されます。



■ 文字列を編集して表示する

プログラム Random01 は、10 秒間ランダムに動きます。どのよう
に動くかは、実際に動かしてみないとわかりません。このときの動
く値を、スクリーンに表示してみましょう。

---
/**
 * Random02.nxc
 * 「トライボット」使用
 * ランダムに10秒間動く。そのときの情報をスクリーンに表示す
る。
 * @author maruno
 * @version 1.0, 2008-01-05
 * @since 1.0
 */
#include "NXCDefs.h"

#define MOVE_LIMIT_TIME 1000 * 10
#define TURN_TIME 180

task main()
{
    int move_total_time = 0;
    do {
        int outputs;
        string moter;
        switch (Random(3)) {
        case 0:
            outputs = OUT_B;
            moter = "B";
            break;
        case 1:
            outputs = OUT_C;
            moter = "C";
            break;
        default:
            outputs = OUT_BC;
            moter = "BC";
            break;
        }
        int power = Random(201) - 100;
        int move_time = Random(100) * 10;
        OnFwd(outputs, power);

        string msg;
        string num;
        
        msg = StrCat("moter = ", moter);  // (1)
        TextOut(0, LCD_LINE3, msg);

        num = NumToStr(power);  // (2)
        msg = StrCat("power = ", num);
        TextOut(0, LCD_LINE4, msg);

        num = NumToStr(move_time);
        msg = StrCat("time = ", num);
//        TextOut(0, LCD_LINE5, StrCat("time = ", NumToStr(m
ove_time)));  // (3)
        TextOut(0, LCD_LINE5, msg);

        Wait(move_time);
        move_total_time += move_time;

        Off(OUT_BC);

    } while (move_total_time <MOVE_LIMIT_TIME);
---

□ msg = StrCat("moter = ", moter); // (1)

StrCat 関数は、文字列を連結し、その結果を返す関数です。


□ StrCat 関数
---
StrCat(str1, str2, ..., strN) Value

引数で指定された複数の文字列を連結して返します。

例:
msg = StrCat("test", "please"); // returns "testplease"
---


□ num = NumToStr(power); // (2)

NumToStr 関数は、数値を文字列に変換する関数です。


□ NumToStr 関数
---
NumToStr(value) Value

指定された数値 num を文字列に変換して返します。

例:
msg = NumToStr(-2); // returns "-2" in a string
---


□ TextOut(0, LCD_LINE5, StrCat("time = ", NumToStr(move_tim
e)));

なお、このように指定したいところですが、コンパイルエラーにな
ります。
どうやら、NXC は、関数のネストができないようです。★



以上

1-3月 IT特講の予定

2008年01月05日 | IT 特講
01/27(日) 16:00-18:30 アルゴリズムを考えよう
02/02(日) 16:00-18:30 デザイン・パターン
02/10(日) 16:00-18:30 データ・モデリング
02/24(日) 16:00-18:30 プログラミング作法 - Java 編

03/01(土) 10:00-12:30 テスト・ファースト - JUnit 編
03/01(土) 13:15-15:45 テスト・ファースト - jWebUnit 編
03/15(日) 10:00-15:45 Struts入門

03/29(日) 10:00-15:45 XML 入門

Lego NXT のプログラミング (NXC) (1)

2008年01月04日 | LEGO
■ Lego NXT ファームウェアを更新

Lego NXT ファームウェアを更新。といっても、もともと入ってい
るものを入れただけ。
1.01 から、1.03 に更新。

BricxCC 3.3 の前提のファームウェアは 1.02 なので、1.01 の場
合は更新する必要がある。現在 (2008-01-03) の最新は 1.05.

[NXT ソフトウェア] - [ツール]メニュー - [NXT ファームウェア
の更新]を選択 - [NXT ファームウェアの更新]ダイアログボックス
が表示
[チェック]ボタンをクリックして、最新のファームウェアを確認し、
あればダウンロード
[ダウンロード]ボタンをクリックして更新を開始


■ C 言語もどきの NXC を使ってプログラミングしてみる。

まだ、NXT は、C/C++ に対応していないみたい。NXC は、NQC の
NXT 版のような感じ。
開発環境として、 BricxCC を使う。


■ BricxCC のインストール

Bricx Command Center 3.3 (BricxCC) のインストール
http://bricxcc.sourceforge.net/nbc/
http://sourceforge.net/project/showfiles.php?group_id=68600&
package_id=67285
(08-01-05 追記)
から、
bricxcc_setup_33718.exe
をダウンロード。

ダウンロードした実行ファイルをダブルクリックして、そのままイ
ンストールする。

NXT に電源を入れて、USB 接続にしておけば、ほとんど設定するこ
ともなく、NXC プログラミングができる。
NXC プログラムは、ほぼ C 言語の感じだが、ちょっとだけ文法が
違う。ロボットを制御するのに適した感じになっている。まず、タ
スクを言語仕様として作ることができる。bool 型も標準仕様。ま
あ、そこら辺の話はおいおいすることにして、参考資料は以下の 2
つ。

Not eXactly C (NXC) Programmer's Guide
Version 1.0.1 b33, October 10, 2007
http://bricxcc.sourceforge.net/nbc/nxcdoc/NXC_Guide.pdf

Programming LEGO NXT Robots using NXC (beta 27 or higher)
(Version 2.1, Apr 9, 2007)
http://bricxcc.sourceforge.net/nbc/nxcdoc/NXC_tutorial.pdf

これらにしたがって、「トライボット」使用するプログラムを作っ
てみた。これだけみると、C言語とほぼ同様で違和感はない。


■ 1秒間前進し、その後1秒間停止、1秒間後退するプログラム

---
/**
 * ForwardAndBackword01.nxc
 * 「トライボット」使用
 * 1秒間前進し、その後1秒間停止、1秒間後退する。
 * @author maruno
 * @version 1.0, 2008-01-03
 * @since 1.0
 */
#include "NXCDefs.h"
task main()
{
    OnFwd(OUT_BC, 75); // BCモーター前進、1000ミリ秒間
    Wait(1000);
    Off(OUT_BC);       // BCモーター停止、1000ミリ秒間
    Wait(1000);
    OnRev(OUT_BC, 75); // BCモーター後退、1000ミリ秒間
    Wait(1000);
    Off(OUT_BC);       // BCモーター停止
}

---

基本的に、動きを設定して、それをどれくらい持続するか、という
ことを Wait 関数でミリ秒を指定する。

ちなみに、NXT ソフトウェアで作った同じ動きをするプログラム。
まあ、ソフトウェアブロックでは、時間待機ブロックを使って作っ
たものが、NXC では、Off 関数 + Wait 関数での実現がいいかどう
かは、これから調査する。

プログラムの作成手順は以下のとおり。
1. ソースを入力 / 編集
2. コンパイル (F5)
3. プログラムのダウンロード (F6)
4. NXT で実行


これらの関数の引数で使っている outputs の定数は、以下のとお
りで、それぞれ、OUT_x の「x」部分がポートの番号をあらわして
いる。「OUT_BC」は、B ポートと C ポートを同時に動かすことを
意味する。
---
OUT_A
OUT_B
OUT_C
OUT_AB
OUT_AC
OUT_BC
OUT_ABC
---


それぞれの関数の仕様 [NXC_Guide]

□ Off
---
Off(outputs) Function

outputs ポートのモータを停止する。

例:
Off(OUT_A); // turn off output A
---

□ OnFwd
---
OnFwd(outputs, pwr) Function

outputs ポートのモータをパワーpwr(-100 ~ 100)で前進させる。

例:
OnFwd(OUT_A, 75);
---

□ OnRev
---
OnRev(outputs, pwr) Function

outputs ポートのモータをパワーpwr(-100 ~ 100)で後退させる。

例:
OnRev(OUT_A, 75);
---

□ Wait
---
Wait(time) Function

タスクを指定ミリ秒数分スリープさせる。
この関数の前にモーターを動かせば、ここで指定したミリ秒間、
モーターがそのまま動き続けることになる。

例:
Wait(1000); // wait 1 second
Wait(Random(1000)); // wait random time up to 1 second
---


■ 500ミリ秒間前進し、その後右に180度曲がるプログラム

---
/**
 * TrunRight01.nxc
 * 「トライボット」使用
 * 500ミリ秒間前進し、その後右に180度曲がる。
 * @author maruno
 * @version 1.0, 2008-01-03
 * @since 1.0
 */
#include "NXCDefs.h"

#define MOVE_TIME 500
#define TURN_TIME 360
task main()
{
    OnFwd(OUT_BC, 75);
    Wait(MOVE_TIME);
    OnRev(OUT_B, 75);
    Wait(TURN_TIME);
    Off(OUT_BC);
}

---

#define で、マクロを定義できるのも C 言語と同じ。

TURN_TIME として、 360 を指定しているが、これでいいかどうか
は疑問。
モーターのパワーなんかとも関係するし、わたしのつくった「トラ
イボット」ではほぼ 180 度反転しているが、参考資料のチュート
リアルでは、90 度と書かれている。まあ、ちゃんと計算したりし
ないとだめ、ということでしょうね。
床の素材にも関係するようである。(08-01-05 追記)


■ NXC 文法

コーディングをするための仕様は、[NXC_Guide] にまとまっている。
これ以降、詳細な説明は後日考えるが、とりあえず、コーディング
できるだけの分を記す。
C 言語でコーディングできることを前提にする。
なお、わたしもはじめての言語で、まだ、ぱら見しかしていないの
で、もしかしたら間違っている箇所があるかもしれないし、推測で
記しているところ(★を補足)があるので、注意されたい。


■ キーワード

---
__RETURN__     char      long      sub
__RETVAL__     const     mutex     switch
__STRRETVAL__  continue  priority  task
__TMPBYTE__    default   repeat    true
__TMPWORD__    do        return    typedef
__TMPLONG__    else      safecall  unsigned
abs            false     short     until
asm            for       sign      void
bool           goto      start     while
break          if        stop
byte           inline    string
case           int       struct
---

■ タスク

C 言語との大きな違いはタスクを作れることでしょう。これには t
ask キーワードを使います。C 言語での入口点は main 関数でした
が、NTC では、main タスクになります。タスクの起動 / 停止は、
start 文 / stop 文を使います。また、priority 文を使ってタス
クの優先順位をつけることもできます。

---
task 名前()
{
    // タスクのコード
}

---


■ 関数

関数は C 言語とほぼ同じです。ただ、inline キーワードを使って、
関数をインライン展開できます。また、safecall キーワードを使
って、この関数の実行を排他的に行うことができます(★)。

---
[safecall] [inline] 戻り値型 名前(引数リスト)
{
    // 関数のコード
}

---

配列を引数とする関数は、関数をアセンブラーで記述する必要があ
ります。 (08-01-18 追記) ★


■ データ型と変数

整数の型は C 言語と同じように使えます。ただし、int は 16 ビ
ットです。
また、string が用意されているのもうれしい限りです。

型名 内容
------------------- -------------------------------
bool 8 bit unsigned
byte, unsigned char 8 bit unsigned
char 8 bit signed
unsigned int 16 bit unsigned
short, int 16 bit signed
unsigned long 32 bit unsigned
long 32 bit signed
mutex 排他制御を行うための型(と思われる。ロッ
クフラグ?)★
string byte 型の配列
struct 構造体
Arrays 配列

bool 型があるので、リテラル true / false もキーワードとして
用意されています。

なお、Arrays は配列ですが、C 言語と同じように
int array[10];
と書くこともできます。

整数リテラルは、通常の 10 進数と、前に 0x をつけた 16 進数で
書くことができます。

string リテラルは二重引用符 (") で囲みます。

なお、NXC には、ポインター型はありません。どうやら、その
まま
int 型や long 型を使うようです。
asm キーワードで、アセ
ンブラーを使って表現するようです (08-01-18 修正)★

また、共用体もないようです。★

構造体は、C++ 形式です。すなわち、typedef しなくても、タグ名
をそのまま構造体名として使えます。 (08-01-18 追記)


■ 演算子

つぎの演算子があります。C 言語と同じですが、abs() と sign()
も演算子に入っています。これ以外は C 言語と一緒です。

---
abs()         絶対値
sign()        符号
++, --        インクリメント、デクリメント
-             単項マイナス
~             ビット否定
!             論理否定
*, /, %       乗算、除算、剰余算
+, -          加算、減算
<<, >>        左シフト、右シフト
<, >, <=, >=  関係演算
==, !=        等価、非等価
&             ビット論理積
^             ビット排他的論理和
|             ビット論理和
&&            論理積
||            論理和
? :           条件演算

---


■ 制御文

□ if 文

if (条件式) 条件式の結果が true のとき実行するボディ

if (条件式) 条件式の結果が true のとき実行するボディ
else そうでないときに実行するボディ


□ switch 文

switch (式) ボディ

case 定数式 :
default :


□ while 文

while (条件式) ボディ


□ do while 文

do ボディ while (条件式)


□ for 文

for (初期値化式 ; 条件式 ; 更新式) ボディ


□ repeat 文

repeat (式) ボディ

C 言語にはない制御文です。式の結果の回数分だけ繰り返します。


---
repeat (10) {
    // 繰り返すコード
}

---

□ until マクロ (08-01-05 追記)

つぎのように定義されています。センサーを使うときによく使いま
す。

#define until(c) while(!(c))


■ プリプロセッサー

□ #include

ヘッダーのファイル名を二重引用符 (") で囲って指定します。
なお、山カッコ(<、>) を使うことはできません。

#include "NXCDefs.h"
はデフォルトで入っているらしいので書かなくても大丈夫みたい。



□ #define

マクロの定義

□ 条件付コンパイル

#ifdef symbol
#ifndef symbol
#else
#endif
#if condition
#elif


□ 連結

##

□ プラグマ

#pragma


基本的な文法事項は以上です。
細かい内容はまた、別途。

また、API についても、いずれまた。


■ 参考文献

[NXC_Guide]
Not eXactly C (NXC) Programmer's Guide
Version 1.0.1 b33, October 10, 2007
http://bricxcc.sourceforge.net/nbc/nxcdoc/NXC_Guide.pdf


[NXC_tutorial]
Programming LEGO NXT Robots using NXC (beta 27 or higher
)
(Version 2.1, Apr 9, 2007)
http://bricxcc.sourceforge.net/nbc/nxcdoc/NXC_tutorial.p
df

以上

Lego Mindstoms NXT 「トライボット」 組立て (3)

2008年01月02日 | LEGO
Legoロボットは、最後の「6. 物体の位置を突き止める (超音波センサー)」の取り付け。これは、今までついていた棒のブロックを抜いて、超音波センサーをそれを使って取り付けるだけ。指を怪我していても大丈夫な程度。
動きは、目の前にボールを置いたら、それに反応して動き出すというもの。
距離は、50cmに設定。
けっこう動きも複雑になってきた感じだが、必要なソフトのブロックは、超音波センサーの待機ブロックを置いただけ。

今後は、ロボットの組立てはともかく、プログラミングをいろいろとしようと思う。

つぎのページをUP。
Lego Mindstorms NXT

Lego Mindstoms NXT 「トライボット」 組立て (2)

2008年01月01日 | LEGO
Legoのロボットはやはり、ピースを探すのが大変。
「3. グラバー」まで作って実行してみたが、どうしても、ボールをつかみに行くとバンパー部分が外れてしまう。
よくよく見てみると、バンパーを作るときの部品が逆の位置についていた。
そこから作り直す。
なお、グラパーの作成時間は1時間半くらいか。
作り直した結果、ギアーがちゃんとかみ合うようになった。
そして、ちゃんとボールをつかんで戻ってくる。

「4. サウンド制御 (サウンドセンサー)」、「5. 光センサー (光センサー)」の部分を組み込む。
これらは、全部で1時間くらい。大分なれてきた感じ。
サウンド制御では、ボールにぶつかったときに待ち状態になり、音を鳴らすと、ボールをつかんで戻ってくるというもの。

光センサーは、テストトラックの円周にある黒い実線を認識して、そこで止まるというもの。
いままでは、ボールの位置に関係なく、戻ってくる距離は一定だったので、自分で止まる位置を識別することができた。
あわせて、モーターをひとつずつ制御して、ほぼその場で進行方向を逆転させることもできた。
ただし、これは、今までとは違って、今まで組み込んだピースをちょっと外さないといけない。
なんだかんだで、右手の中指の先が割れてしまった。それ以外にも、指の先が痛い。

どうしてもピースを探すのが大変なので、チャックつきの小分けパックを買う。85mm X 120mm 20枚入りで105円。
これで、大分楽になるかな。