chakokuのブログ(rev4)

テック・コミック・ごくまれにチャリ

お越しいただきありがとうございます。

このブログでは主に以下の内容を記載しています(自分の記録用)。

  • 何か作った話(自分は何かを作るために生きている..できたらクスっと笑えるものや、アートと絡みたい)(何かに手を出しても続かず、飽きるのが問題)
  • 音楽(AerophoneやDTM(FL-Studio))を勉強しようと思ったが、音楽はレッスンが続けられないので、、絵に転向*1
  • 趣味のロードバイク(坂道をひーこら走ると結構大変な人生(笑)と重なる。。仕事のストレスが軽いと走らなくなる。。いかん)

記事が渾然一体となってカオス状態ですが、マイコンのプログラムも、ちゃりで走るのも、料理するのも、自分の中ではそれぞれが繋がっているので、カオスな日常をそのまま反映させています。リンクや引用はご自由にどうぞ。質問等ありましたらコメントに書いてください。微力ながら自分の分かる範囲で回答します。
とにかく、、人に「あほやなぁ」と言われる事に精進するのが我が人生。

*1:音楽にも絵にも失礼だろうか。。自分

GASでWebAPIを試作、doPostに渡される引数の仕様がよくわからない->そのまま応答文にして確認

課題:doPostに渡される引数eの内容がよくわからない。デバッグ用のログ出力もよくわからず
結果:そのまま応答文で返すように実装、POST本文は、 e.postData.contentsで取得できると理解

確認用のGASのソース

function doPost(e) {

  //var params = JSON.stringify(e);
  //var postContent = e.postData.getDataAsString();

  return ContentService.createTextOutput(JSON.stringify(e)).setMimeType(ContentService.MimeType.JSON)
}

API実行コマンド

curl -X POST -H "Content-Type: application/json"  -H "myarg: i_dont_know"  -d "{1234 : 5678}"  $URL


応答文(直接は返却されず、redirect先のURLにアクセスすると受け取れる)

{
  "parameters": {},
  "contextPath": "",
  "parameter": {},
  "postData": {
    "contents": "{1234 : 5678}",
    "length": 13,
    "name": "postData",
    "type": "application/json"
  },
  "queryString": "",
  "contentLength": 13
}

上記より、POSTの本文は、e.postData.contentsで取得できる。また、リクエストヘッダの値はdoPostには渡されない

■追記
以下のコードでJSON形式の本文(文字列)をオブジェクトに変換できると理解

function doPost(e) {

  var data = JSON.parse(e.postData.contents);
  // dataに対して属性名でアクセス可

}

Sensirion のCO2センサ(SCD30)を接続

やりたいこと:SensirionのCO2センサ(SCD30)を購入した(マルツオンラインで)。SCD30をESP32に接続してCO2を計測したい。
結果:いつものようにハマりポイントにはまったが、最終的にはCO2が計測できた

SCD30は結構高いセンサですが、高精度らしいです。ここ一か月ぐらいMH-Z19Cで部屋のCO2を計測していますが、計測誤差が蓄積されるようで*1、SCD30とどの程度差が出るのかを確認したいと思っています。あとは、、BME280の温湿度と、SCD30の温湿度でどのぐらい差が出るかを確認する予定。

センサ類はキャリブレーションがキモと理解しているのですが、どうやって補正するのか仕組みがよくわからず、理解のためにマニュアルを和訳
1.4.6 (De-)Activate Automatic Self-Calibration (ASC)

  • 下記のコマンドにより、継続的な自動セルフキャリブレーションを有効化できます
  • 初めてASCを有効化した時、アルゴリズムがASC用の初期パラメーターセットを検知できるまで最低7日間かかります
  • 毎日少なくとも 1 時間、センサーを新鮮な空気にさらす必要があります。その間センサーの電源を切ることができません。もし電源を切った場合、キャリブレーションパラメータの検知する処理が中断されてしまい、最初からやり直す必要があります。
  • 正常に計算されたパラメータは SCD30 の不揮発性メモリに保存されます。センサを再起動した後も、最後に検知されたASC のパラメータが再利用されます。
  • この機能のステータスに関係なく、セルフキャリブレーションにおいて最後に検出されたセルフキャリブレーションパラメータが有効値として使用されることに注意してください。(??)
  • ASCより新しいパラメータセットが検知されると、外部再キャリブレーション (第 0 章を参照) で設定された値に対して上書きされます。その逆も同様です(後に外部再キャリブレーションが行われると、セルフキャリブレーション(ASC?)による値に対して上書きされる)。
  • この機能(ASC?)はデフォルトでオフになっています。
  • SCD30 が適切に機能するためには、SCD30を定期的に新鮮な空気にさらす必要があります。センサーを毎日 1 時間新鮮な空気をにさらすことで最適な作業条件が整い、ASCが常に再校正可能になります。
  • ASCは連続測定モードでのみ機能します。
  • ASCの状態は不揮発性メモリに保存されます。
  • ASC が有効になっているときにセンサーの電源がオフになると、SCD30 はコマンドを送信せずに再電源投入後に自動セルフキャリブレーションを続行します。

Set Forced Recalibration value (FRC)

  • 強制再校正 (FRC) は、SCD30 が置かれている環境のCO2濃度の計測値が利用可能な場合に、センサーのドリフトを補正するために使用されます。
  • 最良の結果を得るには、FRCコマンドを適用して基準値を送信する前に、安定した環境にセンサーを設置し、少なくとも2 分間、2秒の測定レート連続モードを実行する必要があります。
  • 本章で説明する方法(FRC)でCO2 濃度を設定すると、ASCによる校正に対して上書きされます (1.4.6 章を参照)、その逆も同様です(FRCで設定後、ASCを実行するとASCの結果で上書きされる)。
  • 基準となるCO2濃度は、400 ppm ≦ cref(CO2) ≦ 2000 ppm の範囲内である必要があります。
  • センサーの電源再投入後も持続される CO2 キャリブレーション曲線に対して、FRC方式は恒久的な更新がなされます(??)。
  • 最後に使用された基準値は揮発性メモリに保持され、以下に示すコマンド シーケンスで読み取ることができます。
  • センサーの電源再投入後にコマンドを実行すると、標準参照値である400ppmを返します。

自分の理解まとめ

  • キャリブレーションには自動(ASC)と強制(FRC)がある
  • ASCでは定期的に新鮮な空気(CO2が400ppm)にセンサを置くことで、自動的に400ppmに補正される。だから、新鮮な空気に触れさせる必要ががある(一日1回、1H)
  • FRCでは現在のCO2値が分かっている場合、センサにCO2濃度を伝えることで、センサが補正される

I2CでESP32とSCD30は接続できて、疎通は正常と思えるのだが、SCD30のセンサデータがReadyにならない。どうしてだろうか。。

>>> sensor_get_data_ready_status(i2c)
sensor_get_ready_status
write: c2 02 02
stat:03
value: 00 00 81
sensor data is not ready

■追記
なぜSensor がReadyにならないのかいろいろ調べたが分からず、自力でどうにかする気力がなくなって、先人のドライバとサンプルコードをつかってみることにした。例えば、、以下のドライバがあるのでこれを使わせていただく。
GitHub - agners/micropython-scd30: MicroPython I2C driver for SCD30 CO2 sensor module

I2CバスでSCD30は検出されたので、以下のループで計測値を出力させようとしたが、status_readyの判断が成立せずずっと待ちになってしまう。だから、、自分のプログラムの問題ではなくて、デバイス内で何か強固に動きたくない状態になっているか、変なモードに入れてしまっているか。。

=== while True:
===     while scd30.get_status_ready() != 1:
===         time.sleep_ms(200)
===     scd30.read_measurement()
===
===
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
KeyboardInterrupt:
>>>

購入後、センサのパラメータを変えなかったら、電源を投入した時点でLEDが点滅して計測を始めるのでは??と思えるのだが、どうやってもLEDが点滅しない。うーん。。困ったものだ。。

今後の取り組み
0. マニュアルをプリントアウトして全部読む
1. Python版のドライバがあるのでそれを使って制御してみる。
2. それでもだめなら、、サポート対応も可能なようなので、、「LEDが点滅せず、Readyにならない」旨質問、
3. 回答がない場合はもう一つ同じのを買うか、、
4. 後継機のSCD40を買う。

マニュアルを読み直して、修正すべき点を2点見つけて改修した。結果、以下のようにデータを取れるようになった。

>>> sensor_read_measurement(i2c)
sensor_read_measurement
write: c2 03 00
stat:03
value: 44 0a 50 08 8b 26 41 fc 03 c7 36 e6 42 78 90 e4 e1 84

今回のハマりポイント(もちろん自分が悪いのだが)

  1. I2Cの転送レートについて、マニュアルでは推奨50KHz以下と書かれているのに対して、桁を間違って400KHzで設定していた。まちがうのにも程があるが。。
  2. Get data ready statusコマンドにおいて、コマンドを送信してから、ステータスを受信操作するまでの間、3ms以上待つべしと書かれているが、待ち無しですぐに受信操作していた。
  3. 計測用のLEDはチカチカ光ると思っていて、光らないので計測自体行われていないと思っていた(目で見て分からないもの?)*2

値は取れたので、それが妥当な値であるかどうかを確認する。CO2については400ppm前後になっているはずなのだが。

MicroPythonのunpack関数を使うことで4byteの配列からfloat型に変換できる。CO2/温度/湿度を変換してみた。

>>> get_co2_tmp_hum(i2c)
sensor_read_measurement
write: c2 03 00
stat:03
value: 43 f8 1e cb 3d b8 41 ff 50 47 c2 83 42 66 cc ee 21 2d
None
43 f8 1e cb 3d b8 41 ff 50 47 c2 83 42 66 cc ee 21 2d
(497.5878, 31.91004, 57.73255)

上記より、CO2:497ppm、温度:31.9℃、湿度:57.7%と報告されている
BME280 + MH-Z19Cの結果は以下
CO2: 400ppm、温度:31.04℃、湿度:60.33% 気圧:983.15hPa
温湿度について多少の誤差はあるが、大枠は合っている。 CO2については20%の誤差がある。これは少し大きいと思う。センサーのキャリブレーションを正しく行っていないためかも。長期的に変化の傾向を比べるべきかもしれない。窓開けっぱなしで外気がどんどん入ってる状態なので、理想値は410~420ppmなのではないかと、。と、考えると、SCD30の計測値(497ppm)が高すぎるのではないかと思える。強制再校正(FRC)の設定値がまずいのかもしれない。

■CO2計測値が高い件
外気による換気中なのに、497ppmは高いと判断して、FRCを実行した。

415ppmを引数としてFRCを実行する。

>>> sensor_set_FRC(i2c,415)
sensor_set_FRC (415)
write: c2 52 04 01 9f 62

>>> sensor_get_FRC(i2c)
sensor_get_FRC
write: c2 52 04
value: 01 9f 62
CO2 concentration: 415 ppm

この状態で再度計測。
FRCで強制校正したせいか、CO2の計測値は414ppmになった。

>>> get_co2_tmp_hum(i2c)
sensor_read_measurement
write: c2 03 00
value: 43 cf 4c 43 f1 96 41 f9 f6 d1 7b a3 42 6a b1 18 a3 77
(414.5308, 31.22729, 58.52406)

■電源投入後に安定して起動できない件
SCD30でCO2を計測できるようになったが、電源投入後はすぐには動作せず、何か特定のシーケンスでコマンドを実行しないといけないようであった。どのコマンドシーケンスを使うと確実に動作できるのかを確認する。→不揮発性メモリに記録されたパラメータがSCD30にとって都合がいいのか、電源を投入すると勝手に計測が始まるようになった。だから、、異常状態からの復帰方法がテストできない。

仕様書には起動シーケンスが書かれていないのでどのコマンドをどういう順番で投入したら確実に動作するのかちょっと分からない。経験的に動作がおかしい場合は以下で正常化するような感じ(異常動作を再現できていないので自分用のメモレベル)

  1. Soft reset (0xD304)
  2. Stop continuous measurement (0x0104)
  3. Set measurement interval (0x4600)
  4. Trigger continuous measurement (0x0010)

Sensirion社からサンプルコードが出ているのでそれをよく読むと分かるかもしれない。

■ご参考URL
Sensirionのドキュメント類
SCD30 - CO₂ accuracy of ±(30 ppm + 3% MV) @400-10000 ppm
https://sensirion.com/media/documents/4EAF6AF8/61652C3C/Sensirion_CO2_Sensors_SCD30_Datasheet.pdf
https://sensirion.com/media/documents/D7CEEF4A/6165372F/Sensirion_CO2_Sensors_SCD30_Interface_Description.pdf
GitHub - Sensirion/python-i2c-scd30: Python I2C driver for Sensirion SCD30 sensor

情報が整理されていて助かるAdafruitのサイト(SCD30)
Overview | Adafruit SCD-30 - NDIR CO2 Temperature and Humidity Sensor | Adafruit Learning System

CRCメモ
CRC の計算方法 : kei@sodan
GitHub - agners/micropython-scd30: MicroPython I2C driver for SCD30 CO2 sensor module
CO2 meter on your mobile with ESP32 and Sensirion SCD30 sensor [BASIC VERSION] - eMariete
Grove 湿度、温度とCO2センサー(SCD30) - Seeedウィキ(日本語版)

■追記
やはりSCD30の起動方法をミスするとCO2の計測がReadyにならない。補足すると、電源Onの後、前回不揮発性メモリに記録した状態を維持したまま何も変更せずに自動計測に入るのは問題ないだろうが、電源On以降で何か下手に設定するとCO2計測がReadyにならない。この場合、自分がやった復帰方法は以下(無駄な操作も多そうで何が必須なのか不明)

  1. 計測周期を2秒に設定
  2. soft reset
  3. 自動計測stop
  4. 自動計測start
  5. この操作だけではCO2計測が正常化せず
  6. 電源Off/On
  7. CO2計測正常化(2秒周期でLED点滅)

ひょっとして、、計測周期を変える場合は、自動計測をStopする必要がある??

■CO2濃度ご参考
屋外のCO2濃度は一般的に410ppmらしいです(細かい話をすると、毎年計測されていて気象庁からデータが公開されている。それによると毎年少しずつ上昇しているようだ)。室内のCO2濃度は1000ppmが基準値で、1000ppmを超えると換気が望ましい。24時間換気が導入されている家は500ppm程度に抑えられるらしいです。
気象庁 | 二酸化炭素

*1:キャリブレーションを正しく設定してないせいか?

*2:電源を5Vにして、周期を2秒で運用するとLEDが点滅している。安定して動作するための必須条件が何なのか、まだ確認できず

サーマルプリンタ(EM5820)が届いたので接続してみる

やりたいこと:サーマルプリンタ(EM5820)をマイコンから制御して文字を印刷したい
結果:ハマりポイントがあったものの、、ESP32から制御してHello World!!と印刷できた

Amazonでサーマルプリンタを購入して先日届いたのでマイコンから制御してテストプリントしてみる。
プリンタと接続する経路は、USB,RS232C,TTLとあり、当初TTLで繋ぐ予定であった。届いてから気づいたのだが、、TTLとは5Vであり、ESP32は3,3Vなのでレベル変換が必要なのであった。最初はまじめにFXMA108で3.3V<->5V変換をやっていたが、どうもうまくいかず、結局ESP32(3.3V)からプリンタ(5V)への出力は直結、プリンタ(5V)からESP32(3.3V)への接続は2.2Kの抵抗を介して電圧を下げて接続させた。プリンタを制御するTTLとはどんな仕様なのか??、5VのSerial I/Fであった(9600bps)。制御コマンドは業界標準のESC/POSらしい。久しぶりにESP32を触って忘れている事も多かったけど、最終的にはHello World!!等簡単なASCII文字は打てるまでにはなった。

今回のハマりポイント

  • 感熱プリンタは熱で印刷という性質によるのか、2Aぐらい消費する。だから、、それ相応のACアダプタから供給する必要あり。ESP32の5V供給ではESP32自体が動かない*1。以前使っていた5V出力の定電圧電源でもテスト印刷時はかすれたり、紙送りがおかしくなった。オソロシイ。アマチュア無線用の32Aまで出せるのを使った。実際何A流れているのか計測したい。
  • レベル変換IC(FXMA108)がどうもうまく動かず、正しい信号の送受信ができなかった。レベル変換ICは使わず、プリンタ(5V) --> ESP32(3.3V)の経路は、抵抗で電圧を下げた。
  • 感熱プリンタのIFにRX,TXと書かかれていて、シリアル接続なので、普通の発想ならマイコン側のTX(送信)とプリンタ側のRX(受信)とクロスに接続すると思うのだが、、どうやら、プリンタ側のRXは受信という意味ではなくて、、マイコンのRXと繋ぐという意味のようだった(か、あるいは単純にプリントミスか)。最初、TX<--->RXで接続して、オシロで波形見たらどうも出力同士がぶつかっているような波形だったのでおかしいと思ったら、どうやら、TX<--->TX , RX<---->RXで繋ぐ想定のようであった。こんなの誰が分かるんですか??という感じ

想定外だったこと

  • 感熱プリンタは安くて手頃、静かなのはいいけど、でかい電源が必要。省電力で動くマイコンの感覚では動かせない。
  • "hello world"と印字したとして、印刷用の発熱部は本体の奥にあるので、印刷された場所も奥になっている。だから、、紙を何回か送らないと目にすることができない。長い文章やグラフィックを打つのなら気にならないが、短いログを一行印刷してすぐに確認したい場合、ログを印刷してから3行ぐらい紙を上げないといけないので、結構無駄になってしまう。
  • 箱型に見えたのでそのまま床や机に置けるかと思ったが、接続ケーブルが底から出ることになり、プリンタを収めるためのケースが必要

サーマルプリンタで、Hello World!!を印刷しているところ

今回購入したサーマルプリンタ、ハマりポイントはありますが、、ESP32から制御はできました

ご参考に、、MicroPythonのテストプログラムはこの程度です(動くまで試行錯誤したので初期化等抜けていたらまた加えますが)

from machine import UART
import time

uart1 = UART(1,baudrate=9600, tx=32,rx=33)
while True:
    uart1.write('Hello World!!\r\n')
    time.sleep(1)

■追記
m5-docs
ATOM Thermal Printerの結線情報より、、やはり、マイコン側のデータ送信(TX相当)とプリンタのTXを結線する発想らしい。うーん。。。

■参考資料等
AdafruitのThermal Printerページ、この製品にかなり近い
Tiny Thermal Receipt Printer - TTL Serial / USB : ID 2751 : $49.95 : Adafruit Industries, Unique & fun DIY electronics and kits
Manual: A2 Micro panel thermal printer (同一製品ではないですが近い)
https://cdn-shop.adafruit.com/datasheets/A2-user+manual.pdf
AdafruitのThermal Printer Python版ドライバ
GitHub - adafruit/Adafruit_CircuitPython_Thermal_Printer: CircuitPython module for interacting with serial thermal printers.
ATOM Thermal Printer Kit(プリンタユニットはこれと同じ)
ATOM Thermal Printer Kit | m5stack-store
ATOM Thermal Printerのコマンド仕様書(英語ではない)
https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/atombase/atom_pritner/ATOM_PRINTER_CMD_v1.06.pdf
ATOM Thermal Printerのドライバ、C++で書かれているらしい。筋金入り!
GitHub - m5stack/ATOM-PRINTER: M5Stack ATOM PRINTER firmware and examples

レベル変換の解説記事(有限会社 ケプストラム様に感謝)
5V系・3.3V系信号レベル変換
FXMA108説明書
https://akizukidenshi.com/download/ds/akizuki/ae-llcnv8-doc.pdf

*1:USBの定格の最大電流値を超えているのだろう

米ハーバード大学学生向けコンピュータ・サイエンス入門講義「CS50」

ハーバード大学の学生向けに展開されているコンピュータ・サイエンス入門講義「CS50」
https://cs50.jp/

定年近くまで、ソフトウエア畑で食べてきたけど、こういう講座で自分の分かっている事、分からない事を改めてさらったほうがいいのかも。ありがたいことに日本語テロップ付きなのであった。テロップは消せるので、最初は英語で聞いて、2回目は日本語テロップを見ながら聞くというのも可能。結構長いので、、自分がそこまで時間をつぎ込む根性があるかどうか。。

CO2センサ(MH-Z19C)を接続してみた

先日入手した、CO2 Sensor Module(MH-Z19C)をシリアル接続して値を取ってみた。普通にデータを取得すると以下のような値で、これは高すぎると思われる。

sensing CO2
['0xff', '0x86', '0x7', '0x89', '0x46', '0x0', '0x0', '0x0', '0xa4']
value:1929 (0789H)

単純に、上位8bit+下位8bitで演算した結果がCO2濃度(PPM)らしいのですが、、CO2濃度が1929ppmってこれが本当なら危険なぐらいにCO2濃度が高い。キャリブレーションをやり直すか、なんかその辺がおかしいのではと思える。コマンドでもキャリブレーション(Zero Point On/Off?)を指定できるらしいので、、コマンド経由で指示するか。。
マニュアルでは「デフォルトでAuto-CalibrationがONに設定されていて、24H周期でキャリブレーションがなされる」と読み取ったのだが、、手っ取り早くPin1(HD; zero point calibration)を一定時間Lに固定してCalibrationを実行させた。現在は404ppmとなっている。

sensing CO2
['0xff', '0x86', '0x1', '0x94', '0x47', '0x0', '0x0', '0x0', '0x9e']
value:404 (0194H)

電源投入後に、HDピンをLに落とす機能を作るべきかどうするか、、マニュアルでは24時間周期でZero Point Calibrationが実行されると書かれているので、それを信じて今は実装しない。今回作成したCO2Sensor(MH-Z19C)用のライブラリは以下*1

from machine import UART
from machine import Pin
import utime

uart0 = None
READ_CO2 = bytes((0xff, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79))
SENSOR_TX_PIN = 16
SENSOR_RX_PIN = 17

def setup_CO2_sensor():
    global uart0
    uart0 = UART(0, baudrate=9600, tx=Pin(SENSOR_TX_PIN), rx=Pin(SENSOR_RX_PIN))

def get_co2_sensing():
    value = 0
    uart0.write(READ_CO2)  
    data = uart0.read()         
    if data and len(data) == 9:
          value = data[2] << 8 
          value += data[3]
    return value

def get_check_sum(packet):
    checksum = 0
    for data in packet[1:]:  # to skip Start Byte(0xff), use [1:]
        checksum += data
    checksum &= 0xff
    if checksum  == 0:
        return 0
    else:
        return 0x100 - checksum

#
# set calibration for Zero Point On/Off
#
def set_calibration(cb_zp_on=True):
    calib_zp = bytearray((0xff,0x01,0x79,0x00,0x00,0x00,0x00,0x00,0x00))
    if cb_zp_on:
       calib_zp[3] = 0xa0
    else:
       calib_zp[3] = 0x00
    calib_zp[-1] = get_check_sum(calib_zp[-1:])
    uart0.write(calib_zp)

def co2_monitoring():
    while True:
        print("sensing CO2")
        value = get_co2_sensing()
        print(f"value:{value:d} ({value:04x}H)")
        utime.sleep(3)

先日AppSheetで作成した部屋の空気質の見える化アプリにCO2を加えた。夏なので窓開けっぱなしのせいか、ずっと400ppmを維持している。

■関連URL
MH-Z19Cの仕様書
https://www.winsen-sensor.com/d/files/mh-z19c-pins%26terminal-type-co2-manual(ver1_2).pdf
ちなみに、、checksumの実装例がC言語で掲載されているが、これは間違っていると思う(最後にchecksum += 1が必要では?)

*1:先人のGithubのドライバを見ていると、温度計測等の隠しオプションもあるらしい

清算できていなかった区間の乗車賃を支払いに近鉄駅へ

8/19に羽曳野で開催されたスマート農業に参加した帰り、近鉄からJRと乗り継いだ。ICOCAで古市駅に入って、柏原駅近鉄→JRの乗り継ぎの際も改札が無かったのでそのままJRに乗って最寄り駅まで戻ってきた。駅で改札を出ようとしたら、エラーになって出られなかった。駅員の人に聞くと、入場記録が付いていないとのことであった(と自分は理解)。多分、柏原駅で、ちょっとよく分からないカード読み取り機にタッチしなかったせいと思われる。カード読み取り機には、「近鉄の切符でJRの改札を出る場合は、カード読み取り機にタッチしてください。タッチしないと出られません」と書かれていると理解して、自分は近鉄の切符ではないし、柏原駅では改札出ないし、と思って何もしなかったのが原因と思う。今から思うと、あの読み取り装置は、近鉄からJRに乗り継ぎする人のための、近鉄区間清算装置なのだと思う。多分。
JRの駅で以下のような証明書(多分未払いの証明書)を発行してもらい、「後日近鉄で払ってください」と言われた。

そのままだと無賃乗車になるので、紙を持って比較的近い近鉄の上本町駅まで出かけた。上本町駅は高校の最寄り駅なので懐かしい。この辺りの風景はあまり変わっていない。

改札で事情を説明してJRで発行してもらった証明書を見せたら、未払い分210円を払うことになった。古市~柏原間が210円なのだと思う。領収証をもらった。

上本町まで来たので、、難波まで歩いてジュンク堂の本屋に行った。元々、量子コンピュータの解説本を買おうと思っていたのだが、立ち読みしてみるとかなり難しく、代わりに?? GASの本を買った。。