はじめに
『MSX0 Stack』付属の IoT BASIC サンプルを『Turbo Pascal』へ移植してみます。
I2C.BAS
◆I2Cアクセス
項目 | 説明 |
---|---|
概要 | DHT20 に対して直接 I2C アクセスを行い温度を取得します。 |
ファイル名 | I2C.BAS |
対応デバイス | DHT20 (黒色) |
コメント | Seeed Grove Kit の DHT20(黒色)温度湿度センサを Port A に接続してください。温度取得の際にCRCチェックなどは省略しています。 |
このサンプルを動作させるには外部センサーが必要です。
See also:
MSX0 Stack での挙動
まず事前にセンサーをつないでおきます。
右中央のセンサーが温湿度センサーなのですが、 『Grove Beginner Kit for Arduino』の製造ロットによって、センサーの種類が異なります。具体的には 2022/04 以降製造分に DHT20 (黒色) が付属するようです。DHT11 (水色) は I2C 接続のセンサーではないので使えません。
センサー | 接続方法 | Grove ポート |
---|---|---|
[I2C] DHT20 (黒) | I2C | Port A (赤) |
『Grove Beginner Kit for MSX0』には DHT20 が付属します。
I2C.BAS
をロードして、
実行してみました。アレ?行番号 160 のトコでエラーになりますね。
160 FORI I=0 TO C-1:IF A$(I)="38" THEN 180 ELSE NEXTI
I
が一つ多いようです。
160 FOR I=0 TO C-1:IF A$(I)="38" THEN 180 ELSE NEXTI
今度は正しく表示されました。
センサーが正しく接続されていないとその旨を表示して終了します。
プログラムは〔Ctrl〕+〔Stop〕(リモートコントロールパネルからは〔Ctrl〕+〔F12〕)で中断できます。
See also:
Turbo Pascal へ移植
別途、MDL-LIB と SYSUTILS.LIB
、IOT.LIB
が必要です。
program I2C;
type
LibStr = string[80];
{$I SYSUTILS.LIB}
{$I IOT.LIB}
var
List, Last, p: PItem;
N, S: LibStr;
T: Real;
Flg: Boolean;
begin
ClrScr;
Flg := False;
List := nil;
N := 'device/i2c_a';
IoTFind(N, List, Last);
p := List;
while (p <> nil) and (not Flg) do
begin
if p^.Data = '38' then
begin
Flg := True;
N := N + '/' + p^.Data;
end;
p := p^.Next;
end;
ClearListItem(List);
if not Flg then
begin
Writeln('DHT20 is not found...');
Exit;
end;
while True do
begin
IotPutStr(N, #$71);
S := IoTGetStr(N);
repeat
IotPutStr(N, #$AC#$33#$00);
S := IoTGetStr(N);
if KeyPressed then
Exit;
until (Length(S) >= 6) and (Ord(S[1]) and $18 = $18);
T := Ord(S[4]) and $0F;
T := T * 256 + Ord(S[5]);
T := T * 256 + Ord(S[6]);
T := T / 1048576.0 * 200 - 50;
Writeln(T:5:2);
end;
end.
実行してみました。
センサーが正しく接続されていれば温度で値が変動します。
任意のキーで中断できます。
解説
文字列 -> 配列変換
Turbo Pascal の文字列は文字型配列としてアクセスできるので、文字列 -> 配列変換は行っていません。
ループ (1)
GOSUB で記述されていたものをループで書き替えました。何をやっているか解りやすくなっているかもです。
ループ (2)
repeat のループは次のようになっています。
repeat
IotPutStr(N, #$AC#$33#$00);
S := IoTGetStr(N);
if KeyPressed then
Exit;
until (Length(S) >= 6) and (Ord(S[1]) and $18 = $18);
Turbo Pascal 3.0 は式の評価が標準 Pascal と同じ完全論理評価なので、Length(S) が 0 の場合 (文字列が存在しない場合) でも S[1] にアクセスしてしまう問題を孕んでいます。
例えば次のコードは i が 11 になった時でも (a[i] = 0)
が評価されてしまい、実行時エラーになります。
program EvalTest;
{$R+}
var
i: Integer;
a: array [1..10] of Byte;
begin
i := 0;
repeat
i := i + 1;
until (i > 10) or (a[i] = 0);
end.
なので、万全を期すなら2重ループでなくてはいけません。
repeat
repeat
IotPutStr(N, #$AC#$33#$00);
S := IoTGetStr(N);
if KeyPressed then
Exit;
until Length(S) >= 6;
until Ord(S[1]) and $18 = $18;
ただ、Turbo Pascal 3.0 の文字列型はサイズが決まっており s[1] は範囲外ではないため、Length(S) が 0 の時に S[1] へアクセスしても問題ありません。
べき乗
BASIC での温度計算は次のようになっています。
370 T=D(3) AND &HF
380 T=T*256+D(4)
390 T=T*256+D(5)
400 T=T/(2^20)*200-50
Pascal にはべき乗演算子 (^
) がないので、展開した 1048576
という値を使っています。ただ、そのままだと整数値として扱われてしまう (オーバーフローでエラー) ので、小数点を付けて 1048576.0
にし、実数型の定数としています。
べき乗の部分は次のようにする事もできますが、速度が目に見えて落ちます。やはり展開した実数型の定数を使うのが正解だと思います。
T := T / Exp(Ln(2) * 20) * 200 - 50;
See also:
おわりに
GOTO 抜き&マルチステートメントなしのプログラムなので、オリジナルの BASIC ソースに比べると少々長くなってはいますが、その分読みやすいプログラムにはなっているかと思います。