プログラミング教育
microbit
MakeCode
PG0

PG0でmicro:bitのプログラムを書く方法

はじめに

PG0は、プログラミング学習を目的としたプログラミング言語です。
PG0の詳細は『初心者用プログラミング言語「PG0」のススメ』を参照してください。

micro:bitについて

BBC micro:bit(マイクロビット)はプログラミング可能な小さなコンピューターです。
25個のLED、2個のボタン、明るさセンサー、温度センサー、加速度センサー、磁力センサー、無線通信機能がついています。
※「BBC micro:bit」はMicro:bit Educational Foundationの登録商標です。

PG0でmicro:bit

PG0でmicro:bitを使うには常時USBケーブルでPCとmicro:bitを接続しておく必要があります。
PG0は最初のプログラム実行時にmicro:bitにPG0用のHEXファイル(MicroPythonファームウェア)をmicro:bitに転送します。その後はPG0での処理を随時micro:bitに送信します。
これによりプログラム実行中の変数の内容をPG0で確認しながら動かすことが可能となっています。
HEXファイルの置き換えは最初のプログラム実行時のみとなっているため2回目の実行からはすぐにプログラムを動かすことができます。

micro:bitとの連携はPG0.5の機能で行うため開発画面ではメニューの「PG0.5」にチェックを入れてPG0.5モードにしておくことをお勧めします。
pg0.5_menu.png

PG0で出来ること

  • micro:bit用APIにある制御
  • プログラムのステップ実行や停止、カーソル位置まで実行など
  • 実行中の変数の確認
  • 大きいプログラムの実行
    • PC側でプログラムが動くため

PG0で出来ないこと

  • USBケーブルを抜いた状態での実行
  • シリアル通信をするプログラム
    • PG0の実行はmicro:bitとのシリアル通信を利用しているため

サンプルプログラム

以下の手順でサンプルプログラムを実行できます。
※ サンプルプログラムを動かすにはmicro:bitが別途必要です。

  1. PG0をPCにインストール
  2. PG0を実行してサンプルプログラムをPG0にコピー&ペースト
  3. PCとmicro:bitをUSBケーブルで接続
  4. PG0で実行

基本サンプル

文字列をmicro:bitのLEDに表示するサンプルです。

PG0
#import("microbit")

led_show_string("Hello, World!")

ハートをmicro:bitのLEDに表示するサンプルです。

PG0
#import("microbit")

led_show_icon("heart")

丸の大きさを交互に変えてmicro:bitのLEDに表示し続けるサンプルです。

PG0
#import("microbit")

while (1) {
    // 大きい丸を表示
    led_show_leds({
        0,6,9,6,0,
        6,9,9,9,6,
        9,9,9,9,9,
        6,9,9,9,6,
        0,6,9,6,0,
    })
    sleep(500)
    // 小さい丸を表示
    led_show_leds({
        0,0,0,0,0,
        0,5,9,5,0,
        0,9,9,9,0,
        0,5,9,5,0,
        0,0,0,0,0,
    })
    sleep(500)
}

PG0のコンソールにmicro:bitからの情報を表示する順次のサンプルです。

PG0
#import("microbit")

print("動作時間: " + int(running_time() / 1000) + "秒\n")
print("温度: " + temperature() + "℃\n")
print("明るさ: " + light_level() + "\n")
print("乱数: " + rand() + "\n")

ボタンAが押されていれば「←」を、ボタンBが押されていれば「→」をmicro:bitのLEDに表示する分岐のサンプルです。

PG0
#import("microbit")

while (1) {
    if (button_a_is_pressed() == 1) {
        // ボタンAが押されたら「←」を表示
        led_show_icon("arrow_w")
    } else if (button_b_is_pressed() == 1) {
        // ボタンBが押されたら「→」を表示
        led_show_icon("arrow_e")
    } else {
        // LEDを消す
        led_show_leds({0})
    }
}

micro:bitのLEDにカウントダウンを表示して最後にアイコンを表示する反復のサンプルです。

PG0
#import("microbit")

// カウントダウン
var i = 9
while (i >= 0) {
    led_show_string(i)
    sleep(1000)
    i = i - 1
}
// アイコン表示
led_show_icon("happy")

センサーサンプル

micro:bitの明るさセンサーで暗くなるとLEDが点くライトです。
(PG0の実行速度は「速い」か「待ち無し」で実行してください)

同じものをMakeCodeで作成しました。

PG0
#import("microbit")

while (1) {
    // 取得した明るさを0~9段階に変換
    // 「50」の値を変えると明るさによって光る量が変化する
    var level = 9 - int(light_level() * (9 / 50))
    if (level < 0) {
        level = 0
    }
    // LEDの配列を作成
    var light
    for (var i = 0; i < 25; i++) {
        light[i] = level
    }
    // LEDの表示
    led_show_leds(light)
}

micro:bitの加速度センサーを使った水準器です。
(PG0の実行速度は「速い」か「待ち無し」で実行してください)

同じものをMakeCodeで作成しました。

PG0
#import("microbit")

// 「50」の値を変えると傾きへの反応が変わる
var coef = 2 / 50
while (1) {
    // x軸の傾きを検出
    var x = 2 - int(acceleration_x() * coef)
    if (x < 0) {
        x = 0
    } else if (x > 4) {
        x = 4
    }
    // y軸の傾きを検出
    var y = 2 - int(acceleration_y() * coef)
    if (y < 0) {
        y = 0
    } else if (y > 4) {
        y = 4
    }
    // 水平位置を「+」で表示
    var level = {
        0,0,0,0,0,
        0,0,0,0,0,
        0,0,0,0,0,
        0,0,0,0,0,
        0,0,0,0,0,
    }
    level[y * 5 + x] = 9    // 中心
    if (x > 0) {
        level[y * 5 + (x - 1)] = 9
    }
    if (x < 4) {
        level[y * 5 + (x + 1)] = 9
    }
    if (y > 0) {
        level[(y - 1) * 5 + x] = 9
    }
    if (y < 4) {
        level[(y + 1) * 5 + x] = 9
    }
    led_show_leds(level)
}

ゲームサンプル

落ちてくるボールをキャッチする簡単なゲームです。
落ちてくるボールはだんだん早くなります。
ボタンAを押すと始まります。
自分の移動はボタンAとボタンBを使います。
(PG0の実行速度は「速い」か「待ち無し」で実行してください)

同じものをMakeCodeで作成しました。

改造案

  • 自分の移動に加速度センサーを使う
  • ヒットポイントを作って数回落ちても大丈夫にする
PG0
#import("microbit")

while (1) {
    // ボタンAが押されるまで待機
    led_show_icon("arrow_w")
    while (button_a_is_pressed() == 0) { }
    while (button_a_is_pressed() == 1) { }
    // 初期化
    led_show_leds({0})
    button_a_get_presses()
    button_b_get_presses()

    var prev_time = running_time()
    var speed = 500
    var score = 0

    var me_x = 2
    led_set_pixel(me_x, 4, 9)

    var ball_x = rand() % 5 // ボールの横位置はランダムに決定
    var ball_y = 0
    led_set_pixel(ball_x, ball_y, 9)

    // ゲーム用ループ
    while (1) {
        // 自分の処理
        if (button_a_get_presses() >= 1 && me_x > 0) {
            // ボタンAで自分を左に移動
            led_set_pixel(me_x, 4, 0)
            me_x = me_x - 1
            led_set_pixel(me_x, 4, 9)
        }
        if (button_b_get_presses() >= 1 && me_x < 4) {
            // ボタンBで自分を右に移動
            led_set_pixel(me_x, 4, 0)
            me_x = me_x + 1
            led_set_pixel(me_x, 4, 9)
        }

        // ボールはspeed(ミリ秒)毎に処理
        if (prev_time + speed >= running_time()) {
            continue
        }
        prev_time = running_time()

        // ボールの処理
        led_set_pixel(ball_x, ball_y, 0)
        if (ball_y >= 3 && ball_x == me_x) {
            // キャッチ成功
            score = score + 1
            speed = speed - 20  // スピードアップ
            led_set_pixel(me_x, 4, 9)
            // 次のボール
            ball_x = rand() % 5
            ball_y = 0
        } else if (ball_y == 4) {
            // キャッチ失敗
            print("Score: " + score + "\n")
            led_show_string("Score:" + score)
            break
        } else {
            // ボールを1マス落とす
            ball_y = ball_y + 1
        }
        led_set_pixel(ball_x, ball_y, 9)
    }
}

ブロック崩しゲームです。
クリアする度にボールの速度が早くなります。
ボタンAを押すと始まります。
バーの移動はボタンAとボタンBを使います。
(PG0の実行速度は「速い」か「待ち無し」で実行してください)

同じものをMakeCodeで作成しました。

改造案

  • 難易度を上げるためにバーを短くする
  • ステージ毎にブロックの配置を変える
  • 一回では壊れないブロックの配置
PG0
#import("microbit")

// ブロックの削除
function delete_block(x, y, cnt) {
    led_set_pixel(x, y, 0)
    return cnt - 1
}

var init_speed = 700
var speed = init_speed
while (1) {
    // ボタンAが押されるまで待機
    led_show_icon("arrow_w")
    while (button_a_is_pressed() == 0) { }
    while (button_a_is_pressed() == 1) { }
    // 初期化
    button_a_get_presses()
    button_b_get_presses()
    var prev_time = running_time()
    led_show_leds({
        8,8,8,8,8,
        8,8,8,8,8,
        0,0,0,0,0,
        0,0,0,0,0,
        0,0,0,0,0,
    })
    var block_cnt = 10

    var bar_x = 1
    led_set_pixel(bar_x, 4, 9)
    led_set_pixel(bar_x + 1, 4, 9)

    var ball_x = rand() % 5 // ボールの開始位置はランダムに決定
    var ball_y = 3
    var move_x = 1
    var move_y = -1
    led_set_pixel(ball_x, ball_y, 9)

    // ゲーム用ループ
    while (1) {
        // バーの処理
        if (button_a_get_presses() >= 1 && bar_x > 0) {
            // ボタンAでバーを左に移動
            led_set_pixel(bar_x + 1, 4, 0)
            bar_x--
            led_set_pixel(bar_x, 4, 9)
        }
        if (button_b_get_presses() >= 1 && bar_x < 3) {
            // ボタンBでバーを右に移動
            led_set_pixel(bar_x, 4, 0)
            bar_x++
            led_set_pixel(bar_x + 1, 4, 9)
        }

        // ボールはspeed(ミリ秒)毎に処理
        if (prev_time + speed >= running_time()) {
            continue
        }
        prev_time = running_time()

        // ボールの処理
        led_set_pixel(ball_x, ball_y, 0)

        // 壁の判定
        if (ball_x + move_x < 0 || ball_x + move_x > 4) {
            move_x *= -1
        }
        if (ball_y + move_y < 0) {
            move_y *= -1
        }

        // ブロックの判定
        var prev_block_cnt = block_cnt
        if (ball_x + move_x >= 0 && ball_x + move_x <= 4 &&
            led_get_pixel(ball_x + move_x, ball_y) == 8) {
            // 水平方向のブロック判定
            block_cnt = delete_block(ball_x + move_x, ball_y, block_cnt)
            move_x *= -1
        }
        if (ball_y + move_y >= 0 && ball_y + move_y <= 4 &&
            led_get_pixel(ball_x, ball_y + move_y) == 8) {
            // 垂直方向のブロック判定
            block_cnt = delete_block(ball_x, ball_y + move_y, block_cnt)
            move_y *= -1
        }
        if (prev_block_cnt == block_cnt &&
            ball_x + move_x >= 0 && ball_x + move_x <= 4 &&
            ball_y + move_y >= 0 && ball_y + move_y <= 4 &&
            led_get_pixel(ball_x + move_x, ball_y + move_y) == 8) {
            // ブロックの角判定
            block_cnt = delete_block(ball_x + move_x, ball_y + move_y, block_cnt)
            move_x *= -1
            move_y *= -1
        }
        if (block_cnt <= 0) {
            // 全部のブロックを削除したのでクリア
            var level = ((init_speed - speed) / 100) + 1
            print("Clear: Level " + level + "\n")
            led_show_string("Clear:" + level)
            speed -= 100    // スピードアップ
            break
        }

        // バーの判定
        if (ball_y + move_y == 4) {
            if (led_get_pixel(ball_x, ball_y + move_y) == 9) {
                move_y = -1
            } else if (led_get_pixel(ball_x + move_x, ball_y + move_y) == 9) {
                // バーの角判定
                move_x *= -1
                move_y = -1
            }
        }

        // 再度壁の判定
        if (ball_x + move_x < 0 || ball_x + move_x > 4) {
            move_x *= -1
        }
        if (ball_y + move_y < 0) {
            move_y *= -1
        }

        // ボールの移動
        ball_x += move_x
        ball_y += move_y
        if (ball_y > 4) {
            // ボールが一番下まで行くとゲームオーバー
            print("Game Over\n")
            led_show_string("GameOver")
            speed = init_speed
            break
        }
        led_set_pixel(ball_x, ball_y, 9)
    }
}

micro:bit用APIリファレンス

PG0でmicro:bit用のAPIを使うにはmicro:bit用ライブラリをインポートする必要があります。

PG0
#import("microbit")

基本

sleep(time)

一定時間待機します。
引数timeには待機する時間をミリ秒で指定します。
戻り値は常に0を返します。

PG0
#import("microbit")

// 10秒待機
sleep(10 * 1000)

rand()

乱数を取得します。
引数は無しです。
戻り値は生成された乱数を整数で返します。
乱数のシードは現在時間で初期化されています。

PG0
#import("microbit")

// 20~29の乱数をPG0のコンソールに表示
print(rand() % 10 + 20)

running_time()

micro:bitの実行時間を取得します。
引数は無しです。
戻り値はmicro:bitの実行時間をミリ秒で返します。
micro:bitのリセットボタンを押すと0に初期化されます。

temperature()

micro:bitから温度を取得します。
引数は無しです。
戻り値はmicro:bitの温度を摂氏で返します。

light_level()

micro:bitから明るさを取得します。
引数は無しです。
戻り値は明度を表す0から255を返します。値が大きいほど強い明度となります。

LED

led_show_string(str)

micro:bitのLEDに文字列を表示します。
引数strには表示する文字列か数値を指定します。
戻り値は常に0を返します。
1文字の場合は表示し続けて、2文字以上の場合はスクロールして表示します。
英数字記号のみ表示可能で日本語は表示できません。

PG0
#import("microbit")

// LEDに文字列を表示
led_show_string("Hello, World!")

led_show_leds(array)

micro:bitのLEDにイメージを表示します。
引数Arrayには5x5の大きさの配列を指定します。
5x5より小さい配列の場合は残りを0で埋めます。
配列の中の数値は0~9で明るさを指定します。
戻り値は常に0を返します。

PG0
#import("microbit")

// LEDに丸を表示
led_show_leds({
    0,6,9,6,0,
    6,9,9,9,6,
    9,9,9,9,9,
    6,9,9,9,6,
    0,6,9,6,0,
})
PG0
#import("microbit")

// LEDを消す
led_show_leds({0})

led_show_icon(str)

micro:bitのLEDに用意されたアイコンを表示します。
引数strには表示するアイコンの名前を指定します。
戻り値は成功したら0を返します。
不明な名前の場合は-1を返します。

以下が登録されている名前です。
heart, small_heart, yes, no, happy, sad, confused, angry, asleep, surprised, silly, fabulous, meh, tshirt, rollerskate, duck, house, tortoise, butterfly, stickfigure, ghost, sword, giraffe, skull, umbrella, snake, rabbit, cow, quarter_note, eigth_note, pitchfork, target, triangle, left_triangle, chessboard, diamond, small_diamond, square, square_small, scissors, arrow_n, arrow_ne, arrow_e, arrow_se, arrow_s, arrow_sw, arrow_w, arrow_nw

PG0
#import("microbit")

// LEDにハートを表示
led_show_icon("heart")

led_get_pixel(x, y)

指定ピクセルの明るさを取得します。
引数xはLEDの横位置を0~4で指定します。
引数yはLEDの縦位置を0~4で指定します。
戻り値は明るさを0~9で返します。

led_set_pixel(x, y, value)

指定ピクセルの明るさを設定します。
引数xはLEDの横位置を0~4で指定します。
引数yはLEDの縦位置を0~4で指定します。
引数valueは明るさを0~9で指定します。
戻り値は常に0を返します。

PG0
#import("microbit")

// 3x3の位置の明るさを設定
led_set_pixel(3, 3, 5)
// 3x3の位置の明るさをPG0のコンソールに表示
print(led_get_pixel(3, 3))

led_on()

micro:bitのLEDを有効にします。
引数は無しです。
戻り値は常に0を返します。

led_off()

micro:bitのLEDを無効にします。
引数は無しです。
戻り値は常に0を返します。

ボタン

button_a_is_pressed()

micro:bitのボタンAが押されているかを取得します。
引数は無しです。
戻り値はボタンAが押されていれば1、押されていなければ0を返します。

button_a_get_presses()

前回の呼び出し以降にmicro:bitのボタンAが押された回数を取得します。
引数は無しです。
戻り値はボタンAが押された回数です。
本APIを呼ぶと回数は0にリセットされます。

button_b_is_pressed()

micro:bitのボタンBが押されているかを取得します。
引数は無しです。
戻り値はボタンBが押されていれば1、押されていなければ0を返します。

button_b_get_presses()

前回の呼び出し以降にmicro:bitのボタンBが押された回数を取得します。
引数は無しです。
戻り値はボタンBが押された回数です。
本APIを呼ぶと回数は0にリセットされます。

PIN

micro:bitのPINは0~16と19~20を利用できます。
3, 4, 6, 7, 9, 10はディスプレイに使われているため他の用途で利用する場合はled_off()を使ってLEDを無効にする必要があります。

pin_read_digital(pin)

PINのデジタル値を取得します。
引数PINにはPIN番号を指定します。
戻り値はPINがハイの場合は1を返し、ローの場合は0を返します。

PG0
#import("microbit")

// PIN0のデジタル値を取得
print(pin_read_digital(0))

pin_write_digital(pin, value)

PINにデジタル値を設定します。
引数PINにはPIN番号を指定します。
引数valueが1の場合はハイに設定し、0の場合はローに設定します。
戻り値は常に0を返します。

PG0
#import("microbit")

// PIN0のデジタル値を設定
pin_write_digital(0, 1)

pin_read_analog(pin)

PINの電圧を取得します。
引数PINにはPIN番号を指定します。
戻り値はPINの電圧(0~1023(3.3V))を返します。

PG0
#import("microbit")

led_off()
while (1) {
    // PIN3のアナログ値を取得
    print(pin_read_analog(3) + "\n")
}

pin_write_analog(pin, value)

PWM信号をPINに出力します。
引数PINにはPIN番号を指定します。
引数valueには0(時間幅周期0%)から1023(時間幅周期 100%)の間の数値を指定します。
戻り値は常に0を返します。

PG0
#import("microbit")

// PIN0のアナログ値を設定
pin_write_analog(0, 800)

pin_set_analog_period(pin, period)

PWM信号の周期をマイクロ秒単位で指定します。
引数PINにはPIN番号を指定します。
引数periodにはPWM信号の周期をマイクロ秒単位で指定します。最小値は256μsです。
戻り値は常に0を返します。

コンパス

calibrate_compass()

micro:bitでコンパスの調整処理を開始します。
引数は無しです。
戻り値は調整が成功したら1を、失敗したら0を返します。

compass_heading()

3軸の磁力から計算された方位を取得します。
引数は無しです。
戻り値は時計回りの角度を示す0から360までの整数で、0は北となります。

磁力センサー

magnetic_force_x()

micro:bitからx軸の磁場強度を取得します。
引数は無しです。
戻り値はx軸の磁場強度を磁場の方向に応じて正または負の整数値(ナノテスラ)で返します。

magnetic_force_y()

micro:bitからy軸の磁場強度を取得します。
引数は無しです。
戻り値はy軸の磁場強度を磁場の方向に応じて正または負の整数値(ナノテスラ)で返します。

magnetic_force_z()

micro:bitからz軸の磁場強度を取得します。
引数は無しです。
戻り値はz軸の磁場強度を磁場の方向に応じて正または負の整数値(ナノテスラ)で返します。

magnetic_strength()

micro:bitから磁場の強さを取得します。
引数は無しです。
戻り値は磁場の強さを示す整数値(ナノテスラ)を返します。

加速度センサー

current_gesture()

micro:bitから現在のジェスチャーを取得します。
引数は無しです。
戻り値は"up", "down", "left", "right", "face up", "face down", "freefall", "3g", "6g", "8g", "shake"のいずれかになります。

was_gesture(name)

micro:bitから前回の呼び出し以降に指定した名前のジェスチャーが認識されたかを取得します。
引数nameにはジェスチャーを指定します。指定可能なジェスチャーは"up", "down", "left", "right", "face up", "face down", "freefall", "3g", "6g", "8g", "shake"のいずれかになります。
戻り値は前回呼び出し以降に認識されていれば1、認識されていなければ0を返します。

acceleration_x()

micro:bitからx軸の加速度を取得します。
引数は無しです。
戻り値は方向に応じて正または負の整数値(±2000mgの範囲内)を返します。

acceleration_y()

micro:bitからy軸の加速度を取得します。
引数は無しです。
戻り値は方向に応じて正または負の整数値(±2000mgの範囲内)を返します。

acceleration_z()

micro:bitからz軸の加速度を取得します。
引数は無しです。
戻り値は方向に応じて正または負の整数値(±2000mgの範囲内)を返します。

無線通信

radio_set_group(group)

送受信のグループを指定します。
引数groupにはグループ番号を指定します。0~255までの整数で指定します。
初期値は0となっています。
戻り値は常に0を返します。

radio_set_power(power)

送受信の信号強度を指定します。数値が小さいと近くの端末にしか信号が届きません。数値が大きいと遠くの端末にまで信号が届きます。
引数powerには信号強度を指定します。0~7までの整数で指定します。
初期値は6となっています。
戻り値は常に0を返します。

radio_send_string(str)

文字列をmicro:bitの無線通信を使って送信します。
引数strには送信する文字列を指定します。最大251文字までとなっています。
戻り値は常に0を返します。

radio_receive_string()

micro:bitの無線通信で文字列を受信します。
引数は無しです。
戻り値は受信した文字列です。受信しなかった場合は空の文字列("")となります。

Music

music_playtone(note)

指定された周波数を指定した時間再生します。
引数noteには周波数を整数で指定します。
周波数についてはTable of note frequenciesを参考にしてください。
戻り値は常に0を返します。

PG0
#import("microbit")

// C(ド)を1秒間再生
music_playtone(262)
sleep(1000)
music_stop()

music_stop()

再生を停止します。
引数は無しです。
戻り値は常に0を返します。

謝辞

PG0でmicro:bitに対応するにあたり、望月 陽一郎先生のmicro:bit「サンプルプログラミング集」(第1.1版)の一部を動作確認で利用させていただきました。