3G / GSMモデムからSMSを送信する

こんにちはHabr。 この記事では、GSMモデムでの経験、またはSMSメッセージの送信経験を共有したいと思います。 以下に、SMSメッセージの送信、およびモデムからの着信/発信メッセージの読み取りと削除のためのDelphiでのプログラムの実装について説明します。 私の場合、それはMTSのHUAWEIモデムでした。 興味のある方は、猫の下でお願いします。



アイデアの誕生



それはすべて、1つの小さな店舗チェーンでは、各店舗の収益について毎日当局に通知する必要があるという事実から始まりました。 販売情報は中央オフィスに蓄積され、これはヘッドに伝えられなければなりません。 総監督 (顧客)は非常に忙しい人であり、ディレクターとして店の収益に関する情報をタイムリーに受け取る必要があるため、インターネットが利用できない場合があり、携帯電話が常に手元にあるため、各店の収益に関するデータを含むSMSを簡単に送信することが決定されました読み取り可能なフォーム。



SMSの例:


08.11.2011

1. A, 123045 .

2. B, 134520 .

3. C, 215403 .

...




08.11.2011

1. A, 123045 .

2. B, 134520 .

3. C, 215403 .

...




;

後で判明したように、これは必要なすべてではありませんが、後でさらに詳しく説明します。



最初は、ゲートを介してSMSを送信することが提案されましたが、その多くが現在あります。 しかし、顧客はセキュリティ上の理由からこの申し出をすぐに拒否しました。毎日の収益に関するデータは非常に機密事項だからです。 その後、USBケーブルでコンピューターに接続された電話からSMSを送信することが決定されました。その結果、かなり長い間アイドル状態だった電話の代わりにUSBモデムが使用されました。



どこから始めるか



もちろん、COMポートで作業する必要がありましたが、ATコマンドを使用してモデムと通信できませんでした。 ATコマンドは非常に多くありますが、すべてが予想よりもはるかに単純であることが判明しました。 私たちの目的では、たった5つのチームでした。



AT + CMGF-操作モードを設定します:0デジタルまたは1テキスト。 このコマンドが最初に呼び出され、後続のコマンドとモデム応答の形式はこれに依存します。



AT + CMGS-メッセージを送信する場合、パラメーターの形式はモードに強く依存します(つまり、最後のコマンドから)。



AT + CMGL-モデムからメッセージを読み取り、パラメーターとして5つの値のいずれかを転送できます。モード(AT + CMGF)に応じて、デジタル値または文字列値を送信する必要があることに注意してください。

画像



AT + CMGD-メッセージ番号を転送するパラメーターとして、モデムから1つのメッセージを削除します。



AT + CMGR-モデムから1つのメッセージを読み取り、メッセージ番号も送信します。



最初の結果



数時間後(試行/エラー)、モデムからSMSメッセージを送信することは、通常の携帯電話から送信するよりもそれほど難しくないことがわかりました。 上記のように、SMSを送信するには、コマンド「AT + CMGS」を使用する必要があります。 そのため、彼はハイパータームを開き、モデムに(COMポートを介して)接続し、次のコマンドをポートに走り書きしました。

 AT+CMGF=1 [Enter] AT+CMGS=+79261234567 [Enter] hello habr, this is test message [Ctrl+Z]
      
      





Delphiでの同じ例:


 procedure SendSMS(AComPort: integer; AMsg: String; ANumTel: String); var hFile: THandle; procedure WriteStr(AStr: String); //     var LWrited: Cardinal; begin //   WriteFile(hFile, PAnsiChar(AStr)^, Length(AStr), LWrited, nil); end; begin //  hFile := Windows.CreateFile(PChar('\\.\COM' + IntToStr(AComPort)), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0); //  if (hFile <> INVALID_HANDLE_VALUE) then begin try //   WriteStr('AT+CMGF=1' + #$D#$A); //    "+79xxxxxxxxx" WriteStr('AT+CMGS="'+ANumTel+'"' + #$D#$A); //  ,   WriteStr(AMsg + #$D#$A#$1A); finally //  Windows.CloseHandle(hFile); end; end; end;
      
      





出来上がり、ポケットサイズのデバイスは、予想されるイベントを発表しました。

しかし、悲しいかな、この成功の時に私に生じたポジティブな感情は長くは続かず、より正確にはロシア語のテキストではすべてがより複雑であることが発見されました。 最初に、ロシア語でメッセージを送信するには、モードをテキストからデジタル(AT + CMGF = 0)に切り替える必要があり、次にメッセージ自体をUCS2エンコードで送信する必要があります。 そして、少なくとも最初の問題が発生した場合、2番目の問題を修正する必要がありました。



UCSでのテキストエンコーディングとその逆(Delphiでも同様):


 function UCSToAnsi(AStr: AnsiString): AnsiString; function Convert(ACnvStr: AnsiString): AnsiChar; var j: integer; begin j := StrToIntDef('$'+ACnvStr, 0); case j of 1040..1103: j := j - 848; 1105: j := 184; end; Result := Chr(j); end; var c, i: integer; begin Result := ''; c := Length(AStr) div 4; for i := 0 to c - 1 do Result := Result + Convert(Copy(AStr, i*4+1, 4)); end; function AnsiToUCS(AStr: AnsiString): AnsiString; function Convert(AChar: AnsiChar): AnsiString; var j: integer; begin Result := ''; j := ord(AChar); case j of 192..255: j := j + 848; 184: j := 1105; end; Result := IntToHex(j, 4) end; var c, i: integer; begin Result := ''; c := Length(AStr); for i := 1 to c do Result := Result + Convert(AStr[i]); end;
      
      





すべてがすぐに簡単に判明したとは言えませんが、それでも判明しました。 以前にモデムに送信した場合:

 AT+CMGF=1 [Enter] AT+CMGS=+79261234567 [Enter] hello habr, this is test message [Ctrl+Z]
      
      





次に、ロシア語でメッセージを送信するには、送信する必要があります。

 AT+CMGF=0 [Enter] AT+CMGS=84 [Enter] 0011000B919762214365F70008C146043F04400438043204350442002004450430043 10440002C0020044D0442043E00200442043504410442043E0432043E043500200441 043E043E043104490435043D04380435 [Ctrl+Z]
      
      





最初にモードスイッチ(デジタル)、次にメッセージが送信され(84)、最後の行には電話番号、メッセージテキスト、さまざまな設定(SMSセンター番号、受信者にメッセージを保存するかどうかなど)が含まれます。



Delphiの例:


 procedure SendSMSMessage(AComPort: integer; AMsg: String; ANumTel: String); var Lng, i: Integer; LRead, LText, LMes, LTel, ANum: String; hFile: THandle; procedure WriteStr(AStr: String); var LWrited: Cardinal; begin //   WriteFile(hFile, PAnsiChar(AStr)^, Length(AStr), LWrited, nil); end; begin //  hFile := Windows.CreateFile(PChar('\\.\COM' + IntToStr(AComPort)), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0); //  if (hFile <> INVALID_HANDLE_VALUE) then begin ANum := ANumTel; if (Length(ANum) mod 2) = 1 then ANum := ANum + 'F'; for i := 1 to Length(ANum) do if i mod 2 = 0 then LTel := LTel + ANum[i] + ANum[i-1]; LText := AnsiToUCS(AMsg); //    SMS . 0 - ,     . LMes := '00'; // SMS-SUBMIT LMes := LMes + '11'; //    . 0 -      . LMes := LMes + '00'; //    LMes := LMes + IntToHex(Length(ANumTel), 2); // -. (91     , 81 -  ). LMes := LMes + '91'; //      . LMes := LMes + LTel; //   LMes := LMes + '00'; //     SMS     (Flash SMS),   - (0- 8-). LMes := LMes + '08'; //   . 1 -  LMes := LMes + 'C1'; //   . LMes := LMes + IntToHex(Trunc(Length(LText)/2),2); LMes := LMes + LText; Lng := Round((Length(LMes)-2)/2); WriteStr('AT+CMGF=0' + #$D#$A); WriteStr('AT+CMGS=' + StrToInt(Lng) + #$D#$A); WriteStr(LMes + #$D#$A#$1A); Windows.CloseHandle(hFile); end; end;
      
      





アイデア開発



待望のロシア語のテキストが「詐欺」の代わりに携帯電話に届いたとき、私は幸せだったと言うことは、何も言わないことを意味します。 翌日、プログラムの主要部分を終了し、すべてが終わったように見えました。 収益データを含むメッセージが顧客の電話に送信されます。人生は成功したようですが、そこにはありませんでした。 約1週間後、顧客は、アプリケーションを完成させるように、つまり、テキスト付きのSMSを受信した後にアプリケーションを完成させるように私に依頼しました。

08.11.2011

1. A, 123045 .

2. B, 134520 .

3. C, 215403 .

...






このメッセージへの応答として店舗番号を送信すると、新しいメッセージが届きますが、指定された店舗の詳細情報が含まれます。

まあ、一般的に、ロジックは単純です:プログラムはすべてのストアの最後に送信された「レポート」を保存し、条件付きテキスト(たとえば、「store = 12」)が表示されるとすぐに着信メッセージを読み取り、そこからストア番号を引き出し、最後に送信されたメッセージを確認しますこの番号の下にあるものと、このストアに関する詳細情報を送信します(残念ながら、現時点では「詳細レポート」の形式と内容をお客様が決定していないため、例として挙げるものはありません)。 上記のロジックを実装するには、モデムが以下を行う必要があります。メモリからSMSを読み取り、メモリからSMSを削除します(蓄積しないように)。 メッセージの読み取りには、AT + CMGRコマンドとAT + CMGLコマンドを使用しました(簡単な説明は前に示しました)。 すべての投稿を読むと次のようになります。

 AT+CMGF=1 [Enter] AT+CMGL="ALL" [Enter] +CMGL: 6,"REC READ","778467",,"11/09/03,18:49:40+16" 007700770077002E006D00740073002E00720075 +CMGL: 7,"REC READ","+79261234567",,"11/10/18,18:38:00+16" 04220435044104420020043F044004380435043C043000200073006D0073002004410 43E043E043104490435043D04380439002100200421043C04410020043D043D043D04 3004340430003F0021003F00210028002D005F002D00290020 [Enter]
      
      





すべてが以前より簡単です。 各メッセージは2行で構成され、最初の行にはメッセージに関するデータ(送信元、送信元、メッセージ番号など)が含まれ、2行目にはメッセージのテキストが含まれます(UCSエンコードでは、上記のUCSToAnsi関数が指定されています)。 単一のメッセージの読み取りは次のように行われます。

 AT+CMGF=1 [Enter] AT+CMGR=7 [Enter] +CMGR: "REC READ","+79261234567",,"11/10/18,18:38:00+16" 04220435044104420020043F044004380435043C043000200073006D0073002004410 43E043E043104490435043D04380439002100200421043C04410020043D043D043D04 3004340430003F0021003F00210028002D005F002D00290020 [Enter]
      
      





同様に、メッセージの削除が発生します。 たとえば、私の場合、コマンドAT + CMGD = 7を送信すると、次のAT + CMGL = "ALL"でメッセージ番号7は表示されなくなります。 削除されます。



おわりに



そのため、GSMモデムを介してSMSメッセージを操作するための基本的なコマンドは分解され、メッセージの送信、読み取り、削除が検討されました。 最後に、このSMSメッセージの使用の範囲は非常に広いことに注意してください(特に、双方向通信を編成できるため)。 たとえば、ユーザーがSMSを送信し、モデムが受信し、プログラムが読み取り、メッセージテキストに基づいていくつかのアクションを実行し、結果をユーザーに送信します。 またはその逆:PC上で何らかのイベントが発生し、プログラムはこのイベントに関するメッセージをユーザーに送信します。 実験の成功をお祈りします、ありがとう!



テストプログラムのソースはこちら



All Articles