電子工作 / モールス / VoIP
モールス信号で電話をかける
2026. Nov. 11 7M4MON
ArduinoとRaspberry Piで作るSIP電話
この記事の目次
以前の記事でSIPを使った内線電話網について紹介しましたが、普通の電話機をつなぐだけでは少し物足りなくなってきました。せっかく自作するのであれば、電話番号の入力方法そのものも普通ではないものにしてみたいところです。
そこで今回は、モールス信号で電話番号を入力して発信する電話機を作ってみました。モールスキーで数字を入力するとLCDに電話番号が表示され、発信ボタンを押すとRaspberry PiがSIP電話として相手を呼び出します。もちろん着信、応答、終話もでき、スピーカーマイクを接続して無線機のようにPTTを押して話すこともできます。
実用性は全くありませんが、モールスキーで「3」「1」と打ったあとにボタンを押すと、本当に内線31番の電話が鳴るというのはなかなか面白いものです。
全体構成
モールスキー・LCD・ボタン → Arduino / ATmega32U4
↕ USB(Serial + HID)
Raspberry Pi / Python + PJSUA2
↕ SIP
Asterisk / PBX
Arduinoはモールスキーやボタンの読み取り、LCD表示、着信音、PTT処理といった「電話機として人間が触る部分」を担当します。一方、Raspberry PiではPythonからPJSUA2を使用して、SIPサーバーへの登録、発信、着信、応答、切断、そして音声デバイスの接続を行います。
今回の用途では、スイッチやLCDの制御はマイコンに任せ、SIPやLinuxのオーディオ処理はRaspberry Piに任せるのが単純で合理的です。そのため、最初から両者をUSBで接続する構成としました。Arduino側から見ればSIPの仕組みを知る必要はなく、「31番へ電話する」「電話に出る」「電話を切る」という命令だけRaspberry Piへ渡せばよいことになります。
モールスで電話番号を入力する
電話番号には数字しか必要ありませんので、モールスデコーダも0~9だけに限定しました。キーが押されている時間から短点と長点を判定し、5エレメントがそろうと数字に変換します。文字間の無信号時間も監視しているため、通常のモールス操作と同じ感覚で続けて入力できます。
| 数字 | モールス符号 |
|---|---|
| 0 | ----- |
| 1 | .---- |
| 2 | ..--- |
| 3 | ...-- |
| 4 | ....- |
| 5 | ..... |
| 6 | -.... |
| 7 | --... |
| 8 | ---.. |
| 9 | ----. |
入力された番号は1602 LCDの下段に「D:31」のように表示します。先頭の「D:」はDestinationの意味で、後ろに現在入力している電話番号が続きます。たとえば内線31番へ電話する場合は「...--」で3、「.----」で1を入力します。
操作ボタンは2個でMAINとENDです。MAINボタンは待受中なら発信、着信中なら応答です。ENDボタンは通話中であれば終話、待受中であれば入力した番号の消去に使います。LCD上段には現在の状態をIDLE(待受)、RING(着信)、CALL(発信中)、TALK(通話中)として表示します。電話として必要な操作はこの2個で行います。
Raspberry Piとの通信は簡単な文字コマンド
ArduinoとRaspberry PiはUSB CDC Serialで通信しています。プロトコルと呼ぶほど大げさなものではなく、Arduinoから送るコマンドは基本的に3種類です。
D31 31番へ発信
A 着信応答
H 終話
実際にはそれぞれ改行を付けて送信します。反対にRaspberry PiからArduinoへは、R(着信中)、C(通話成立)、E(通話終了)という1文字だけを送ります。着信中はRaspberry PiからRを100ms周期で送信し、ArduinoはRが届いている間を着信中と判断してベルを鳴らします。一定時間届かなくなれば自動的に着信状態を解除します。
MAINボタンを押すとArduinoからAを送り、Raspberry PiがSIP着信へ応答します。通話が成立するとCが戻ってきてLCDがTALK表示になり、終話すればEが送られて再びIDLEへ戻ります。シリアル通信を複雑にする必要もないので、この程度の単純なプロトコルにしています。ターミナルソフトから手入力して動作確認することもでき、デバッグも容易です。
Raspberry PiをSIP電話にする — PJSUA2とは
Raspberry Pi側ではPythonプログラムを動作させ、実際のSIPによる発着信を処理しています。ここで使用しているのがPJSUA2です。アマチュア無線の世界ではあまり聞き慣れない名前かもしれませんが、PJSUA2はSIPやVoIPを扱うオープンソースライブラリPJSIPに含まれる高レベルAPIで、SIPサーバーへの登録、発信、着信、通話状態の管理、マイクやスピーカーとの音声接続など、ソフトウェア電話機を作るために必要な機能をまとめて扱えます。
PJSIPにはpjsuaというコマンドラインで動作するSIP電話アプリケーションもありますが、今回はArduinoとの連携処理を組み込む都合上、PythonからPJSUA2を直接扱う必要があります。そのため、PJSIPのpjprojectをソースからビルドしました。Pythonバインディングを利用するには共有ライブラリが必要だったため、--enable-sharedを付けてビルドしています。
以下は製作時に使用したビルド手順です。
sudo apt update
sudo apt install -y build-essential pkg-config git \
libasound2-dev libssl-dev swig python3-dev
cd ~
git clone https://github.com/pjsip/pjproject.git
cd pjproject
./configure --enable-shared
make dep
make -j4
sudo make install
sudo ldconfig
さらに、PythonからPJSUA2を呼び出せるようにPythonバインディングを作成します。
cd ~/pjproject/pjsip-apps/src/swig/python
make
sudo python3 setup.py install
cd ~/pjproject
sudo make install
sudo ldconfig
これでPythonから「import pjsua2 as pj」として利用できます。
PJSUA2が実際にしていること
起動すると、まずRaspberry PiがPBXへSIP端末として登録します。たとえばRaspberry Pi側を内線99番とした場合、PythonプログラムはArduinoからのシリアルコマンドとSIP側からの着信の両方を監視します。
ArduinoからD31を受信すると、PythonプログラムはPJSUA2に31番への発信を指示します。PJSUA2がPBXへSIP発信を行い、相手が応答すると通話成立イベントがPythonへ通知されます。そこでArduinoへCを返すと同時に、PJSUA2上で通話音声とRaspberry Piのマイク、スピーカーを接続します。
逆に99番へ電話がかかってくると、PJSUA2が着信イベントをPythonへ通知します。PythonはArduinoへRを100ms周期で送り、パネル側で着信表示とベル鳴動を行います。MAINボタンを押してArduinoからAが送られると、PythonがPJSUA2へ着信応答を指示します。つまり今回のPythonプログラムは、PJSUA2が扱うSIPの世界と、Arduinoが扱うボタンやモールスキーの世界を橋渡しするものです。
スピーカーマイクで無線機風に通話する
音声の入出力には無線機用のスピーカーマイクを使用します。普通の電話はフルデュプレックスなので、こちらが話している最中も相手側の音声はスピーカーから出ています。しかし今回のようにスピーカーマイクを使用すると、そのままではスピーカーの音をマイクが拾ってハウリングしてしまいます。そこでPTTを押している間はスピーカー側をミュートすることにしました。
PTT信号はプルアップしてアナログ入力で監視しており、PTTを押すと電圧が下がります。Arduinoはこの変化を検出してミュート用リレーを切り替え、送話中はスピーカーから音が出ないようにします。SIP通話そのものはフルデュプレックスですが、操作感としては「PTTを押すとスピーカーをミュートして話す、PTTを離すと相手の音声を聞く」という、無線機に近い使い方になります。
オーディオアンプと内蔵電池
スピーカー駆動用にはHT82V739を使用しました。動作電圧は2.2~5.5Vで、最大出力は1200mWです。ケースの中にはニッケル水素充電池を3本入れていますが、これは将来、スマートフォンなどへ接続する構成に拡張した際、接続先からオーディオアンプの電源を十分に取れない場合に備えたものです。
PTT入力はミュートリレーを動かすだけではなく、ATmega32U4のUSB HID機能を使ってゲームパッドのButton 0としても出力しています。この仕組みは、月刊FB NEWSに掲載された無線機用マイクのUSB HID化インターフェースで使った機能と同じものです。今回の電話機ではUSB HIDは必要ありませんが、機能を入れておいても特に邪魔にはならず、将来PCやスマートフォンなど別の機器へ接続したくなった場合にも使えるため、そのまま搭載しました。
モールス速度調整
モールスを打つ速度は人によってかなり違うため、判定速度も変更できるようにしています。MAINとENDを同時に押すと速度調整モードに入り、ブザーから文字A(.-)が繰り返し鳴ります。これを聞きながらENDで速度を上げ、MAINで速度を下げます。もう一度2個のボタンを同時に押せば設定をEEPROMへ保存して通常モードへ戻るため、一度自分の打鍵速度に合わせれば電源を切っても設定は保持されます。
実際に電話をかけてみる
内線31番へ電話したければ、まずモールスキーで3と1を入力します。LCDにD:31と表示されたところでMAINボタンを押すと、ArduinoからRaspberry PiへD31が送られ、Raspberry PiのPJSUA2が31番へSIP発信します。相手が応答するとRaspberry PiからCが戻り、LCDはTALKになります。あとはスピーカーマイクのPTTを押して話し、離して相手の音声を聞きます。
通話を終えるときはENDボタンを押します。ArduinoからHが送られてSIP通話が切断され、パネルも待受状態へ戻ります。
おわりに
モールスキーとVoIPを組み合わせることに、実用性があるわけではありませんが、こうしたちょっとした思いつきを実際に形にできるところも、電子工作の面白さだと思います。
こういう、しょうもない作品でも、作ってみるとソフトウェア、組み込み、通信、オーディオ、ハードウェアなど、いろいろな技術を使うことになります。仕事とは少し離れた題材で試してみることで、普段は触らない技術に触れることができます。そういう経験が、あとになって意外と自分の引き出しになっている気がします。