[MSX0][Arduino] シリアル通信で受け取った内容をI2Cで読み出す #msx #msx0 #arduino #カシオ #ポケコン


このところ、MSX0でいろいろ遊んでいる中の人ですが、中の人といえばいわゆる「ポケコンバカ」でもあり、MSX0とのIoTの輪にポケコンを加える余地はないものかと常々考えておりました。

しかしながら、中の人がターゲットにしているポケコン(PB-1000)で可能な外部通信手段といえば、RS−232Cのシリアル通信、あとは中の人が超適当な実装でアウトプットだけ実現したなんちゃってSPI通信くらいしかありません。

一方、MSX0は今のところIoT BASICでは I2C、HTTP(ソケット通信)、PWMによるアナログ入出力しか情報を外部デバイスから入出力する手段を持っていないため、何かしらで相互の通信手段を変換、吸収する手段がなければやりとりはできません。


Arduinoでシリアル通信の受信とI2Cスレーブを行う

そこで、WebAPI通信のときと同じく、Arduinoに両者の仲介をしてもらいます。


ちなみにArduino Mini Proと接続しているST7735(1.8inch TFT Display)は本件の目的とは直接関係ないのですが、直近でいろいろ試していた流れで、そのまま使ってしまっています。なくても全く支障はありません。

Arduinoは「UARTでシリアル通信を受信」「I2C SlaveとしてMasterからの要求に応じてデータ送信」の2つの役目を持っています。

まずいったんはシリアル通信の受信を待ち受けて受信し、受信が完了したあとにI2C Masterからの要求があったら、16バイトずつまとめて受信内容を返信しています。

#include <SoftwareSerial.h>
SoftwareSerial mySerial(2,3);    // RX,TXの割り当て
char buf[512];
int receive_buf_pos = 0;
int send_buf_pos = 0;
int max_pos = 0;
volatile boolean received = false;

#include "Wire.h"
int i2c_slave_addr = 0x08;

void setup(){
  
  Serial.begin(115200);
  
  mySerial.begin(9600); // ソフトウェアシリアル通信の開始(ボーレート9600bps)
  pinMode(2,INPUT);
  pinMode(3,OUTPUT);

  for(int i=0;i<sizeof(buf);i++) {
    buf[i] = 0xff;
  }

  delay(500);

  Wire.begin(i2c_slave_addr);
  Wire.onReceive(dataReceive);
  Wire.onRequest(requestEvent);

}

void eof() {
      max_pos = receive_buf_pos-1;
      Serial.println("receive_buf_pos = "+String(receive_buf_pos));
      Serial.println("max_pos = "+String(max_pos));
      send_buf_pos = 0;
      receive_buf_pos = 0;
      received = true;
      Serial.println("\n----EOF----");
}

void loop(){
  boolean started=false;
  
  while(mySerial.available()>0){
    started=true;
    if (receive_buf_pos == 0 && received == false) {
      Serial.println("---- receive start ----");
    }
    int val = mySerial.read();       // 受信したデータを読み込む
    if (val == 0x1A) {
      eof();
      break;
    } else {
      Serial.write(val);
      Serial.println("["+String(mySerial.available())+"]");
      buf[receive_buf_pos] = val;
      receive_buf_pos++;
      if (receive_buf_pos == sizeof(buf)) {
        Serial.println("\n---- buffer full ----");
        eof();
        break;
      }
    }
  }

  int timeout = 0;
  while(started && mySerial.available()<=0 && received==false && send_buf_pos == 0) {
      if (timeout>15) {
        Serial.println("Timeout.");
        eof();
        started = false;
        break;
      }
      Serial.println("(delay 100ms["+String(timeout)+"])");
      delay(100);
      timeout++;
  }
  delay(100);
}

void dataReceive(int number) {
}

void requestEvent() {
  if (received && send_buf_pos <= max_pos) {
    for(int i=0;i<16;i++) {      
      if (send_buf_pos>max_pos) {
        Wire.write(0);
      } else {
        Wire.write(buf[send_buf_pos]);
        Serial.write(buf[send_buf_pos]);
        send_buf_pos++;
      }
    }
  }
  if (send_buf_pos >= max_pos) {
    received = false;
    send_buf_pos = 0;
  }
}

MSX0側の実装

MSX0側の実装は比較的単純で、簡単に言えば_IOTGET()で応答があるまでひたすら待ち、応答があったらPRINT文で画面に表示。これだけ、というかんじです。

1 'SAVE"I2CARECV.BAS"
1000 'Init
1010   _IOTFIND("device/i2c_a",C)
1020   PRINT "IoT Nodes:";C
1030   IF C=0 THEN PRINT "Device not found.":END
1040   _IOTFIND("device/i2c_a",A$(0),C)
1100   N$="device/i2c_a/"+A$(0)
1110   PRINT "Node item: "+N$
2000 'Main loop
2010   PRINT "---- I2C receive ----"
2500   _IOTGET(N$,S$)
2510   IF S$="" THEN PRINT ".";:GOTO 2500
2520   IF ASC(S$)=0 THEN 2500
2530   PRINT S$;
2990   GOTO 2500

PB-1000(ポケコン)側の実装

ポケコン側の実装もごく簡単で、送り出したい文字列内容を PRINT #文でシリアルポートに書き出すだけです。

1000 OPEN "COM0:7,N,8,1,N,N,N,B,N" FOR OUTPUT AS #1
2000 FOR I=0 TO 19
2010 PRINT I;":CASIO PB-1000!"
2020 PRINT #1,STR$(I);":CASIO PB-1000!"
2030 NEXT I
3000 PRINT #1,CHR$(&H1A)
3010 CLOSE #1

これで、PB-1000側からシリアルで送り出した文字列が、I2C通信でMSX0に送られるようになります。

現場からは以上です。



コメント