注釈

こんにちは、SunFounder Raspberry Pi & Arduino & ESP32 Enthusiast Community on Facebookへようこそ!他の愛好家と一緒に、Raspberry Pi、Arduino、ESP32の世界により深く入り込みましょう。

参加する理由

  • 専門家サポート: 購入後の問題や技術的な課題を、コミュニティと私たちのチームの助けを借りて解決します。

  • 学習と共有: ヒントやチュートリアルを交換して、スキルを向上させましょう。

  • 限定プレビュー: 新製品の発表や先行プレビューに早期アクセスできます。

  • 特別割引: 最新製品を特別割引でお楽しみいただけます。

  • 季節限定キャンペーンとプレゼント: プレゼント企画やホリデーキャンペーンに参加しましょう。

👉 一緒に発見し、創造する準備はできましたか? [こちら] をクリックして、今すぐ参加しましょう!

(Example) AI 搭載モールス信号デコーダー

はじめに

このプロジェクトでは、ボタンの押下タイミングのパターンを AI で解釈するインテリジェントな モールス信号デコーダー を作成します。 システムは正確なタイミングデータを取得し、OpenAI の GPT を活用して、モールス信号のメッセージをリアルタイムでデコードします。主な機能は以下の通りです:

  1. 押下・解放の正確な時刻を記録する タイミングベース入力

  2. GPT を用いた AI デコード による点・線パターンの解釈

  3. デコード中であることを示す LED の 視覚インジケーター

  4. 入力用ボタンと制御用ボタンを分けた 2 ボタンインターフェース

  5. 入力中のタイミングデータをその場で表示する リアルタイムフィードバック

このシステムはボタンの押下時間を記録し、そのタイミングデータを AI に送って解釈させることで、世界共通の救難信号である「SOS」のようなモールス信号列を正確にデコードできます。

タイミングに敏感な入力と AI による解釈を組み合わせることで、さまざまな符号化システムに応用できます。以下も参照してください:


必要なもの

このプロジェクトに必要な部品は以下の通りです:

COMPONENT

PURCHASE LINK

ボタン

購入 (x2)

LED

購入

抵抗器

購入

Fusion HAT+

-

ジャンパーワイヤー

購入

Raspberry Pi

-


配線図

以下のように部品を Raspberry Pi に接続します:

../_images/morse_decoder_bb.png

APIキーの取得と保存

  1. OpenAI Platform にアクセスしてログインします。 API keys ページで Create new secret key をクリックします。

    ../_images/llm_openai_create.png
  2. 必要事項(Owner、Name、Project、必要に応じて権限)を入力し、 Create secret key をクリックします。

    ../_images/llm_openai_create_confirm.png
  3. キーが作成されたら、すぐにコピーしてください。後から再表示できません。紛失した場合は新しく作成し直す必要があります。

    ../_images/llm_openai_copy.png
  4. プロジェクトフォルダ(例: / )内に secret.py というファイルを作成します:

    cd ~/ai-lab-kit/llm
    sudo nano secret.py
    
  5. ファイルにキーを次のように貼り付けます:

    # secret.py
    # Store secrets here. Never commit this file to Git.
    OPENAI_API_KEY = "sk-xxx"
    

請求設定の有効化と利用可能モデルの確認

  1. キーを使用する前に、OpenAI アカウントの Billing ページで支払い情報を追加し、少額のクレジットをチャージしてください。

    ../_images/llm_openai_billing.png
  2. 続いて Limits ページで、アカウントで利用可能なモデルを確認し、コードで使用する正確なモデルIDをコピーします。

    ../_images/llm_openai_models.png

サンプルの実行

  1. コードを実行する

    cd ~/ai-lab-kit/llm
    sudo python3 llm_openai_morse_decoder.py
    
  2. 簡単なモールス信号メッセージを試す(例:"SOS")

    プログラム開始後、start/stop ボタンを押して記録を開始します。 その後、モールス用ボタンを使って、点(短押し)と線(長押し)を入力します。

    入力が終わったら、もう一度 start/stop ボタンを押して記録を終了し、メッセージをデコードします。

  3. コンソール出力を確認する

    コンソールには押下/解放のタイムスタンプが表示され、AI がそのタイミングデータを解析して、 デコード結果を出力します。

    "SOS" を入力したときの典型的なコンソール出力:

    To decode the Morse code message based on the button press times provided, we need to interpret the duration of each press. Typically, a short press (dot) is around 0.2 to 0.3 seconds, while a long press (dash) is about 0.5 seconds or longer. Let's analyze the press durations:
    
    1. `1767773542.1257536` to `1767773542.285196` - Duration: ~0.16 seconds - Dot (.)
    2. `1767773542.4936137` to `1767773542.6315389` - Duration: ~0.14 seconds - Dot (.)
    3. `1767773542.9092748` to `1767773543.0543947` - Duration: ~0.15 seconds - Dot (.)
    4. `1767773544.2299025` to `1767773544.5774245` - Duration: ~0.35 seconds - Dash (-)
    5. `1767773545.1017563` to `1767773545.4954002` - Duration: ~0.39 seconds - Dash (-)
    6. `1767773546.11932` to `1767773546.5881057` - Duration: ~0.47 seconds - Dash (-)
    7. `1767773547.824543` to `1767773547.9534554` - Duration: ~0.13 seconds - Dot (.)
    8. `1767773548.1879761` to `1767773548.2895174` - Duration: ~0.10 seconds - Dot (.)
    9. `1767773548.5281847` to `1767773548.6453152` - Duration: ~0.12 seconds - Dot (.)
    
    Now let's decode the sequence into letters using Morse code:
    
    - `...` (Dot Dot Dot) = S
    - `---` (Dash Dash Dash) = O
    - `...` (Dot Dot Dot) = S
    
    Putting it all together, the decoded message is "SOS".
    
  4. ワークフローを理解する

    1. 記録開始:start/stop ボタン(GPIO 17)を押すと LED が ON になります

    2. モールス信号を入力:モールス用ボタン(GPIO 22)で点と線を入力します

    3. リアルタイム表示:コンソールに押下/解放のタイムスタンプが表示されます

    4. 停止してデコード:もう一度 start/stop ボタンを押すと LED が OFF になります

    5. AI 解析:タイミングデータが OpenAI GPT に送られて解釈されます

    6. デコード結果:AI が解読したメッセージを表示します

コード

以下は AI 搭載モールス信号デコーダーの Python スクリプト全体です:

from fusion_hat.llm import OpenAI
from secret import OPENAI_API_KEY
from fusion_hat.pin import Pin
import random, time

# Register OpenAI API
# openai.com

# Export your openai api key with :LLM_API_KEY
# export LLM_API_KEY=sk-xxxxxxxxxxxxxxxxx

# Setup GPIO pins
morse_input = Pin(22, mode=Pin.IN, pull=Pin.PULL_DOWN, bounce_time=0.05)
start_stop_button = Pin(17, mode=Pin.IN, pull=Pin.PULL_DOWN, bounce_time=0.05)
led = Pin(27, Pin.OUT)  # Indicator LED on GPIO 27

# Store the morse code events with timing data
morse_events = []
input_active = False  # Flag to indicate if input is active

# Setup LLM with Morse code decoding instructions
INSTRUCTIONS = "You are a Morse code decoder. Decode based on the button press time, interpreting short presses as dots and long presses as dashes. The message you receive may be a word or a sentence, please decode it and output it."

WELCOME = "Hello, I am a Morse code decoder. Please press the button to start decoding. When you are done, press the button again to stop."

llm = OpenAI(
    api_key=OPENAI_API_KEY,
    model="gpt-4o",
)

# Set how many messages to keep
llm.set_max_messages(20)
# Set instructions
llm.set_instructions(INSTRUCTIONS)
# Set welcome message
llm.set_welcome(WELCOME)

print(WELCOME)

# Send the morse code timing data to the AI for decoding
def decode_and_print():
    global morse_events

    # Convert timing events to string for AI processing
    input_text = str(morse_events)

    # Get response from AI with streaming
    response = llm.prompt(input_text, stream=True)

    # Print streaming response
    for next_word in response:
        if next_word:
            print(next_word, end="", flush=True)

    print("")  # New line after complete response

    morse_events = []  # Clear the morse code events for next message

# Morse code input handling variables
start_time = 0

# Function called when morse input button is pressed
def morse_input_pressed():
    global start_time
    start_time = time.time()
    morse_events.append(('pressed', start_time))
    print(f" Pressed at {start_time} -", end="")

# Function called when morse input button is released
def morse_input_released():
    global morse_events, start_time
    release_time = time.time()

    # Debounce: ignore releases within 0.1 seconds
    if release_time - start_time < 0.1:
        return

    morse_events.append(('released', release_time))
    print(f" {release_time}")

# Start/stop button handler
def handle_start_stop():
    global input_active, morse_events

    if input_active:
        # Stop recording and decode
        led.off()
        print("Input stopped and decoded.")
        decode_and_print()
        input_active = False
    else:
        # Start recording new message
        input_active = True
        morse_events.clear()  # Clear previous events
        led.on()
        print("Input started.")

# Add event listeners to buttons
start_stop_button.when_activated = handle_start_stop
morse_input.when_activated = morse_input_pressed
morse_input.when_deactivated = morse_input_released

# Main program loop
try:
    while True:
        time.sleep(0.1)
except KeyboardInterrupt:
    pass

コードの理解

  1. GPIO ピンの設定

    3 本の GPIO ピンをそれぞれ異なる用途で設定しています:

    morse_input = Pin(22, mode=Pin.IN, pull=Pin.PULL_DOWN, bounce_time=0.05)
    start_stop_button = Pin(17, mode=Pin.IN, pull=Pin.PULL_DOWN, bounce_time=0.05)
    led = Pin(27, Pin.OUT)
    
    • バウンスタイム(0.05 秒):メカニカルスイッチのチャタリングによる多重検出を防ぎます

    • プルダウン:ボタン未押下時に安定した LOW 信号を保ちます

    • 役割分離:入力ボタンと制御ボタンを分けることで誤入力を防ぎます

  2. タイミングデータの保存

    押下/解放イベントは正確なタイムスタンプとともに保存されます:

    morse_events = []  # Empty list to store events
    
    # Each event stored as tuple: ('pressed'/'released', timestamp)
    morse_events.append(('pressed', 1767773542.1257536))
    morse_events.append(('released', 1767773542.285196))
    
  3. デバウンス機構

    スイッチのチャタリングによる誤検出を防ぎます:

    def morse_input_released():
        if release_time - start_time < 0.1:  # 100ms debounce
            return  # Ignore very short releases
    
        morse_events.append(('released', release_time))
    
  4. 状態管理

    システムはフラグを使って記録状態を管理します:

    input_active = False  # Initially not recording
    
    def handle_start_stop():
        if input_active:
            # Stop recording and decode
            input_active = False
        else:
            # Start recording
            input_active = True
            morse_events.clear()  # Clear previous data
    
  5. 視覚インジケーター

    LED によって記録状態を視覚的に確認できます:

    def handle_start_stop():
        if input_active:
            led.off()  # LED OFF when not recording
        else:
            led.on()   # LED ON when recording
    
  6. AI へのプロンプト構築

    タイミングデータは AI が処理しやすいよう文字列化して送信します:

    input_text = str(morse_events)
    
    # Example format sent to AI:
    # "[('pressed', 1767773542.1257536), ('released', 1767773542.285196), ...]"
    
  7. ストリーミング応答

    AI の応答はリアルタイムで処理・表示されます:

    response = llm.prompt(input_text, stream=True)
    
    for next_word in response:
        if next_word:
            print(next_word, end="", flush=True)
    
  8. イベント駆動アーキテクチャ

    ボタンイベントに対して即時にコールバックが実行されます:

    # Assign callback functions to button events
    start_stop_button.when_activated = handle_start_stop
    morse_input.when_activated = morse_input_pressed
    morse_input.when_deactivated = morse_input_released
    
  9. タイミング精度

    time.time() を使って高精度のタイミングを取得します:

    start_time = time.time()  # Current time in seconds since epoch
    
    # Calculate press duration:
    duration = release_time - start_time
    
  10. データのクリア

    デコード後は、次のメッセージ入力に備えてイベントリストをクリアします:

    def decode_and_print():
        # ... process events ...
        morse_events = []  # Clear for next message
    

モールス信号のタイミング基準

  • 標準タイミング(単語 PARIS 基準):

    • 点:1 単位

    • 線:3 単位

    • 文字内間隔(点・線の間):1 単位

    • 文字間隔(文字と文字の間):3 単位

    • 単語間隔(単語と単語の間):7 単位

  • 実用上の目安:

    • 点:0.3 秒未満(短押し)

    • 線:0.5 秒超(長押し)

    • 要素間:0.5 秒未満の間隔

    • 文字間:0.5〜1.5 秒の間隔

    • 単語間:1.5 秒超の間隔

  • よく使うモールス信号の文字:

    • A: • —(点・線)

    • B: — • • •(線・点・点・点)

    • C: — • — •(線・点・線・点)

    • S: • • •(点・点・点)

    • O: — — —(線・線・線)


トラブルシューティング

  • ボタン入力が認識されない

    • 配線を確認してください:GPIO 22 / 17 からボタンへ、反対側は Ground へ接続

    • プルダウン設定を確認してください

    • 簡単なスクリプトでテスト: print(Pin(22, mode=Pin.IN, pull=Pin.PULL_DOWN).read())

    • バウンスタイム設定を確認してください(0.05 秒が長すぎる場合があります)

  • LED が点灯しない

    • LED の極性を確認してください:アノード(長い脚)は抵抗を介して GPIO 27 へ

    • 抵抗値を確認してください(220Ω 推奨)

    • 直接テスト: Pin(27, Pin.OUT).on() で LED が点灯するはずです

    • GND 接続が正しく完了しているか確認してください

  • タイミングデータがおかしい

    • システムクロックを確認: date コマンド

    • 反応が敏感すぎる場合はデバウンス時間を下げてください

    • print を追加してコールバックが正しく実行されているか確認してください

    • 一定の長さで押すテストを行ってください

  • AI が正しくデコードできない

    • API キーとインターネット接続を確認してください

    • AI に送っているタイミングデータを確認してください( morse_events を print)

    • 押下時間を安定させてください(点は短く、線は長く)

    • 文字の間により明確な間隔を入れてください

  • 1 回押しただけで複数回トリガーされる

    • bounce_time パラメータを増やしてください(0.1 秒など)

    • メカニカルスイッチのチャタリングを確認してください

    • コンデンサによるハードウェアデバウンスを追加してください

    • ボタン配線が正しいか確認してください

  • start/stop に反応しない

    • 他のコールバックが干渉していないか確認してください

    • input_active フラグのロジックを確認してください

    • handle_start_stop() にデバッグ用 print を追加してください

    • 他のプロセスが GPIO を使用していないことを確認してください

  • AI の応答が遅い

    • インターネット接続速度を確認してください

    • イベント数を減らしてください(短いメッセージで試す)

    • 代替としてローカルデコードの導入も検討してください

    • AI 応答にタイムアウト処理を追加してください

  • 点と線を区別しにくい

    • タイミングを一定にする練習をしてください

    • AI への instructions 内の閾値を調整してください

    • AI に送る前にローカル前処理を追加してください

    • 入力中に視覚フィードバックを追加すると分かりやすくなります


この AI 搭載モールス信号デコーダーは、正確なタイミングデータと高度なパターン認識を組み合わせることで、歴史ある通信手段を現代的に再解釈し、新しい世代にとって学びやすく親しみやすい形で活用できることを示しています。