※本記事に掲載のソースは、GitHubにも公開しております。
MSX0といえばIoT BASIC(IoT関連機能の拡張命令を追加した拡張BASIC)ということで、IoTっぽいことをやってみようと思ったのですが、温湿度センサーとか持っていないので、まずは疎通確認的なのできないかなあということで。
I2C通信については、添付のディスクイメージ(SAMPLE.DSKファイル)にI2C.BASというサンプルコードが入っているので、それをベースにサンプルコードよりもより単純な通信を試してみました。
通信というからには通信相手も必要です。
I2Cはマスター・スレーブでの主従関係を持つ通信になるのですが、MSX0側をマスターとみなすと考えますと、スレーブになる通信相手が必要です。
こういう時は、何と言ってもお手軽さが群を抜いているのがArduinoですね。
文献がネットにたくさんあるので、すぐにサンプルを持ってきて試すことができます。
ということで、まずは、マスター(MSX0)側からスレーブ(Arduino)側へ一方的に値を送信するパターンです。
スレーブ(受信側)の準備
スレーブ(Arduino)側のコードについては、ネットを調べるといろいろとサンプルコードが出てきます。中の人は、こちらの記事を参考に作ってみました。というか、お手本ほとんどそのままです。
Arduino初心者編:I2C通信によるArduino間のデータ送受信 | STEMSHIP https://stemship.com/arduino-beginner-i2c/
このページを参考に作ったスレーブ側の待ち受けプログラムです。
動作としては、マスターからのデータ送信を待ち受け、データを受け取ったらシリアルコンソールに表示するというだけの単純なもの・・・などと偉そうに言っておりますが、上記の記事のおかげで短時間で完成することができました。ありがとうございます。
#include "Wire.h"
volatile byte receiveValue = 0;
volatile boolean received = false;
void setup() {
Wire.begin(8);
Wire.onReceive(dataReceive);
Serial.begin(9600);
Serial.println("I2C slave.");
}
void loop() {
if (received) {
Serial.println(receiveValue);
received = false;
}
}
void dataReceive(int number) {
if(Wire.available()) {
receiveValue = Wire.read();
received = true;
}
}
ちなみに、Wire.begin(8);で指定している引数8は、スレーブのデバイスIDの指定と思われます。特に決まりみたいなものはないと思うのですが、今回はこのスレーブのデバイスIDを8としました。
[追記] ちなみにスレーブのデバイスIDは何でも良いわけではなく、予約されている番号などもあるので、注意が必要です。
また、Wire.onReceive()で指定しているリスナー関数 dataReceiveには引数としてint型のnumberを受け取るようになっていますが、このnumberはマスターから送信されるデータのバイト数になります。なので、本来は関数内のWire.read()はnumberの数分だけ繰り返し受け取る必要がありますが、今回は1バイト送信決め打ちにしているので無視しています。
続いていよいよMSX0側です。
マスター側(MSX0側)の準備
_IOTFIND("device/i2c_a",C)
で、おそらくI2Cスレーブデバイスの個数が変数Cに格納されて返されるのではないかと思います。マニュアルでは「対象のノードが持つアイテムの個数を取得します」とあるので、I2Cの場合はスレーブの数なのではないかと思われます。引数に指定している"device/i2c_a"は、デバイスのノードパスで、アクセスしたいデバイスに応じて変化します。ちなみに、"i2c_a"と「A」がついているのは、PORT Aを示しているものと思われます。_IOTFIND("device/i2c_a",A$(0),C)
のように配列変数を指定すると、アイテム名(I2Cの場合はスレーブのデバイスID)が配列としてA$に格納されます。変数Cにはおそらく先ほどと同じくスレーブデバイスの数が返されると思われます。N$="device/i2c_a/"+A$(I)
の箇所です。_IOTPUT(ノードパス,送信したい値)
と指定します。8bitの情報を送りたい場合は、CHR$(送りたい値)のように文字列として指定するようです。複数バイト一挙に送りたい場合は、CHR$(nn)を連結して複数文字の文字列とすればよいようです。MSX0とArduinoを接続
MSX0のPORT Aは、向かって左から「SCL(クロック信号)」「SDA(データ)」「VCC」「GND」となっています。電源も含めてArduino側に供給することもできると思われるのですが、中の人はGroveコネクタ(M5StackのPORTに繋ぐ用の4ピンケーブル)を持っていないくて手持ちのジャンプワイヤーで繋いでいるのですが、これだとワイヤの幅が足りず3本までしか繋げないっぽいので、とりあえずArduino側の電源は別で取ることにします。(Groveケーブルはポチったので、届いたらまた試してみます)
それと、MSX0のマニュアルに「MSX0貸出機ではPORTAのI2CとFaccsを同時に利用することはできないため、Facesを外してください。BattryBotomを本体に装着してPORTAのI2Cを利用することは可能です。」という記載があったので、ここでも、FacesからはMSX0本体は取り外してBatteryBottomを付けています。
スレーブから値を受け取れるようにする
#include "Wire.h"
volatile byte receiveValue = 0;
volatile boolean received = false;
void setup() {
Wire.begin(8);
Wire.onReceive(dataReceive);
Wire.onRequest(requestEvent);
Serial.begin(9600);
Serial.println("I2C slave.");
}
void loop() {
if (received) {
Serial.println(receiveValue);
received = false;
}
}
// numberは送信されてくるバイト数
void dataReceive(int number) {
if(Wire.available()) {
receiveValue = Wire.read();
received = true;
}
}
void requestEvent() {
Wire.write(9-receiveValue);
}
次に、MSX0側ですが、_IOTPUT()を実行した直後に、_IOTGET()を使ってスレーブからの送信値を受け取ります。_IOTGET(ノードパス,受け取りたい値)という感じに指定ができます。
リストはこんな感じです。
実行結果のスクリーンショットです。
Arduino側ではマスター(MSX0)から送信されてきた値(0,1,2,3・・・9)が表示されているのに対して、MSX0側ではスレーブから送信されてきた「9-マスターからの送信値」→ 9,8,7・・・0が表示されていることが分かり、正しくスレーブからの値を受け取れているらしいことがわかります。
[追記] ちなみに、_IOTGET()で得られる値なのですが、上記リストではASC(S$)として1バイト分のデータだけを取り出していますが、どうも実際には_IOTGET()で返却される文字列は16バイト固定のようです。ASC()関数は文字列の先頭1文字目のアスキーコードを返す仕様なので、上記のコードではあたかもスレーブから1バイトだけ受け取っているように見えていますが、実際には後ろの15文字は捨てていることになります。実際、スレーブからは1バイトしか返信していないので、実際上の問題はないのですが。
なにぶんにも、中の人は電子工作、電子回路については全くの素人なので、いろいろと間違いもあるかと思います。
Arduinoは互換機含め様々入手経路があり、価格も安価なものがたくさんありますが、MSX0については、2023年9月時点で破損すると、基本的には一般販売開始を待たなければいけなくなりますので、この記事の内容を試される場合はくれぐれも自己責任でお願いいたします。
また何か間違いを発見された場合は、ブログのコメントかTwitter等でご指摘いただけましたら幸いです。
コメント
コメントを投稿