.. note::

    こんにちは、SunFounderのRaspberry Pi & Arduino & ESP32愛好家コミュニティへようこそ！Facebook上でRaspberry Pi、Arduino、ESP32についてもっと深く掘り下げ、他の愛好家と交流しましょう。

    **参加する理由は？**

    - **エキスパートサポート**：コミュニティやチームの助けを借りて、販売後の問題や技術的な課題を解決します。
    - **学び＆共有**：ヒントやチュートリアルを交換してスキルを向上させましょう。
    - **独占的なプレビュー**：新製品の発表や先行プレビューに早期アクセスしましょう。
    - **特別割引**：最新製品の独占割引をお楽しみください。
    - **祭りのプロモーションとギフト**：ギフトや祝日のプロモーションに参加しましょう。

    👉 私たちと一緒に探索し、創造する準備はできていますか？[|link_sf_facebook|]をクリックして今すぐ参加しましょう！

.. _4.1.3_py:

4.1.3 音声時計
======================

はじめに
--------------------

このプロジェクトでは、スピーカーと4桁の7セグメントディスプレイを用いて音声時計を作成しましょう。4桁の7セグメントディスプレイで時間を表示し、スピーカーで毎時時間をアナウンスします。

必要な部品
------------------------------

このプロジェクトには以下のコンポーネントが必要です。

.. image:: ../img/3.1.17components.png
  :width: 800
  :align: center

一式をまとめて購入するのが便利です。リンクはこちら：

.. list-table::
    :widths: 20 20 20
    :header-rows: 1

    *   - 名前	
        - このキットのアイテム
        - リンク
    *   - Raphael Kit
        - 337
        - |link_Raphael_kit|

以下のリンクから個別に購入することも可能です。

.. list-table::
    :widths: 30 20
    :header-rows: 1

    *   - コンポーネントの紹介
        - 購入リンク

    *   - :ref:`cpn_gpio_board`
        - |link_gpio_board_buy|
    *   - :ref:`cpn_breadboard`
        - |link_breadboard_buy|
    *   - :ref:`cpn_wires`
        - |link_wires_buy|
    *   - :ref:`cpn_resistor`
        - |link_resistor_buy|
    *   - :ref:`cpn_audio_speaker`
        - \-
    *   - :ref:`cpn_4_digit`
        - \-
    *   - :ref:`cpn_74hc595`
        - |link_74hc595_buy|

回路図
--------------------------

============ ======== ======== ===
T-Board Name physical wiringPi BCM
GPIO17       Pin 11   0        17
GPIO27       Pin 13   2        27
GPIO22       Pin 15   3        22
SPIMOSI      Pin 19   12       10
GPIO18       Pin 12   1        18
GPIO23       Pin 16   4        23
GPIO24       Pin 18   5        24
============ ======== ======== ===

.. image:: ../img/schmatic_4_digit.png

.. image:: ../img/3.1.17_schematic.png
  :width: 500
  :align: center

実験手順
------------------------------

**ステップ1：** 回路を作成する。

.. image:: ../img/3.1.17fritzing.png
  :width: 900
  :align: center

このプロジェクトに取り組む前に、 :ref:`3.1.4_py` を完了している必要があります。

**ステップ2：** ``date`` コマンドでローカル時間を確認する。

.. raw:: html

   <run></run>

.. code-block::

    date

ローカル時間が実際の時間と異なる場合、以下のコマンドを使用してタイムゾーンを設定します。

.. raw:: html

   <run></run>

.. code-block::

    sudo dpkg-reconfigure tzdata

タイムゾーンを選択します。

.. image:: ../img/tzdata.png

**ステップ3：** コードのフォルダに移動する。

.. raw:: html

   <run></run>

.. code-block::

    cd ~/raphael-kit/python/

**ステップ4：** 実行する。

.. raw:: html

   <run></run>

.. code-block::

    python3 4.1.3_SpeechClock.py

コードが実行されると、4桁の7セグメントディスプレイが時間を表示し、毎時にチャイムが鳴ります。

**コード**

.. note::
    下記のコードは **修正/リセット/コピー/実行/停止** が可能です。ただし、その前にソースコードのパス、例えば ``raphael-kit/python`` に移動する必要があります。コードを修正した後、直接実行して効果を確認することができます。

.. raw:: html

    <run></run>

.. code-block:: python

    import RPi.GPIO as GPIO
    from tts import TTS
    import time

    tts = TTS(engine="espeak")
    tts.lang('en-US')

    SDI = 24
    RCLK = 23
    SRCLK = 18

    placePin = (10, 22, 27, 17)
    number = (0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90)

    def setup():
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(SDI, GPIO.OUT)
        GPIO.setup(RCLK, GPIO.OUT)
        GPIO.setup(SRCLK, GPIO.OUT)
        for i in placePin:
            GPIO.setup(i, GPIO.OUT)

    def clearDisplay():
        for i in range(8):
            GPIO.output(SDI, 1)
            GPIO.output(SRCLK, GPIO.HIGH)
            GPIO.output(SRCLK, GPIO.LOW)
        GPIO.output(RCLK, GPIO.HIGH)
        GPIO.output(RCLK, GPIO.LOW)    

    def hc595_shift(data): 
        for i in range(8):
            GPIO.output(SDI, 0x80 & (data << i))
            GPIO.output(SRCLK, GPIO.HIGH)
            GPIO.output(SRCLK, GPIO.LOW)
        GPIO.output(RCLK, GPIO.HIGH)
        GPIO.output(RCLK, GPIO.LOW)

    def pickDigit(digit):
        for i in placePin:
            GPIO.output(i,GPIO.LOW)
        GPIO.output(placePin[digit], GPIO.HIGH)

    def loop():
        status = 0                   
        while True:
            time.localtime(time.time())
            hour = int(time.strftime('%H',time.localtime(time.time())))
            minute = int(time.strftime('%M',time.localtime(time.time())))

            clearDisplay() 
            pickDigit(0)  
            hc595_shift(number[minute % 10])
            
            clearDisplay()
            pickDigit(1)
            hc595_shift(number[minute % 100//10])

            clearDisplay()
            pickDigit(2)
            hc595_shift(number[hour % 10])

            clearDisplay()
            pickDigit(3)
            hc595_shift(number[hour % 100//10])

            if minute == 0 and status == 0:
                tts.say('The time is now ' + str(hour) + ' hours and ' + str(minute) + ' minutes')
                status = 1
            elif minute != 0:
                status = 0

    def destroy():   # When "Ctrl+C" is pressed, the function is executed.
        GPIO.cleanup()

    if __name__ == '__main__':  # Program starting from here
        setup()
        try:
            loop()
        except KeyboardInterrupt:
            destroy()

**コード説明**

.. code-block:: python

    time.localtime(time.time())
    hour = int(time.strftime('%H', time.localtime(time.time())))
    minute = int(time.strftime('%M', time.localtime(time.time())))

関数 ``time.time()`` を通して、現在時刻のタイムスタンプ（1970年のエポックから経過した浮動小数点数の秒数）を取得し、timeモジュールの時刻フォーマットメソッド ``(time.localtime(time.time()))`` を使って現在のタイムスタンプを処理することで、タイムスタンプをローカルタイムとしてフォーマットすることができる。

入力結果は以下のようになります。

``time.struct_time(tm_year=2021, tm_mon=5, tm_mday=28, tm_hour=13, tm_min=54, tm_sec=26, tm_wday=4, tm_yday=148, tm_isdst=0)``

最後に、 ``time.strftime()`` メソッドを使用して、多くの情報が含まれている文字列を目的に合わせて書式設定します。現在の時間（時）を取得する場合、 ``time.strftime('%H',time.localtime(time.time()))`` 関数を通して取得できます。

指定された書式で得られる文字列の出力は、以下のとおりです。

+----+----------------------------------------------------+
| %y | 2桁の年表示(00-99)                                 |
+----+----------------------------------------------------+
| %Y | 4桁の西暦表示(000-9999)                            |
+----+----------------------------------------------------+
| %m | 月(01-12)                                          |
+----+----------------------------------------------------+
| %H | 月の曜日(0-31)                                     |
+----+----------------------------------------------------+
| %I | 24時間表示(0-23)                                   |
+----+----------------------------------------------------+
| %M | 12時間表示(01-12)                                  |
+----+----------------------------------------------------+
| %y | 分(00=59)                                          |
+----+----------------------------------------------------+
| %S | 秒(00-59)                                          |
+----+----------------------------------------------------+
| %a | 地元の簡略化された週名                             |
+----+----------------------------------------------------+
| %A | 地元の完全な週名                                   |
+----+----------------------------------------------------+
| %b | 現地簡体字月名                                     |
+----+----------------------------------------------------+
| %B | 現地の月名                                         |
+----+----------------------------------------------------+
| %c | 現地対応日付・時刻表示                             |
+----+----------------------------------------------------+
| %j | 曜日(001-366)                                      |
+----+----------------------------------------------------+
| %p | 現地の午前または午後に相当。                       |
+----+----------------------------------------------------+
| %U | 日曜日から始まる1年間の週数(00-53)                 |
+----+----------------------------------------------------+
| %w | 週（0～6）、日曜日から開始                         |
+----+----------------------------------------------------+
| %W | 月曜日から始まる1年間の週数(00-53)                 |
+----+----------------------------------------------------+
| %x | 現地で対応する日付表示                             |
+----+----------------------------------------------------+
| %X | ローカル対応時間表現                               |
+----+----------------------------------------------------+
| %Z | 現在のタイムゾーン名                               |
+----+----------------------------------------------------+


.. note::
    ``time.strftime()`` メソッドの出力はすべて文字列変数です。使用する前に、強制的な型変換を忘れずに行ってください。

.. code-block:: python

    clearDisplay()
    pickDigit(0)
    hc595_shift(number[minute % 10])

    clearDisplay()
    pickDigit(1)
    hc595_shift(number[minute % 100//10])

    clearDisplay()
    pickDigit(2)
    hc595_shift(number[hour % 10])

    clearDisplay()
    pickDigit(3)
    hc595_shift(number[hour % 100//10])

7セグメントのデジタルディスプレイの最初に時間の10の位が表示され、次に1の位が表示されます。その後、分の10の位が第3のデジタルディスプレイに表示され、1の位は最後に表示されます。

.. code-block:: python

    if minute == 0 and status == 0:
        tts.say('The time is now ' + str(hour) + ' hours and ' + str(minute) + ' minutes')
        status = 1
    elif minute != 0:
        status = 0

分の数が0（毎時）である場合、Raspberry PiはTTSを使用して時間をお知らせします。

現象の画像
------------------------

.. image:: ../img/4.1.3speech_clock.JPG
   :align: center
