Raspberry Pi Pico で littlefs

Raspberry Pi Pico を使ってラジオのアプリを開発しています。以前は MicroPython を使用していたのですが、最近は Raspberry Pi Pico の pico-sdk で開発するようになりました。Arduino IDE / PlatfoemIO を使用してもいいのですが、マイコンチップの理解度を高めたかったので pico-sdk でやっています。ラジオのアプリケーションでは、受信していた周波数や音量などの設定を保存したいものです。そのほかにも放送局のプリセット情報なども該当します。MicroPython でやってた頃は MicroPython のローカルストレージにそれらの情報を簡単に保存でき、PC の開発ツール thonny ide などからファイル転送などが簡単に行えましたが、pico-sdk でやる場合ローカルストレージのライブラリを組み込む必要があります。

マイコンのローカルストレージ定番は littlefs ということになります。MicroPython でもそうですし、Arduino / PlayformIO 環境でもサポートされています。これを pico-sdk から使用する時にあれこれやったメモ

Windows 側開発環境

私の場合 MSYS2 Mingw64 環境で開発しています。Mingw64 の環境に pico-sdk / picotool / picoprobe / pico-project-generator など Raspberry Pi github で公開されているツールを git clone して pico-sdk の環境変数の設定、また、mingw-w64-x86_64-arm-none-eabi-gcc などのコンパイル環境のインストールなどは済ませて、picotool などが build 出来ることを確認します。

Raspberry Pi 公式で Windows 版開発環境のインストーラが公開されていますが、そんなの公開される前に開発環境構築は終わっていて、今更移行するのが面倒なため Mingw64 でやってる次第です。

Windows 版 vscode を Mingw64 のプロンプトから起動できるよう
PATH 環境変数に /c/Users/ユーザー名/AppData/Local/Programs/Microsoft VS Code を .bashrc に追記。また、
alias vscode='code 1>/dev/null 2>/dev/null'
を .bashrc に追記し vscode というコマンド名でカレントディレクトリを開く状態で起動するようにしています。
$ cd プロジェクトのフォルダー
$ vscode . &

vscode が Mingw64 の環境変数を引き継ぐので、コンパイラパスの設定とかが楽になります。

openocd

https://github.com/openocd-org/openocd

openocd v0.12.0 からは Raspberry Pi Pico もマージされたかして使えるようになっています。Raspberry Pi github の openocd v0.11 は使わなくてもいいと思います。

これも git clone してビルドしてください。

$ git clone https://github.com/openocd-org/openocd.git
$ cd openocd
$ ./bootstrap
$ ./configure --prefix=/usr/local

configure の終了時にサポートされるインターフェースが yes で表示されます。cmsis-dap に yes が付いてれば picoprobe が使用可能になります。Msys2 パッケージの web で hidapi を探して pacman コマンドで入れたらいけるはずです。もちろん libusb なども必要ですので入れます。ここら辺は公式サイトを参考にして下さい。

mklittlefs

マイコンのフラッシュ領域を使用してファイルシステムを構築するライブラリ。
Arduino IDE や PlatformIO では昔からサポートされています。

https://github.com/earlephilhower/mklittlefs

https://github.com/littlefs-project/littlefs

mklittlefs のダウンロード

$ git clone https://github.com/earlephilhower/mklittlefs.git
$ cd mklittlefs
$ git submodule update --init
Submodule 'littlefs' (https://github.com/ARMmbed/littlefs) registered for path 'littlefs'
Cloning into '/home/masahiro/work/mklittlefs/littlefs'...
Submodule path 'littlefs': checked out '6a53d76e90af33f0656333c1db09bd337fa75d23'

$ git log --oneline
6a53d76 (HEAD, tag: v2.5.1) Merge pull request #744 from littlefs-project/fix-fetchmatch-err-path

バージョン 2.5.1 がチェックアウトされますが、最新版を使用したいので master ブランチをチェックアウト
$ git checkout master
$ git log --oneline
f53a0cc (HEAD -> master, tag: v2.9.0, origin/v2, origin/master, origin/HEAD) Merge pull request #929 from littlefs-project/devel

littlefs バージョン 2.9.0 になった。

mklittlefs をビルド

$ cd ..
$ make
g++ -std=gnu++11 -Os -Wall -Wextra -Werror   -Itclap -Iinclude -Ilittlefs -I. -D VERSION=\"0.2.3-64-g6e2fa17\" -D LITTLEFS_VERSION=\"v2.9.0\" -D BUILD_CONFIG=\"\" -D BUILD_CONFIG_NAME=\"-generic\" -D __NO_INLINE__ -D LFS_NAME_MAX=32   -c -o main.o main.cpp
cc -std=gnu99 -Os -Wall -Wextra -Werror   -Itclap -Iinclude -Ilittlefs -I. -D VERSION=\"0.2.3-64-g6e2fa17\" -D LITTLEFS_VERSION=\"v2.9.0\" -D BUILD_CONFIG=\"\" -D BUILD_CONFIG_NAME=\"-generic\" -D __NO_INLINE__ -D LFS_NAME_MAX=32   -c -o littlefs/lfs.o littlefs/lfs.c
cc -std=gnu99 -Os -Wall -Wextra -Werror   -Itclap -Iinclude -Ilittlefs -I. -D VERSION=\"0.2.3-64-g6e2fa17\" -D LITTLEFS_VERSION=\"v2.9.0\" -D BUILD_CONFIG=\"\" -D BUILD_CONFIG_NAME=\"-generic\" -D __NO_INLINE__ -D LFS_NAME_MAX=32   -c -o littlefs/lfs_util.o littlefs/lfs_util.c
g++ main.o littlefs/lfs.o littlefs/lfs_util.o -o mklittlefs
strip mklittlefs
C:\msys64\mingw64\bin\strip.exe: 'mklittlefs': No such file
make: *** [Makefile:96: mklittlefs] Error 1

Error 1 が表示されますが mklittlefs.exe ファイルは出来ています。
エラーの原因は Mingw64 環境ではビルドした実行ファイル名は mklittlefs.exe ですが、Linux 環境でビルドしたら実行ファイル名 mklittlefs です。 .exe が付くため strip コマンドがファイルが見つからないエラーを出すだけです。

実行ファイルからデバッグ情報などを消すだけなので strip コマンドをやらなくても問題ありません。
$ cp mklittlefs.exe /usr/local/bin
などパスの通っている場所に実行ファイルを配置すれば完了です。

注意点としては、mklittlefs コマンドをデフォルトでビルドするとファイル名は 32 文字までです。main.cpp の #define で定義されています。

Raspberry Pi Pico 側に littlefs を組み込む場合、最新の littlefs のファイル名は 255 文字がデフォルトですが mklittle に合わせるためRaspberry Pi Pico 側のビルド環境のコンパイルオプションに -DLFS_NAME_MAX=32 を付けましょう。

CMakeLists.txt だと
add_compile_definitions(LFS_NAME_MAX=32)
を追加することで arm-non-eabi-gcc / arm-none-eabi-g++ 実行時に -DLFS_NAME_MAX=32 が付与されます。

お勧めしませんが lfs.h の #define LFS_NAME_MAX 255 を直接修正する方法もあります。

littlefs のイメージ作成

Raspberry Pi Pico のフラッシュメモリのパラメタは hardware/flash.h で定義されています。

  • FLASH_PAGE_SIZE 256
  • FLASH_SECTOR_SIZE 4096 (4k)
  • FLASH_BLOCK_SIZE 65536 (64k)

Raspberry Pi Pico のフラッシュメモリサイズは 2MB なので、64kB で割ると合計32ブロックあります。実行するプログラムのコードはフラッシュメモリの先頭側から書き込まれますがフラッシュメモリの後ろの方は大抵未使用領域となります。littlefs で確保する領域を 128k (BLOCK 30,31)としました。

$ mkdir temp
$ cd temp
littlefs に書き込むファイルを用意する
$ cd ..
$ mklittlefs -b 4096 -p 256 -s 131072 -c ./temp イメージファイル名
-s 131072 は 128kB を表します。
$ mklittlefs -l イメージファイル名
ファイル名が表示される

これで littlefs のイメージが作成されました。

イメージファイルを Raspberry Pi Pico のフラッシュ領域に書き込む

picotool を使用する場合は Raspberry Pi Pico の BOOTSEL を押しながらリセットすると windows に Rasoberry Pi Pico のストレージが接続されます。

$ picotool load -v -x イメージファイル名 -t bin -o 101e0000

picotool コマンドの load で littlefs のイメージが Raspberry Pi Pico のフラッシュ領域の後ろ 128k 分に書き込まれます。

openocd を使用する場合

$ openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -c "bindto 0.0.0.0"

openocd を起動します。そして、openocd のプロセスに TeraTerm などから localhost:3333 に接続します。接続が出来たら

Open On-Chip Debugger
>

という表示が出ます。

プロセッサを停止する
> halt
フラッシュメモリの確認(任意)
> flash banks
> flash list
> flash probe 0
> flash probe 1

書き込む領域を消去する
> flash erase_address 0x101e0000 0x20000

イメージファイルを書き込む
> flash write_bank 0 イメージファイル名 0x1e0000

イメージファイルをベリファイする
> flash verify_bank 0 イメージファイル名 0x1e0000

これで littlefs のイメージ書き込みできます。

書き込みとは逆に読み出しは read_bank で読み出します。
> flash read_bank 0 イメージファイル名 0x1e0000 0x20000

ついでに、プログラムも書きたい場合は
> program プロジェクト/build/プログラムファイル名.elf verify reset

Raspberry Pi Pico 側

https://github.com/lurk101/pico-littlefs

を参考にしました。ここのは古い littlefs 用なのでそのまま使えませんが、参考にはなります。Raspberry Pi Pico の littlefs コールバック関数サンプルだけは掲載しないと分からないと思いますので下記に乗せておきます。

/**
 * @file lfs_wrapper.c
*/
#include "pico/stdlib.h"
#include "hardware/flash.h"
#include "hardware/sync.h"
#include "lfs_wrapper.h"
#define LFS_NAME_MAX 32
#include "littlefs/lfs.h"

#define FS_SIZE ( 128 * 1024 )
const char* FS_BASE = (char*)(PICO_FLASH_SIZE_BYTES - FS_SIZE);

lfs_t lfs;
lfs_file_t file;;

static int pico_flash_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void* buffer, lfs_size_t size);
static int pico_flash_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void* buffer, lfs_size_t size);
static int pico_flash_erase(const struct lfs_config *c, lfs_block_t block);
static int pico_flash_sync(const struct lfs_config *c);
static int pico_lock(void);
static int pico_unlock(void);

// configuration of the filesystem is provided by this struct
// for Pico: prog size = 256, block size = 4096, so cache is 8K
// minimum cache = block size, must be multiple
const struct lfs_config pico_cfg = 
{
    // block device operations
    .read = pico_flash_read,
    .prog = pico_flash_prog,
    .erase = pico_flash_erase,
    .sync = pico_flash_sync,
#if LIB_PICO_MULTICORE
    .lock = pico_lock,
    .unlock = pico_unlock,
#endif
    // block device configuration
    .read_size = 1,
    .prog_size = FLASH_PAGE_SIZE,
    .block_size = FLASH_SECTOR_SIZE,
    .block_count = FS_SIZE / FLASH_SECTOR_SIZE,
    .cache_size = FLASH_SECTOR_SIZE / 4,
    .lookahead_size = 32,
    .block_cycles = 500
};

static int pico_flash_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t offset, void* buffer, lfs_size_t size)
{
    memcpy(buffer, FS_BASE + XIP_BASE + (block * pico_cfg.block_size) + offset, size);
    return LFS_ERR_OK;
}

static int pico_flash_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t offset, const void *buffer, lfs_size_t size)
{
    uint32_t p = (uint32_t)FS_BASE + (block * pico_cfg.block_size) + offset;
    uint32_t ints = save_and_disable_interrupts();
    flash_range_program(p, (uint8_t *)buffer, size);
    restore_interrupts(ints);
    return LFS_ERR_OK;
}

static int pico_flash_erase(const struct lfs_config *c, lfs_block_t block)
{
    uint32_t p = (uint32_t)FS_BASE + block * pico_cfg.block_size;
    uint32_t ints = save_and_disable_interrupts();
    flash_range_erase(p, pico_cfg.block_size);
    restore_interrupts(ints);
    return LFS_ERR_OK;
}

static int pico_flash_sync(const struct lfs_config *c)
{
    asm("NOP");
    return LFS_ERR_OK;
}

#if LIB_PICO_MULTICORE
static recursive_mutex_t fs_mtx;
static int pico_lock(void) {
    recursive_mutex_enter_blocking(&fs_mtx);
    return LFS_ERR_OK;
}

static int pico_unlock(void) {
    recursive_mutex_exit(&fs_mtx);
    return LFS_ERR_OK;
}
#endif

ls コマンドの例です。どこかから拾ってきました。lfs_ls(“/”); のようにつかいますとシリアルポートにファイル一覧が出ます。

int lfs_ls(const char *path)
{
    lfs_dir_t dir;
    int err = lfs_dir_open(&lfs, &dir, path);
    if (err)
    {
        return err;
    }

    struct lfs_info info;
    while (true)
    {
        int res = lfs_dir_read(&lfs, &dir, &info);
        if (res < 0)
        {
            return res;
        }

        if (res == 0)
        {
            break;
        }

        switch (info.type)
        {
            case LFS_TYPE_REG: printf("reg "); break;
            case LFS_TYPE_DIR: printf("dir "); break;
            default:           printf("?   "); break;
        }

        static const char *prefixes[] = {"", "K", "M", "G"};
        for (int i = sizeof(prefixes)/sizeof(prefixes[0])-1; i >= 0; i--) 
        {
            if (info.size >= (1 << 10*i)-1)
            {
                printf("%*u%sB ", 4-(i != 0), info.size >> 10*i, prefixes[i]);
                break;
            }
        }

        printf("%s\n", info.name);
    }

    err = lfs_dir_close(&lfs, &dir);
    if (err) {
        return err;
    }

    return 0;
}

INA260 電流センサを使って電池の放電グラフを出してみた

Strawberry Linux の Web を見ていたら、「INA260 【高精度】I2Cディジタル電流・電圧・電力計モジュール」というのが有ったので購入。古いハンディ機に使っている充電池の放電グラフを描いてみようのがきっかけ。

古い充電池。Kenwood のハンディ機で使用される PB-32 タイプの電池です。

この電池は、Aliexpress で購入した再生品。中身は 1000mAh 位の電池6セルです。×印付けているのは、以前バッテリーチェックしたとき即放電終止電圧 1V × 6セル = 6V に落ちたので、もうこれは駄目かなということで×印をつけています。

バッテリーのチェックは Aliexpress かアマゾンか忘れましたが、抵抗負荷で放電させて電圧・電流・容量を7セグに表示するシンプルなものです。設定した放電終止電圧になると放電停止して、その時の容量(mAh)を表示してくれるシンプル仕様なのは簡単で良いのですが、放電途中の経過がどうかはわかりません。

放電途中の電圧・電流を調べたければセンサーが必要ということで、Strawberry Linux で扱っている「INA260 【高精度】I2Cディジタル電流・電圧・電力計モジュール」というのを購入しました。ターミナルに電池や放電器を配線して、I2C 接続で電圧・電流を読み取るモジュールです。この手のセンサーは Strawberry Linux だけでなくいろんなところから同等のモジュールは販売されているので入手は難しくありません。

I2C 接続というとマイコン接続となるのですが、今回はグラフ表示をしたかったのでアプリケーションは Windows ですることにしました。ということで、PC から I2C 接続するため購入したのは FT232H モジュールです。

FTDI の FT232H を搭載したモジュールです。今回は秋月通販で購入しました。写真は Adafruit のものですが、FT232H であれば他所のモジュールでも問題ありません。

IN260 とFT232H モジュールは、これも秋月で買った4ピンケーブルで接続して終わり

バッテリー端子とセンサー間の線は、ばね式クランプで押しつけてます。3D プリンタでも持っていれば電池ホルダー的な物を作ってスマートに行きたいところですが、持っていないので仕方ありません。測定する資材はこんな感じです。

測定アプリケーションは Visual Studio 2022 の C# フォームアプリケーションです。

依存関係で導入した Nuget パッケージは FtdiSharpという FTDI から提供される D2xx.dll の C# ラッパーと scottplot バージョン4安定版のグラフプロットライブラリの2つ。プロジェクトをデフォルトで作成したので .NET8 アプリです。

アプリケーションを起動すると、PC に USB 接続されている FTDI アダプタを一覧で表示。そのアダプタをクリックすると、I2C アドレスのスキャンをして I2C デバイスのアドレスをを表示。

I2C アドレスをクリックして、START ボタンを押すとセンサーから電圧・電流を1秒ごとに取得してグラフをリアルタイム更新していくシンプル仕様です。グラフの縦軸は画面右上の数字を入力したら随時反映するようにしました。この数字入力は測定中でも変更可能です。

このバッテリーの直近の容量は以下のようになってます。中の1セルが駄目になったと思われますがこのような状態になれば、充放電しなおしても復活は無理っぽい感じ。グラフ表示も 1.2V × 6セル = 7.2V 付近で粘る感じではなく下がり始めが早い状態になっていました。


2023/12/01	放電	50mAh
2023/12/01	放電	5V 終止で再測定	341mAh
使用停止
2023/12/04	充電	12時間
2023/12/07	放電	476mAh
2023/12/07	充電	14時間
2023/12/08	放電	544mAh
2023/12/08	充電	14時間
2023/12/10	放電	523mAh

アプリケーションのサンプルは https://github.com/ngc6589/ina260_CSharp_example に置いています。

Spresense お試し

今更ながら SONY Spresense です。秋月の Web をうろうろしていたら見つけました。発表は数年前でしたでしょうか。1個くらい買っておいてもいいかと思いショッピングカートに入れて購入。

開発環境は Arduino 環境と tool-chain を入れてコマンドラインからビルドするか Visual Studio Code を使った開発となります。私の場合は Visual Studio Code 少しわかる感じなので tool-chain でやることにしてチュートリアルをやり始めた所です。

普段使いの PC はデスクトップで Windows 11 です。SONY 開発者サイトによると、WIndows 11 の場合は wsl2 を使用するとありました。Virtual Box で Ubuntu 22.04 を入れているので、今更 wsl2 を入れてもディスクの容量食うだけなので、ビルド環境は Virtual Box の Linux でやりました。

チュートリアルの単純なアプリケーションなどはビルド出来て、ブートローダー経由の書き込みも出来て、サンプルプログラムの実行も出来てここまでは調子よかったです。問題は、デバッガです。Spresense の拡張基板には SWD デバッグ端子が付けられます。2×5 1.27mmピッチのピンヘッダです。ここにデバッガからのフラットケーブルを刺せば、Visual Studio Code からデバッグやら単純に書き込みをすればブートローダー経由の書き込みではなく、elf ファイルを直接流し込めます。逸れますが Raspberry Pi Pico なども uf2 ファイルを Pico をUSBストレージ状態にしてファイルを放り込んで書き込みますがやはり手間なのでデバッグ端子経由で elf ファイルを書き込むのをよくやっています。そういう意味でデバッガを使ってコンパイルされたファイルをインストールするのは大変便利です。

Spresense 開発環境で公式サポートされているデバッガは Keil ULINK2 と NXP LPC-Link2 です。 LPC-Link2 は持っているのでいいのですが、今となってはどちらも新規購入は出来ません。ここら辺は公式としても見直してほしいところですね。

んで、その LPC-Link2 でのアプリケーションの書き込みは、私の所の固有の問題かもしれませんが、Virtual Box の ubuntu では LPC-Link2 が使用できませんでした。LPCScrypt でファームを更新しても変化なし。udev の rules 見ても問題なし。ファームの更新は出来るから壊れてないわなと思いつつも進展なし。

vmware workstation player 17 を試しに入れて、ubuntu を入れた環境では LPC-Link2 による書き込みは可能でした。ただ、vmware player の仮想マシン起動中に CTRL-G でキーボード・マウスを vmware player 側にしていないと、 ubuntu 起動後キーボード入力が出来なくなるというワケワカラン事象が発生します。そこさえ辛抱すれば LPC-Link2、PicoProbe 両方動くので良いです。

PicoProbe という言葉が出てきましたが、PicoProbe も SWD デバッガなので 2×5 コネクタを付けて Spresense ボードに接続することが出来ます。これは Virtual Box / vmware workstation player 17 両方デバッガを認識して書き込みに成功しています。

ついでの確認で windows11 wsl2 環境では、openocd 立ち上がるのですが、その後エラーで失敗します。これは、LPC-Link2, PicoProbe 両方で発生しました。wsl2 で起動した openocd は Windows の USB に接続されているデバッガを使いますが、wsl2 で何か設定必要だったかなぁ。Microsoft Ignite に、「USB デバイスを接続する」 という題名で、wsl 関連の記事が出てたりもするので、何か必要な気もします。

古いノートPCに入れた ubuntu 22.04 環境は LPC-Link2 ・ PicoProbe での書き込みに成功しています。実機は悩むことなく動くので Linux 入れてるマシンをお持ちの方は素直にそれを使うのがいいと思います。

今後役に立つかもしれないので、↑の写真の PicoProbe 部分だけ切り出した基板を作ってもらうことにしました。

こうやってチュートリアルやる前に燃え尽きてしまった状況です。

nuttx asmp サンプルをデバッガで書くと動かず、ブートローダーで書くと動くという事象も確認はしているのですが、ここら辺は書き込むファイル抜けとか思うのでそのうち掲示板でも見てみようと思うところです。今のところ hello world しかできていなくて gps などの exampes が全く手つかずの状況。

公式が用意・確認した gcc / openocd などを使うように開発環境がまとまられているのは良いです。Web サイトもひつこいくらいに説明は入っているので見やすさは別にして良いです。nuttx なんで printf デバッグでええやんという感じはするのですが、まぁデバッガも使えるならちょっとつないでみようとやり出してハマった結果、これ以上はお腹いっぱいになって動けなくなったパターンでした。

TEF6686 DSP RADIO キット組み立て

Twitter でフォローしている方が、「TEF6686 DSP ラジオキットを出します」との案内。キットを一つ下さいと連絡し入手した次第です。TEF6686 は NXP 社から販売されているラジオチップですが、当キットではシールドケースに組み込まれたモジュールが使用されていました。

ちなみに本家サイトは TEF DSP AM/FM tuner v1 – pe5pvb.nl になります。

TEF6686 はラジオ用IC単品ですが、キットには写真の用なシールドーケース付きのモジュールが入っていました。ヘッダピンは I2C 制御のピン、電源のピン、LR音声出力のピンだけだったかな。が出ています。モジュールとしてはシンプルな構成です。

これを、 ESP32 のマイコンでコントロールしてラジオ受信を行います。

表面実装部品を取り付け

リード部品を取り付け。

各基板を接続して組んでいきました。このほぼ組みあがりで5時間くらいかかっています。

ファームウエアファイルを書き込んで完成です。一応、リポバッテリーが付いていますので、充電完了するかの確認でしばらく放置して充電完了確認などしてます。

少し使ってみた感じでは、感度は十分良いと思います。

自分の Si4735 DSP ラジオと並べてみたの図。ラジオチップも異なるし操作方法など全く別物なので比較はできませんが、いちばん下の基板にスピーカーがあるのは同じです。で、TEF6686 DSP ラジオの音が良く出ています。私の Si4735 ラジオは音の指向性が強くラジオ正面は結構聞こえるけど向きによって変化が大きいです。

私のは秋月マイクロスピーカー 1個50円を基板に貼り付けてますが、TEF6686 DSP ラジオは写真のようなプラスチックの箱型スピーカーです。お値段 1個400円超えと Aliexpress で買う部品にしては高い部類ですが、これが結構いい感じです。早速購入して自分の Si4735 DSP ラジオのスピーカーを交換してみたいと思ってます。現在輸送中なので届き次第交換して効果があれば、Si4735 DSPラジオのスピーカーは型番変更しようと思ってます。

Raspberry Pi Pico で Si4735 DSP ラジオ製作

最近は ATMEGA328P 終了の兆しが見えてきて、もうちょっとフラッシュやメモリの大きいマイコンに移行したいと思っていました。5V ならまだ Arduino MEGA2560 とかあって GPIO もたくさんあるので使っていく予定ですが、それ以外は Raspberry Pi Pico 中心でいいかなと思い始めるようになりお試しで使ってみました。前から ESP32 とかも持っているのですが ESP-IDF に慣れないのと、ESP-IDF は x.x のリリースされたバージョンに対しても更新をかけてきます。commit 番号によっては動かなくなったりもします。 Raspberry Pi Pico は現在 RP2040 チップだけなので SDK のリリースもシンプルです。ビルドも cmake のインタフェースなので理解できました。また Raspberry Pi Pico は MicroPython も動作するので、C/C++ で書くほどの物でない場合は MicroPython を使用する手も有りで、SDK の持つ基本的なGPIO操作やハイレベルAPIもポーティングされているので、とっかかりは MicroPython から始めるのいいと思いました。

Raspberry Pi Pico の基本をやるとき思いついたのが Si4735 DSP ラジオです。過去 ATMEGA328P にキャラクタ液晶つけて動かしたことがあったのですが、これなら i2c が動けばラジオチップは動くので、ラジオチップを動かすところから始めました。

試作用基板作成

ブレッドボードでやり始めたのですが LEDチカチカ程度とは違い配線が面倒。ということで Raspberry Pi Pico 用に GPIO ピンをピンヘッダに出す基板を作成。Raspberry Pi Pico が2個乗っかているのは一個は動作用でもう一つは PicoProbe という C/C++ で使うデバッガです。MicroPython では使用しません。ピンヘッダを3列出しているので、本来の信号線を配線するのに加えて Analog Discovery のロジックスコープもつけられます。これをMDF板に貼り付けて液晶とTTGOラジオといわれる基板も付けました。

開発環境選定

開発環境は最初から MicroPython に決めていました。初めてなので C/C++ SDK に慣れていなかったためです。また、ラジオの周波数表示などを液晶に出すため、グラフィック環境も欲しいということでググっていたら LVGL というグラフィックライブラリをみつけました。これも本家は C で書かれていますが、lv_micropython という MicroPython に LVGL がバインドされたものがあったので、これなら使えそうと思いました。gitgub の https://github.com/lvgl/lv_micropython にリポジトリがあります。

ファームウエアは ubuntu / Raspberry Pi OSなど Linux の環境であればリポジトリに書いてある手順通りにやれば作成できます。MSYS2 MinGW64 では MicroPython のクロス環境ビルド途中で、Windows のコマンドライン長限界を超えるものがあってエラーになります。make の変数を与えれば分割ビルドできそうなのですが、面倒なので やっていません。

プロトタイプ開発

python IDE の thonny を使って MicroPython のコードを書いていきます。GPIO/SPI/i2c などの使い方は MicroPython のドキュメント https://micropython-docs-ja.readthedocs.io/ja/latest/ に、LVGL グラフィックスライブラリは https://docs.lvgl.io/master/index.html にあります。

少しコーディングしては RUN させるというトライ&トライです。その結果フォントの組み込みなどの方法も理解出来て写真のようにいろいろなウイジェットが使えるようになりました。

  • 周波数をテンキーから入力して選局
  • 表示されいる周波数からステップチューニング
  • AM・FMなどバンドスキャン
  • 選局リストから放送局をタップすると選局

が主な機能です。

本番用基板作成

ここまで出来たら残るは本番用基板です。今回は ILI9341 の液晶に大きさを合わせました。上から液晶・ラジオ基板・マイクロスピーカーを貼り付ける基板の3枚です。

基板は何回か作り直ししましたが、それっぽい感じに仕上がりました。

回路図やアプリケーション本体などは、https://github.com/ngc6589/Si4735_Radio_RaspberryPiPico にて公開しています。

Blog のレンタルサーバー切り替え

今までお名前.com のレンタルサーバー SD-12 プランで12年ほど運用していましたが、 wordpress の運用でプラグイン更新に失敗するなど問題が起きていました。また月額費用も現行のものより高いため移行することに。

DNS が新しいレンタルサーバーに切り替わるまで最大72時間程度必要でしたのでその間は移行作業保留にしてやっとのことで移行終わりました。Blog が表示されなかった期間ご迷惑をおかけしました。

VN-L5 MarkII 人柱版 CW トランシーバーキット製作

JL1VNQ さん ( https://jl1vnq.blogspot.com/ ) が頒布されている CW トランシーバーキット VN-L5 が MarkII となりました。この MarkII キットの人柱版製作募集を Twitter でみかけたので、まだ頒布あるかと聞いた所最後の1台とのこと。お願いして頒布していただいた次第。

(写真や図はクリックすると大きくなります。スペアナ画面は元から小さいので除く)

思いの外大き目の箱で届きまして、過去製作したキットに比べて時間がかかりそうな予感でした。で、実際にもかかりました。また箱に貼られたラベルには 10W とありますが実際には 20W です。リチウム電池運用ならファイナルの電源電圧が低くなるので 10W 程度になると思います。

中身は、基板・パーツ一式・説明書です。説明書については部品表・回路図と組んだ時の写真だけで、旧版の説明書も合わせて読んで組むとの指示でした。人柱なのでそこら辺は問題ありません。

基板は3枚構成になっていて写真左上がコントロール(CTRL)基板。右上は送信(TX)基板。手前が受信(RX)基板になっています。裏面の写真は掲載していませんが、裏面にも実装する部分もあります。

結構時間はかかりましたが組みあがりました。組みあがったので保証認定を取るべく資料を作成します。今回は JARD に保証認定してもらうので、送信機系統図とスペクトラムアナライザの測定結果を添付します。

RIGOL スペアナの入力に 50dB のアッテネーターを接続して測ります。今回確認したのは次の3つです。

  • 送信出力
  • 近傍のスプリアス
  • 高調波

このキットの送信回路基板の LPF は 160mb 用と 80mb 用の2つだけなので、スペアナ測定は 1.8MHz と 3.5MHz の測定した結果を提出しました。

160mb 送信出力

スペアナの RBW/VBW を 10Hz SPAN 100Hz で見ました。結果 44.25dBm で 26.6W の出力となりました。160mb の工事設計は 25W で出すことにしました。

160mb 近傍

送信周波数近傍を確認します。Span 10kHz で測ります。RBW・VBW は 10Hz が良いのですが、RIGOL DSA815TG では測定時間がものすごく長くなるので、30Hz にして測定。これでも 12秒程度必要です。17dBm(50mW)以下かつ基本周波数の尖頭電力より 50dB 低い値をクリアしていました。

160mb 高調波

高調波は本来 9kHz から 1GHz の範囲を取らないといけないかもしれませんが、第5高調波あたりまで測りました。50dBm 以下なので問題ありませんでした。

80mb 出力

スペアナの RBW/VBW を 10Hz SPAN 100Hz で見ました。結果 43.23dBm で 21W の出力となりました。80mb の工事設計は 20W で出すことにしました。

80mb 近傍

送信周波数近傍を確認します。17dBm(50mW)以下かつ基本周波数の尖頭電力より 50dB 低い値をクリアしていました。

80mb 高調波

160mb 同様第5高調波あたりまで測りました。50dBm 以下なので問題ありませんでした。

送信機系統図作成

測定の結果申請いけそうなので資料作成をします。送信機系統図は、無線局免許状の記載で 1.8MHz帯、3.5MHz帯、3.8MHz帯の申請となります。なので、各周波数帯の送信周波数を記載。また新スプリアス規格で設計・製作している旨の記載は必要です。

キットの回路構成はファイナル送信出力の LPF が 160mb と 80mb の切り替えになっているので LPF が2つあること、送信出力も 160mb と 80mb でそれぞれ何ワット出るかを図に記載しておきます。

電子申請Lite 入力

電子申請 Lite の工事設計は以下のように入力

  • 変更の種別:増設
  • 適合表示無線設備の番号:空欄
  • 発射可能な電波の型式及び周波数の範囲情報
    希望する周波数帯
    1.9MHz A1A
    3.5MHz A1A
    3.8MHz A1A
  • 変調方式:空欄
  • 終段管
    名称個数:MTA100N10KRI3 x 2
    電圧:13.8V
  • 定格出力: 25W と 20W
  • 添付資料は測定資料と送信機系統図。保証認定のPDF

工事設計入力画面で、変調方式など入れないと入力完了のチェックで未入力欄がありますが続けますか?のような確認ダイアログが出ます。問題ないので続行でOKです。

申請状況

・JARD 保証認定 2022/11/06申請 2022/11/10 保証完了
・JARD から認定シール(普通郵便)待ち 2022年11月24日完了
届かなくて再送してもらったため、シール入手に日数がかかりました。
・近畿総通宛の変更申請 2022/11/10申請 2022/11/14 審査終了

ということで、無線局免許状に新しい無線機の追加が完了しました。

2023年5月中旬に本番リリースされたのに伴いまして、人柱版スプリアス測定の資料ダウンロードリンクは外しました。ご自身で作成お願いします。

Raspberry Pi Pico I/O 拡張基板

普段使用するマイコンは、Arduino UNO(ATMEGA328P) か Arduino MEGA2560( ATMEGA2560) を良く使っています。最近はマイコンに接続するモジュールも 3.3V のが多いので、Raspberry Pi Pico を使えるようにとお勉強始めたところです。

ESP32 も持っているのですが、ESP32-S(XTensa LX7) ESP32-C(RISC-V) の新製品が技適取って十分出回っていればこちらに行ったかもです。ESP32 の WROVER-E が技適あるので、これでもいいのですが。無線モジュール要らないなら今のところ Raspberry Pi Pico で十分かな。というところで、Pico を使い始めた次第です。

ちょこっと動かすだけなら、Raspberry Pi Pico に秋月の細ピンヘッダつければブレッドボードに差しても左右2列取れますので十分です。

最初はブレッドボードで始めたのですが、やっぱごちゃるなぁということで基板を作成。2×20 ピンヘッダに IO ピンを出しているので、ロジックアナライザの線を刺すこともできますし、SPI や I2C でバス共有するの予備端子としても使えます。

今は、こんな感じで使用しています。基板手前の Raspberry Pi Pico のソケットは Pico Probe デバッグのソケットです。現在 Micropython 勉強中なので openocd + gdb-multiarch は使用していないため手前の Pico Probe は外しています。

基板を10枚作ったので余りは頒布にのせました(売り切り終了)

Raspberry Pi Pico I/O 拡張基板(キット) | 頒布物のページ (em9system.com)

https://github.com/ngc6589/Raspberry-Pi-Pico_IO_Expansion-1

PZEM-004T v3.0 で商用電源の電圧と周波数をロギング

最初に、参考にしたサイトを掲載しておきます。

PZEM-004T v3.0 はアマゾンや Aliexpress で検索するとたくさん出てきます。今回はアマゾンで買いました。1週間ほどで到着です。何も気にせず買ったので、USBシリアルなしを買ってしまったようです。USBケーブル付きがあればそれを買うほうが楽です。

仕方がないので、ポケコンのファイル転送で使っていた FTDI の USB シリアルケーブルに犠牲になってもらうことにしました。使用する線は 5V, GND, TX, RX の4本です。USBシリアルを使うのは Raspberry Pi の USB ポートで使用するためです。マイコン基板と接続する場合は、マイコンの 5V, GND, TX, RX を直結するだけです。

USBシリアルの TX RX と基板の TX RX はクロスで結線しましょう。これ間違えると通信できません。

プラケースの蓋を外して、XH コネクタのポストを外して

USB シリアルの線を半田付け。スクリューターミナルNとLの所にコンセントからの電線を差し込んでネジ止めします。ハードの準備は以上です。

Raspberry Pi 側のプログラムは python で動かします。PZEM-004T v3.0 からデータを取得して、ambient にデータ送信するようにしています。

#!/usr/bin/env python3

import datetime
import time
import serial
import ambient
import requests
import modbus_tk.defines as cst
from modbus_tk import modbus_rtu

# sudo pip3 install git+https://github.com/AmbientDataInc/ambient-python-lib.git
# sudo pip3 install modbus-tk

if __name__ == "__main__":
    # Connect to the slave
    serial = serial.Serial(
                           port='/dev/ttyUSB0',
                           baudrate=9600,
                           bytesize=8,
                           parity='N',
                           stopbits=1,
                           xonxoff=0
                          )

    master = modbus_rtu.RtuMaster(serial)
    master.set_timeout(2.0)
    master.set_verbose(True)
    am = ambient.Ambient(チャネル番号, ' ライトキー ')

    while True:
        try:
            data = master.execute(1, cst.READ_INPUT_REGISTERS, 0, 10)
            result = am.send({'d1': data[0] / 10.0, 'd2': data[7] / 10.0})
            print(datetime.datetime.now(),
                    'voltage=',
                    data[0] / 10.0,
                    'freq=',
                    data[7] / 10.0,
                    'send status=',
                    result.status_code)
            time.sleep(60)

        except KeyboardInterrupt:
            print('exiting pzem script')
        except requests.exceptions.RequestException as e:
            print('request failed: ', e)

USBシリアルのポート名。/dev/ttyUSB0 のところは syslog を見て確認。
am = ambient.Ambient(チャネル番号, ‘Writeキー’)
ambient のデータ送信は、ご自身のグラフを作成するところで、チャネル番号、ライトキー、リードキーが発行されるので、そのチャネル番号、ライトキーを入れたら送信できます。

プログラムの起動は、raspberry pi 起動後手動でしています。
tmux or screen コマンドを起動する
上記 python プログラムを走らせる
tmux or screen から抜ける。
です。tmux や screen でプログラムを動かしておけば、teraterm とかが切れても動き続けますので良いです。

ambient に投げたデータは公開にしていますので、下記 URL からご覧いただけます。

自宅の商用電圧と周波数 -Ambient (ambidata.io)

上のプログラムには記載省略していますが周波数は 0.1Hz 単位なので、実際にグラフを出してみると見にくい感じになりました。2022年9月5日から 周波数データの3回平均したものも追加で表示するようにしました。

2023年2月3日 上記 ambient へのデータ投入は終わりました。理由は0.1Hz の分解では全く意味なしというところでした。周波数カウンタ作ったほうが良いと思いましたので、改めてやり直そうと思った次第です。

MCS-52 Basic 基板作成

ヤフオクを見ていましたら、BASIC-52 の基板を見つけたので購入。基板に書かれたアドレスから https://www.billy.gr/mcs-basic-52/ で公開されているガーバーファイルのようです。以前作成した BASIC-52 基板は EEPROM 領域がないため、プログラムの保存が出来なかったのですが、こちらの基板は EEPROM のソケットが付いています。

この基板を作成された方は EEPROM の代わりに DALLAS DS1230AB-70 という バッテリーバックアップ付き 32KB SRAM (NVRAM) を載せておられました。不揮発性メモリなので機能は果たしますが、Mouser 価格 4000円程度とお高い状況。また、EEPROM は HC138 のアドレスデコードで 8KB バウンダリになっているので、8KB 分しかつかいません。NVRAM はもったいないので AT28C64B など8KBx8bit の汎用EEPROMを使用することにしました。

WEBサイトを見ると基板のエラー情報が書かれています。

DC ジャックの GND 未配線は、DCジャックの端子を抵抗の切れ端で接続して、電源が入るようになりました。また、リセット回路からの信号は CPU のリセット端子まで来ていましたが、74HC32 のリセット信号が配線されるべき端子には来ていなかったためジャンパー飛ばして結線。私の所ではこの2か所手当することで動作しました。

*MCS-BASIC-52 V1.31*
READY

>erase       

READY
>
>10     REM Basic ROM directory
>20    ADR=8010H : PN=1
>30     IF XBY(ADR)<>55H THEN  END
>40    ADR=ADR+1 : BA=ADR
>50     IF XBY(ADR)=1 THEN  GOTO 80
>60    ADR=ADR+XBY(ADR): GOTO 50
>80     GOSUB 100: PRINT : PN=PN+1 : ADR=ADR+1:   GOTO 30
>100    PRINT "PRG",PN,"from ", :  PH1. BA, :  PRINT " to ", :  PH1. ADR,
>110    PRINT " (",ADR-BA,"bytes)",
>120   NB=XBY(BA)-2: IF NB<4 THEN RETURN
>130 IF XBY(BA+3)<>96H THEN  RETURN
>140    FOR XX=BA+4 TO BA+NB:PRINT CHR(XBY(XX)),: NEXT : RETURN
>prog
 1

READY

erase 命令で EEPROM 領域を初期化します。3分くらいかかるので気長に待ちます。その後プログラムを入れて、prog 命令を実行すると EEPROM にプログラムが保存されます。↑のプログラムは、EEPEOM にどんなプログラムが格納されているか表示するプログラムで、http://www.nomad.ee/micros/8052bas.shtml で公開されているものです。

>rrom

PRG 1 from  8011H to  8134H ( 291 bytes) Basic ROM directory

READY
>

rrom 命令を実行すると、EEPROM の1番目のプログラムを実行します。先ほど書き込んだ EEPROM に何が入っているかを表示するプログラムです。

>ram

READY
>new

>1 REM ASCII ART
>10 FOR Y=-12 TO 12
>20 FOR X=-39 TO 39
>30 CA=X*0.0458
>40 CB=Y*0.08333
>50 A=CA
>60 B=CB
>70 I=0
>80 T=A*A-B*B+CA
>90 B=2*A*B+CB
>100 A=T
>110 IF (A*A+B*B)>4 THEN GOTO 150
>120 I=I+1:IF I<=15 THEN GOTO 80
>130 PRINT " ",
>140 GOTO 170
>150 IF I>9 THEN I=I+7
>160 PRINT CHR(48+I),
>170 NEXT X
>180 PRINT
>190 NEXT Y
>
>prog
 2

READY
>rrom

PRG 1 from  8011H to  8134H ( 291 bytes) Basic ROM directory
PRG 2 from  8136H to  8417H ( 232 bytes) ASCII ART

READY
>rrom2

新しいプログラムを追加します。prog を実行すると EEPROM の2番目に格納されます。rrom2 と引数2で2番目を実行できます。

マニュアルを見ると、電源投入時に自動起動するプログラムの設定などもあるようです。また GPIO ピンもCPUについているので簡単に使えますし、このマイコンボードの右端にはメモリマップドI/Oの端子も出ています。プログラムのセーブが出来るマイコンボードは当時としては優れた機能だったかと思います。普通はROM差し替えですしね。まぁBASIC言語なので実行速度はお察しというところで。