みなさん、こんにちは。
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へ移行する際のポイントをまとめます。
- JSONからform-urlencodedへの変更でメッセージ構築がシンプルに!
STR$()の先頭スペースをMID$で削って数値フォーマットを綺麗に整える- 送信先をホスト名指定に変更(DNS未対応環境向けの手順も用意)
- フィールド名を
field1〜 に変更し、設定項目をWrite APIキーのみに簡素化 - 熱中症判定を不快指数ベースにし、より実用に即した判定へ進化!
- ロジック変更時はデバッグ用のダミーデータも忘れずに検算する
レトロなBASIC言語であっても、API仕様の違いを丁寧に把握し、ロジック変更に合わせてテストデータまで見直すという基本は、現代のソフトウェア開発と全く変わりません。
修正後のフルコードはGitHubにて公開していますので、MSX0をお持ちの方はぜひ試してみてください!
- GitHubリポジトリ: sendalert_for_msx0
「自分もMSX0で何か作ってみたい」と思われた方、そのアイデア、頭の中で寝かせておくのはもったいないかもしれません。
ビューローみかみでは、構想段階の壁打ちからPoC・実装・現場導入まで、現場で「使い続けられる」ものづくりを支援しています。
アイデアを短期間で「動くもの」にし、PoCで終わらせず、実運用まで伴走します。まずは「これ、作る価値ありますか?」という壁打ちからでも大歓迎です。
▶ PoC・MVP開発サービスの詳細はこちら
気になることがあればお気軽にご相談ください。
本日も最後までお読みいただき、ありがとうございました。
それでは、よいMSXライフを!



