Quantcast
Channel: 仙石浩明の日記
Viewing all 47 articles
Browse latest View live

M5Stack ATOM Lite を USBシリアル変換アダプタにしてみた 〜 Raspberry Pi Pico の UART コンソールを使う 〜

$
0
0

はじめてラズパイを買ったら意外に面白くてハマってしまった。 電子工作なんて 30年ぶりで、 半田ごてを握るには年を食いすぎている (手元がふるえる) のだけど、 ブレッドボードやら電子パーツやらをいろいろ買い揃えて、 オリンピックが終わってからの 2週間、寝る間も惜しんで楽しんでいる。

Raspberry Pi Zero WH が 1848円、 Raspberry Pi Pico が 550円、 M5Stack ATOM Lite が 1287円。 安いので次々と買ってしまった。 ラズベリーパイ (Raspberry Pi)、略してラズパイと呼ばれるようになって久しいが、 M5Stack をはじめとする ESP32 (や ESP8266) を使ったコントローラは、 何と呼ばれているのだろう?

Raspberry Pi Zero WH は普通の Linux マシンなので何でもありだが、 他はマイコン (マイクロコンピュータ) ならぬマイクロコントローラなので、 普通の OS を動かすことは難しく制約が多くて一筋縄にはいかない。

ググってみると、 開発環境として Arduino IDE を使い C (C++ ?) や Java などで開発している人が多いようだ。 30年前ならいざ知らず、 マイクロコントローラと言えども計算リソースが潤沢にある (30年前の汎用機並?) 昨今、 なぜコンパイラ言語 (しかも C や Java みたいなアセンブラと大差ない低級言語) を使うのか? インタプリタ言語なら対話的にコード片を実行して、 動作を確認しながらプログラミングできる (REPL, Read-Eval-Print Loop) ので、 開発効率が圧倒的に高い。

というわけで、 わたし的には MicroPython 一択 (ちなみに Python を使うのは今回がはじめて) なのであるが、 困ったことに Raspberry Pi Pico (以下 Pi Pico と略記) は、 開発環境である PC との通信手段が限られる。 USB コネクタが一つしかなく、 M5Stack ATOM Lite 等と違って Wi-Fi 機能もない。 つまり、 Pi Pico に USB 機器をつなぐ (Pi Pico が USB ホスト) 場合は、 USB で PC へつなぐ (PC が USB ホスト) ことができなくなるので、 PC と通信する手段が無くなってしまう。 プログラム実行中に PC と通信できなくては REPL にならない。

USB (Universal Serial Bus) がダメなら Universal じゃないシリアル通信を使えばいい、ということで Pi Pico にもシリアル通信のための UART (Universal Asynchronous Receiver/Transmitter, 調歩同期式汎用送受信機) が装備されている (ただし Pi Pico 用の MicroPython は UART では REPL できないので再ビルドの必要がある。 後述)。 PC 側でも UART 機能があれば通信できる。 というか USB や Wi-Fi が無かった時代は UART 通信 (RS-232C など) の方が一般的だった。

ところが、 いまどきの UART は 3.3V だという。 ±3~25V の信号線を使っていた RS-232 規格とは隔世の感がある。 ±25V な機器はさすがに捨ててしまったが、 いまでも手元にある USBシリアル変換アダプタは 0〜5V (TTL レベル) のものばかり。

5V を Pi Pico が扱える 3.3V まで下げるのは抵抗を使って分圧すればいいが、 その逆、 つまり Pi Pico から PC へ 5V の信号を伝えるのは少々やっかいである。 3.3V のままでも PC に H レベルと認識してもらえなくもないが、 マージンが狭くなるのは否めない。 もちろん 115200bps とかなら問題も起きないだろうが、 現代なら 1.5Mbps くらいは出したいところ。

もちろん素直に 3.3V 対応の USB to TTLシリアルアダプタを買えばいいのだが、 USBシリアル変換アダプタを既に (何個も) 持っているのに新たに買うのはモッタイナイ気がするし、 元々 200〜300円くらいしかしないパーツを、 本体と同じくらいの送料を払って買うのも業腹である (こんど秋葉原へ行ったときにでも買おうっと)。

Double Pico ! ATOM Lite as a Serial Converter to Pi Pico

要は 3.3V な UART があればいいわけで、 M5Stack ATOM Lite (以下 ATOM Lite と略記) を USB シリアルアダプタにしてしまえばいい!と思いついた。 つまり ATOM Lite も Pi Pico と同様 USB で REPL が使えるが、 ATOM Lite の REPL ではなく、 ATOM Lite (写真上) と UART シリアル (写真上の 3本のジャンパー線, うち黒は GND) でつないだ先の Pi Pico (写真下) の REPL を使おうという目論見。 ATOM Lite は PC と Pi Pico との通信を中継するだけ。 ATOM Lite もチップの名前は ESP32-PICO-D4 なのでダブルピコ!

PC (開発環境) ←──USB──→ ATOM Lite ←──UART──→ Pi Pico

MicroPython では flash メモリに boot.py を置いておくと起動時に実行してくれる。 ATOM Lite を常にシリアルアダプタとして使いたいわけではないので、 ATOM Lite のボタンを押しながら起動したときだけシリアルアダプタとして機能するようにしてみた。 シリアルアダプタとして動作中はボタン中央の LED が緑色に点灯する。 もう一度ボタンを押すと LED が消灯し、 通常の REPL モードになる。 boot.py に以下のプログラムを追記した:

import machine
import sys
import neopixel
import utime
import _thread

btn = machine.Pin(39, machine.Pin.IN)
if btn.value():
    sys.exit()

pxls = neopixel.NeoPixel(machine.Pin(27), 1)
pxls[0] = (0, 25, 0)
pxls.write()
start_time = utime.time()

uart = machine.UART(1, 115200, tx=21, rx=25)
done = False

def thread():
    global done
    while not done:
        c = sys.stdin.read(1)
        if c == "\n":
            uart.write("\r\n")
        else:
            uart.write(c)
    _thread.exit()

_thread.start_new_thread(thread,())

while not done:
    if btn.value() == 0:
        if utime.time() - start_time > 10:
            done = True
    if uart.any() > 0:
        sys.stdout.write(uart.read(1))
    else:
        utime.sleep_ms(1)

pxls[0] = (0, 0, 0)
pxls.write()

ATOM Lite の GPIO 21番ピンを UART TX として、 GPIO 25番ピンを UART RX として使い、 それぞれ Pi Pico の UART0 RX および UART0 TX につなぐ。 Pi Pico の VBUS と GND に 5V 電源を供給する (写真右下の赤と黒のジャンパー線) ことで USB コネクタを使わずに空けておける。

はじめての Python (文法すらろくに知らなかった) だけど、 ATOM Lite をシリアルアダプタとして使うことを思いついてから、 実際に使えるようになるまで半日もかかっていない。 まさに REPL さまさま。

MicroPython ESP32 版は poll クラスがサポートされていないようなので、 スレッドを用いた。 また stdin (標準入力) を生のまま (バイナリとして) 入力することができず、 0x00〜0x1F のコントロール文字を中継することができない。 例えば REPL ではプログラムの実行を止めたいときは Control-C を押すが、 Pi Pico で実行中のプログラムではなく、 ATOM Lite で実行中の boot.py が止まってしまう。 実用を考えるなら C で書き直すべきだが、 プロトタイプとしてならこれで充分。 (8月26日追記: C で書き直してみた)

ATOM Lite のボタン中央の LED には Neo Pixel LED が使われている。 単なる RGB LED (赤, 緑, 青の 3つの発光ダイオードが一つの LED パッケージに封入されている) とは異なり、 一つ一つの LED にコントロール用の IC が内蔵されていて、 1本の信号線で多数の LED (例えば長尺のテープに実装したり、 マトリックス状に配置してサイネージのピクセルとして使う) を自在にコントロールできる。

MicroPython では前掲したプログラムのように配列 (pxls[]) の各要素が一つの LED に対応していて、 RGB の値をそれぞれ書込むことで多数の LED の色・明るさを自由に設定できる。 ATOM Lite のように LED 一つだけだとあまり意味がない (pxls[0] の値を変えるだけ) が、 それでも Neo Pixel に触れてみるという目的には有用と思う。 実際、私も初めて使った。 ATOM Lite に 6軸 IMU (慣性計測装置) MPU6886 を追加した上位機種 ATOM Matrix には 25個の Neo Pixel が実装されているが、 Neo Pixel の本領を発揮するには数千〜数万個 (数百万個?) の LED が欲しいところ。

なお、ここまで書いておいてアレだが、 Pi Pico 用に提供される MicroPython のファームウェアUART シリアルで REPL できない。 ports/rp2/mpconfigport.h の設定を一箇所書き換え (MICROPY_HW_ENABLE_UART_REPL を 1 に変更) て、 ファームウェアをビルドし直す必要がある。

diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h
index ccf11d4..2c59047 100644
--- a/ports/rp2/mpconfigport.h
+++ b/ports/rp2/mpconfigport.h
@@ -35,7 +35,7 @@
 
 // Board and hardware specific configuration
 #define MICROPY_HW_MCU_NAME                     "RP2040"
-#define MICROPY_HW_ENABLE_UART_REPL             (0) // useful if there is no USB
+#define MICROPY_HW_ENABLE_UART_REPL             (1) // useful if there is no USB
 #define MICROPY_HW_ENABLE_USBDEV                (1)
 
 // Memory allocation policies

この手の設定変更はソースを書き換えるのではなく、 環境設定ファイルの変更だけで済むようにしたほうがいいと思うのだけど...

ビルドには (当然ながら) クロスコンパイル環境が必要。 といっても既に Arduino IDE を利用して Pi Pico 用のプログラムを開発している PC なら Pi Pico のバイナリを生成できるクロスコンパイル環境がインストールされているハズで、 ~/.arduino15/packages/rp2040/tools/pqt-gcc 以下の gcc を PATH に含めるだけでよい。 ファームウェアの再ビルドというと紆余曲折がつきものだが、 あっさりビルドできてしまって拍子抜け。 ports/rp2/build-PICO/firmware.uf2 が得られるので、 Pi Pico の BOOTSELボタンを押しながら PC に接続したときに現れるストレージへ、 この firmware.uf2 ファイルをコピーするだけでファームウェアの書き換えが完了。

以下、Linux サーバ上でのビルド作業の記録 (一部省略):

senri:/usr/local/src $ git clone https://github.com/micropython/micropython.git
Cloning into 'micropython'...
   ・・・
senri:/usr/local/src $ cd micropython
senri:/usr/local/src/micropython $ git submodule update --init -- lib/pico-sdk
Submodule 'lib/pico-sdk' (https://github.com/raspberrypi/pico-sdk.git) registered for path 'lib/pico-sdk'
Cloning into '/usr/local/src/micropython/lib/pico-sdk'...
Submodule path 'lib/pico-sdk': checked out 'bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7'
senri:/usr/local/src/micropython $ git submodule update --init -- lib/tinyusb
Submodule 'lib/tinyusb' (https://github.com/hathach/tinyusb) registered for path 'lib/tinyusb'
Cloning into '/usr/local/src/micropython/lib/tinyusb'...
Submodule path 'lib/tinyusb': checked out 'd49938d0f5052bce70e55c652b657c0a6a7e84fe'

senri:/usr/local/src/micropython $ export PATH=/home/sengoku/.arduino15/packages/rp2040/tools/pqt-gcc/1.3.1-a-7855b0c/bin:$PATH
senri:/usr/local/src/micropython $ make -C mpy-cross
   ・・・
LINK mpy-cross
   text	   data	    bss	    dec	    hex	filename
 269329	    776	    896	 271001	  42299	mpy-cross
make: Leaving directory '/usr/local/src/micropython/mpy-cross'
senri:/usr/local/src/micropython $ cd ports/rp2
senri:/usr/local/src/micropython/ports/rp2 $ make
[ -d build-PICO ] || cmake -S . -B build-PICO -DPICO_BUILD_DOCS=0 -DMICROPY_BOARD=PICO
PICO_SDK_PATH is /usr/local/src/micropython/lib/pico-sdk
Defaulting PICO_PLATFORM to rp2040 since not specified.
Defaulting PICO platform compiler to pico_arm_gcc since not specified.
PICO compiler is pico_arm_gcc
-- The C compiler identification is GNU 10.3.0
-- The CXX compiler identification is GNU 10.3.0
-- The ASM compiler identification is GNU
-- Found assembler: /home/sengoku/.arduino15/packages/rp2040/tools/pqt-gcc/1.3.1-a-7855b0c/bin/arm-none-eabi-gcc
PICO target board is pico.
Using board configuration from /usr/local/src/micropython/lib/pico-sdk/src/boards/include/boards/pico.h
-- Found Python3: /usr/bin/python3 (found version "3.9.6") found components: Interpreter 
TinyUSB available at /usr/local/src/micropython/lib/tinyusb/src/portable/raspberrypi/rp2040; adding USB support.
Found User C Module(s): 
-- Configuring done
-- Generating done
-- Build files have been written to: /usr/local/src/micropython/ports/rp2/build-PICO
make -s -C build-PICO
   ・・・
[ 99%] Linking CXX executable firmware.elf
   text	   data	    bss	    dec	    hex	filename
 280172	     88	 201652	 481912	  75a78	/usr/local/src/micropython/ports/rp2/build-PICO/firmware.elf
[100%] Built target firmware

git clone でソースプログラム一式をダウンロードして、 make -C mpy-cross で MicroPython のクロスコンパイラをビルドして、 make で ARM EABI (Embedded Application Binary Interface) 版 MicroPython をビルド。

8/26 追記:

前掲の Python プログラムを C で書き直してみた。 ESP-IDF は有難いことに UART select() をサポートしているのでスレッドを使わずに USB ⇔ UART 中継プログラムが書けるが、 UART がハードウェア フロー制御 (RTS CTS) を使えないと嬉しさ半減といったところ。

#include <stdio.h>
#include <sys/fcntl.h>
#include <sys/errno.h>
#include <sys/unistd.h>
#include <termios.h>
#include <sys/select.h>
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_vfs_dev.h"
#include "driver/uart.h"

static const char* TAG = "usb_uart";

const int buf_max = 256;

void app_main(void)
{
    uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity    = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_APB,
    };

    ESP_ERROR_CHECK(uart_param_config(UART_NUM_0, &uart_config));
    ESP_ERROR_CHECK(uart_driver_install(UART_NUM_0, 2*1024, 0, 0, NULL, 0));

    ESP_ERROR_CHECK(uart_param_config(UART_NUM_1, &uart_config));
    ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, 21, 25, -1, -1));
    ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, 2*1024, 0, 0, NULL, 0));

    while (1) {
        int fd[2];
	int len[2];
	char buf[2][buf_max];
	fd_set rin, win, ein;
	fd_set rout, wout, eout;
	int i;

        if ((fd[0] = open("/dev/uart/0", O_RDWR)) == -1) {
            ESP_LOGE(TAG, "Cannot open UART 0");
            usleep(5000000);
            continue;
        }
        if ((fd[1] = open("/dev/uart/1", O_RDWR)) == -1) {
            ESP_LOGE(TAG, "Cannot open UART 1");
	    close(fd[0]);
            usleep(5000000);
            continue;
        }
	FD_ZERO(&rin);
	FD_ZERO(&win);
	FD_ZERO(&ein);
	for (i=0; i < 2; i++) {
	    struct termios termios;
	    esp_vfs_dev_uart_use_driver(i);
	    fcntl(fd[i], F_SETFL, O_NONBLOCK);
	    FD_SET(fd[i], &rin);
	    tcgetattr(fd[i], &termios);
	    termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
				 | INLCR | IGNCR | ICRNL | IXON);
	    tcsetattr(fd[i], TCSANOW, &termios);
	}

        while (1) {
            int ret;
	    int i;
	    struct timeval tv = {
		.tv_sec = 1,
		.tv_usec = 0,
	    };
	    rout = rin;
	    wout = win;
	    eout = ein;
            ret = select(FD_SETSIZE, &rout, &wout, &eout, &tv);
	    if (ret < 0) break;
	    for (i=0; i < 2; i++) {
		if (FD_ISSET(fd[i], &rout)) {
		    len[i] = read(fd[i], buf[i], buf_max);
		    if (len[i] > 0) {
#ifdef USE_HW_FLOWCTRL
			FD_CLR(fd[i], &rin);
			FD_SET(fd[i], &win);
#else
			write(fd[1-i], buf[i], len[i]);
#endif
		    } else if (len[i] < 0) {
			ESP_LOGE(TAG, "UART %d read error", i);
		    }
		}
#ifdef USE_HW_FLOWCTRL
		if (FD_ISSET(fd[i], &wout)) {
		    int l = write(fd[i], buf[1-i], len[1-i]);
		    if (l == len[1-i]) {
			FD_CLR(fd[i], &win);
			FD_SET(fd[1-i], &rin);
			len[1-i] = 0;
		    } else if (l > 0) {
			memcpy(buf[1-i], buf[1-i]+l, l);
			len[1-i] -= l;
		    } else if (l < 0) {
			ESP_LOGE(TAG, "UART %d write error", i);
		    }
		}
#endif
	    }
	}
	ESP_LOGE(TAG, "Select failed: errno %d", errno);
	close(fd[0]);
	close(fd[1]);
	usleep(5000000);
    }
}

前掲の Python プログラムと同様、 GPIO 21番ピンを UART1 TX に、 GPIO 25番ピンを UART1 RX として使用する。 RTS CTS は使用しないが、 115200bps くらいなら取りこぼしも起きないようなので実用上の問題は無い?

ATOM Lite を USB シリアル変換アダプタ (もちろん C 版) として使って、 Thonny で Pi Pico にアクセスしてみた。 ふつうに開発できる。 PC で作成した Python プログラムをアップロードできるし、 プログラムの実行を contorl-C で止めることもできる。 ATOM Lite 経由で Pi Pico へアクセスしていることを忘れそう...

Thonny Pi Pico via ATOM Lite

MICROPY_HW_ENABLE_UART_REPL の値を 1 にしてビルドした MicroPython なので、 バージョンが v1.16-236-gb51e7e9-dirty などと末尾に「-dirty」が付いている。


所得税を分割して、複数の高還元率クレジットカードを何回も使って納付してみた

$
0
0

所得税の納付は、 確定申告書等作成コーナー経由だと全額一括払いになってしまうが、 国税クレジットカードお支払サイトを直接アクセスすることで、 任意に分割して支払うことができる。

この「お支払サイト」は、 氏名、住所、電話番号、納付先税務署、納付税目 (申告所得税及復興特別所得税) を入力した上で、 クレジットカードで任意の金額を支払う仕組みになっている。 それぞれの納税者が合計いくら納付済みか集計するには、 納税者と一対一に対応する ID (識別番号) が必要だが、 ID の入力は求められない。 つまり支払うだけの納付専用サイト。

e-Tax を使って確定申告を行った納税者なら e-Tax の 「利用者識別番号」が ID になるが、 e-Tax を使わなくても確定申告はできるわけで、 「お支払サイト」としては ID 無しで納付できる仕組みにするしかないのだろう。

この仕組みだと税務署側で、 「お支払サイト」 から送られてきた納付データと、 確定申告のデータを突合する作業が必要になる。 やっぱり手作業? DX への道は遥か遠い。 個人番号 (マイナンバー) を ID として入力させればいいのにねぇ...

ID とは言えないものの各税務署が「整理番号」を発行していて、 この「お支払サイト」でも「整理番号」を入力することができる。 整理番号の入力は必須ではないが、 入力しておくことで税務署の人たちの突合の手間を減らし、 間違いが起きるリスクを減らすことができるだろうから、 自分の「整理番号」を調べて入力したほうがヨサゲ。

Tax Payment Statement

整理番号は、 e-Tax のメッセージボックスに届く 「確定申告等についてのお知らせ」に記載されている他、 毎年 1月下旬に郵送で届く「確定申告のお知らせ」 「確定申告の納付書」や、 還付金がある場合は「国税還付金振込通知書」にも記載されている。

所得税をクレジットカードで支払う場合、 税額 1万円につき決済手数料 76円+消費税がかかる。 例えば所得税が 99万1円〜100万円の範囲なら税込 8,360円 (つまり 0.84%) もの決済手数料を払う必要があるわけで、 決済手数料より高いポイント等がもらえる高還元率クレジットカード (還元率が 1% 以上) でないと意味がない。

ところが高還元率クレジットカードは、 ポイント等の還元率が高いいっぽう還元を受けられる上限額が低いことが多い。 例えば「Visa LINE Pay クレジットカード」(以下「LINEカード」と略記) は、 支払額の 2% の LINE ポイントがもらえるが、 税金/保険において、1回あたりの支払につき 5万円を超える分は、 ポイント還元の対象外となる。

また、ソニー銀行が発行する「Sony Bank WALLET デビット付きキャッシュカード」 (以下「Sonyカード」と略記) は、 Club S のステータスに応じて最大 2% のキャッシュバックがもらえるが、 寄付、納税、公共料金などの支払ではキャッシュバック合計額が最大 1万円/月までとなる。

どちらの高還元率カードも、 所得税を分割して納付することで上限額を超えてしまうことを回避できる。 LINEカードの場合は 1回 5万円ずつ複数回に分けて払えば 2% のポイントが得られるし、 Sonyカードの場合は (確定申告を 2月に行えば) 50万円ずつ 2月と 3月の 2回に分けて支払うことで各月 2% 満額 1万円のキャッシュバックが得られる。

例えば 140万円の納税を行う場合なら、 2月と 3月に Sonyカードで 50万円ずつ納付し、 3/15 までに LINEカードで 5万円ずつ 8回納付すれば良い。

実際の納付では決済手数料がかかるので、 Sonyカードでは納付額 495,820円と決済手数料 4,180円を、 LINEカードでは納付額 49,582円と決済手数料 418円を払うことになる。 ここで注意すべきなのは決済手数料が 1万円単位で、 1万円に満たない端数は切り上げられるという点。 分割の仕方によっては余計な決済手数料を払うことになってしまう。

例えば所得税額が 12万300円だった場合、 一括で納付すれば決済手数料は税込 1,086円 (=13*76*1.1) だが、 3等分して 4万100円ずつ納付すると決済手数料は税込 1,254円 (5*76*1.1 = 418円ずつ 3回) になってしまう。 3回に分けて納付したときの決済手数料を一括納付の場合と同額にするには、 まず 4万300円を納付 (決済手数料 5*76*1.1 = 418円) し、 残り 2回は 4万円ずつ納付 (決済手数料は 4*76*1.1 = 334円ずつ) すればよい。

というわけで、 所得税 1,390,400円を 9回に分けて 5枚のクレジットカードで納付してみた。 決済手数料は計 11,702円なのに対し、 キャッシュバックやポイント等で計 28,241円の還元を得た。 差し引き 16,539円の利益。

納付日税額手数料支払額カード還元額
2022-02-19495,820円4,180円500,000円Sony10,000円
2022-02-1949,582円418円50,000円LINE1,000円
2022-02-2479,996円668円80,664円Diners403円
2022-02-2540,000円334円40,334円LINE806円
2022-03-0240,000円334円40,334円LINE806円
2022-03-03100,000円836円100,836円SMBC NL3,025円
2022-03-0749,182円418円49,600円LINE992円
2022-03-08495,820円4,180円500,000円Sony10,000円
2022-03-0840,000円334円40,334円Toyota1,209円
合計1,390,400円11,702円1,402,102円28,241円

LINEカードでの支払は、 同じ納税を何度も繰り返すとポイント付与を止められるかも?と思って、 ポイント付与を確認して (さらに LINE証券へ送金して) から次の支払を行った。 幸い今回は 2% 満額の LINE ポイントを得たが、 分割納付する人が増えるとポイント付与ルールが改訂されるかもしれない。 もっとも、2% 還元自体が 2022年4月30日までであり、 5月以降どうなるか不明。

「三井住友カード ゴールド(NL)」(上表では「SMBC NL」と略記) は、 「最大30,000円相当の XRP交換券 プレゼントキャンペーン」(4月30日まで) を行っているので、 支払額の 3% 相当の XRP (暗号資産) が付与される。 また、 このカードには 「年間100万円のご利用で翌年以降の年会費永年無料」特典があり、 この納税で年間利用額 100万円を達成できた。

「ダイナースクラブカード」(上表では「Diners」と略記) は、 この納税で年間利用額が 60万円に達したので、 次年度の年会費が無料になる。 ポイント還元率はわずか 0.5% だが、 24,200円の年会費が無料になるのは大きい。

「TOYOTA Wallet」(上表では「Toyota」と略記) は、 物理的なカードが無いバーチャルカードで、 他のクレジットカード等から残高をチャージして使う。 残高の上限は 5万円なので 5万円を超える支払には利用できない。 実店舗では iD または Mastercard コンタクトレス (Apple Pay のみ) として使える。 支払額の 1% が残高にキャッシュバックされる。

オンライン支払では、 カード名義が 「TOYOTAWALLET MEMBER」 固定なので、 利用できないサイトも多いが、 そもそもこの 「お支払サイト」 はトヨタファイナンス(株)が運営するサイトなので、 同じトヨタファイナンスなのだから使えるかも?と思って試してみたら、 あっさり納付完了してしまった。

残高のチャージを高還元率カード、例えば Sonyカードで行えば、 チャージの際に 2% のキャッシュバックが得られるので、 TOYOTA Wallet のキャッシュバックと合わせて 3% の還元率となる。 上限が同じ 5万円なら、 LINEカードでなく最初から TOYOTA Wallet を使えばよかった。(^^;

Sonyカードはクレジットカードではなく Visaデビットだが、 決済代金が即引き落とされること以外はクレジットカードと変わらない。 もちろん 3Dセキュアに対応している。 高還元率カードとしては群を抜いている (しかも還元はポイントではなくキャッシュバック!) と思うのだけど、 イマイチ認知されていないのは何故だろう?

WSL2 (Windows Subsystem for Linux 2) で物理ディスク上の独自 OS を動かしてみた

$
0
0

Windows で VMware Workstation Player を使って Linux ベースの独自 OS (以下 GCD OS と呼ぶ) を動かしていたのだけど、 WSL2 (Windows Subsystem for Linux 2) が物理ディスクをマウントできるようになったと聞き、 WSL2 が VMware の代りに使えるか試してみた。 ただし、物理ディスクをマウントするには Windows 11 ビルド 22000 以上が必要。

本当は Linux カーネルも自前でビルドしたものを使いたいが、 とりあえず WSL2 のカーネルをそのまま使い、 root ファイルシステムは物理ディスク上にインストールしてある GCD OS を使う方向で考えた。

以前、 OpenVZ な VPS サービス上で GCD OS を動かしたことがある (10年前!) ので、 その時と同様に chroot して物理ディスク上の GCD OS を起動するのが簡単そう。 つまり、 以下のシェルスクリプト gcd.sh を WSL2 上で実行するだけ:

#!/bin/sh
PATH="/mnt/c/WINDOWS/System32:/usr/bin:/bin:/usr/sbin:/sbin"
ROOT="/usr/local/GCD"

cd /mnt/c
wsl.exe --mount '\\.\PHYSICALDRIVE1' --bare

test -d $ROOT || mkdir $ROOT
mount LABEL=/ $ROOT

$ROOT/etc/init.d/chroot

このスクリプトは WSL2 の仮想ディスク (Virtual Hard Disk) 内に置いてもよいが、 私は Windows の C: ドライブに置いた。 WSL2 の内容を変更する必要がなくなるし、 WSL2 でどのディストリビューションを使っているかに依存しなくなる。 仮想ディスクが肥大化したら、インストールし直して初期状態に戻してもよい。

例えば C:\bin\gcd.sh に置いて、 次のように実行する:

C:\Windows\System32\wsl.exe -u root -- /mnt/c/bin/gcd.sh

タスクスケジューラーで Windows の起動時に自動的に実行すれば、 Windows にログインしなくても外部から ssh で GCD OS へログインできるので便利。 VMware より軽くて手軽で便利かも?

以下、このスクリプト gcd.sh を順に説明する:

まず、 「wsl.exe --mount '\\.\PHYSICALDRIVE1' --bare」 で WSL2 から物理ディスク 「PHYSICALDRIVE1」 が見えるようにする。 wsl.exe は Windows のコマンドだが、 WSL2 は Linux なのに /mnt/c にマウントされた C: ドライブ上の Windows のコマンドが実行できる (Linux カーネルの binfmt_misc を使っている)。

(Linux の) lsblk コマンドを使うと、 物理ディスクが見えることが確認できる:

Linux 5.10.16.3-microsoft-standard-WSL2.
kayano:~ $ lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
sda      8:0    0   256G  0 disk
sdb      8:16   0 339.7M  1 disk
sdc      8:32   0   256G  0 disk /mnt/virtual
sdd      8:48   0   7.3T  0 disk
├─sdd5   8:53   0   200G  0 part 
├─sdd6   8:54   0    16G  0 part [SWAP]
└─sdd7   8:55   0   6.8T  0 part /

sdd が WSL2 から見えるようになった物理ディスク (8TB HDD)。 パーティションが 3つあることが分かる。 うち sdd7 が GCD OS の root ファイルシステム。

一点注意すべきなのは、 Windows のシステムドライブ 「PHYSICALDRIVE0」 は指定できないので、 システムドライブとは異なる物理ディスクを使う必要があるという点。 8TB HDD など大容量のハードディスクを、 パーティションで区切って Windows と Linux の両方をインストールしている人は多いと思うのだけど、 残念ながら Windows と同じディスク上にある Linux パーティションは、 WSL2 から使うことはできない。 VMware ならできるのに...

仕方がないので私は (わざわざ) M.2 NVMe SSD を買って Windows のシステムドライブを NVMe へ移し、 SATA0 につないだ 8TB HDD を (Windows をインストールしていたパーティション 1〜4 を削除して) Linux 専用にした。

また、WSL2 上で wsl.exe を実行するとき、 カレントディレクトリが WSL2 の仮想ディスクだと、 どんな引数でも常に 「Invalid argument」 エラーになる:

root@kayano:~# /mnt/c/Windows/System32/wsl.exe --shutdown
/mnt/c/Windows/System32/wsl.exe: Invalid argument
                                                 root@kayano:~#

エラー出力後の改行がうまくいかないあたり、 いかにもバグっぽい?

カレントディレクトリが C: ドライブだと正常に実行できるようなので、 wsl.exe を実行する前に 「cd /mnt/c」 を行っている。

物理ディスクが WSL2 で見えるようになったら、 次にこの物理ディスクをマウントする: 「mount LABEL=/ $ROOT」。 ここでは LABEL が 「/」 のパーティションを 「$ROOT」 つまり /usr/local/GCD へマウントしている。

あとは GCD OS を起動するだけ: 「$ROOT/etc/init.d/chroot」。 /etc/init.d/chrootOpenVZ 上で GCD OS を起動するときも使った、 以下のシェルスクリプト:

#!/bin/sh
root=`echo $0 | sed -e 's@/etc/init.d/chroot$@@'`
if [ ! -d $root ]; then
   echo "Can't find root: $root"
   exit 1
fi
sed -n 's/^[a-z][_a-z]* \([^ ][^ ]*\) .*/\1/p' < /proc/mounts | while read d; do
    if [ -d "$d" ]; then
	test -d "$root$d" || mkdir "$root$d"
	mount -obind "$d" "$root$d"
    elif [ -f "$d" ]; then
	test -f "$root$d" || touch "$root$d"
	mount -obind "$d" "$root$d"
    fi
done
if [ -d /lib/modules/`uname -r` ]; then
    mount -obind /lib/modules $root/boot/lib/modules
fi
chroot $root /bin/sh <<EOF
swapon -a
mount -a -t ext4
/etc/init.d/svscanboot &
/etc/rc.d/rc.M
EOF

このシェルスクリプトは、 まず WSL2 の /proc/mounts を参照して、 WSL2 がマウントしているディレクトリとファイルを、 そのまま GCD OS のルート (/usr/local/GCD) へマウントする。 これで GCD OS でも /mnt/c にマウントされた Windows コマンドを実行できるようになる。

次に 「chroot /usr/local/GCD /bin/sh」 を実行して、 chroot 環境下で svscanboot/etc/rc.d/rc.M を実行する。 svscanboot は daemontools の起動スクリプト。 GCD OS のほとんどのデーモン類は daemontools の管理下で起動される。 一方 /etc/rc.d/rc.M は、 GCD OS のブートスクリプトで、 ネットワーク等の各種設定と、 一部のデーモン類の起動を行なう。

ssh サーバや WWW サーバも /etc/rc.d/rc.M が立ち上げるが、 いままで WSL2 のネットワークは Windows 内でしか見えないバーチャルネットワーク (内部ネットワーク) だったらしい。 「WSL2 外部から接続」 などとググっても、 外部から WSL2 の ssh サーバにログインするには (netsh.exe の) portproxy を使え、という話ばかり出てくる。

Hyper-V Virtual Switch

どのバージョンから可能になったのかは知らないが、 「仮想スイッチ マネージャー」 で設定すれば VMware のようなブリッジモードが WSL2 でも使えるようになる。

まず Windows 管理ツールの 「Hyper-V マネージャー」 を実行する。 仮想マシン (この例では KAYANO) を選択し、 「操作(A)」 メニューから 「仮想スイッチ マネージャー(C)...」 を選ぶと、 「仮想スイッチ マネージャー」のウィンドウが開く。

左ペイン 「仮想スイッチ」 の中から 「WSL」 を選び、 右ペインに表示される 「接続の種類」 として 「外部ネットワーク(E):」を選択し、 適切なネットワーク アダプターを選択する。

これで WSL2 が外部ネットワークと通信できるようになる。 (いまのところ) タグVLAN が使えるようにはできていないが、 VMware ではタグVLAN が使えるので、 なんとかしてタグVLAN が使えるようにしたいところ...

他のマシン (以下の例では senri) から ssh で GCD OS (kayano) へログインしてみる:

senri:/ # ssh kayano
Last login: Wed May 11 04:16:34 2022 from senri.gcd.org
Linux 5.10.16.3-microsoft-standard-WSL2.
kayano:~ # ip addr show dev eth0
6: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
    link/ether 00:15:5d:ff:11:b1 brd ff:ff:ff:ff:ff:ff
    inet 192.168.18.40/24 brd 192.168.18.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::215:5dff:feff:11b1/64 scope link
       valid_lft forever preferred_lft forever
kayano:~ # tcpspray senri
Transmitted 102400 bytes in 0.000315 seconds (317460.317 kbytes/s)
kayano:~ # free
             total       used       free     shared    buffers     cached
Mem:       8055704     573892    7481812          0      30704     178088
-/+ buffers/cache:     365100    7690604
Swap:     18874364          0   18874364
kayano:~ # chroot_escape /bin/bash
groups: cannot find name for group ID 11
groups: cannot find name for group ID 14
root@kayano:/# lsb_release -d
Description:    Ubuntu 20.04.4 LTS
root@kayano:/# exit
kayano:~ #

chroot 環境から脱出 (chroot_escape /bin/bash) すると WSL2 の Ubuntu 環境に戻る。 で、exit すると GCD OS に戻る。

この GCD OS で物理ディスクの読み書き速度を測ってみた:

kayano:/ # hdparm -t /dev/sdd7

/dev/sdd7:
 Timing buffered disk reads:  538 MB in  3.01 seconds = 178.93 MB/sec

kayano:/ # dd if=/dev/zero of=/tmp/test bs=1024k count=10240
10240+0 records in
10240+0 records out
10737418240 bytes (11 GB, 10 GiB) copied, 60.5833 s, 177 MB/s

読み書きともに 177MB/sec くらい。 Core i5-8500 マシンだとこんなもの? 同じ PC (同じ Windows) で VMware 上の同じ GCD OS でも測ってみると、 読込みが 180.62 MB/sec で書き込みが 132 MB/s だった。 簡易な測定なので、ほぼ同等と言っていいと思う。

ちなみに仮想化なしで直接このマシン上で測ると、 読込み 178.65 MB/sec 書込み 233 MB/s なので、 仮想マシンによるオーバーヘッドは多少あるようだ。 とはいえ AMD FX-4100 マシンとかだと読込み 174.82 MB/sec 書込み 95.3 MB/s なので、 実用上は 177MB/sec もあれば充分?

AMD FX とかの 10年前のマシンだと CPU がボトルネックになってる感じ。そろそろ 「Sandyで十分おじさん」 は卒業すべき?

ネットワークの速度も測ってみた:

kayano:/ # iperf3 -c esaka
Connecting to host esaka, port 5201
[  5] local 172.17.14.235 port 35504 connected to 192.168.18.20 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec   116 MBytes   970 Mbits/sec    0   3.01 MBytes       
[  5]   1.00-2.00   sec   111 MBytes   933 Mbits/sec    0   3.01 MBytes       
[  5]   2.00-3.00   sec   112 MBytes   944 Mbits/sec    0   3.01 MBytes       
[  5]   3.00-4.00   sec   112 MBytes   944 Mbits/sec    0   3.01 MBytes       
[  5]   4.00-5.00   sec   112 MBytes   944 Mbits/sec    0   3.01 MBytes       
[  5]   5.00-6.00   sec   111 MBytes   933 Mbits/sec    0   3.01 MBytes       
[  5]   6.00-7.00   sec   112 MBytes   944 Mbits/sec    0   3.01 MBytes       
[  5]   7.00-8.00   sec   112 MBytes   944 Mbits/sec    0   3.01 MBytes       
[  5]   8.00-9.00   sec   112 MBytes   944 Mbits/sec    0   3.01 MBytes       
[  5]   9.00-10.00  sec   111 MBytes   933 Mbits/sec    0   3.01 MBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  1.10 GBytes   943 Mbits/sec    0             sender
[  5]   0.00-10.05  sec  1.10 GBytes   938 Mbits/sec                  receiver

iperf Done.

1Gbits/sec の LAN なので、ほぼ上限の速度が出ている。 同条件で VMware でも測ってみると、 送信が 928 Mbits/sec で受信が 925 Mbits/sec だった。 仮想化なしでも、 送信が 943 Mbits/sec 受信が 941 Mbits/sec なのでほとんど同じ。

というわけで、 速度的にはディスクもネットワークも問題無さそう。

Wio Node (ESP8266) に MicroPython をインストールして、Wi-Fi 照度&人感センサを作ってみた

$
0
0

市販されている IoT 機器は、 いつサービス終了してしまう (クラウドサーバ停止で使えなくなる) かも知れず、 そのたびに API を解析するのは現実的ではないなぁと思っていたところ、 MicroPython (日本語) を使えば手軽に IoT 機器を自作できることに気付いた。

例えば部屋の明るさを Wi-Fi 経由でサーバへ通知する照度センサは、 こんな感じで書ける:

import machine
import socket
import network_cfg

pwr = machine.Pin(15, machine.Pin.OUT)
pwr.value(1)

def webhook(v):
    addr = socket.getaddrinfo('gt.gcd.org', 12345)[0][-1]
    s = socket.socket()
    s.connect(addr)
    s.send('{"magic":"'+network_cfg.MAGIC+'","type":"Illuminance", "Darkness":"'+str(v)+'", "DeviceName":"wionode"}\n')
    s.close()

adc = machine.ADC(0)

def illumi(p):
    darkness = adc.read()
    try:
        webhook(darkness)
    except OSError as exc:
        print("exception\n")

tim = machine.Timer(-1)
tim.init(period=60000, callback=illumi)

webhook(v) はサーバ (この例では gt.gcd.org のポート 12345番。 LAN 内のサーバなので外部からはアクセスできない) へ通知する関数。 「adc.read()」で照度センサの値 (0 〜 1024) を読み取って、 webhook(v) でサーバへ通知するだけ。 タイマを使って 60秒ごとに illumi 関数を実行して、 その時点の照度をサーバへ通知するようにしてみた。

C などのコンパイル型言語と違って、 Python だから対話的にコード片を実行してみることができて (REPL)、 とりあえず動くプログラムを書くだけなら、 あっと言う間に書ける。 Arduino のスケッチみたいに、 いちいち 「コンパイルしてボードへ書込んで実行」 を何度も何度も繰り返すのと比べると、 REPL は圧倒的に楽。 IoT 機器だと実行速度はさほど要求されない (ことが多い) わけで、 REPL 一択だと思う。

いわゆる 「ラズパイ電子工作」 だとブレッドボードで配線するのが定番で、 ブレッドボードだから配線を手軽にできるのはいいのだけど、 IoT 機器として実地に使おうとすると電子部品が剥き出しのままでは困るし、 ケースにいれると大きすぎて設置方法に困る。

WioNode + PIR & Light Sensor

その点、 Wio Node (Seeed Studio 102110057) は、 極めてコンパクトなのに Grove コネクタが 2個ついているので、 Grove シリーズと互換なセンサ類をつなぐだけ。 電子部品の出っ張りとかが無いので、 ケースにいれなくてもそのまま使えそう (というか実際、このままで実地に使っている)。

Wio Node の 2個の Grove コネクタに、 照度センサと人感センサをつないでみた。 写真中央左寄りが M5Stack用 光センサユニット (517円) で、 写真右上が HC-SR312 (AM312) 人体検知センサ (1個 166円)。 Wio Node (写真下) は私が買ったとき (2021年11月) は 1250円だったのに、 急に値上がりして今は 1700円くらい。

値上がりしてしまったとはいえ、 合計 2400円くらいで Wi-Fi 通信する照度&人感センサが作れてしまう。 いつサービス終了してしまうか恐れながら市販の IoT 機器を使うよりずっといい。

Grove コネクタは 黄 白 赤 黒 の 4ピン。 黄と白は信号線で、 赤は電源 (5V or 3.3V)、黒は GND。 Seeed studio オリジナルの Grove ケーブルは 1番ピンが黄色 (写真左側) だけど、 M5Stack などの互換ケーブルだと 1番ピンが白色で 白 黄 赤 黒 の順番 (写真右側)。

オリジナルと互換ケーブルとでは黄と白が入れ替わっているが、 違うのは色だけで互換性は配慮されている (と思う)。 例えば、 アナログ出力の Grove センサのほとんど (全て?) は、 1番ピンがアナログ信号線となっている。

3番ピン(赤) は、 Wio Node の場合 3.3V が供給されるが、 M5Stack や Arduino だと 5V のものが多いので注意を要する。 例えば人感センサの定番 HC-SR501 (1個 136円) の電源電圧は 5V 〜 20V なので Wio Node の Grove コネクタから電源を供給することはできない。

Wio Node は PORT 1 (写真右側の Grove コネクタ) がアナログ信号を入力することができて、 前掲のプログラムのように

import machine

adc = machine.ADC(0)
darkness = adc.read()

などとすることで PORT 1 の 1番ピンに印加された電圧を読み取ることができる。 このプログラムの場合、 入力電圧 0V 〜 3V に対して変数 darkness は 0 〜 1024 となる。 3V 以上では常に 1024 となった。

M5Stack用 光センサユニット は照度に応じた電圧を 1番ピンに出力する。 Wio Node PORT 1 に接続した場合、 その電圧を受けて変数 darkness が 0 (照度最大) 〜 1024 (真っ暗) になる。 また、2番ピンには 0 (明) または 1 (暗) のデジタル値を出力する。 0/1 の閾値 (つまり 0 が出力される照度の最小値) は、 ユニット表面にある可変抵抗で設定できる。

Wio Node PORT 1 の 2番ピンは ESP8266 (Wio Node のマイクロコントローラ) の GPIO 4 につながっているので、

drk = machine.Pin(4, machine.Pin.IN)

drk.irq(trigger=machine.Pin.IRQ_RISING | machine.Pin.IRQ_FALLING,
        handler=illumi)

などと GPIO 4 の 0/1 が変化したときに割り込みが起きるようにしておけば、 タイマによる一定間隔の割り込みに加えて、 照度が閾値を超えた瞬間に照度をサーバへ通知することができる。 例えば部屋の照明を (手動で) オン/オフした瞬間をサーバが検知できる。

HC-SR312 (AM312) PIR Sensor

Wio Node の PORT 0 には前掲の写真のように人感センサ HC-SR312 をつないだ。 1番ピン(黄) を HC-SR312 のセンサピン (写真左中央) につなぎ、 3番ピン(赤) を電源ピン (写真左上)、 4番ピン(黒) を GND ピン (写真左下) につなぐ。

HC-SR312 には説明書がついていない (日本の Amazon で注文したのにキルギスから届いた) し、 基板のシルク印刷にもピンの役割は書かれていなかったので、 接続方法は基板の配線パターンから推測した。 人の動きを検出すると、 センサピンから 1 のデジタル値を出力する。

人感センサ HC-SR312 は接続がピンヘッダなので、 そのまま実地に使うにはやや難があるが、 M5Stack用PIRセンサユニット UNIT-PIR なら Grove コネクタで接続できる。

M5Stack UNIT-PIR Sensor

でもケースに入ってる分、UNIT-PIR は割高 (←写真では 825円もする)。 他製品と比べるとフレネルレンズの口径が小さいので、 広範囲の人の動きを検知したい場合は不向きかもしれない。

HC-SR312 と UNIT-PIR、 どちらの人感センサも AS312 PIR センサを使っていて 3.3V 電源で使うことができる。 ちなみに PIR は 「Pyroelectric Infrared Radial」 つまり 「焦電赤外線放射」 の略。 PIR センサには、 赤外線放射を集めるための半球状の白いフレネルレンズがついている。

人感センサ (HC-SR312 または UNIT-PIR) のセンサピンをつないだ Wio Node PORT 0 の 1番ピン(黄) は ESP8266 の GPIO 3 につながっているので、 GPIO 3 の 0/1 が変化したときに割り込みが起きるようにして、 その時の GPIO 3 の値を webhook(v) でサーバへ通知すればよい。

led = machine.Pin(2, machine.Pin.OUT)
rx  = machine.Pin(3, machine.Pin.IN)

def pir(p):
    v = p.value()
    try:
        webhook(v)
    except OSError as exc:
        print("exception\n")
    if v > 0:
        led.value(0)
    else:
        led.value(1)

rx.irq(trigger=machine.Pin.IRQ_RISING | machine.Pin.IRQ_FALLING,
       handler=pir)

Wio Node では GPIO 2 に 0 を出力すると青LED が点灯し、 1 を出力すると消灯する。 人感センサが人の動きを検知したときに青LED が点灯するようにしてみた。

Wio Node における GPIO のまとめ:

GPIO役割
0FUNC ボタンの状態 (0:押下, 1:離上)
1PORT 0 の 2番ピン(白)
2青 LED (1:消灯, 0:点灯)
3PORT 0 の 1番ピン(黄)
4PORT 1 の 2番ピン(白)
5PORT 1 の 1番ピン(黄)
15PORT 0 および 1 への電源供給 (1:ON, 0:OFF)

Wio Node には、 Wio Link のコントローラとして動作させるためのファームウェアが あらかじめ書き込まれている。 Wio Link はプログラミング不要で IoT アプリケーションを構築できるのが 「売り」らしいが、 むしろプログラミングしたい私は、 速攻でファームウェアを MicroPython に書き換えた。

M5Stack Downloader

見れば明らかだが Wio Node には ESP-WROOM-02 (ESP8266EX を搭載した Wi-Fi モジュール) がそのまま載っている (写真左下)。 したがって ESP8266 用の MicroPython ファームウェアを書込んで使うことができる。

まず Wio Node の PORT 0 に TTL シリアル通信ケーブルをつなぐ。 TTL レベル (3.3V) でシリアル通信ができるものであれば何でも使える (はず)。 私は M5Stack用 ESP32ダウンローダキット (写真上) を使った。 Wio Node は M5Stack ではないし ESP32 でもないが、 問題なく使える。 PORT 0 の 1番ピン(黄) が RX で、2番ピン(白) が TX (シルク印刷の矢印の向きが直感的でないので注意)。

Wio Node の Func ボタンを押しながら RST ボタンを押して離し (RST を離してから Func を離す)、 esptool.py コマンドでファームウェアを書込む:

senri:/ftp/pub/ESP8266 $ wget https://micropython.org/resources/firmware/esp8266-20230426-v1.20.0.bin
Length: 634016 (619K) [application/octet-stream]
Saving to: ‘esp8266-20230426-v1.20.0.bin’

esp8266-20230426-v1 100%[===================>] 619.16K   620KB/s    in 1.0s    

2023-06-10 09:37:47 (620 KB/s) - ‘esp8266-20230426-v1.20.0.bin’ saved [634016/634016]

senri:/ftp/pub/ESP8266 $ esptool.py -p /dev/ttyACM1 -b 115200 write_flash --flash_size=detect 0 esp8266-20230426-v1.20.0.bin
esptool.py v3.1
Serial port /dev/ttyACM1
Connecting....
Detecting chip type... ESP8266
Chip is ESP8266EX
Features: WiFi
Crystal is 26MHz
MAC: 5c:cf:7f:XX:XX:XX
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Auto-detected Flash size: 4MB
Flash will be erased from 0x00000000 to 0x0009afff...
Flash params set to 0x0040
Compressed 634016 bytes to 420365...
Wrote 634016 bytes (420365 compressed) at 0x00000000 in 37.3 seconds (effective 136.0 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...

この実行例では「Flash size: 4MB」と表示されているが、 ESP-WROOM-02 のフラッシュメモリは 2016年12月頃から 2MB になったらしい。 この Wio Node は 2021年11月に千石電商で買った (1250円)。 パッケージの日付は 2021年4月 (04/16/21)。

MicroPython は普通の Python と同様 REPL (Read-Eval-Print Loop) をサポートしている。 「import webrepl_setup」 を実行してパスワードを設定すると Web ブラウザで Wi-Fi 経由で REPL が使える。 Grove コネクタの両方にセンサ等をつなぐと、 シリアル通信ができなくなるので、 Web ブラウザで REPL を使えるようにしておくべき。

senri:~ $ cu -s 115200 -l /dev/ttyACM1
Connected.

>>> help()
Welcome to MicroPython!

For online docs please visit http://docs.micropython.org/en/latest/esp8266/ .
For diagnostic information to include in bug reports execute 'import port_diag'.

Basic WiFi configuration:

import network
sta_if = network.WLAN(network.STA_IF); sta_if.active(True)
sta_if.scan()                             # Scan for available access points
sta_if.connect("<AP_name>", "<key>") # Connect to an AP
sta_if.isconnected()                      # Check for successful connection
# Change name/password of ESP8266's AP:
ap_if = network.WLAN(network.AP_IF)
ap_if.config(ssid="<AP_NAME>", security=network.AUTH_WPA_WPA2_PSK, key="<key>")

Control commands:
  CTRL-A        -- on a blank line, enter raw REPL mode
  CTRL-B        -- on a blank line, enter normal REPL mode
  CTRL-C        -- interrupt a running program
  CTRL-D        -- on a blank line, do a soft reset of the board
  CTRL-E        -- on a blank line, enter paste mode

For further help on a specific object, type help(obj)
>>> import webrepl_setup
WebREPL daemon auto-start status: disabled

Would you like to (E)nable or (D)isable it running on boot?
(Empty line to quit)
> e
To enable WebREPL, you must set password for it
New password (4-9 chars): XXXXXXXX
Confirm password: XXXXXXXX
Changes will be activated after reboot
Would you like to reboot now? (y/n) y

Web クライアントをダウンロードして Web ブラウザで webrepl.html をアクセスすることで、 ブラウザ上で MicroPython の REPL が使える。 ファイル転送機能もある。

MicroPython WebREPL

exFAT な Ubuntu ブータブル USBメモリ (exFAT Bootable USB Flash Drive) の作り方

$
0
0

USBメモリや DVD などのリムーバブルメディアから起動可能な Ubuntu (以下 「Live Ubuntu」 と呼ぶ) は、 PC がトラブったときのレスキュー (障害復旧) の道具として重宝する。 最近の USBメモリは容量が大きく、 3GB 程度の Live Ubuntu を入れておいても大して邪魔にならない。 ふだん持ち歩く USBメモリにも、 それぞれ Live Ubuntu を入れておくとイザというとき便利。

ところが、 Ubuntu (公式) が公開している Live Ubuntu は exFAT からの起動に対応していない。 exFAT は FAT の後継として Microsoft が開発したファイルシステムで、 従来 4GB までのファイルしか扱うことができなかった FAT の制約が大幅に緩和されている。

昨今の動画ファイルはサイズが 4GB を超えるものも多く、 USBメモリは FAT ではなく exFAT でフォーマットしたい。 もちろん、 NTFS でフォーマットすれば大きなファイルを入れられるし、 Live Ubuntu も起動できるが、 NTFS は USBメモリには牛刀すぎる。

USBメモリは様々な機器に挿す可能性があるわけで、 NTFS にするのは躊躇してしまう。 スマホやラズパイ、 さらにはコンビニ等のプリント機など、 その全てで NTFS が問題無く使えるのだろうか? やっぱりデフォルトである exFAT のほうが安心。

Ubuntu などのインストールメディアの ISO イメージを exFAT に置いて起動する方法 (Make an exFAT Bootable USB Flash Drive) が既に公開されているが、 仕組み (Ventoy) が複雑だし、 そもそも ISO イメージを loopback デバイス経由でマウントして起動すると重くなるし、 必要なメモリ量も多いので、 利用したいとは思わなかった。 トラブった PC のスペックが低い場合など、 道具は軽ければ軽いほど好ましい。

というわけで、 exFAT でフォーマットした USBメモリに、 Live Ubuntu を入れる方法を考えてみた。 要は Live Ubuntu の initrd ファイル (cpio アーカイブ) をどう改変して exFAT に対応させるか?

基本的には Live Ubuntu の initrd を展開して exFAT に対応させる修正を行なった後、 cpio コマンドを使って initrd を作り直せばいいのだが、 initrd は複数の cpio アーカイブをつなげた形になっていて、 作り方によっては互換性の問題が起きるかも? Live Ubuntu 自身の update-initramfs コマンドを使って initrd を更新したほうが手軽だし確実。

なお、 (PC の起動に必要な) UEFI パーティションは (いまのところ) FAT でフォーマットしておいた方が無難と思われる。 ほとんど (全て?) の PC が exFAT や NTFS でフォーマットした UEFI パーティションを認識するらしいが、 最大限の互換性を求めるなら FAT にしておくべきだろう。

というわけで、 USBメモリの末尾 4MB (以下の実行例では sdc2) だけ FAT でフォーマットして UEFI パーティションとし、 残り (sdc1, ここでは 「boot パーティション」と呼ぶ) を exFAT でフォーマットする:

senri:/ # gdisk -l /dev/sdc
GPT fdisk (gdisk) version 0.8.4

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.
Disk /dev/sdc: 121098240 sectors, 57.7 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): 619FAAB5-5325-4404-99C4-F0541D53B069
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 121098206
Partitions will be aligned on 2-sector boundaries
Total free space is 0 sectors (0 bytes)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048       121090047   57.7 GiB    0700  Microsoft basic data
   2       121090048       121098206   4.0 MiB     EF00  EFI system partition
   3              34            2047   1007.0 KiB  EF02  BIOS boot partition
senri:/ # mkfs -t fat /dev/sdc2
mkfs.fat 4.0 (2016-05-06)
senri:/ # mkfs -t exfat /dev/sdc1
mkexfatfs 1.2.3
Creating... done.
Flushing... done.
File system created successfully.
senri:/ # 

UEFI パーティションは Windows ユーザ (あるいはコンビニのプリント機) からは見えないので、 boot パーティションのみを、 ふつうの USBメモリとして使うことになる。

次に grub-install コマンドで UEFI パーティションに GRUB (ブートローダ) をインストールする。 ついでに UEFI に対応していない PC (は滅多にないと思うが) でも起動できるように、 「--target i386-pc」オプションを使って MBR にも GRUB をインストールしておく。

senri:/ # mount /dev/sdc1 /mnt/usb
senri:/ # mount /dev/sdc2 /mnt/efi
senri:/ # grub-install --target x86_64-efi --efi-directory /mnt/efi --boot-directory=/mnt/usb/boot --removable
Installing for x86_64-efi platform.
Installation finished. No error reported.
senri:/ # grub-install --target i386-pc --boot-directory=/mnt/usb/boot --removable /dev/sdc
Installing for i386-pc platform.
Installation finished. No error reported.
senri:/ # blkid | grep sdc1
/dev/sdc1: UUID="E7F3-77CD" BLOCK_SIZE="512" TYPE="exfat" PTTYPE="dos" PARTLABEL="Microsoft basic data" PARTUUID="23041859-cc53-4164-be6a-44af6a966e5d"
senri:/ # 

UEFI パーティションに書込まれるのは GRUB コア (/EFI/BOOT/BOOTX64.EFI) のみで 124KB しかない。 UEFI パーティションは 4MB も要らないかも? GRUB 本体 (/boot/grub/x86_64-efi ディレクトリ) は boot パーティションに書込まれる。 /EFI/BOOT/BOOTX64.EFI は /boot/grub/x86_64-efi/core.efi の内容と同じ。

GRUB の設定ファイル /boot/grub/grub.cfg は ↓ こんな感じ:

set uuid="E7F3-77CD"
insmod all_video

menuentry "ubuntu 22.04.2 desktop amd64" {
  linux /casper/vmlinuz boot=casper uuid=$uuid
  initrd /casper/initrd
}

ここで "E7F3-77CD" は USBメモリの exFAT パーティション /dev/sdc1 の UUID (Universally Unique Identifier, 汎用一意識別子)。 blkid コマンドなどで調べることができる (上記 grub-install の実行例の末尾を参照)。

/casper ディレクトリは、 Ubuntu DVD (以下の実行例では /cdrom にマウントしている) からコピーする:

senri:/ # cp -a /cdrom/casper /mnt/usb/
senri:/ # ls -la /mnt/usb/casper
total 2813952
drwxr-xr-x 2 root root      32768 Feb 23 13:13 .
drwxr-xr-x 4 root root      32768 Jul  3 18:10 ..
-rwxr-xr-x 1 root root      59931 Feb 23 13:09 filesystem.manifest
-rwxr-xr-x 1 root root       2885 Feb 23 13:09 filesystem.manifest-minimal-remove
-rwxr-xr-x 1 root root       3578 Feb 23 13:09 filesystem.manifest-remove
-rwxr-xr-x 1 root root         11 Feb 23 13:09 filesystem.size
-rwxr-xr-x 1 root root 2731876352 Feb 23 13:09 filesystem.squashfs
-rwxr-xr-x 1 root root        833 Feb 23 13:12 filesystem.squashfs.gpg
-rwxr-xr-x 1 root root  137120699 Feb 23 13:09 initrd
-rwxr-xr-x 1 root root   12186376 Feb 23 13:09 vmlinuz
senri:/ # 

filesystem.squashfs が Live Ubuntu の root ファイルシステム。 これ以外の filesystem.* は不要なので削除して構わない。 vmlinuz が Linux カーネルで、 initrd がこれから書き換える initrd ファイル。

boot パーティションが FAT 等であれば、 この状態でブータブル USBメモリとして機能するが、 exFAT だと initrd が /casper/filesystem.squashfs を見つけられず Ubuntu を起動できない。

そこで initrd の内容を書き換えて exFAT を扱えるようにする。 といっても Linux Kernel 5.4 以降は (以前の exfat-fuse ではなく) カーネルレベルで exFAT を扱うことができる。 つまり必要なのはカーネルモジュール kernel/fs/exfat/exfat.ko を initrd へ追加することだけ。

まず Ubuntu DVD を用いて Live Ubuntu を起動する (インストール DVD から起動して「Ubuntu を試す」を選択)。 /etc/initramfs-tools/modules および /usr/share/initramfs-tools/scripts/casper-helpers に以下のパッチ ubuntu.patch をあてる:

--- etc/initramfs-tools/modules~	2023-02-23 12:59:33.000000000 +0900
+++ etc/initramfs-tools/modules	2023-07-03 08:46:22.000000000 +0900
@@ -9,3 +9,4 @@
 #
 # raid1
 # sd_mod
+exfat
--- usr/share/initramfs-tools/scripts/casper-helpers~	2022-05-30 23:40:38.000000000 +0900
+++ usr/share/initramfs-tools/scripts/casper-helpers	2023-07-03 08:48:03.000000000 +0900
@@ -36,7 +36,7 @@
     # FIXME: do something better like the scan of supported filesystems
     fstype="${1}"
     case ${fstype} in
-        vfat|iso9660|udf|ext2|ext3|ext4|btrfs|ntfs)
+        vfat|exfat|iso9660|udf|ext2|ext3|ext4|btrfs|ntfs)
             return 0
             ;;
     esac
@@ -234,7 +234,7 @@
             # will cause data loss when a live CD is booted on a system
             # where filesystems are in use by hibernated operating systems.
             case "$(get_fstype ${devname})" in
-                vfat)
+                vfat|exfat)
                     :;;
                 *)
                     continue;;
@@ -337,7 +337,7 @@
         for dev in $(subdevices "${sysblock}"); do
             devname=$(sys2dev "${dev}")
             case "$(get_fstype ${devname})" in
-                vfat|ext2)
+                vfat|exfat|ext2)
                     :;;
                 *)
                     continue;;
@@ -367,7 +367,7 @@
 is_supported_fs(){
     [ -z "${1}" ] && return 1
     case ${1} in
-        ext2|ext3|ext4|xfs|jfs|reiserfs|vfat|ntfs|iso9660|btrfs)
+        ext2|ext3|ext4|xfs|jfs|reiserfs|vfat|exfat|ntfs|iso9660|btrfs)
             return 0
             ;;
     esac
@@ -388,6 +388,7 @@
     modprobe xfs
     modprobe jfs
     modprobe vfat
+    modprobe exfat
     modprobe fuse
     [ "$quiet" != "y" ] && log_end_msg "...devs loaded..."
     touch /dev/.initramfs/lupin-waited-for-devs

この ↑ パッチでは、 起動時に (つまり initramfs で) 必要なカーネルモジュールを指定するファイル /etc/initramfs-tools/modules に 「exfat」 を追記している。 また、 initramfs 内のスクリプト 「/usr/share/initramfs-tools/scripts/casper-helpers」 を boot パーティションが exFAT でもエラーにならないよう修正している。

というか、 たったこれだけの修正で exFAT から起動できるのだから、 公式の Live Ubuntu で exFAT からの起動をサポートして欲しい。 exFAT を除外する理由でもあるのだろうか?

そして update-initramfs.distrib コマンドを使って新しい initrd を生成する。 ここで (通常の Ubuntu と同じ感覚で) update-initramfs を使ってしまうと 「update-initramfs is disabled since running on read-only media」 と言われてしまうので注意。 「read-only media」 だからダメなのではなく、 Live Ubuntu の update-initramfs は、 このメッセージを出力するだけの sh スクリプトに置き換えられている (いったい何のために?)。

例えば Live Ubuntu の Terminal を使って以下のように実行する:

root@ubuntu:/# wget https://www.gcd.org/sengoku/docs/ubuntu-22.04.2-desktop-amd64.patch
--2023-07-04 01:47:50--  https://www.gcd.org/sengoku/docs/ubuntu-22.04.2-desktop-amd64.patch
Resolving www.gcd.org (www.gcd.org)... 71.19.146.203, 74.207.241.21, 219.94.252.139, ...
Connecting to www.gcd.org (www.gcd.org)|71.19.146.203|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1799 (1.8K) [text/plain]
Saving to: ‘ubuntu-22.04.2-desktop-amd64.patch’

2023-07-04 01:47:57 (38.1 MB/s) - ‘ubuntu-22.04.2-desktop-amd64.patch’ saved [1799/1799]

root@ubuntu:/# patch -p0 < ubuntu-22.04.2-desktop-amd64.patch
patching file etc/initramfs-tools/modules
patching file usr/share/initramfs-tools/scripts/casper-helpers
root@ubuntu:/# uname -a
Linux ubuntu 5.19.0-32-generic #33~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Mon Jan 30 17:03:34 UTC 2 x86_64 x86_64 x86_64 GNU/Linux
root@ubuntu:/# update-initramfs.distrib -c -k 5.19.0-32-generic
update-initramfs: Generating /boot/initrd.img-5.19.0-32-generic
cryptsetup: ERROR: Couldn't resolve device /cow
cryptsetup: WARNING: Couldn't determine root device
W: Couldn't identify type of root file system for fsck hook
root@ubuntu:/# 

生成された /boot/initrd.img-5.19.0-32-generic を USBメモリの boot パーティションの /casper/initrd へコピーする。

以上で、 Ubuntu が起動できる exFAT な USBメモリができた。 ふだんは普通の USBメモリとして使える。 /casper/filesystem.squashfs は 2605MB もあるが、 いつでも Ubuntu DVD からコピーすることで元に戻せるので、 USBメモリに空きがないときは気軽に削除して構わない。

突然死した Pixel4 から nanaco 残高を救い出した話

$
0
0

何の前触れもなく Pixel 4 が壊れた。 寝る前フツーに使っていたのに、 朝 7:00 ごろ見ると電源オフ状態になっている。 電源ボタンを長押しても何の反応もない。

単なるバッテリー切れ? と思って USB ケーブルをつないで充電してみる。 が、 画面は暗いまま。 電流を測ってみると 200mA ほど。 充電が行われているようには見えない。 もちろん USB の信号線にも何の応答もない。 「電源ボタン + ボリューム小」を長押し (強制再起動) しても無反応。

自宅の Wi-Fi ルータのログを見ると、 朝 6:46:06 に Pixel 4 から Google (*.1e100.net) に対して https アクセスをしたのが Pixel 4 からの最後の通信。 朝方までは正常に動いていたみたい。 自宅の Wi-Fi に接続しているスマホは、 その状態を監視するために、 定期的にサーバから ping を打っているが、 6:51:21 に打った ping には応答がない。 つまり起床直前、 06:46 〜 06:51 ごろ Pixel 4 が機能を停止したのだろう。

一週間ほど USB ケーブルをつないだまま放置したが、 ついに何も変化は起きなかった。 バッテリーや充電回路の問題というよりは、 ロジックボードの突然死が疑われる。 つまり分解してバッテリーを直接充電したとしても、 復活する望みは薄い。

機種変更できるアプリは、 速攻で他のスマホにインストールし直したが、 問題は おサイフケータイ。 Suica や nanaco には残高があったはず。

Suica は PC 等でモバイルSuica会員メニューサイトにログインして、 利用停止手続き を行うだけ。 10分後に新しいスマホに引き継げる。 翌朝 5:00 までは残高は 0円と表示されるが、 それ以降に確認したら残高が復活していた。

ところがセブン・カードサービス (nanaco の運営会社) は、 会員メニューサイトにログインするだけでは本人確認が充分でないと考えているらしい。 他人が (何らかの方法で窃取したパスワードを使って) 勝手にログインして利用停止手続きを行い、 残高を奪うことを恐れているのだろう。 まあ、nanaco のパスワードなんてテキトーに設定している人も多いだろうから、 その懸念は理解できなくもない。

nanaco の場合、 まず nanaco の機能停止を行った上で 「引継番号」 を発行し、 その「引継番号」を使って新しいスマホに nanacoモバイルアプリをインストールする。 ただし、 この段階では nanaco 残高は引き継がれない。 セブン・カードサービスから送られてくる 「nanaco引継申請書」に新しいスマホの nanaco番号を記入し、 本人確認書類 (マイナンバーカードなど) のコピーを添付して返送する必要がある。

この引継番号は、 機種変更の際の「引継番号」と同じものと思われるが、 機種変更の場合は残高が直ちに引継がれる点が異なる。

「引継番号」の発行までは以下のように WEB で手続きできる:

nanaco transfer 1

Suica のように会員メニューから故障 (or 紛失) したスマホの Suica の機能停止ができれば簡単なのだが、 nanaco の場合は、 氏名・生年月日・電話番号 (以下、「登録名義」と略記) を指定することで、 故障 (or 紛失) したスマホの nanaco (以下、「紛失nanaco」と略記) を特定するらしい。

つまり登録名義さえ分かれば、 他人が勝手に nanaco を機能停止できる? これって会員メニューサイトのパスワードを窃取するより簡単じゃない? 誕生日なんて facebook 等で公開している人が多いような… (ちなみに私は公開していない)

まあ、 紛失nanaco のユーザ全員が会員メニューにログインできるとは限らない (そもそもパスワードを忘れてしまっていたりする) から、 このような方式にしているのだろう。

ところが、 同じ登録名義の nanacoモバイルを複数持っているとやっかいである。 それぞれ違う電話番号を登録しておいた方がいいのかも?

nanaco transfer 1

登録名義だけでは紛失nanaco を特定できないので、 紛失nanaco を利用したときのレシートが必要になる。

つまり、 レシートに記載されている 「店名」 「日付/時間」 「支払額」 「nanaco番号下4桁」 を使って紛失nanaco を特定する。

そんな回りくどいことをしなくても、 16桁の nanaco番号を入力させればいいのにと思うが、 紛失nanaco のユーザ全員が nanaco の番号を覚えているとは限らない、 という配慮なのだろう。

とはいえ、 私の場合は最後に Pixel 4 の nanaco を使ったのは 1年前だったので大変だった。 普通の人は 1年前のレシートなんか保存していないのでは?

なお、 レシートは 1年以内のものでなければならない。 おそらくセブン・カードサービスが 1年以内の取引履歴しか保存していないのだろう。

nanaco transfer 1

というわけで無事、紛失nanaco の特定が行われて、 翌朝 6:00 までに紛失nanaco が機能停止することとなった。

nanaco reissue

続いて、 引継申請書を郵送する住所を入力すると、 引継番号が発行される。 新しいスマホに nanacoモバイルアプリをインストールして nanaco の再発行を行い、 引継番号を入力すればよい。

この段階では残高は 0円である。 以下、 この nanaco を「新nanaco」と略記する。

3日後、 nanaco引継申請書が (郵便で) 送られてきた。 新nanaco の nanaco番号と、 氏名、 生年月日、 連絡先住所を記入し、 公的書類の写しを貼付けて (郵便で) 返送する。

公的書類は、 運転免許証、 パスポート、 健康保険証、 年金手帳、 住民票 (6ヶ月以内)、 マイナンバーカード (裏面は不要) などが使える。

nanaco center 11300yen

一週間が経ったころ、 何の知らせもなかったが何気なく新nanaco アプリを確認すると、 センターお預かり分 マネー残高が 11,300円になっていた (画面中央、マネー残高 0円の右隣のカッコ内)。

これで安心と思った直後、 悲劇が襲った。

「新しいスマホ」 と書いたが、 実は Pixel 4 より古い Pixel 3 である。 とりあえず余っているスマホに、 紛失nanaco の残高を移しておこうと思って、 Pixel 3 を使ってしまったが、 これが致命的な失敗だった。

Pixel 3 は 2018年11月に発売されたスマホであり、 Google Play システムアップデートは 2021年12月1日を最後に終了している。 おサイフケータイのサポートも終了してしまっていたらしい。

つまり、 おサイフケータイのバージョンは 2021年当時のまま止まっている。 一見、 正常に使えるように見えたのだが、 おサイフケータイの残高を増やそうとするとエラーになる。

センターお預かり分が 11,300円になっていたのを見て、 すぐ 「センターお預かり分反映」 操作を行ってしまった。 いまから思えば、 センターお預かり分の反映を行う前に、 おサイフケータイ アプリのバージョンが最新か、 最新でなければ Google Play ストア アプリで最新版にアップデートできるか、 確認すべきだった。

nanaco center to card





nanaco error RSYE28

nanacoモバイルアプリが、 おサイフケータイの残高を増やそうとするが、 おサイフケータイのバージョンが古すぎてエラーになる。 画面には 「エラー」 「通信環境や端末の機内モード設定をご確認ください。(RSYE28)」 という表示。

こうして nanaco 残高は失われた。 「マネー残高」は 0円のままで、 「センターお預かり分」も 0円になってしまった。 1万円あまりがパー。

さて、どうすればいいのだろう? しばし途方に暮れる。 アプリの更新ボタン (右上隅) を押すと 「カード情報更新に失敗しました」 というエラーが表示されるので、 アプリ側からすると残高を追加しようと試み続けているのかもしれない。 が、 おサイフケータイを最新版にアップデートする方法はなく、 万事休す。

もちろん話はここで終わらない。 高々 1万円あまり、 あっさり諦めてもいいのだが、 試行錯誤してみるという経験はプライスレス。

要は、 こちらの Pixel 3 で起ったことを、 セブン・カードサービスに納得さえしてもらえれば、 彼らにとって「センターお預かり分」に残高を追加することなぞ容易いことだろう。

nanaco など、 FeliCa チップ内に残高を蓄えるタイプの電子マネーは、 一見スマホと店舗端末だけで取引が完結しているように見えるが、 実際にはサーバが深く係わっている (唯一の例外は Suica の自動改札)。 したがって nanaco でどんな取引が行われたか、 全てサーバ側に記録が残っている。

ということは、 Pixel 3 上で 「失われた 1万円」は、 サーバ側にとっても 「失われた 1万円」 として認識できるはず。 だからサーバ側のログを正しく解釈できる人の協力が得られれば、 Pixel 3 で起きたことを納得してもらうことは容易いハズ。

もちろん、 そんな人がコールセンタのオペレータを担当しているわけはなく、 「ログを読める人」 に取り次いでもらわなければ話が前に進まない。 といっても、 いきなり「上司に代われ」では、 「ログを読める人」 ではなく クレーマー処理係に取り次がれてしまうのがオチである。 できるだけ平易に、 かつこちらが大変困っていることを訴えて、 なんとか助けてあげようという気にさせなければダメである。

想定問答を準備しつつ、 万全の体制を整えて、 nanacoお問合せセンター 0422-71-2266 へ電話した。 まず、 新nanaco へ残高の引き継ぎが行われた時の履歴を確認してもらう。 本来なら登録名義から新nanaco を検索するフローだと思うが、 時間短縮のため、 いきなり新nanaco の nanaco番号を伝えさせてもらう。

続いて、 新nanaco でその後の履歴が、 今日に至るまで全く無い (つまり新nanaco で 1円たりとも支払っていない) ことを確認してもらう。 電話をかけてからここまで 8分。 そもそもオペレータに電話がつながるまで何分か、かかっているので悪くない。

そしてついに、 「他の担当者に引き継ぐので、 その者から折り返し電話させる」 との回答を得た! やったね! 電話をかけてからここまで、わずか 14分。 イレギュラーな対応をしてもらったことを考えれば、 履歴の確認から担当者交替まで 6分しかかからなかったのは上出来と言える。

nanaco quit

電話を切ってから 15分後、 新しい担当者から電話がかかってきた! センター預りを反映した時刻および、 それが新nanaco の残高に反映されてないことを、 直ちにサーバ側のログから確認してもらえた。

彼曰く、 「FeliCa チップに書込むところで処理が止まっている。 これから 1ポイント付与するので、 もう一度センター預りを反映してみてもらえないか?」

すばらしい! 問題の所在だけでなく、 解決策の提案までしてもらえた。 が、おサイフケータイのバージョンが古いことが原因なので、 反映をもう一度やったところで結果は変らないだろうと私が言うと、 直ちに同意してもらえた。 話が早い!

続いて彼が提案したのは、 新nanaco アプリでの退会操作。 ええっ? 退会したら残高が消えちゃうんじゃないの? まあ既に消えているわけだけど。 彼曰く、 残高を他の nanaco に付与するには、 新nanaco の残高が確実に抹消されたことを保証する必要がある。 なるほど、 そりゃそうだ。

というわけで、 新nanaco アプリで退会操作を行う。 退会手続きなんて、 最初で最後じゃなかろうか? ちょっと緊張。

正しく退会処理が完了したことをサーバ側でも確認してもらって、 続いての提案は 「nanaco引継申請書を郵送するので、記入&公的書類の添付して返送して欲しい」。 え? またもや「nanaco引継申請書」? しかも今度は引継番号なしで、 申請書に記入した nanaco番号の nanacoモバイルへ残高を追加してくれるらしい。

残高をどの nanaco に追加するかは、 申請書に nanaco番号を記入するから特定できるとして、 どの残高 (今回の場合であれば Pixel 3 で失われた 1万円あまり) を追加するか、 どうやって申請書から読み取るのだろう? おそらく登録名義で突合するのだと思うが、 そもそも今回は引継じゃなく退会してしまったんだし… まあ、もしうまくいかなかったらその時また電話すればいいやと思って、 電話を切った。 通話時間 15分。 こんなに早く話が片付くとは感動モノ。

3日後、 nanaco引継申請書が (郵便で) 送られてきた。 前回とほとんど同じだが、 今回は新nanaco の nanaco番号ではなく、 Pixel 7 Pro の nanaco番号を記入した。 万が一のトラブルに備えて、 わざわざ残高を (セブン-イレブンで楽天ギフトカードを購入して) 0円にしたが、 ふだん使ってる Pixel 8 Pro を nanaco 残高そのままで使っても問題無かったかもしれない。

そして nanaco引継申請書を返送してから 10日ほど経った 3月19日、 ついに Pixel 7 Pro の nanacoモバイルに、 「センターお預り分 11,300円」の表示が現れた! Pixel 7 Pro なので問題無くセンター預りを反映できて、 ぶじマネー残高が 11,300円になった! nanaco残高の救出作戦完了!

おしまい。

私の FX取引 10年間の振り返り

$
0
0

今から 10年前、 2014年当時の私は米ドルをほとんど持っていなかったので、 ドル建て債券を買おうとすると、 まずドルを買う (円をドルに替える) 必要がありました。

ところが当時は黒田バズーカによる急激な円安が進行中だったので、 少しでもドルが安い (つまり円高) 時にドルを買いたいわけです。 一括で買えば手数料は安くて済みます (当時の証券会社は数万ドル程度の小口買いだと手数料が 1ドルあたり片道 50銭くらいしたと記憶してます) が、 小口に分けて買いコストを平均化したい (ドル・コスト平均法) ですし、 証券会社の営業時間外でもドルが安値をつけたら、 (たとえ深夜でも) その瞬間に買いたいものです。

そこで、 FX で数日間にわたってドル買いポジションを増やすことで、 ドルの平均買いコストを下げることにしました。 実際に債券を買うときに (より) 円安になっていて差損を被っても、 FX で取り戻せるわけですね。 いわば為替予約の FX 版。 2014年11月1日 01:00 に、 初めての FX 取引を行いました。 1ドル=112.108円で ドル/円 (USD/JPY) 1万通貨単位を買い建て。 つまり 1万ドルを 1,121,080円で買う予約をしたのと同じ効果があるわけです。

こうして始まった私の FX取引が、 来月 11月1日 01:00 でちょうど 10年になります。 この機会に、 これまでの 10年間を振り返ってみます。

10年前、 初めのうちは外貨の平均買いコストを下げるヘッジ取引が中心でしたが、 しだいに欲が出て、 為替変動が激しいときは積極的に利益を取りに行くようになりました。

最初はギリシャ危機再燃によるユーロ暴落でした。 2015年6月29日(月) 週明けから暴落したユーロに果敢に立ち向かったのでした。 このときの必要証拠金が 900万円超。 レバレッジ 25倍なので、 2億円を超える外貨を売買したことになります。 はじめて持った大きなポジションでした。 結果、 13時間で 200万円ほど含み益を増やしています。 ビギナーズラックだとは思いますが、悪くないですね。

2015年 当時 USD/JPY は 124円前後でしたが、 円安すぎると思ってました。 まあ、2024年の現在と比べるとかなりの円高水準ではあるのですが、 当時は米国の景気も悪くて低金利でした (米2年債が 0.7% くらい, 日本はゼロ金利)。 実際、経済ニュース等を見ていても円の実質実効為替レートが 1973年1月以来の弱さになっているという論調が多かったと記憶してます。

行きすぎた円安はどこかで揺り戻しがあるはずで、 実際 8月には USD/JPY が急落する場面もありましたし、 来年 2016年こそは円高に向かうだろうと思い、 年末から USD/JPY やクロス円を売りまくりました

ちなみにクロス円というのはドル以外の通貨 (ユーロ, ポンド, 豪ドルなど) と円との通貨ペア (つまり EUR/JPY, GBP/JPY, AUD/JPY など) のことですが、 例えば EUR/JPY は、 EUR/USD と USD/JPY を掛け合わせて (クロス) 算出することから、 クロスレートと呼ぶそうです。
つまり基軸通貨たるドルとの通貨ペア 「ドルストレート」 (EUR/USD, GBP/USD, USD/JPY, AUD/USD, USD/CAD など) だけ決めておいて、 それ以外の通貨ペアのレートは掛け算で算出するわけです。 で、円を含むクロスレートだから 「クロス円」。
確かに実需ではドルとの取引が大半 (9割近く) ですが、 為替取引全体における実需の占める割合は 1割未満に過ぎないので、 クロス円という呼び方が適切なのか疑問だったりします。
(日本の) FX では円との取引のほうが圧倒的ですからねぇ。 むしろ円ストレートとか呼んだ方が実体に即しているかも知れません。 スプレッドも円との通貨ペアのほうが、 ドルストレートより狭くなりがちですし。

ところが 2016年1月29日(金) に日銀がマイナス金利導入を決定、 それを受けて USD/JPY が 2円も急騰した (100万通貨単位くらい円を買い持ちしていたので含み損が一日で 200万円も拡大した) ので怖くなり、 ポジションを縮小してしまいました2月から再び怒涛の円高 (2月11日には 110円台、6月には 100円割れ!) が進んだわけで、 円買いポジションを維持していれば爆益でした。 千載一遇の大チャンスを逃してしまったわけで、 なかなかうまくいかないものですね。

同じマイナス金利ネタでも、 2016年4月28日の 「日高ショック」 による 3円の円安には耐えることができました。 「日銀:金融機関への貸し出しにもマイナス金利を検討-関係者」 というデマが、 Bloomberg で流れたのです。 市況かぶ全力2階建でも紹介されました。

世界的な景気悪化と原油価格の下落でリスク回避 (当時は 「リスク回避 = 円高」) が強まる中、 2016年6月24日のブレグジットが混沌に拍車をかけました。 怒涛の円高で 98円台へ突入したのでした。 ポジションを縮小してしまったとは言え、 ドラギ・マジックなどもあって、 8月までの円高で 500万円ほどの利益を得ました

2016-02-01 USD-JPY 1-day chart

FX を始めて 3年くらい経ったとき疑問に感じたのは、 「なぜ FX で損をする人が多いのか?」 です。 FX会社が儲かるのは損する人が多いから (店頭FX) ですよね。 大きく勝つのは難しいにしても、 年率 10% くらいなら無理なく稼げると思うのですけれど。

中の人に知り合いはいないので全くの想像ですが、 店頭FX って顧客を何段階かにランク分けして、 「カモ」はカバー率を下げてるんじゃないかなぁ? 全ての注文 (の合計) をバカ正直にカバー先へ取り次いでるだけだと大して儲からない (注文が一方向に傾くと、昨今のスプレッドは狭すぎて逆鞘になってしまいます。 まあ、大抵は売り買い同数に近くて、 狭くてもスプレッドぶん丸儲けなのかもしれませんが…) ですよね? カモの注文を取り次がなければ、 カモが損した分がそのまま儲けになりますから。 ランクの最下位は「逆神」かな? 売り買い逆にしてカバー。
2018年に始めた取引でいきなり 100万円以上の損失を出した私はカモ認定されたハズで、 2022年に取引を再開したとき、 私の注文はカバーしなかったんじゃないかと妄想してます。 最大 400万円の損害を与えたかもしれませんね。

結局のところ、 みなさん早すぎる損切り (あるいは早すぎる利益確定) で儲け損なっているのでしょう。 前述の例で言えば、 円高になると確信していても、 マイナス金利で市場が動揺して (一時的に) 円安に振れると、 怖くなってポジションを縮小してしまう。 儲け損なうのは、いつもそんなパターンです。

もちろん、 不本意な損切りをしなくて済むような資金的余裕、 あるいは同じことですが身の丈に合った投資額に抑えることは必須ですね。 強制ロスカットまで行かなくても、 資金の底が見えると、 どーしても早めに損切りしたくなってしまいますから。

ある日突然レバレッジが 1倍になっても (後述するように、 ウクライナ戦争のときにルーブルが突然 1倍になって、 夕方から 8時間で 800万円ほどかき集める必要がありました)、 全く動じずに証拠金を積み増すことが可能な範囲で取引したいものです。

新規注文を出す際、 逆指値注文を同時に出しておくと、 為替が想定と逆方向に動いたとき自動的に損切りできるので便利ですが、 為替が大きく動く時って、 得てしてまず逆方向に動いたりするものです (というかそれを意図的に仕掛ける人たちがいますね)。 逆指値注文が早すぎる損切りにつながっていないか確認することが重要でしょう。

さらに言えば逆指値注文の執行方法にも注意が必要です。 FX会社の多くは、 逆指値の売り注文はビッド (FX会社が買ってくれる価格) を参照しますが、 スプレッド (ビッドとアスクの差) が開くと逆指値が刈られてしまうんです。 早すぎる損切りが多発する一つの要因でしょう。
逆指値の売り注文はアスク (FX会社が売ってくれる価格) を参照してくれれば、 このような問題は起きませんが、 そういう FX会社は残念ながら少数派のようです。

以上のような考えで、 逆指値をつけずに取引することが増えていきます。 FX を始めてから 2017年までの 3年間、 毎年 100万円以上、 累計だと 700万円の利益を得た (ビギナーズラック?) ので、 少しばかり気が大きくなったというのもあるのでしょう。 各年の FX会社ごとの損益 (各年の確定申告書から転記しました) はこんな感じ:

SBI FXαライブ
スター
DMM.comIGサクソ
バンク
ヒロセ通商楽天
2014890,6501,059,0771,949,727
20151,570,747-346,400-81,66617,2031,159,884
2016298,8061,600,651-603,791131,7541,427,420
2017759,1001,699,653-4,4202,454,333
2018-1,341,250895,513-1,131,775-1,577,512
2019-52,4474,867,7674,815,320
2020-307,090-9,687,265-9,994,355
20211,900,38313,999,03715,899,420
2022841,193-7,485,726996,500-5,648,033
2023-1,331,7123,043,8552,942,3674,654,510
20242,099,06510,568,14512,667,210
890,6502,928,6303,054,1404,370,875144,5372,908,58013,510,51227,807,924

どの FX会社の取引アプリが自分に合っているか? いろいろ試してみる過程でどんどん FX会社を変えています。 2018年に新たに試したのはヒロセ通商でした。 始めての FX専門業者 (有価証券関連業を行なわない会社は「証券」の商号を付けられない)。 「レートが上昇するとともに逆指値注文の値も上昇していく」 トレール注文を試してみたかったのです。

ところがこのトレール注文が、 わたし的には最悪でした。 逆指値がレートについていくものだから、 上昇基調にある通貨がちょっと下がるだけですぐ逆指値にヒットしてしまい、 大きな利益を取り損なってしまうのです。 コツコツ損してドカンと利益を取りに行く私の投資スタイルには全く合いません。

結局、 わずか 3ヶ月でヒロセ通商は 100万円を超える損失を出し、 2018年全体としても初めてのマイナスに沈みます。 もうヒロセ通商は使うものかと、 いったん資金を引き揚げました。

が、負けたまま退散したというのはどうにも心残りで、 取引アプリの使いにくさが改善されたのを機に 2022年11月にヒロセ通商の取引を再開し、 翌 2023年と合わせて 400万円の利益を出し、 累計で 300万円近いプラスを達成、 ぶじ勝ち逃げすることができました。:-)
ヒロセ通商に限らず、 取引したすべての FX会社で、 最終的な FX損益がプラスなのが自慢です。
なお、 楽天証券の利益が突出していますが、 これは楽天証券が私に向いているからではなくて、 楽天証券の FX を使い始めた 2023年以降は、 IG証券を主に 「プランB」 用に使っているからです。 想定通りにコトが進めば、 利益の大半は 「プランA」 用の楽天証券に偏ります。

トレール注文を使ってみて、 「早すぎる損切りをしていては勝てない」 という考えは確信に変わりました。 また、 7年以上も使い続けた DMM.com を 2022年に止めたのは、 取引アプリで損切り幅が 99pips までしか設定できないからです。 損切り幅が (最大でも) わずか 99pips というのは、 早すぎる損切りばかりになり、 わざわざ負けるために FX をやってるようなものだと思うのですが…

2018年はマイナスに沈み、 さらに 2020年のコロナ禍と 2022年のロシアの侵略という 2つの厄災で大負けしたにもかかわらず、 10年を通してみると平均して年 278万円くらい稼げているわけで、 (意外と) 堅実な運用ができていると思います。

コロナ禍や戦争、あるいは為替介入などの有事を除けば、 ふだんの必要証拠金は 600万円を下回ることがほとんどです。 必要証拠金を投資額と考えれば、 年率 46% の運用になるわけで、悪くないですよね? :-) さらに 2019年以降に限定すれば (コロナ禍や戦争にもかかわらず) 年率 62% にも達します。

2018年までは、 ドカンと儲けている一方で初心者にありがちなミスで損失も大きかったようです。 例えば、 この表では 2016年の利益が 140万円になっていますが、 前述したように 2016年は 8月までに DMM.com で 500万円以上儲けたハズ…?

不思議に思って過去の取引履歴を掘り起こして見てみると、 2016年10月3日(月) からの わずか 1週間で 400万円以上を失っていました (;_;)。 月曜日は 1.29ドル台だった ポンド/ドル (GBP/USD) が、 金曜日には 1.18ドルまで実に 8.5% も下落しています。 特に金曜日 (2016年10月7日) は 1日で 6.1% も下落、31年来の安値を更新しました。

まさに 「落ちてくるナイフは掴むな」 の典型なんですが、 もう少し細かく見ると、 金曜日 21:30 に 1.23866ドルで買って 2分後に 1.23576ドルで売って 30,035円 損した直後、 21:33 に 1.23310ドルで売って 24分後に 1.23814ドルで買って 26,037円 損しています。 典型的な 「往復ビンタ」 ですね。まさにありがち… (>_<)

相場の乱高下にうまく乗れないときは、 いったん休むのが重要ですね。 うまく乗れると乱高下は大チャンスだったりするのですけれど。 例えば、高市〜石破ショックは、うまく波に乗れて 4時間で 70万円ほど稼ぎました。

2018年に初めての年間マイナスを経験して、 気を引き締めてのぞんだ翌 2019年は一転 500万円近いプラスでした。 勝因は、 言わずと知れたトルコリラのスワップポイントです。

ドル/リラ (USD/TRY) 22万通貨単位を売り持ち (つまり リラ買い) していたのですが、 2019年3月28日のスワップポイントが 376,002円にもなりました (1万通貨単位あたり 17,091円)。 想定元本は 22万ドル (24,419,859円) なので、 年率 548%! 闇金 (トイチ) もビックリ。 :-)

2019年3月28日のスワップポイントは異常値としても、 3月27日〜4月2日の 7日間の合計で 717,288円もあったので、 平均すると 1万通貨単位あたり 4,658円/日、 年率 153% ということになります。 つまり 1年で 2.5倍。
スワップポイントは FX会社によって異なりますが、 このときのトルコリラは、 IG証券のスワップポイントが特に (異常に?) 高かったようです。
ちなみに必要証拠金は 92,004リラ (1,822,240円) でした。 レバレッジは 25倍じゃなくて 13.4倍に下がっていたようです。 トルコリラは変動が激しいからでしょうね。

そして翌 2020年はコロナ禍ショックです。 新型コロナウイルスの世界的な感染拡大や原油相場の急落がリスク回避を加速させ、 豪ドル/ドル (AUD/USD) は 0.70ドル (2020年初) から 0.55ドル (2020年3月19日) へ実に 21% も下落しました。

2020-03-19 AUD-USD 1-day chart

市場がリスク回避的だと豪ドルは売られやすく、ドルは買われやすいので、 AUD/USD の下落が特に大きくなりますが、 他の通貨ペアもあり得ないようなレートになり、 逆指値をつけていなかった私も損切りせざるを得ず、 1000万円近い損失をかかえました

ただ、損切りで損失を確定させる一方で、 底値では果敢に買いを入れました。 FX同様、ありえない価格 (史上初のマイナス価格!) になった原油先物についても (よく分からないまま) 原油ETF を買ってみました

結果的にこの底値買いが奏功し、 1年後の 2021年2月下旬には、AUD/USD が 0.8ドルに達するなど各通貨ペアで軒並み含み益が拡大し、 原油ETF と合わせて 1800万円の利益を得ました。 結果的に 2020年の損失の倍返しとなっています。:-)

そして翌 2022年はロシアのウクライナ侵略です。 2018年以来、 強いドルの影響で割安に放置されていた新興国通貨を買っていたのですが、 その中にロシアルーブル (RUB) もありました。

2022年2月24日(木) にロシアが侵略を開始した時に暴落したルーブルですが、 いずれ元の水準に戻るはずと考えてポジションを維持したままでした。 当時の USD/RUB のレートは 1ドル=82.73ルーブルでしたが、 さらにルーブルが下落する可能性を考えて 2月27日(日) に証拠金を積み増していたのです。

ところが、2022年3月1日(火) 16:04 に IG証券から必要証拠金率の引き上げの通知が来ました。 それまで 20倍だったルーブルのレバレッジが、 いきなり 3月2日から 1倍になったのです。 2月27日(日) に証拠金を 700万円 積み増していたのに、 さらに 3月1日(火) に 800万円 積み増す羽目に陥りました。

計 1500万円も短期間で集めるのは大変です。 しかも猶予は 3月1日 16:04 から 8時間しかありません。 すでに金融機関の窓口が閉っているような時間にどうしろと? このときは複数の銀行・ 証券会社の口座に分散して置いていた予備の資金をかき集めました。 有価証券の売却は時間がかかるわけで、 いざという時に備えてキャッシュを残しておくのは重要ですね。

ところが、それで終わりではなかったのです。 翌 3月2日 14:36 に強制決済を予告するメールが来ました。 2022年3月4日(金)午前0時 (つまり 3月3日の晩) 時点で、 ルーブルの未決済ポジションの強制決済を行うとのこと。 ご丁寧なことにメールに続いて 15:33 に電話もかかってきました。 こんな事態になってもポジションを維持し続けていた人って、 すごく少なかったのでしょうね。

もう猶予は 32時間余りしかありません。 翌 3月3日に USD/RUB が暴騰したら目もあてられません。 仕方ないので 3月2日 16:01 ごろ USB/RUB の取引が再開された直後に 1ドル=109.5ルーブルで 13万ドル分を手仕舞い、 トータル 471万円の損失を食らいました。 強制決済さえなければ、 3月30日には侵略前の水準 1ドル=84ルーブル台に戻ったので、 損失どころか利益が出ていたのですが。

ウクライナ戦争のインパクトが強すぎて注意が疎かになっていたのですが、 2022年は FRB が急激に利上げした年でもありました。 コロナ禍対策のため直前までゼロ金利だった (利上げ開始が遅すぎた) ことが急激さに拍車をかけました。 米2年債のチャートを見ると、 その利上げの速さ、到達点ともに、 いかに 2022年の利上げが異次元だったか分かります。 異次元過ぎて金融システム不安を引き起こしました

2014-2024 US02Y 1-month chart

2022年2月下旬に USD/JPY は 114円台だったのですが、 3月7日(月) から急に上昇を始めます。 どんどん進むドル高を目の当たりにしてあろうことか私は、 こんな流れがいつまでも続くハズはないと 123円超えあたりから徐々にドル売り (ドル以外の通貨買い) に転じてしまったのです。

当時の私は 2015年6月の 125円あたりをドルの高値のメドと安易に考えていたのでしょう。 当時はまだ 「有事の円買い」 と言われていましたし、 これから円が独歩安になるとは夢にも思っていませんでした。

USD/JPY は 3月に 125円に達した後、 いったん下がりますが 4月からは再び上昇に転じて 125円を軽々と超え、 6月には 135円、 9月には 145円を超えてしまい、 損失がどんどん膨らみます。

今から振り返れば、 もともと戦争による 「有事のドル買い」 でドル需要が高まっていたところに、 日米金利差の急拡大でキャリートレード (円売りドル買い) の需要が一気に増大したのだろうと思うわけですが、 2022年の渦中にあっては 「行きすぎた円安」 と見えてしまうのも仕方がない気もします。
6月の時点で既に 20円も円安になってるのに、 これから年内のうちにさらに 20円も円安になるとは、 ふつう予想しませんよねぇ?
バブルがどこまで膨らむかなんてことは予測不可能なわけで、 むしろ損失が膨らむのは不可避と諦め、 バブルが崩壊する過程 (2022年11月11日の CPI ショック) で損失を取り戻すほうがマシのような気もします。

損切りが遅いわけでは決してなく、 途中何度も損切りしているのですが、 逆張りを試みては損切して何百万円か失う、 を何度も繰り返しているうちに損失がどんどん累積していきました。

2022年9月22日(木) の介入で多少は挽回できたものの、 これでドルはようやく下落に転じるだろうと予想してしまい、 さらに傷口を拡げる結果となりました。 10月には USD/JPY が 152円に達してしまい、 年初からの損失の累計が 1600万円 (ルーブル強制決済の損失を含む) に達してしまったのです。

2022年9月22日(木) の介入は、 すぐ全モ (全戻し) になって 「神田暴威」呼ばわりされたのですが、 10月21日(金) と24日(月) にも介入を実施したことで USD/JPY の上昇がようやく止まりました。 この 3回の介入額は合計 9兆1880億円だったそうです。

円ほどではなかったにせよ、 (ウクライナ戦争の影響をモロに受ける) ユーロも異常事態で、 2022年7月12日には (ギリシャ危機でも割れなかった) パリティがついに割れてしまったのでした。 ドルインデックスは 2022年9月28日に 20年ぶりの高値 114.78 を記録しています。 当時は円だけでなくドル以外は全て弱かったわけで、 円だけが弱い 2024年の円安 (ドルインデックスは 2024年4月16日の 106.58 がピーク) とは違いますね。

年末が近づいてきた 11月初め、 このままでは記録的な年間損失になるなぁ… と半ば諦めていたところ、 2022年11月11日の CPI ショックで 800万円以上挽回し、 さらに 12月19日の日銀の利上げで 200万円以上取り戻すことができました。 結果、 1年のトータルでは 565万円という常識的な額の損失に抑えることができました。

twitter などを見ていると、 この CPIショックで大損した人もいるようですが、 CPI 自体は多少予想を下回った程度で、 米景気はまだまだ底堅いことを示しています。 にもかかわらず 「CPIショック」 が起きたのは、 それだけ 米ドル バブルが膨らんでいて、 CPI 発表は最後の針の一突きだったということでしょう。
つまりこの CPI 発表前の局面ではドル売りが正しくて、 ドル買いはキケンだったということです。 私は必要証拠金が 1000万円を超える (つまり想定元本が 2.5億円超) ほどドル売りポジションを膨らませていたので、 この CPI ショックで 800万円もの含み益を得ることができました。

翌 2023年は、 2期10年を務めた黒田日銀総裁が退任し、 2023年4月に植田総裁が就任しました。 就任早々利上げすることは無いだろうし、 米国の景気もしばらくは安定しているだろうとふんで、 5月からスワップポイント狙いに宗旨替えしました

少しでも波乱があると、 スワップポイントによるコツコツ利益なんて一発でドカンと吹っ飛ぶわけですが、 幸いなことに 5月から 11月まで半年間も続けることができて、 スワップポイントだけで 300万円を稼ぎました

既に口座を持ってる (FX 取引が可能な) 証券会社の中では、 楽天証券が一番スワップポイントが高かったので、 このときから楽天証券に乗り換えています。 2023年11月21日時点での GBP/JPY のスワップポイントが 1万通貨単位あたり 295円、 必要証拠金が 74,240円だったので日歩 40銭。 USD/JPY だと 220円と 59,370円で日歩 37銭。

このとき楽天証券における全ポジションの必要証拠金合計が 5,421,580円で、 スワップポイントの合計が毎日 20,045円でした。 もしこの状態を 1年間続けることができたら、 542万円の必要証拠金で 732万円を稼ぐことができることになります。 元手が 2.45倍になるのだから魅力的ですね。 為替が安定していれば、ですが。

2023年10月19日に米2年債の金利が 5.26% のピークをつけたあと下落に転じたので、 11月に入ってからは USD/JPY を一部手仕舞って GBP/JPY や EUR/JPY の比重を高めていました。

ところが 2023年12月7日(木) に、 植田総裁の 「年末から年始にかけてチャレンジングな状況になる」 発言、 いわゆる 「植田チャレンジング・ショック」 で 147円台だった USD/JPY が一気に 141円台へ突入してしまいます。 もちろん GBP/JPY と EUR/JPY も揃って下落、 600万円もあった含み益が 200万円まで減ってしまいました。 こういうことが起きるからスワップポイント狙いは怖いですねぇ。 先が見えなくなったので、 いったん手仕舞いました。

12月19日(火) に植田総裁は 「チャレンジング」 の意図を問われ、 「一段と気を引き締めてというつもりで発言した」 と弁明しています。 まったく人騒がせな話です。 とはいえこれで状況は 11月と何も変わっていないということになりましたから、 気を取り直して GBP/JPY を中心に再びスワップポイント狙いです。

2022年に続き 2023年も円安基調だったので、 スワップポイント狙いには最適な年でした。 2022年11月の CPIショックをきっかけに急激に進んだ円高でしたが、 2023年1月16日(月) の 127.22円が USD/JPY の底だったように見えます。
2022年の始めと比べると 15円も円安ですが、 たとえ日米金利差が縮まっても、 もう 120円を割るような水準には戻らないかもしれません。 唐鎌氏が 「弱い円の正体 仮面の黒字国・日本」 で述べているように 「強い円」 の時代は終わってしまったのだと思います。
おそらく、 コロナ禍で急激な円高が進んで USD/JPY が底値をつけた 2020年3月9日(月) の 101.18円が、 最後の 「有事の円買い」 となるのでしょう。 コロナ禍がまさに歴史的な転換点だったのだと思います。 2021年1月6日(水) の底値 102.59円以降は、 一貫して円安傾向が続いています。
ちなみにその前の底値は 2018年3月26日(月)、2019年1月3日(木)、2019年8月26日(月) で、 いずれも 104円台半ばです。 米中貿易摩擦の激化や中東情勢の悪化などで世界経済が減速した時期ですね。 当時はまだリスクオフといえば円高でした。 ちなみに 3つの底値のうち真ん中 2019年1月3日(木) はフラッシュ・クラッシュで、 その前後は 110円以上で推移していました。

翌 2024年の 2月からメキシコペソ (MXN/JPY) にも手を出しています。 新興国通貨はいつ暴落するか分からないので少額ですが、 10万通貨あたり MXN/JPY のスワップポイントが 260円、 必要証拠金が 35,300円なので、 日歩 73銭です。USD/JPY の倍ですね。 もし 4半期に一度スワップポイント (元手の 67.2%) を再投資しつつ 1年間続けることができたら元手が 7.8倍! (=1.672^4) にもなるわけです。 まあその前に為替差損でやられるでしょうけど。

当時最強通貨だったペソですが、 2024年4月9日に USD/MXN が 1ドル=16.26ペソ (MXN/JPY 9.3円) の 2015年以来の最安値 (ペソ最高値) をつけたあと上昇 (ペソ下落) に転じ、 4月19日に 1ドル=18ペソ (MXN/JPY 8.6円) まで大暴騰 (ペソ大暴落)し、 いったん戻すものの再び上昇 (ペソ下落) し 1ドル=19ペソ台 (MXN/JPY 7円台) で推移しています。

もしこのときまでスワップポイント狙いを続けていたら、 それまでの利益を全て吹っ飛ばして、 それよりはるかに大きい含み損を抱えるところでした。 スワップポイントを狙うのは為替が安定しているときに限りますね。 というか新興国通貨は暴落しがち (典型はトルコリラですね) なので、 スワップポイントを狙うのは時期を選ぶべきでしょう。 ちなみにコロナ禍のときはペソが大暴落して、 2020年4月6日に USD/MXN が 1ドル=25.78ペソ (MXN/JPY 4.2円) の最高値 (ペソ最安値) を記録しました。

4月19日のペソの大暴落に買い向かった後、 (急な円高に備えるべく) ポジションを徐々に円買い (豪ドル売り) へシフトしていました。 止まらない円安に打つ手は無いと思いましたが、 それでも日銀が何かするんじゃないかと警戒していたのです。 そして今年のゴールデンウィーク直前、 2024年4月26日(金) の日銀総裁会見で市場に衝撃が走ります。 植田総裁の「円安は物価の基調に大きな影響なし」発言ですね。

わたしは滅多にドテンをやらないんですが、 植田総裁の発言を知って、 この時ばかりは全力でドテン円売りを決行しました。 遅いランチを食べてくつろいでいた時だったのですが、 スマホで夢中で円を売りまくって (USD/JPY, EUR/JPY, AUD/JPY, MXN/JPY, NZD/JPY, CAD/JPY 買い) 気づいてみれば必要証拠金が 1500万円超。 4億円近く円を売ったことになります。

そして週が明けてゴールデンウィーク真っ最中の 2024年4月29日(月) こんどは円買い介入です。 散歩中に 13:05 から始まった USD/JPY の急落 (159.5円→158.2円) を見て、 13:06 から円を買いまくりました (USD/JPY, EUR/JPY, GBP/JPY, AUD/JPY, MXN/JPY, NZD/JPY, CAD/JPY 売り)。

5月2日(木) 05:10 にも再び介入が行われて、 歴史に残るゴールデンウィークになりました。 介入額は 4月29日が 5.9兆円、5月1日が 3.8兆円だったそうです。 神田暴威の面目躍如ですね。

USD-JPY 1-hour chart

そして最後が植田ショック、 いわゆる令和のブラックマンデーですね。 2024年7月31日(水) の金融政策決定会合後の記者会見で、 植田総裁がさらなる利上げを否定しなかったことで急激に円高が進み、 週明け 8月5日には先週比 10円ほど円高の 144円台に突入し、 株価が大暴落しました。

植田総裁の発言をきっかけとしたショックって、
2023年12月7日(木) の 「年末から年始にかけてチャレンジングな状況になる」
2024年4月26日(金) の 「円安は物価の基調に大きな影響なし」
に続いて 3度目ですよね? ちょっと多すぎません?

その 3週間前、 2024年7月11日(木) 21:30 の米 CPI 発表で、 USD/JPY が 161.5円から 158.3円へ 3.2円ほど急落しました。 一方、日本でも 7月11日 or 12日に 5.5兆円の介入が行われたと言われています。 確かに 7月12日(金) 8:25, 8:45, 22:05 に 1円ほどの急落が起きていますが、 介入にしては下落幅が小さいので、 もしかすると CPI 発表のタイミング 11日(木) 21:30 に合わせた (まさにステルス) のかもしれません。

「植田ショック」 というよりは、 CPI & 介入 & 植田発言の 3連発が、 植田ショックを増幅した (3倍増?) のでしょう。 私は 3連発最初の CPI 発表のタイミングで円買いへシフトしていたので、 3連発最後の植田ショック後 8月6日(火) 01:09 (未明) に手仕舞って 200万円ほどの利益を得ました。 これでしばらくは利上げが不可能になったと思い、 8月6日(火) 08:00 (つまり令和のブラックマンデーの翌朝) からは円売りです。 さらに 8月7日(水)、 日銀副総裁が 金融資本市場が不安定な状況で「利上げをすることはない」と明言したので、 安心して円売りを拡大しています。

9月に入って円高へ向かったので損切りして一時的に円買いへシフトしましたが、 2024年9月27日(金) の自民党総裁選で石破氏が選ばれて一気に円高になったのを受けて円売りに戻しています。

細かく言うと、 高市氏優勢で円安が加速した 9月27日(金) 12:52 に USD/JPY を損切り (高値で買い戻し) し、 高市氏が勝ったら USD/JPY を買おう (つまり円売り) と待ち構えていたところ、 14:38 あたりで石破氏が勝ちそうな感じがしてきた (ふだん録画でしか見ないテレビをずっとリアルタイムで見てました。 開票作業を どアップしてくれたので非常に参考になりました) ので一気に USD/JPY を売り (つまり円買い)、 実際に石破氏が勝ってから USD/JPY を一部買い戻しています。

本当は麻生氏が高市氏支持を決めた時点でドテン円売りすべきだったと反省。 そうすれば高市氏が優勢になるにつれ円安が進んだ局面でも稼げたのに、 ドテンどころか高市氏の優勢が報じられて円安の流れが確定的になるまで、 損切りすらためらってしまいました。 私は 27日(金) 午前中の時点では、 高市氏の優勢がどの程度なのか分かっていなかったのです。 今回のように誰が選出されるかで円高/円安がはっきり分かれるときは、 もっと注意深く動向を見守るべきですね。

石破氏が自民党総裁に選出されて円高が一服した 9月30日(月) 朝から、 もちろん円売り (USD/JPY, EUR/JPY, GBP/JPY, AUD/JPY, CAD/JPY 買い) です。 石破氏が金融緩和基調を変えないことを明確にするにつれ、 円安の流れが決定的になりました。

その後は現在に至るまで円売りポジションを継続していますが、 明日 10月27日(日) の衆議院選挙 (公示直後に期日前投票を済ませました) で与党が過半数割れしても、 先行き不透明感が増して円高へ向かうというよりは、 日銀の利上げがいっそう不可能になって、 しかも景気の先行きも不透明になって、 与党が過半数を維持できた場合よりも円安へ向かいそうなので、 このまま円売りポジションを維持すべきだろうと考えています。

映画 「シン・ゴジラ」 が 2016年に公開されたとき、 「1ドル=167円! ゴジラ上陸なら円安襲来!?」 という記事がありましたが、 いまや 167円も時間の問題であるように思えます。 この 8年は 「静かなゴジラ襲来」 と言っても過言ではないのでしょう。 まあ、ホントにゴジラが首都圏に襲来したら、 そんなレベルじゃ済まないと思いますが… (^^; 日本経済の 「壊滅」 じゃなくて 「消滅」 ですね。 円の価値は限りなくゼロに近づくでしょう。

こうして振り返ってみると、 この 10年は、 強い円から弱い円へ大きく変化した転換点だったように思えます。 前半は有事の円買いだったのに、 後半は有事だと円が売られるようになりました。 この 10年は黒田日銀総裁の 10年にほぼ重なるわけですが、 黒田バズーカ、 すなわち 「異次元の金融緩和」 がこの大変化を引き起こしたのでしょうか?

資金は最も有利なところへ流れます。 1980年代の金融緩和 (公定歩合を 9% から 2.5% へ引き下げ) のときは、 「Japan as Number One」 だったので、 供給された資金は国内に留まり円高とバブル景気を引き起こしました。

2013年から始まった異次元の金融緩和では、 供給された資金が国内企業への貸付へ向かうハズはなく、 国内よりずっと有利な投資先である海外へ流れてしまいました。 不動産関連へ流れて地価高騰を招いたバブル期のほうが、 国内に留まっていたぶんまだマシだったかもしれません。 世界中で余った資金は主に米国へ流れてドル高とインフレを引き起こし、 FRB の急激な利上げにつながっています。 こう考えると、 黒田バズーカがそもそもの始まりだった可能性はあると思います。 そこにコロナ禍とウクライナ戦争が重なったのは単なる偶然なのでしょうか。

来年 2025年はプラザ合意から 40年です。 日本の国運における40年周期説というのがありますが、 なにか諸々のことが関連しているように思えてなりません。 たくさんのことがこの 10年のうちに起きました。 仕事をしていたらそのほとんどを見過ごしていたかも知れません。 そもそも介入があっても、 仕事中だと対応できませんからねぇ。 いつ、どのようにして 「敗戦後の復興」 は始まるのか? これからも世界の動きを注意深く見ていく必要性を痛感しています。 13年前に FIRE していて本当によかったと思います。

Viewing all 47 articles
Browse latest View live