MSX0のBASICプログラムをAmbientからThingSpeak対応にし、熱中症判定を不快指数ベースに変更した話

みなさん、こんにちは。

IoTデータ可視化サービス「Ambient」の停止に伴い、可視化基盤をThingSpeakに移行したお話は以前の記事でお伝えしました。

M5StickCとRaspberry Pi向けの改修も完了し、わが家の環境は無事に復旧しています。

しかし、常用はしていないものの、MSX0マイコンボード(M5Stack Core2 ベース)で動かしている熱中症アラートプログラム(ALERT.BAS)の移行作業がまだ残っていました。

今回は、このMSX0版プログラムの修正ポイントを解説します。

今回もコードの修正自体はClaude Codeであっという間に完了したのですが、せっかくなので熱中症の判定ロジックも「温度・湿度の単純な閾値判定」から「不快指数(Discomfort Index)」ベースへとアップグレードしてみました。

それでは、早速見ていきましょう!


背景とMSX BASICならではのハードル

MSX0のBASICサンプルには、SEND2NET.BAS というプログラムがあります。DHTセンサーで取得した温度・湿度をAmbientに送信するサンプルなのですが、これを利用して前述の ALERT.BAS という熱中症アラートシステムを組んでいました。

今回、送信先であったAmbientのサービス停止に伴い、ThingSpeakへ対応させる必要が出てきたわけです。

Pythonなど現代の言語と違い、MSX BASICには以下のような独特の制約があります。

  • 変数名が実質先頭2文字までしか区別されない
  • 行番号でジャンプ先を管理するGOTO / IF...THEN
  • JSON文字列もダブルクォートを手動でエスケープしながら組み立てる必要がある

そのため、Python版の移行とはまた違ったBASICならではのポイントがいくつかありました。


修正のポイント

1. JSON送信からform-urlencoded送信へ変更

AmbientはJSON形式でPOSTしていましたが、ThingSpeakの /update エンドポイントはシンプルな application/x-www-form-urlencoded 形式を受け付けます。

これにより、JSONのダブルクォート用に定義していた変数 DQ$=CHR$(&H22) が不要になり、コードをかなりスッキリさせることができました!

Before(Ambient / JSON形式)

120    DQ$=CHR$(&H22):NL$=CHR$(13)+CHR$(10)  'newline character
...
330    CN$="{"+DQ$+"writeKey"+DQ$+":"+DQ$+WK$+DQ$+","
340    CN$=CN$+DQ$+"d1"+DQ$+":"+DQ$+STR$(D1)+DQ$+","
350    CN$=CN$+DQ$+"d2"+DQ$+":"+DQ$+STR$(D2)+DQ$+","
360    CN$=CN$+DQ$+"d3"+DQ$+":"+DQ$+STR$(D3)+DQ$
370    CN$=CN$+"}"+NL$
380    SM$(0)="POST /api/v2/channels/"+CH$+"/data HTTP/1.1"+NL$
390    SM$(1)="Host: 54.65.206.59"+NL$
410    SM$(3)="Content-Type: application/json"+NL$

After(ThingSpeak / form-urlencoded形式)

120    NL$=CHR$(13)+CHR$(10)    'newline character
...
400    CN$="api_key="+WK$+"&field1="+T1$+"&field3="+T2$+"&field4="+T3$+NL$
410    SM$(0)="POST /update HTTP/1.1"+NL$
420    SM$(1)="Host: "+IP$+NL$
440    SM$(3)="Content-Type: application/x-www-form-urlencoded"+NL$

:, などの記号を手作業で連結していた苦労がなくなり、行数も削減できています。

2. STR$()が返す先頭スペースの処理

MSX BASICの STR$() は、正の数を文字列に変換すると先頭に半角スペースが1つ付く仕様になっています(例:STR$(28)" 28")。

AmbientのJSON形式では文字列として扱われていたため実害が出にくかったのですが、form-urlencoded形式では field1= 28 となり、余計なスペースが混入して数値として認識されない懸念がありました。

そこで、先頭が半角スペースの場合に1文字取り除く簡易トリム処理を MID$ で追加しました。

370    T1$=STR$(D1):IF MID$(T1$,1,1)=" " THEN T1$=MID$(T1$,2)
380    T2$=STR$(D2):IF MID$(T2$,1,1)=" " THEN T2$=MID$(T2$,2)
390    T3$=STR$(D3):IF MID$(T3$,1,1)=" " THEN T3$=MID$(T3$,2)

負の数の場合は先頭が - になるため、この処理でも符号が削られることはありません。トリム関数がないレトロBASICではおなじみの定番テクニックですね。

3. サーバーアドレスをホスト名化(IP直書きからの脱却)

以前のAmbient版では _IOTPUT(PA$+ "conf/addr","54.65.206.59") とIPアドレスを直接指定していました。

ThingSpeakは負荷分散でIPが変わる可能性があるため、まずはホスト名 api.thingspeak.com を設定できるように変数化しました。

150    IP$="api.thingspeak.com" 'ThingSpeak server address (hostname or IP)
...
250    _IOTPUT(PA$+ "conf/addr",IP$)

※MSX0のファームウェアバージョンによってはホスト名のDNS解決に対応していない場合もあるため、リポジトリのREADMEには「解決できない場合は nslookup 等で調べたIPアドレスを直接設定する」という手順も記載しています。

4. フィールド名と設定項目の簡素化

Ambientの d1/d2/d3 から、ThingSpeakの field1/field2/field3… へ対応を変更しました。

今回のMSX0構成にはCO2センサーがないため、Python版プロジェクトと仕様を合わせて以下のように割り当てています。

フィールド内容
field1温度(D1)
field2(未使用・将来の拡張用)
field3湿度(D2)
field4バッテリー残量(D3)

また、Ambientでは「チャンネルID」と「ライトキー」の2つが必要でしたが、ThingSpeakの /update エンドポイントは Write APIキー1つだけ で送信可能です。設定項目が減ったのも嬉しいポイントです。

' Before (Ambient)
CH$="xxxxx"  'チャンネルコード
WK$="xxxxx"  'ライトキー

' After (ThingSpeak)
WK$="xxxxx"  'Write APIキーのみ!

5. 熱中症判定を「不快指数」ベースへ変更

今回の改修で最も大きなロジック変更がこちらです!

従来は「温度28℃以上 かつ 湿度60%以上」というシンプルな条件で判定していましたが、これだと「気温27℃・湿度95%」といった非常に蒸し暑い環境でアラートが鳴らないという課題がありました。

そこで、気象庁の統計等でも用いられる不快指数(Discomfort Index / DI)による判定へ変更しました。

不快指数 = 0.81 * 気温 + 0.01 * 湿度 * (0.99 * 気温 – 14.3) + 46.3

BASICでは以下のように計算できます。

900 'calculate discomfort index (fukaishisu) and check condition for Google Home Notifier alert
910    DI=0.81*D1+0.01*D2*(0.99*D1-14.3)+46.3
920    PRINT NL$+"Discomfort Index (Fukaishisu): "+STR$(DI)
930    IF DI < DT THEN 1600 'discomfort index below threshold, skip Google Home notification

閾値(DT)はデフォルトで「80」に設定しました。一般的に不快指数80以上は「暑くて汗が出る」水準とされ、熱中症警戒の目安としてぴったりです。

不快指数体感の目安
〜75未満暑さを感じない
75〜80やや暑い
80以上暑くて汗が出る(今回の警戒ライン)
85以上暑くてたまらない(危険水域)

Google Homeからの発話メッセージも、不快指数を読み上げる形にアップデートしました。

950        DR=INT(DI+0.5)
960        R1$=STR$(DR):IF MID$(R1$,1,1)=" " THEN R1$=MID$(R1$,2)
...
980        GM$="fukaishisu wa "+R1$+" desu. Ne-chuushou ni chuui shite kudasai."

不快指数は少数点以下まで計算されるため、INT(DI+0.5) で四捨五入して整数化しています。これで「不快指数は83.276デス」といった不自然な発話にならず、スムーズに読み上げてくれます。

6. デバッグモードのダミー値バグを修正

ロジックを書き換えた後、デバッグモード(DB=1)でGoogle Home(Google Nest Mini)への通知テストを行ったところ、なぜかアラートが飛ばない現象が発生しました。

計算してみると……

  • 旧デバッグ値(28℃ / 60%) → 不快指数:約77.03(閾値80未満!)

旧ロジック(28℃かつ60%)ではギリギリ条件を満たしていた数値でしたが、不快指数に移行したことで閾値を下回ってしまい、デバッグモードなのに通知ルートを通らなくなっていたのです。

ロジックを変更したらテストデータも見直さなければならない、という典型的な例ですね!

デバッグ値を D1=31, D2=60(不快指数:約81.24)に変更し、無事にGoogle Homeからアラートが発話されることを確認しました。

' Before
230    IF DB=1 THEN PRINT "DEBUG MODE: Testing Google Home Notifier Only":D1=28:D2=60:GOTO 900

' After
230    IF DB=1 THEN PRINT "DEBUG MODE: Testing Google Home Notifier Only":D1=31:D2=60:GOTO 900

コード全面見直しで見つかった潜在バグ

今回行番号を整理しながら全体を見直したことで、ネットワーク接続失敗時に GOTO 580 と書かれているものの、580行が存在しない という潜んでいたバグを発見しました。

以前のコードは GitHub Copilot で開発したのですが、行番号の採番がメチャクチャだったため、手動で番号修正した際にバグを作りこんでしまっていたようです。

実行時にエラーになるところでしたので、正しい待機ループの手前へジャンプするようあわせて修正しました。全体のリファクタリングはこうした潜在バグを見つける良い機会になりますね。


移行ポイントのまとめ

MSX0のBASICプログラムをAmbientからThingSpeakへ移行する際のポイントをまとめます。

  1. JSONからform-urlencodedへの変更でメッセージ構築がシンプルに!
  2. STR$()の先頭スペースMID$ で削って数値フォーマットを綺麗に整える
  3. 送信先をホスト名指定に変更(DNS未対応環境向けの手順も用意)
  4. フィールド名を field1〜 に変更し、設定項目をWrite APIキーのみに簡素化
  5. 熱中症判定を不快指数ベースにし、より実用に即した判定へ進化!
  6. ロジック変更時はデバッグ用のダミーデータも忘れずに検算する

レトロなBASIC言語であっても、API仕様の違いを丁寧に把握し、ロジック変更に合わせてテストデータまで見直すという基本は、現代のソフトウェア開発と全く変わりません。

修正後のフルコードはGitHubにて公開していますので、MSX0をお持ちの方はぜひ試してみてください!


「自分もMSX0で何か作ってみたい」と思われた方、そのアイデア、頭の中で寝かせておくのはもったいないかもしれません。

ビューローみかみでは、構想段階の壁打ちからPoC・実装・現場導入まで、現場で「使い続けられる」ものづくりを支援しています。

アイデアを短期間で「動くもの」にし、PoCで終わらせず、実運用まで伴走します。まずは「これ、作る価値ありますか?」という壁打ちからでも大歓迎です。

▶ PoC・MVP開発サービスの詳細はこちら

気になることがあればお気軽にご相談ください。

本日も最後までお読みいただき、ありがとうございました。

それでは、よいMSXライフを!

カテゴリ: IoT

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール