[MSX0][PB-1000][ESP8266] カシオのポケコンPB-1000からMSX0を操作する #msx #msx0 #arduino #ESP8266 #カシオ #ポケコン

 


前回のエントリにあるとおり、PB-1000からシリアル通信で送った文字列がMSX0で受信できるようになり、PB-1000とMSX0の通信にも一定の目処が立ってきました。

ここで、ちょっとネタに走ろうと思い、こんなソリューションを作ってみました。


PB-1000をMSX0にシリアルコンソール接続して、PB-1000からMSX0を自在に制御する!

写真にもある通り、PB-1000から入力したコマンドが、そのままMSX0上で実行されていることがわかります。

これだけ見ると、PB-1000とMSX0でどんな実装をしているのか?と思われそうですが、実はPB-1000もMSX0もほとんど何もしていません(自作のプログラムを動かす、という意味では)。

実現手段は以下のとおりです。


またしても、PB-1000とMSX0の間を仲介するArduino(ESP8266)が頑張ってくれているのです。

MSX0で頑張ってIoTプログラムを書くぞと息巻いていたつもりが、いつの間にかArduinoのコードをたくさん書いているような気がしてきましたが、まあ、そういうときもあるということでww


MSX0が内蔵するリモートコンソールサーバ

MSX0はほとんど何もしていませんと書きましたが、それはあくまでも自作のプログラムを動かすという意味では、ということであって、実際はそうではありません。

MSX0は、内部にデフォルトでリモートコンソール用のサーバが動作しており、IPアドレスとポート番号を指定し、外部からソケット通信で文字列を送り付けますと、受け付けた内容をMSX0への入力として反映してもらえます。


Arduino(ESP8266)の実装

つまり、Arduino(ESP8266)では、

  • UARTでのデータ送信を待ち受けて受け付ける
  • 受信が完了したら、MSX0にソケット通信で受信した文字列を送り込む
  • 新たにUARTから通信があれば再度情報を取得し、再びMSX0に文字列を送り込む
というような処理をひたすら頑張ってしています。

こう書いてしまうと割合単純な処理のようにも見えますが、前回、Arduino Mini Proで行ったソフトウェアシリアル通信と、ESP8266で行うソフトウェアシリアル通信はライブラリが少し異なっていて、なかなかUARTの受信がうまく行かずハマってしまいました。

#include <SoftwareSerial.h>

char buf[512];
int receive_buf_pos = 0;
int send_buf_pos = 0;
int max_pos = 0;
volatile boolean received = false;

#include <arduino.h>
#include <esp8266wifi.h>
#include <esp8266wifimulti.h>

#include "secrets.h"

#define HOSTNAME "webserver"

ESP8266WiFiMulti WiFiMulti;
WiFiClient client;

#ifndef D5
#if defined(ESP8266)
#define D8 (15)
#define D5 (14)
#define D7 (13)
#define D6 (12)
#define RX (3)
#define TX (1)
#elif defined(ESP32)
#define D8 (5)
#define D5 (18)
#define D7 (23)
#define D6 (19)
#define RX (3)
#define TX (1)
#endif
#endif

#ifdef ESP32
#define BAUD_RATE 9600
#else
#define BAUD_RATE 9600
#endif

EspSoftwareSerial::UART mySerial;

void setup(){
  delay(3000);
  
  Serial.begin(9600);
  Serial.println("UART receive and SEND to MSX0 RemoteConsole");
  
  mySerial.begin(9600,EspSoftwareSerial::SWSERIAL_8N1,D7,D8,false);
  if(!mySerial) {
    Serial.println("Invalid EspSoftwareSerial pin configuration, check config");
    while(1) {
      delay(1000);
    }
  }
  Serial.println("EspSoftwareSerial pin configuration is valid.");
  
  for(int i=0;i<sizeof(buf);i++) {
    buf[i] = 0xff;
  }

  if (strlen(ssid) == 0) {
    WiFi.begin();
  } else {
    WiFi.begin(ssid, passPhrase);
  }

  WiFi.setHostname(HOSTNAME);

  Serial.printf("Connect to WiFi...\n");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.printf(".");
  }
  Serial.printf("connected.\n");

  client.connect(IPAddress(192,168,1,227),2223);
}

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;

      while(mySerial.available()>0){
        int val = mySerial.read();
        buf[receive_buf_pos] = val;
        receive_buf_pos++;
      }

      for(int i=0;i<receive_buf_pos-1;i++) {
        Serial.print("["+String(buf[i],HEX)+"]");
      }
      Serial.println();

      receive_buf_pos = 0;
      received = true;
      
      Serial.println("\n----EOF----");
}

void loop(){
  
  while(mySerial.available()>0){
    if (receive_buf_pos == 0 && received == false) {
      Serial.println("---- receive start ----");
      Serial.println("serial buffer is "+String(mySerial.available())+" bytes.");
    }
    int val = mySerial.read();
    if (val == 0x1A) {
      eof();
      break;
    } else {
      Serial.write(val);
      buf[receive_buf_pos] = val;
      receive_buf_pos++;
      if (receive_buf_pos == sizeof(buf)) {
        Serial.println("\n---- buffer full ----");
        eof();
        break;
      }
    }
  }

  if (received) {
    Serial.println("---- Socket Send Start ----");
    WiFi.mode(WIFI_STA);

    if ((WiFiMulti.run() == WL_CONNECTED)) {

      for(int i=0;i<max_pos;i++) {
        client.write(buf[i]);
        Serial.write(buf[i]);
      }
      Serial.println("["+String(max_pos-1)+"]");
      Serial.println("---- Socket Send Complete ----");
      received = false;
      delay(1000);
    } else {
      Serial.println("WiFiMulti is not connected...");
    }
  }
  delay(100);
}

PB-1000側の実装

PB-1000側の実装は前回のエントリとほとんど同じようなもので、INPUT文で受け付けた入力を、PRINT#文でCOM0(RS-232C)に出力しているのみの、至極単純なコードです。

1000 OPEN "COM0:7,N,8,1,N,N,N,B,N" FOR OUTPUT AS #1
2000 PRINT "MSX0>";
2010 INPUT C$
2015 IF C$="" THEN 2000
2020 PRINT #1,C$
3000 PRINT #1,CHR$(&H1A)
3010 GOTO 2000

また、PB-1000とMSXで共通に実行できるレベルの互換性のあるプログラムであれば、この仕組みを使ってPB-1000上で作ったBASICプログラムをリモートコンソール経由でMSX0に直接入力させ、実行させてしまうことも可能になります。

というのも、PB-1000は標準で内蔵しているメニューから、BASICプログラムを選択し、[save]→[RS232C]と選択すると、そのBASICプログラムを中間コードではなくテキストデータとしてシリアル出力してもらえます。

[save]をタッチ

[RS232C]をタッチ

ボーレート等を設定し[Enter]押下で送信

ただし、PB-1000はこの[save]→[RS232C]の一連の操作によるテキストデータ送信においては、終了時にEOF(0x1A)を送信しない仕様になっているらしく、これについては別途でEOFだけ送出するプログラムを作っておき、送信後にそのプログラムを実行して解決します。
1000 OPEN "COM0:7,N,8,1,N,N,N,B,N" FOR OUTPUT AS #1
1010 PRINT #1,CHR$(&H1A)
1020 CLOSE #1

※ EOFなしでシリアル通信が途絶したときの対応方法については、別エントリにてまとめる予定です。

ネタ・・・なんですけどねw

まあ、それだけといえばそれだけなのですが、
  • MSX0
  • PB-1000(+FA-7/MD-100)
  • ESP8266
のそれぞれの実機を持っていらっしゃる方には、ぜひ試してみてもらいたいです。

なんだかんだいって、昭和のポケコンPB-1000から、令和に蘇ったMSX0に対して、直接の有線接続をせずに、自由に命令をくだせてしまうのは、なんとも言えず楽しい気分が味わえます。

中の人がこんなテンションの投稿をしてしまうのも頷けるのではないでしょうかw



試すにあたって残る問題があるとすれば、上記の3パターンの組み合わせで機器を保有している人がどのくらいの人数いるかということなんですが・・・w

ちなみにここまでの内容を読んでいただくと分かる通り、クライアント(MSX0に命令を下す側)のマシンはPB-1000でなくても、UART通信ができるポケコン、パソコン等ならなんでも利用することが可能です。ぜひ、ご自身のお気に入りの機種をクライアントにしてみてくださいw

現場からは以上です。


コメント