2020年5月24日日曜日

Raspberry pi 3でradikoプレミアムを聴く

最近、家にいる時間が多くて、radikoプレミアムで他の地域のラジオを聴くのが面白い。
自分の地域のラジオ局も面白いけど、たまには他の県のも聴いてみたい。
そのためにパソコンの電源を入れるのは電気がもったいないし
そうだ、Raspberry piをradikoプレミアム専用受信機にしよう!!

ということで、Raspberry pi 3 Model Bでradikoプレミアムを聴く
すでにradikoプレミアムの会員になっているものとして、記事を書いていく。

なるべく軽くするためOSは、Raspbian Buster Lite
ファイルサイズも小さいので、他よりダウンロードも早い。
ちなみにmicroSDカードはTranscendの組込向けを使ってみた。

Raspbian Buster Liteを下記のサイトを参考にインストールする。

Raspbian Buster Liteの最速最小インストール

OSのインストールが終わったらWindowsからTera TermでSSH接続して操作

まずradiko用ディレクトリの作成をして
 ~$  mkdir radiko 

下のサイトを参考に進めていく

RaspberryPi 3 で radiko Premium をテレビで聞く(録音ではなく、再生する)

何年か前に同じようなことをやったときは、ffmpegは使えない感じだったが、今はffmpegでよいらしい。
シェルを作成して実行すると、Raspberry piのヘッドフォン端子からラジオが聴こえてきた。
しかし、音がひどい。ノイジー

DACの導入
それを見越してDACも一緒に買ってあった。
接続するのは、Amazonで売っていたKumanのSC08

接続後、このサイトの通りに設定していく

クソ音質のRaspberryPiにDACという光を

この手順で進めても、何故かRaspberry piのヘッドフォン端子から音が出てきて、
DACからは音が出ない。
aplay -lコマンドで確認すると、
~$ aplay -l 

card1 device0の部分に表示されているので認識はしてるっぽい。card 0はRaspberry Piに最初からついているサウンドデバイス

参考にしたサイトの

そしてGUIで設定>Audio Device Settingsを選択してデフォルトの他に「snd_rpi_hifiberry_dacplus」が増えていれば成功!

というのが引っかかるが、Raspbian Buster Liteを入れたのでGUIなんてない。

もしかしたらと、
 ~$  sudo raspi-config 
でAdvanced Optionを開き、Audioを見るが、DACは選択肢にない。

いろいろ悩んで探した結果、このサイトの通り設定するとDACから音が出た。
デフォルトに設定しないといけないようだ。

RaspberryPiでUSBスピーカーをデフォルトの音声出力デバイスにする

このDACはハンダが適当だけど、よい音がする。1万くらいのオーディオインターフェイスと同じぐらいの音がするから驚く。ちょっとナメてました。

しかし問題発生、Tera Termを閉じると再生が止まってしまう。

この対処法としては、nohupと&を使うとバックグラウンドで動作するので、閉じても問題ないらしい。
単にnohupと&を追加しただけだとnohup.out が出力されるので、>/dev/null 2>&1でnohup.outを出力しないようにしている。

新たにシェルを作成してみた。

シェルの名前はradipi.sh
 ~$ touch radipi.sh 

次にnanoなどのエディターでシェルを入力していく
 ~$ nano radipi.sh 

MALLとPASSにラジコプレミアムのアカウントを入力して、とりあえず完成
#!/bin/sh

#mplayerを停止させる。
killall mplayer

#バックグラウンドで動作させる。
nohup ~/radiko/play_radiko2.sh $1 0 MAIL PASS >/dev/null 2>&1 &
最初の部分でmplayerを停止させているのは、mplayerが動作したままこのシェルを実行して、チャンネルを変えるとエラーが出てチャンネルが変わらないので、一度mplayerを停止させ、チャンネルの変更を可能にしている。

chmodで実行許可を出す。
 ~$ chmod +x radipi.sh 

radipi.shの使い方、TBSラジオを聞く場合は、最後にTBSと入れるだけ
 ~$  ./radipi.sh TBS 

ウィキペディアのradikoのページの
放送局記号を入れると好きな放送局が聞ける。
実際の放送との遅延は20秒くらい。

 Raspberry PiのUSBの電源によって、スイッチング電源のノイズでAMラジオが聴けなくなる。
 最初に試した某メーカーのUSB電源は音声ラインにもノイズが乗っていた。ラジオの近くにDACの音声出力ケーブルを近づけるとスイッチングノイズだらけになっていた。
 Nikonのデジカメの充電器をRaspberry Piの電源に使うと電源ノイズが小さくなった。電源の選択は大事!!
 この充電器は1Aだけど、しばらく動かしても特に問題なく動いているので多分電流は大丈夫かな?熱くないし

2020年5月17日日曜日

ラジオの時報で合わせる正確な時計を作る2マイコン編

前回は、ラジオの音声から時報を検出する回路を製作しましたが、
ラジオの時報で合わせる正確な時計を作る1

なんと前回の記事からほぼ1年!
記事は久々に書きますが、空き時間に少しずつ進めていました。
今回は時報検出回路の信号を受けて、時計をマイコンで動かす回路を試作してみようと思います。
新型コロナウィルスの影響で家にいる時間も多いので、結構完成に近づいてきました。

マイコンは作例も多いPIC16F88を使用します。

時刻をデジタル時計で表示するなどしません。
時計はアナログが一番!
写真はSEIKOのバス時計しかもトランジスタのモデルですが、
調子が悪かったので、クォーツに変えてあります。この時計を動かしました。

マイコンでアナログ時計を動かす方法はこちらの記事を参考にしました。

Arduinoで時計にいたずら
https://n.mtng.org/ele/arduino/clock.html
アナログ時計の動かし方はシンプルで、
時計のムーブメントを分解して、コイルに流す電流の+と-を入れ替える度に1秒進みます。インバーターICを追加することで、1pinで駆動できるようにしました。こうすることでPICを壊す心配も減ります。
試作回路はこちら、

32.628kHzの水晶振動子を74HC4060で分周して2Hzを作り、さらに4ビットバイナリカウンタICのTC4520で分周して1Hzを作り、これを1秒の基準とします。
1秒の作り方はこちらのサイトが参考になりました。

74HC4060と水晶発振子を用いた1Hzの生成
http://tyk-systems.com/LEDflash/LEDflash.html

最初から水晶振動子の出力をPICに入れて直接カウントさせることもできますが、ロジックICで遊びたかったんです。TC4520にはカウンタが2個入っているので、空いている方で、時刻の表示もさせてみました。(LEDが点灯して2進数で時刻がわかります)
ブレッドボード上に水晶の発振回路を組むとなかなか動かなくて、カットアンドトライで抵抗やコンデンサの値を変えているので、水晶の周りは回路図の値ではないです。(笑)

今回。試作した回路の写真

使い方は簡単、
1. DIPスイッチで最初の時報を受信する時刻を設定する。
2. 電源を入れると、秒針が進むので秒針が59秒の位置になったら、プッシュスイッチ(SW1)を押す。ラジオの電源が入り、時報受信待機状態になり、2進数で時刻が表示される。
3. 秒針を59秒の位置で合わせたら、時計の裏のツマミを回して時報を受信する時刻に時針と分針を合わせる。(普通にアナログ時計を合わせる感じ)
4. 時報を受信すると時計が動き出す。
あとは午前5時と午後5時の10秒前に時報受信の待機状態になり勝手に時報に合わせてくれます。
午前と午後は区別しない作りになっています。

プログラムはこちら、
interrupt isrで1Hzが割り込んできて、秒針を動かすようになっています。
/* ラジオの時報を受信する時計
 * File:   main.c
 * Author: LEFT-AMD
 *
 * Created on 2020/01/27, 22:09
 */
// PIC16F88 Configuration Bit Settings

// 'C' source line config statements

// CONFIG1
#pragma config FOSC = INTOSCIO  // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON       // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = ON       // Brown-out Reset Enable bit (BOR enabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB3      // CCP1 Pin Selection bit (CCP1 function on RB3)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

// CONFIG2
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)
#pragma config IESO = OFF       // Internal External Switchover bit (Internal External Switchover mode disabled)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>
#include <stdio.h>
#include <stdlib.h>

#define _XTAL_FREQ 8000000  // 8MHz
void secmove(void)
{
    //秒針を一秒動かす関数
    RB2 = ~RB2;
}
void zyushin(void)
{
    //時報を受信する関数
    int n=0;
          RB4 = 0;    //リレーON
         while(n < 2){     //440Hzを2回以上検知するとループを抜ける
           if(RB6 == 1){
           n++;
          __delay_ms(980);   //Delay1秒
      }
        }
         n = 0;
    while(RB5 == 0){     //880Hz検知するとループを抜ける
    } 
}

void printhour(unsigned char hr)
{
 //時刻表示する
    int j=0;
    RB7 = 1;    //時刻表示リセット
    RB7 = 0;
        while(j<hr*2){
            RB3 = ~RB3;
            __delay_ms(50);
            j++;
       }
       j=0;
       hr = 0;
}
int sec=0;
void main(void)
{
    OSCCON = 0b01110000;    // 内蔵クロックの周波数を8MHzに設定
    ANSEL = 0b00000000;     // A/D変換を無効化
    ADCON0 = 0x00;
    ADCON1 = 0x00;
    PORTA = 0x00;           // PORTAを初期化
    PORTB = 0x00;           // PORTBを初期化
    TRISA = 0b00011111;     // PORTAの入出力設定
    TRISB = 0b01100001;     // PORTBの入出力設定
    
    INTCON = 0b00000000;        //GIEとINTEを0にする。割り込み無効化
    __delay_ms(3000);      //水晶振動子が安定するまで待つ3秒
    int i=0;
    unsigned char hour=0;
    unsigned char zyuf=0;   //時報受信フラグ、0受信しない、1受信する
    unsigned char zyuh=5;   //時報を受信する時刻
    RB3 = 0;    //LED
    RB2 = 0;    //秒針駆動端子の初期値
    RB4 = 1;    //リレーOFF
    RB1 = 1;    //秒カウンタOFF
    while(RA4 == 1){     //SW1ボタンを押すまで針が早く進む正時指定
        secmove();
         __delay_ms(500);        // 500ミリ秒の待ち時間
    }
    hour = (unsigned char)(~PORTA & 0x0F);    //Aポートの状態取得 DIPスイッチがリアルコードのため反転している。論理積で上位4ビットはマスクしている。
        printhour(hour);    //時刻の表示
        zyushin();      //時報の受信
    while(1){   //無限ループ
    RB1=0;  //秒カウンタON   
    OPTION_REG = 0b01000000;    //INTEDG 割込みエッジ選択ビット1=INTピンの立ち上がりエッジによる割込み
    INTCON = 0b10010000;        //GIEとINTEを1にする。
    RB4 = 1;    //時報受信リレーOFF

    printhour(hour);    //時刻の表示        
    if(zyuh-1 == hour)  //受信設定時刻の1時間前に受信フラグを立てる
        zyuf=1;   
    
    if(zyuf==0){    //受信フラグの判別
        while(sec <= 3599){  //3599秒になったとき
        }
        sec = 0; //秒はリセットする

    }else{
        while(sec <= 3590){ //時報を受信する条件 3590
        }
        sec = 0;          //秒リセット
        INTCON = 0b00000000;        //GIEとINTEを0にする。無効化
        RB1 = 1;    //秒カウンタOFF
        while(i<9){               // 正時10秒前に秒針を正時の位置に待機させる
            RB3 = ~RB3;
            secmove();
            __delay_ms(200);        // 200ミリ秒の待ち時間
            i++;       
            }
        i=0;
        zyushin();      //時報の受信
        zyuf=0;
        }
       
     if(hour >= 12){  //12時のときは
            hour = 0; //hourをリセットする。
            }
        hour++;  //1時間増やす
    }
}

void interrupt isr(void)
{
    //割込み処理プログラム
    secmove();
    sec++;
    __delay_ms(20);
    INTF = 0; //フラグのクリア
}

C言語の教科書引っ張り出して、作ってみました。
セミコロンの入れ忘れがあったり、文法を結構忘れていて思い出すのに時間がかかりました。