秋月時計ハック

RaspberryPi

秋月時計故障

秋月の組み立てキットに、 1インチ大文字マイコンデジタル時計キット Ver.2 というのがあり、作業部屋で使用している。月に数秒および年に1分くらいずれる程度でそれなりの精度で動くため、置時計として便利に使用していた。

電源はその辺にあった謎のACアダプタ(15V)を使用して、ケースはこれがちょうど入る百均のタッパーに入れて、 緩衝材代わりに百均の食器洗いスポンジで固定して使用していた。

部屋に良い時計がなかったので、便利に使用していたのだが、突然表示しなくなってしまった。 すでに使用し始めて10年以上経過しており、時々動かす際に落っことしたりなどもあったため、何かしらガタが来ていた可能性がある。 そこに時計があることに慣れてしまっていたので、急に見えなくなると少々不便。直せるなら直したい。

故障個所の特定

全く表示しなくなってしまった時計なのだが、どこが壊れたのかを特定してみる。

まずは一番怪しいと思われるACアダプタだ。たぶん電話のADSL機器のアダプタだったと思われるが、かなり古い。 変圧器とかコンデンサとかは経年劣化で壊れやすいので、正しく電力が出力されていないことを疑ってみたが、テスターで測定してみると問題なく動作していた。

となると秋月時計本体のどこかがダメになっているようだ。では秋月時計の電源部を調べてみる。 15Vの入力をレギュレータで5Vに変換して出力している。このレギュレータ周りが壊れたと想像して電源を入力してみると、きちんと5Vが出力されていた。 どうやら秋月時計の電源部も問題なさそう。

案外壊れていないので、今度はLED(7セグ)周りを調べてみる。 ダイナミック点灯方式なので、桁選択している部分と7セグの適当などれかに電源を当ててみると、点灯した。 一通り7セグと全桁で点灯確認してみたら、全部問題なく点灯する。LED周りには問題ないようだ。

となると、壊れたのはCPU(pic)のようだ。この秋月時計はPIC16F57が使用されているが、どうもこいつが動作しなくなったようだ。 PICの中身については調べようがないので、どうしようもなく、プログラムソースは非公開なので新しいPICを用意して焼き直すこともできない。 残念ながらお手上げのようだ。

PI化検討

CPU以外は故障はないようなので、CPUとしてラズベリーパイで代替をさせてみることとする。 過去に大型時計Ver4でも同様のことを実施しているので、問題はないだろう。

ちなみにPIC16F57は260円で販売していた。ラズベリーパイはPIZero2WHを使うこととするが、こちらは3,360円で販売していた。 CPU単価が一気に10倍以上となってしまった。ネット接続可能でNTP時刻合わせも可能となる高級時計に早変わりだ。

ということで、早速PiZero2WHを仕入れた。gpioをしっかり使用するので、最初からピン付のH型を購入。 ギリギリサイズの小さな箱で届き、小ささと簡易包装っぷりにびっくりしたが、経費削減のためには仕方ないのだろう。 初期のPiZeroだと$5(750円くらい?)だったのだが、Wifi対応になり、CPUが2になり…といろいろ高価になってしまったが、 Linuxが動くPCだと思えばやはり安いのでありがたい。

まずは簡単にラズベリーパイのgpioでLED点灯させられるかの確認をしておく。 LEDの電源としては、ラズベリーパイの5Vを使用する。7セグフル点灯で140mAくらい使用する気がするが、 ラズベリーパイの5V電源はその程度は耐えられるはずだった気がする。

電源、GND、そしてGPIOを7セグ点灯できるようにつないでみて、ラズベリーパイ側からGPIO操作をしてみる。 すると、7セグをフル点灯させることができた。 ピン差し替えによる点灯桁位置変更をしてみたが、4つの桁部分はすべて点灯させられたので、ラズパイによる操作は問題なさそうだ。

組立

ではCPU乗っ取り機の作成を行う。といっても何のことはない、ただのピンソケットとラズパイのGPIOの結合ケーブルだ。 秋月時計側向けにはオスのピンを用意し、ラズパイ側にはメスソケットを用意して、電線でつなぐ。 まずはPICと時計の配線図から、PICのソケットの必要な部分を洗い出してみる。

表示のための最低限の配線だけとした。時計本体のボタンを入力に使ってもよいのだが、 配線が増えると面倒なのと、せっかくラズパイなので操作は物理ボタンよりネット越しのほうがオシャレだ。

ではCPU差し替えのためのダミーCPUを作成する。PIC16F57相当サイズに合わせてユニバーサル基盤をカットする。 そしてピンを差し込んでCPUの足相当とする。これでダミーCPU(足だけ)が完成。

このICもどきゲタに、接続用の配線を取り付ける。 本数が多いと大変。

次は、ラズパイ側に接続用の40ピンの雌コネクタを準備。 配線時に間違えないように、側面にはテプラでGPIOの簡単な番号を貼り付けておいた。 また、ラズパイに差し込むときにも方向を間違えないように、内側、外側を記載しておいた。
7セグ向けの配線は白。桁選択の配線は青として、多少区別がつくようにしておいた。

これで実際に接続してみる。時計のCPU側もラズパイ側もピン数が多いので一度差し込んだら抜くのは大変。 無理するとピンを曲げてしまうので、今後はこの状態のまま作業。まあなんとかなるはずだ。

ラズパイ準備

ハード面はとりあえず完了。次はラズベリーパイ側のソフト面を準備する。そこら辺にあったMicroSDを準備して、まずはラズパイOSの書き込みを実施。 「Raspberry Pi Imager」を使用したが最新OSを簡単に書き込んでくれるので便利だ。
機種を選んでおけばそれに見合ったOS選択肢が出てくる。とりあえずRaspbianの32bitを選択してインストールした。

2025/01時点だと、OSはVer11のbullseyeがインストールされたようだ。
初期設定は最低限に。ネット接続すると時刻同期できるよう、NTPは設定しておく。piZero2なのでCPUは少し弱いがGUIとVNCをいれて動かしている。 また、外部ファイル連携しやすいように、sambaも入れておき、ファイル共有可能としておく。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Raspbian
Description:    Raspbian GNU/Linux 11 (bullseye)
Release:        11
Codename:       bullseye

アプリはnodejsを使用するので、nodejsを入れておく。2025/01時点ではv22あたりが最新LTSのようなので、 これを入れた。

$ node -v
v22.9.0
$ npm -v
10.8.3

プログラム

では、プログラム製造を行う。今回はnodejsでの製造を行うこととする。 gpioを制御するので、「pigpio」モジュールを使うこととする。プロジェクトを作ってモジュールを入れる。

 mkdir clockpi
$ cd clockpi
$ npm init -y
$ sudo apt-get install pigpio --save

では、pigpioを使用してコード作成する。まずはモジュール使用せず簡単コマンドで動作確認。

// 指定gpio操作
var fs = require('fs');
const gpioPin = 17;
fs.writeFileSync('/sys/class/gpio/export', String(gpioPin));
fs.writeFileSync(`/sys/class/gpio/gpio${gpioPin}/direction`, 'out');
fs.writeFileSync(`/sys/class/gpio/gpio${gpioPin}/value`, String(1));
console.log("On");
fs.writeFileSync('/sys/class/gpio/unexport', String(gpioPin));

gpio17が時刻の真ん中の2つのledにつながっているので、これを使ってLEDのオンオフを確認。うまく動いた。

$ sudo node led.js
On

では、時計表示プログラムを作る。

const Gpio = require('pigpio').Gpio;
const {setTimeout} = require('timers/promises');
 
(
async() => {
    /* GPIOのピン番号定義 */
    // 7seg gpio pin no(a,b,c,d,e,f,g,*)
    const ledno = [25, 1, 7, 8, 21, 16, 12, 20];
    // 7seg digit pin no(千,百,十,一)
    const digitno = [9, 22, 10, 27];
    // Blink pin no
    const blinkno = 17;
 
    /* 数字表示用レイアウト */
    // 7seg layout
    const ledseg = [
      [1, 1, 1, 1, 1, 1, 0],
      [0, 1, 1, 0, 0, 0, 0],
      [1, 1, 0, 1, 1, 0, 1],
      [1, 1, 1, 1, 0, 0, 1],
      [0, 1, 1, 0, 0, 1, 1],
      [1, 0, 1, 1, 0, 1, 1],
      [1, 0, 1, 1, 1, 1, 1],
      [1, 1, 1, 0, 0, 0, 0],
      [1, 1, 1, 1, 1, 1, 1],
      [1, 1, 1, 1, 0, 1, 1],
      [0, 0, 0, 0, 0, 0, 0]
    ];
 
    // Initialize
    console.log("Initialize Start");
    let gpiopin = [];
    let digitpin = [];
    // gpio outputmode set
    let blinkpin = new Gpio(blinkno,{mode: Gpio.OUTPUT});
    for(let i = 0; i < 4; i++) {
        // 位置選択用pin
        digitpin[i] = new Gpio(digitno[i],{mode: Gpio.OUTPUT});
    }
    for(let i = 0; i < 8; i++) {
        // 7seg用pin
        gpiopin[i] = new Gpio(ledno[i],{mode: Gpio.OUTPUT});
    };
 
    /* 処理開始 */
    console.log("output Start");
    gpiopin[7].digitalWrite(0);
    blinkpin.digitalWrite(0);
    let cnt = 0;
    let saveSec = null;
    // ループ開始
    while (true){
        // 日付時刻を取得
        const date = new Date();
        const hor = ('0' + date.getHours()).slice(-2);
        const mit = ('0' + date.getMinutes()).slice(-2);
        const sec = ('0' + date.getSeconds()).slice(-2);
        // 表示指示
        // await led(mit + sec, cnt); // mm:ss表示
        await led(hor + mit, cnt); // hh:mm表示
        // 変化チェック
        if (!saveSec) {
            saveSec = sec;
        } else {
            if (saveSec != sec) {
                // console.log(saveSec,cnt);
                cnt = 0;
                saveSec = sec;
            } else {
                cnt++;
            }
        }
    }
 
async function led(num, cnt) {
    // 表示処理(4桁分)
    const x = ("0000" + num).slice(-4);
        for(let i = 0; i < 4; i++ ) {
            // 7seg set
            await seg(1, x[i]);
            // digit select
            digitpin[i].digitalWrite(1);
            if (i == 3) {
                // 1の位の時dot表示有無チェック
                if (cnt > 22) { // 大体カウンタは46~48maxとなる
                    gpiopin[7].digitalWrite(0);
                    blinkpin.digitalWrite(0);
                } else {
                    gpiopin[7].digitalWrite(1);
                    blinkpin.digitalWrite(1);
                }
            } else {
                // ほかの位ではdot表示なし
                gpiopin[7].digitalWrite(0);
                blinkpin.digitalWrite(0);
            }
            // 表示安定用のwait
            await setTimeout(5);
            // 表示クリア
            digitpin[i].digitalWrite(0);
        }
}
async function seg(mode, no) {
    // 7segの表示
    if (mode == 1){
        // 表示
        for(let i = 0; i < 7; i++ ) {
            gpiopin[i].digitalWrite(ledseg[no][i]);
        }
    } else {
        // クリア
        for(let i = 0; i < 7; i++ ) {
            gpiopin[i].digitalWrite(0);
        }
    }
}
})();

実行してみる。

$ sudo node clock.js
Initialize Start
output Start

時計がうまく表示できた。 gpio操作の権限が必要なので、sudoで実行しないとうまく動かない。

常駐化

とりあえず動くものができたので、常駐サービス化させる。
sudo nano /etc/systemd/system/piclock.service

[Unit]
Description=RaspberryPi Clock Service App
 
[Service]
ExecStart=/usr/local/bin/node /home/user/share/clockpi/clock.js
Restart=no
Type=simple
 
[Install]
WantedBy=multi-user.target

これで、事前準備は完了。
ファイルの権限類はいろいろ上げておこう。
sudo chown root:root /etc/systemd/system/piclock.service
sudo chmod 755 /etc/systemd/system/piclock.service

ひとまず、systemctlを再ロード
sudo systemctl daemon-reload

では、認識されているか確認してみよう。

$ systemctl list-unit-files --type=service | grep piclock

piclock.service disabled enabled

認識されているようだ。では、起動時に自動動作するように設定

$ sudo systemctl enable piclock
Created symlink /etc/systemd/system/multi-user.target.wants/piclock.service → /etc/systemd/system/piclock.service.

もう一度、設定確認

$ systemctl list-unit-files --type=service | grep piclock
piclock.service enabled enabled


最初はdisabledだった部分がenabledに変化しており、自動起動設定もできている。 これで、ラズパイ起動時に時計表示プログラムが動作する。

これを今までの作業場所に設置して運用しよう。WiFiの無い場所なので時々スマホでテザリングしてやれば、NTPが働いて時刻補正がされるはず。時刻の正確な時計として活躍を期待したい。