一往確認日記 |
2021年01月15日 [長年日記]
_ PLCでmruby (7) irep_header
前回rite_binary_headerについて調べました。
その次は何になるのか?ということです。
手がかりはdump.cのdump_irep関数です。
この中でバイナリーのサイズを計算しているところがあるのですが、rite_binary_headerの次はどうやらirepセクション、linenoセクション、lvセクション、rite_binary_footerと続くようです。
*bin_size = sizeof(struct rite_binary_header) +
section_irep_size + section_lineno_size + section_lv_size +
sizeof(struct rite_binary_footer);
irepセクションの手がかりはsection_irep_sizeを計算しているところで、
section_irep_size = sizeof(struct rite_section_irep_header);
section_irep_size += get_irep_record_size(mrb, irep);
まずはrite_section_irep_headerがあることがわかりました。
恐らくrite_section_irep_headerは子ノードがあるので再起的にサイズを計算しているのがget_irep_record_sizeではないかと思います。
それではrite_section_irep_headerについて調べます。
dump.hに定義があります。
struct rite_section_irep_header {
RITE_SECTION_HEADER;
uint8_t rite_version[4]; /* Rite Instruction Specification Version */
};
RITE_SECTION_HEADERはマクロになっているので、展開するとこの様になります。
struct rite_section_irep_header {
uint8_t section_ident[4];
uint8_t section_size[4];
uint8_t rite_version[4]; /* Rite Instruction Specification Version */
};
Rubyで確認するコードを書きます。
# read binary codes
codes = File.read "a.mrb"
# read rite_section_irep_header
size_of_rite_binary_header = 22
section_top = size_of_rite_binary_header
section_ident = codes[section_top + 0, 4]
section_size = codes[section_top + 4, 4].unpack("N").first
rite_version = codes[section_top + 8, 4]
pp section_ident: section_ident, section_size: section_size, rite_version: rite_version
結果はこのようになります。
{:section_ident=>"IREP",
:section_size=>38,
:rite_version=>"0300"}
IREPというIDがあり、サイズは38でバージョンは300となっています。
300は多分mruby 3.0を示していると思われます。
mrubyのバージョンは多分ここを調べれば良いことが分かりました。
つづく
2021年01月05日 [長年日記]
_ あけましておめでとうございます。
大雪で明けた2021、毎日の雪寄せに明け暮れています。
年末から開始したPLCでmrubyの企画ですが、今年中になんとか実用的なところまで持っていきたい気持ちになっているので、今年の抱負として「完遂」を掲げました。
本年もよろしくお願いいたします。
_ PLCでmruby (6) rite_binary_header
mrbファイルの先頭にはRITEヘッダーデータがあるということで、該当する箇所を探してみます。
dmup.hファイルにrite_binary_headerという構造体がありました。
/* binary header */
struct rite_binary_header {
uint8_t binary_ident[4]; /* Binary Identifier */
uint8_t major_version[2]; /* Binary Format Major Version */
uint8_t minor_version[2]; /* Binary Format Minor Version */
uint8_t binary_crc[2]; /* Binary CRC */
uint8_t binary_size[4]; /* Binary Size */
uint8_t compiler_name[4]; /* Compiler name */
uint8_t compiler_version[4];
};
これを元に調べてみましょう。
CRCとSizeはバイナリーデータでBig Endianになっている様です。
Rubyで調べるコードをこの様に書いて
codes = File.read "a.mrb"
binary_ident = codes[0, 4]
major_version = codes[4, 2]
minor_version = codes[6, 2]
crc = codes[8, 2].unpack("n")
size = codes[10, 4].unpack("N").first
compiler_name = codes[14, 4]
compiler_version = codes[18, 4]
pp binary_ident: binary_ident, major_version: major_version, minor_version: minor_version, crc: crc, size: size, compiler_name: compiler_name, compiler_version: compiler_version
出力されたのがこちらです。
% ruby read_rite_head.rb
{:binary_ident=>"RITE",
:major_version=>"01",
:minor_version=>"01",
:crc=>27764,
:size=>85,
:compiler_name=>"MATZ",
:compiler_version=>"0000"}
RITEというIDがあってバージョンは1.1、サイズは85バイト、コンパイラはMATZ、コンパライバージョンは0ということがわかりました。
サイズの85はファイルのサイズと一致していてヘッダーの先頭からフッター(多分あるでしょう)まで含んだサイズになっていることがわかります。
Amazon | Apple Lightning - USB 3カメラアダプタ | Apple(アップル) | USBアダプタ 通販
2020年12月23日 [長年日記]
_ PLCでmruby (5) mrbファイル
今回はmrbファイルの中をみていきます。
前回確認した時はこの様になっていました。
RITE0101ltUMATZ0000IREP&039gLVARaEN%
これでは訳がわからないので、hexdumpでちゃんと確認してみます。
% hexdump -C a.mrb
00000000 52 49 54 45 30 31 30 31 6c 74 00 00 00 55 4d 41 |RITE0101lt...UMA|
00000010 54 5a 30 30 30 30 49 52 45 50 00 00 00 26 30 33 |TZ0000IREP...&03|
00000020 30 30 00 00 00 1a 00 02 00 03 00 00 00 00 00 08 |00..............|
00000030 08 02 01 01 02 39 02 67 00 00 00 00 4c 56 41 52 |.....9.g....LVAR|
00000040 00 00 00 11 00 00 00 01 00 01 61 00 00 45 4e 44 |..........a..END|
00000050 00 00 00 00 08 |.....|
00000055
ASCIIとして有効な文字が繋がっていただけで、中身はバイナリーデータになっていました。
これはC言語様に出力した内容(codes配列)とも一致しています。
#include <stdint.h>
#ifdef __cplusplus
extern const uint8_t codes[];
#endif
const uint8_t codes[] = {
0x52,0x49,0x54,0x45,0x30,0x31,0x30,0x31,0x6c,0x74,0x00,0x00,0x00,0x55,0x4d,0x41,
0x54,0x5a,0x30,0x30,0x30,0x30,0x49,0x52,0x45,0x50,0x00,0x00,0x00,0x26,0x30,0x33,
0x30,0x30,0x00,0x00,0x00,0x1a,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x08,
0x08,0x02,0x01,0x01,0x02,0x39,0x02,0x67,0x00,0x00,0x00,0x00,0x4c,0x56,0x41,0x52,
0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x01,0x61,0x00,0x00,0x45,0x4e,0x44,
0x00,0x00,0x00,0x00,0x08,
};
この内容をどう解釈していくかということですが、こちらのスレッドが参考になりそうです。
[RFC] Update .mrb file format · Issue #944 · mruby/mruby · GitHub
どうやら最初にRITEヘッダーがある様です。
次回はRITEヘッダーを確認していきます。
Amazon Music - マライア・キャリーのAll I Want for Christmas Is You - Amazon.co.jp
2020年12月20日 [長年日記]
_ PLCでmruby (4) ニーモニック変換に方針転換
mrubyをPLCで動かすにはmrubyのVirtual Machineを実装すればできると思っていましたが、前回の出力をみるとmrubyのニーモニックからラダーのニーモニックに変換するのが手取り早い気がします。
ニーモニック変換器を作ることを当面の目標としましょう。
前回の出力を再掲します。
% mrbc --verbose a.rb
00001 NODE_SCOPE:
00001 local variables:
00001 a
00001 NODE_BEGIN:
00001 NODE_ASGN:
00001 lhs:
00001 NODE_LVAR a
00001 rhs:
00001 NODE_INT 1 base 10
irep 0x7f897b4059c0 nregs=3 nlocals=2 pools=0 syms=0 reps=0 iseq=8
local variable names:
R1:a
file: a.rb
1 000 OP_LOADI_1 R2
1 002 OP_MOVE R1 R2 ; R1:a
1 005 OP_RETURN R2
1 007 OP_STOP
これなら、ラダーで下の様な感じで作れれば置き換えができそうです。(ニーモニックは特定のPLCではなく雰囲気です)
ただ、クラスなど絡んでくるとそんなに簡単ではなく、難しいことは予想されます。
LD ON
@SET SCOPE
LD SCOPE
MOV #1, R2
MOV R2, R1
RST SCOPE
2020年12月16日 [長年日記]
_ PLCでmruby (3) mrbcコマンド
前回はmrubyとmirbコマンドを使いましたが、今度はmrbcコマンドを使ってみます。
まずは簡単なmrubyプログラムa.rbを準備します。
a = 1
変数aに整数1を代入するだけのプログラムです。
mrbcコマンドでmrubyの中間表現のmrbファイルを生成します。
% mrbc a.rb
mrbファイルを見てみると何のことかさっぱりわかりません。 先行き不安です。
RITE0101ltUMATZ0000IREP&039gLVARaEN%
mrbのBオプションを使うとC言語でリンクできるバイト列の配列を生成します。
% mrbc -Bcodes a.rb
codes配列のデータはバイト列っぽいですね、これだけ見てもやっぱりさっぱりわかりません。
#include <stdint.h>
#ifdef __cplusplus
extern const uint8_t codes[];
#endif
const uint8_t codes[] = {
0x52,0x49,0x54,0x45,0x30,0x31,0x30,0x31,0x6c,0x74,0x00,0x00,0x00,0x55,0x4d,0x41,
0x54,0x5a,0x30,0x30,0x30,0x30,0x49,0x52,0x45,0x50,0x00,0x00,0x00,0x26,0x30,0x33,
0x30,0x30,0x00,0x00,0x00,0x1a,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x08,
0x08,0x02,0x01,0x01,0x02,0x39,0x02,0x67,0x00,0x00,0x00,0x00,0x4c,0x56,0x41,0x52,
0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x01,0x61,0x00,0x00,0x45,0x4e,0x44,
0x00,0x00,0x00,0x00,0x08,
};
mrbcにverboseオプションを付けて実行してみます。
% mrbc --verbose a.rb
00001 NODE_SCOPE:
00001 local variables:
00001 a
00001 NODE_BEGIN:
00001 NODE_ASGN:
00001 lhs:
00001 NODE_LVAR a
00001 rhs:
00001 NODE_INT 1 base 10
irep 0x7f897b4059c0 nregs=3 nlocals=2 pools=0 syms=0 reps=0 iseq=8
local variable names:
R1:a
file: a.rb
1 000 OP_LOADI_1 R2
1 002 OP_MOVE R1 R2 ; R1:a
1 005 OP_RETURN R2
1 007 OP_STOP
なんかアセンブリ言語の様な出力が出てきました。 これなら何とか読んでいけそうな気がします。
2020年12月14日 [長年日記]
_ PLCでmruby (2) mrubyとは
mrubyをPLCで動かすにはmrubyのVirtual Machineを実装すればできると思うのですが、果たしてできるのか…
まずはmrubyとはなんぞや?ということです。
こちらのGitHubからCODEボタン>Download ZIPを選んでファイル一式ダウンロードします。gitが使える場合はcloneしても良いです。
GitHub - mruby/mruby: Lightweight Ruby
Mac前提で話を進めると、展開したフォルダーでminirakeコマンド(rakeでもいいです)を実行するとbinディレクトリ以下にmruby関連の実行ファイルが生成されます。
% cd path_to_your_downloaded_mruby
% ./minirake
% ls bin
mirb mrbtest mruby-config
mrbc mruby mruby-strip
Homeのbinにコピーします。
% cp bin/* ~/bin
使っているターミナルのshellに合わせて.zshrcや.bashrcに以下を追加しパスを通します。
export PATH=$PATH:$HOME/bin
ターミナルを起動し直してmruby
を実行しputs 'Hello, world'
を入力後^D
(controlを押しながらDを押す)を入力すると実行されてHello, world
が表示されます。
% mruby
puts 'Hello, world'
^D
Hello, world
Hello, worldが表示され動作確認ができます。
通常はmirbコマンドでインタラクティブに行います。
% mirb
mirb - Embeddable Interactive Ruby Shell
> puts 'Hello, world'
Hello, world
=> nil
>
2020年12月09日 [長年日記]
_ PLCでmruby (1) PLCでmrubyは動かせるのか?
いつもの如く企画倒れ前提でスタートしますが、mrubyをPLCで動かせないかなという企画です。
このシリーズはこちらのリンクで見れます。
mrubyをPLCで動かせないかな〜という企画
mrubyとは
mrubyは平たくいうと組み込み用途でも利用できる様に軽量化されたrubyです。
GitHub - mruby/mruby: Lightweight Ruby
mrubyはコマンドを入力しながら実行するインタプリター機能があるのですが、更にその機能を除いたリソースの少ないマイコンでも動かせる様にしたmruby/cがあります。
GitHub - mrubyc/mrubyc: mruby/c is an another implementation of mruby.
mruby/cならPLCでも動かせる可能性があるかもしれないということでドンキホーテーの様に挑んで行く予定ですが、3回くらいで伸されて終了になるかも知れません。
一応今までやってきたLadder DriveやPLC間の変換の経験が生かせるのではないかと思ってます。
まずは、mruby/cを動かせるほどのリソースがPLCにあるのか?、動いたとして実用的なものか?という疑問もありますが、やってみないことには判断つきません。
諦めなかったら長期戦になると思います。
Amazon | 三菱電機 FX3G-14MR/ES FXシリーズシーケンサ 基本ユニット(AC電源・DC入力タイプ) NN | 電設用部品・資材 | 産業・研究開発用品 通販
2020年12月08日 [長年日記]
_ ENVELOPE - その5 (MIDI編)
もうBBC Microとは関係ないのですが、成り行きでMIDIファイルについて書いてます。
前回の続きです。
midilibの使い方について調べていきます。
GitHub - jimm/midilib: Pure Ruby MIDI file and event manipulation library
from_scratch.rbが参考になりそうでしたので、一通り見ていきます。
The midilib MIDI file reader only understands MIDI file format 1
midilibではMIDIファイルフォーマット1に対応している様です。 MIDIファイルフォーマット1って何?ってなりますが、今は気に留めず先に進みます。
Gemの読み込みとMIDIモジュールのincludeをしています。
require 'midilib/sequence'
require 'midilib/consts'
include MIDI
まずはMIDIシーケンスオブジェクトを作成しています。
seq = Sequence.new()
次にトラックを追加しています。 最初のトラックは特別でテンポやシーケンス名などのメタ情報が入る専用トラックの様です。 ここでもテンポとシーケンス名を入れています。 ちなみにテンポはbpm_to_mpqでbpsからmpq(μs)に換算しています。
track = Track.new(seq)
seq.tracks << track
track.events << Tempo.new(Tempo.bpm_to_mpq(120))
track.events << MetaEvent.new(META_SEQ_NAME, 'Sequence Name')
次に2トラック目を追加し、トラック名と楽器名(音色)を設定しています。
track = Track.new(seq)
seq.tracks << track
track.name = 'My New Track'
track.instrument = GM_PATCH_NAMES[0]
次にトラックに対しイベントを追加しています。 最初はボリュームを設定しています。127は100%を示す様です。 その下は恐らく音色の指定です。
track.events << Controller.new(0, CC_VOLUME, 127)
track.events << ProgramChange.new(0, 1, 0)
こちらには音符データが入ります。 quarter_note_lengthに4分音符の長さを入れています。 NoteOnで音の開始、NoteOffで終了をしていましす。 最初の引数がMIDIチャンネルで、次が音程、次が長さです。 長さは前のNoteOnまたはNoteOffからの差分で指定する様です。
quarter_note_length = seq.note_to_delta('quarter')
[0, 2, 4, 5, 7, 9, 11, 12].each do |offset|
track.events << NoteOn.new(0, 64 + offset, 127, 0)
track.events << NoteOff.new(0, 64 + offset, 127, quarter_note_length)
end
最後にファイルに書き出して終了です。
File.open('from_scratch.mid', 'wb') { |file| seq.write(file) }
では出来あがったファイルを確認してみます。
GarageBandでも良いのですが、MuseScoreを使ってみます。
音階が表示されちゃんと鳴らすこともできました。
それにしても何でCから始めてないんだろう?
2020年12月02日 [長年日記]
_ ENVELOPE - その4 (MIDI編)
前回ENVELOPE - その3の続きです。
何回も出てますが、このコマンドで鳴る音をMIDIデータに変換して鳴らしてみたいと思いました。
ENVELOPE 2,1,2,-2,2,10,20,10,1,0,0,-1,100,100
SOUND 1,2,100,100
が、ここではたと気付きました。
ENVELOPEでは半音を4分割しているんです。
下表では音階しか書いてませんが、その間隔が4になっているのにようやく気付きました。
ガーン、MIDIで鳴らすことなんてできません。
ENVELOPEはきっと効果音を狙ったコマンドなんですよね。
ということで早々に諦めて、関連ということでMIDIファイルを作るには?に変えてお送りします。
MIDIの知識はありますがスタンダードMIDIファイルについては全く知識を持ち合わせていません。
Wikiを見るとOpcode社のフォーマットが元になっているんですね。Visionは使ってたことがあるので、あのファイル(って覚えてませんが)がスタンダードMIDIファイルに発展していたんですね。
OMSって何?とか思ったりしたもんでした。
RubyでスタンダードMIDIファイルを扱うgemを探したところ、midilibが検索で見つかりました。
GitHub - jimm/midilib: Pure Ruby MIDI file and event manipulation library
とりあえずexampleを確認してみます。 from_scratch.rbというのが参考になりそうです。
midilib/from_scratch.rb at main · jimm/midilib · GitHub
つづく
2020年12月01日 [長年日記]
_ M5StackでPac-Man (2)
前回M5StackでPac-Manのデモまでできる様になりましたが、ジョイスティックがないので移動操作は省いていました。
これを何とか操作できる様にしたいというのが今回の取り組みです。
実は加速度センサーを使って、というアイディアはすぐ浮かんでいて、あとはどのデバイスを使うかということになってました。
使っているM5Stack BASICには加速度センサーはついてなくて残念でしたが、M5StickCとM5Atom Matrixには加速度センサーがついています。
どちらでも良いのですが、より小さいM5Atom Matrixを使用することにします。
M5Atom Matrixからデータを受け取るにはI/Oやシリアル通信でも良いのですが、WiFi通信を使うことにします。
WiFiなら前に作ったirBoard Library for ESP32で作った構造が使えそうです。
irBoardとM5Stackデバイスが接続可能になりました
GitHub - irBoard Library for ESP32.
ということでまずは受信側を作ります。
WiFiのAPモードで動作させアクセスポイントとして振舞う様にします。
受け取るデータはどうしようか悩んで、X、Yの傾きを数値で受けようかとも思いましたが、方向だけもらえれば良いはずなので…と思っていたら、そうだそういうのはテンキーでやるんだよなと思い出し、数値(文字)を貰えば良いということになりました。
↖️7 | ⬆️8 | 9↗️ |
---|---|---|
⬅️4 | 5 | 6➡️ |
↙️1 | ⬇️2 | 3↘️ |
ということで追加したのがこちらです。
add JoyM5Atom. · katsuyoshi/M5Stack-Pacman-JoyPSP@5844133 · GitHub
パソコンで SSIDが Pac-Man のWiFiに接続し、パスワードに JOYM5ATOM を入力します。
ターミナルから以下の様にtelnetで接続します。
数値を入力してリターンキーを押すとその方向に移動します。
telnetだとリターンキーを押さないと送信してくれないのが残念です。(もっといいやり方あると思いますが…)
% telnet 192.168.4.1 8888
4
2
次は送信側です。