.. include:: /index.rst :start-after: start_hello_message :end-before: end_hello_message .. _py_morse_code_decoder: (Example) AI 搭載モールス信号デコーダー ======================================== **はじめに** このプロジェクトでは、ボタンの押下タイミングのパターンを AI で解釈するインテリジェントな **モールス信号デコーダー** を作成します。 システムは正確なタイミングデータを取得し、OpenAI の GPT を活用して、モールス信号のメッセージをリアルタイムでデコードします。主な機能は以下の通りです: 1. 押下・解放の正確な時刻を記録する **タイミングベース入力** 2. GPT を用いた **AI デコード** による点・線パターンの解釈 3. デコード中であることを示す LED の **視覚インジケーター** 4. 入力用ボタンと制御用ボタンを分けた **2 ボタンインターフェース** 5. 入力中のタイミングデータをその場で表示する **リアルタイムフィードバック** .. raw:: html このシステムはボタンの押下時間を記録し、そのタイミングデータを AI に送って解釈させることで、世界共通の救難信号である「SOS」のようなモールス信号列を正確にデコードできます。 タイミングに敏感な入力と AI による解釈を組み合わせることで、さまざまな符号化システムに応用できます。以下も参照してください: * :ref:`py_online_llm` ---------------------------------------------- **必要なもの** このプロジェクトに必要な部品は以下の通りです: .. list-table:: :widths: 30 20 :header-rows: 1 * - COMPONENT - PURCHASE LINK * - :ref:`cpn_button` - |link_button_buy| (x2) * - :ref:`cpn_led` - |link_led_buy| * - :ref:`cpn_resistor` - |link_resistor_buy| * - :ref:`cpn_fusion_hat` - \- * - :ref:`cpn_wires` - |link_wires_buy| * - Raspberry Pi - \- ---------------------------------------------- **配線図** 以下のように部品を Raspberry Pi に接続します: .. image:: img/fzz/morse_decoder_bb.png :width: 80% :align: center ---------------------------------------------- .. include:: python_online_llms.rst :start-after: start_setup_openai :end-before: end_setup_openai ---------------------------------------------- **サンプルの実行** #. コードを実行する .. raw:: html .. code-block:: shell cd ~/ai-lab-kit/llm sudo python3 llm_openai_morse_decoder.py #. 簡単なモールス信号メッセージを試す(例:"SOS") プログラム開始後、start/stop ボタンを押して記録を開始します。 その後、モールス用ボタンを使って、点(短押し)と線(長押し)を入力します。 入力が終わったら、もう一度 start/stop ボタンを押して記録を終了し、メッセージをデコードします。 #. コンソール出力を確認する コンソールには押下/解放のタイムスタンプが表示され、AI がそのタイミングデータを解析して、 デコード結果を出力します。 **"SOS" を入力したときの典型的なコンソール出力:** .. code-block:: text 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". #. ワークフローを理解する 1. 記録開始:start/stop ボタン(GPIO 17)を押すと LED が ON になります 2. モールス信号を入力:モールス用ボタン(GPIO 22)で点と線を入力します 3. リアルタイム表示:コンソールに押下/解放のタイムスタンプが表示されます 4. 停止してデコード:もう一度 start/stop ボタンを押すと LED が OFF になります 5. AI 解析:タイミングデータが OpenAI GPT に送られて解釈されます 6. デコード結果:AI が解読したメッセージを表示します **コード** 以下は AI 搭載モールス信号デコーダーの Python スクリプト全体です: .. raw:: html .. code-block:: 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 ピンをそれぞれ異なる用途で設定しています: .. code-block:: python 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. タイミングデータの保存 押下/解放イベントは正確なタイムスタンプとともに保存されます: .. code-block:: python 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. デバウンス機構 スイッチのチャタリングによる誤検出を防ぎます: .. code-block:: python 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. 状態管理 システムはフラグを使って記録状態を管理します: .. code-block:: python 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 によって記録状態を視覚的に確認できます: .. code-block:: python def handle_start_stop(): if input_active: led.off() # LED OFF when not recording else: led.on() # LED ON when recording 6. AI へのプロンプト構築 タイミングデータは AI が処理しやすいよう文字列化して送信します: .. code-block:: python input_text = str(morse_events) # Example format sent to AI: # "[('pressed', 1767773542.1257536), ('released', 1767773542.285196), ...]" 7. ストリーミング応答 AI の応答はリアルタイムで処理・表示されます: .. code-block:: python response = llm.prompt(input_text, stream=True) for next_word in response: if next_word: print(next_word, end="", flush=True) 8. イベント駆動アーキテクチャ ボタンイベントに対して即時にコールバックが実行されます: .. code-block:: python # 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()`` を使って高精度のタイミングを取得します: .. code-block:: python start_time = time.time() # Current time in seconds since epoch # Calculate press duration: duration = release_time - start_time 10. データのクリア デコード後は、次のメッセージ入力に備えてイベントリストをクリアします: .. code-block:: python 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 搭載モールス信号デコーダーは、正確なタイミングデータと高度なパターン認識を組み合わせることで、歴史ある通信手段を現代的に再解釈し、新しい世代にとって学びやすく親しみやすい形で活用できることを示しています。