LoginSignup
0
0

【MSX0】SEND2NET.PAS【Turbo Pascal】

Last updated at Posted at 2023-12-22

はじめに

『MSX0 Stack』付属の IoT BASIC サンプルを『Turbo Pascal』へ移植してみます。

SEND2NET.BAS

◆サーバーとの送受信

項目 説明
概要 温度湿度センサーから値を取得しサーバーへ送信します。
ファイル名 SEND2NET.BAS
対応デバイス DHT11 (水色)
DHT20 (黒色)
コメント IoT データの可視化サービスを行われているアンビエントデーター株式会社様のサーバーにセンサーから取得した値を送信します。送信したデータはサーバー側でグラフ化され、PCやスマートフォンのブラウザから閲覧できます。Ambient サービスはとてもお手軽に IoT を実現できるため、こちらのサンプルコードを用意させていただきました。

このサンプルを動作させるには外部センサーが必要です。

See also:

Ambient

Ambient は IoT データの可視化サービスです。マイコンなどから送られるセンサデータを受信し、蓄積し、可視化(グラフ化)します。

image.png

事前にユーザー登録 (無料) が必要です。サイト右上のボタン (ハンバーガーアイコンの中にあるかもしれません) からユーザー登録を行います。

image.png

ユーザー登録を行うと登録したメールアドレスに 「Ambient」新規登録確認メール というメールが送られてきますので、メールに書かれている URL をクリックしてユーザー登録を完了させます。

メールアドレスによっては新規登録確認メールが送られてこない事があるようです。そのような場合には GMail 等でメールアカウントを作成しましょう。

ユーザー登録が終わってログインすると [チャネル一覧] からチャネルを作成できるようになります。

image.png

MSX0 Stack での挙動

まず事前にセンサーをつないでおきます。

image.png

image.png

右中央のセンサーが温湿度センサーなのですが、 『Grove Beginner Kit for Arduino』の製造ロットによって、センサーの種類と MSX0 への接続先が異なります。具体的には 2022/04 以降製造分に DHT20 が付属するようです。

センサー 接続方法 Grove ポート
[D3] DHT11 (水色) Digital Port C (水色)
[I2C] DHT20 (黒) I2C Port A (赤)

『Grove Beginner Kit for MSX0』には DHT20 が付属します。

SEND2NET.BAS をロードして、

image.png

LIST 140-180 を実行します。xxx となっている所に Ambient のチャネル情報を入力します。

image.png

変数 説明
140 CH$ Ambient のチャネル ID
150 WK$ Ambient のライトキー
170 WT 待ち時間 (秒)
180 RB リブートフラグ

とりあえず 140 行目と 150 行目を書き替えて実行します。

image.png

30 秒おき (変数 WT で変更可能) に Ambient へデータが送られます。データは Ambient のチャネル情報 で確認できます。

image.png

変数 RB の値がデフォルトの 0 の場合には、BASIC プログラムのループ内で定期的にデータが送られますが、変数 RB の値が 1 の場合には、データが送られると MSX0 Stack が再起動します。つまりウォッチドッグタイマー的な動作をします。

BASIC プログラムを書き替えたら、

SAVE "AUTOEXEC.BAS"

として保存すると、MSX0 Stack 起動時に AUTOEXEC.BAS が読み込まれ、データを Ambient へ送信して再起動...を繰り返します。

プログラムは〔Ctrl〕+〔Stop〕(リモートコントロールパネルからは〔Ctrl〕+〔F12〕)で中断できます。ファイル削除コマンドは KILL です。

See also:

Turbo Pascal へ移植

別途、MDL-LIB と SYSUTILS.LIBIOT.LIB が必要です。

SEND2NET.PAS
program SEND2NET;
type
  IntStr = string[8];
{$I MDLLIB.LIB}
{$I SYSUTILS.LIB}
{$I IOT.LIB}
const
  HS = '54.65.206.59';
  CH = 'xxxxx';
  WK = 'xxxxxxxxxxxxxxxx';
  PA = 'msx/me/if/NET0/';
  WT = 30;
  RB = 0;
var
  Flg: Boolean;
  I, D1, D2, D3: Integer;
  SM: array [0..8] of LibStr;
  function ToString(v: Integer): IntStr;
  var
    s: IntStr;
  begin
    Str(v, s);
    ToString := s;
  end;
  function MLen(s, e: Byte): Byte;
  var
    i, v: Integer;
  begin
    v := 0;
    for i:=s to e do
      v := v + Length(SM[i]);
    MLen := v;
  end;
begin
  { user setting }
  if CH ='xxxxx' then
  begin
    Writeln('please change the value CH and WK to your enviroment.');
    Exit;
  end;
  Flg := RB = 1;
  repeat
    { connect }
    IoTPutStr(PA + 'conf/addr', HS);
    IoTPutInt(PA + 'conf/port', 80);
    IoTPutInt(PA + 'connect', 1);
    { check connect status }
    Delay(500);
    if IoTGetInt(PA + 'connect') <> 1 then
      { error }
      Writeln('connect fail')
    else
    begin
      { get sensor value }
      D1 := IoTGetInt('device/dht/temperature');
      D2 := IoTGetInt('device/dht/humidity');
      D3 := IoTGetInt('host/battery/level');
      { create message }
      SM[5] := '{"writeKey":"' + WK + '",';
      SM[6] := '"d1":"' + ToString(D1) + '",';
      SM[7] := '"d2":"' + ToString(D2) + '",';
      SM[8] := '"d3":"' + ToString(D3) + '"}'#13#10;
      SM[0] := 'POST /api/v2/channels/' + CH + '/data HTTP/1.1'#13#10;
      SM[1] := 'Host: ' + HS + #13#10;
      SM[2] := 'Content-Length: ' + ToString(MLen(5, 8)) + #13#10;
      SM[3] := 'Content-Type: application/json'#13#10;
      SM[4] := #13#10;
      { send message }
      Writeln(#13#10'---- Send Message ----');
      for I:=0 to 8 do
      begin
        Write(SM[I]);
        IoTPutStr(PA + 'msg', SM[I]);
      end;
      Delay(500);
      { receive message }
      Writeln(#13#10'---- Receive Message ----');
      for I:=0 to 10 do
      begin
        Write(IoTGetStr(PA + 'msg'));
        Delay(100);
      end;
    end;
    { disconnect }
    IoTPutInt(PA + 'connect', 0);
    { loop }
    if RB = 0 then
    begin
      { wait }
      Writeln(#13#10'---- Wait (', WT, ' sec ) ----');
      sys_timer := 0;
      while (sys_timer < WT * 60) and (not Flg) do
        Flg := Flg or KeyPressed;
    end
    else
    begin
      { system reboot }
      Writeln(#13#10'---- Sleep (', WT, ' sec ) ----');
      IoTPutInt('host/power/wait', WT);
      IoTPutInt('host/power/reboot', 1);
    end;
  until Flg;
end.

定数を環境に合わせて書き替えてください。

定数 説明
CH Ambient のチャネル ID
WK Ambient のライトキー
WT 待ち時間 (秒)
RB リブートフラグ

実行してみました。

image.png

センサーが正しく接続されていれば Ambient へデータが送られます。

RB を 1 に設定する場合 (ウォッチドッグタイマー的に使う) には実行形式ファイル (*.COM) を生成し、AUTOEXEC.BAT (または REBOOT.BAT) で自動実行されるようにしてください。

任意のキーで中断できます。

解説

IoT ルーチン

MSX BASIC の文字列は最大 255 文字で、IoT BASIC の送信文字列もその影響を受けるのですが、内部では 64 byte 区切りで送受信してるようです。Turbo Pascal の IOT.LIB はこれを考慮していません。つまり、自分で 64 byte 以下に区切って送信する必要があります。

IOT.LIB を修正するかは未定です。結局、255 文字を超える文字列をやりとりする際には BASIC でも分割処理を行う必要があるので…

配列

Content-Length の値を算出するために、配列への代入の順序を変更しています。

TIME システム変数 (BASIC)

BASIC の TIME システム変数は ワークエリア FC9E (JIFFY) と同じです。MDLLIB.LIB では sys_timer として定義されています。

See also:

おわりに

文字列分割の所が少し厄介でしたね。コードも少し長くなってしまいました。

記事に関連しますが、以前 MSX-DOS で電源断または再起動を行うトランジェントコマンド M0SHTDN を作った事があります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0