モデム・プロトコル

モデム

概要

モデム関連の情報を取得する方法を紹介します。

取得できるモデム関連の情報

モデム関連の情報として取得できる値は以下の通りとなります。
IMEIやIMSIは端末識別番号としてクラウド側の管理番号として使われる事が多いため、ここから取得して利用することが出来ます。

情報 補足
電波強度 LTE基地局から受信している電波の強度
IMEI 本製品の識別番号
IMSI SIMに紐づく識別番号
電話番号 SIMに紐づく電話番号

モデム情報取得

モデム関連の情報取得API

モデム関連の情報取得をするAPIは以下となります。

API 内容
1 MMG_GetAntLevel() LTE基地局から受信している電波の強度の取得
2 MMG_GetIMEI() 本製品の識別番号の取得(IMEI)
3 MMG_GetIMSI() SIMに紐づく識別番号の取得(IMSI)
4 MMG_GetNUM() SIMに紐づく電話番号の取得

MMG_GetAntLevel()

電波強度を取得するAPIとなります。

MMG_GetAntLevel(int32 timeout, uint8 sync, uint8 ex_result)
    timeout   :   タイムアウト値[msec]
    sync      :   同期方式 (EAPI_SYNC_RESULTの指定固定)
    ex_result :   API実行結果<成功:0>
戻り値
    0〜4:電波強度(0:圏外 〜 4:強)
   -1:失敗
※本APIはモデムが起動していない場合は起動してから値の取得をします。
 このためtimeoutはモデム起動時間を加味した値が必要です。
 ex_resultはモデムの故障等がなければ成功で返ります。
※SIMが挿入されてない場合、戻り値は0で返ります。
[コード例]
#incliude "modemMgrTask.h"
#incliude "eventLib.h"
   ~
func any_func() {
    uint8   ex_result;
    int32   ret;
    // 電波強度を取得
    ret = MMG_GetAntLevel( 10*1000, EAPI_SYNC_RESULT, ex_result);

}

MMG_GetIMEI(), MMG_GetIMSI()

IMEI, IMSIを取得するAPIとなります。

MMG_GetIMEI(int32 timeout, uint8 sync, uint8 ex_result)
    timeout   : タイムアウト値[msec]
    sync      : 同期方式 (EAPI_SYNC_RESULTのみ)
    ex_result : API実行結果<成功:0>
戻り値
    IMEI値 str 15文字、失敗時は空文字 ""
※本APIはモデムが起動していない場合は起動してから値の取得をします。
 このためtimeoutはモデム起動時間を加味した値が必要です。
 ex_resultはモデムの故障等がなければ成功で返ります。
MMG_GetIMSI(int32 timeout, uint8 sync, uint8 ex_result)
    timeout   : タイムアウト値[msec]
    sync      : 同期方式 (EAPI_SYNC_RESULTのみ)
    ex_result : API実行結果<成功:0>
戻り値
    IMSI値 str 15文字、失敗時は空文字 ""
※本APIはモデムが起動していない場合は起動してから値の取得をします。
 このためtimeoutはモデム起動時間を加味した値が必要です。
 ex_resultはモデムの故障等がなければ成功で返ります。
[コード例]
#incliude "modemMgrTask.h"
#incliude "eventLib.h"
   ~
func any_func() {
    uint8   ex_result;
    str  imei[15];
    str  imsi[15];

    // IMEI、IMSIを取得
    imei = MMG_GetIMEI( 10*1000, EAPI_SYNC_RESULT, ex_result);
    imsi = MMG_GetIMSI( 10*1000, EAPI_SYNC_RESULT, ex_result);

}

MMG_GetNUM()

電話番号を取得するAPIです

MMG_GetNUM(int32 timeout, uint8 sync, uint8 ex_result) 
    timeout   :  タイムアウト値[msec]
    sync      :  同期方式 (EAPI_SYNC_RESULTのみ)
    ex_result :  API実行結果<成功:0>

戻り値
    電話番号 str 一般的には11文字、失敗時は空文字 ""
※本APIはモデムが起動していない場合は起動してから値の取得をします。
 このためtimeoutはモデム起動時間を加味した値が必要です。
 ex_resultはモデムの故障等がなければ成功で返ります。   
[コード例]
#incliude "modemMgrTask.h"
#incliude "eventLib.h"
   ~
func any_func() {
    uint8 ex_result;
    str  telnum[15];

    // 電話番号を取得
    telnum = MMG_GetNUM(10*1000, EAPI_SYNC_RESULT, ex_result);

}

注意事項

モデム機能は、LTE通信をしない場合はモデム機能をOFFすることで消費電量を抑えることができます。
電波強度やIMEI値などのAPIを利用した際に、モデム機能がOFFだった場合は、モデム機能はONに変更されています。この場合は必要に応じでモデム機能をOFFしてください。


通信プロトコル

概要

本製品では、通信プロトコルとして、UDP/TCP/HTTP/MQTT/HTTPS/MQTTSを利用できます。ここでは通信プロトコルをレシピ言語から制御する方法を紹介します。

通信プロトコル機能の特徴

通信プロトコル機能の特徴を以下に示します。


[通信プロトコル機能の利用]

通信プロトコル機能ではトランスポート層でのソケット接続、及びTLS認証を行なった上で、アプリケーション層でHTTP(S)/MQTT(S)のデータ送受信を行えるように、APIの作りも分かれた形で提供されています。

Protocol_OSI

OSI参照モデルの様に利用したいプロトコルに応じて使用するAPIを重ねて行く使い方をします。(APIの詳細は後述します。)

Protocol_usage


[セッション管理]

通信プロトコル機能では時分割での8つのセッションを構成することが出来ます。このセッションはAPI上では(send_id)という引数で指定をすることで、セッションごとに別な設定値やパラメータを保持することが出来ます。

Protocol_session

例えばデータを違うクラウドサーバにそれぞれ分けて送りたい場合や、同じサーバ内でも画像データはストレージサービスの違うURLに送信するなどの場合など、複数セッションを張ってそれぞれでデータ送信をすることができます。


[送受信データの制御]

送受信を行う際のデータは、前記の通信プロトコル用のAPIの引数として渡すのではなく「FIFOバッファ」を経由してのやり取りを行います。送信する際には最初に送信したいデータをバッファに入れておき、送信命令の際にバッファの番号を指定する流れとなります。受信の際にはモデムタスクがバッファに受信データを格納後にイベントを発行します。
(バッファの制御方法はAPIの利用方法と共に後述します。)

Protocol_fifobuffer


UDP/TCP制御

トランスポート層でソケット接続をし、UDP/TCPでの送受信を行うAPIの解説をします。

UDP/TCP接続に関連するAPI

UDP/TCPでのソケット接続に関連するAPIは以下の通りです。

API 内容
1 MMG_SetConnectParam() ソケット接続の基本設定を行う
2 MMG_SetReceiveParam() データ受信時の基本設定を行う
3 MMG_ConnectSOCKET() ソケット接続を行う
4 MMG_SendSOCKET() UDP/TCP/TLSでデータ送信を行う

[UDP/TCPのAPI制御]

UDP/TCPのトランスポート層での接続とデータ送受信を行う為のAPI制御の模式図を以下に示します。

Protocol_udp_tcp

UDP/TCPでの送受信は以下の手順でAPIを利用します。

Protocol_udp_tcp


MMG_SetConnectParam()

ソケット接続の基本設定を行うAPIです。

MMG_SetConnectParam(int32 timeout, uint8 sync, uint8 ex_result, uint8 send_id, str hostname, uint16 port, uint8 protocol, uint8 sni, uint8 auth_mode, uint8 auth_index, uint8 use_usb)
    timeout  : API実行時のタイムアウト値
    sync     : APIの同期方式  (EAPI_SYNC_RESULTのみ利用可)
    ex_reslt : APIの実行結果  (0:成功, 1-99:警告, 100-199:エラー)
    send_id  : 送信ID 接続-送信までの管理ID (1-8)
    hostname : 接続先のホスト名 (127文字以下の任意文字列)
    port     : 接続先のポート番号 (0-65535)
    protocol : 利用する通信プロトコル (0:UDP,1:TCP,2:TLS,3:HTTP, 4:HTTPS,5:MQTT,6:MQTTS)
    sni      : TLS通信時のSNI設定(0:無効, 1:有効)
    auth_mode : TLSのモード設定(0:認証無し, 1:サーバ認証, 2:クライアント認証)
    auth_index: 認証ID TLS認証-証明書の管理ID(1-5) TLS未使用時は(0)
    use_usb  : HW内部の通信パス設定(0:無効のみ利用可)

timeout, sync, ex_result
モデム系のAPIでは最初にこの3つの引数があります。APIは同期実行(sync)(APIの処理が終わるまでコンテキストが戻らない)のため、モデムのトラブルや接続出来ない等でユーザータスク側の処理が恒常的にロックされないように、APIのタイムアウト時間(timeout)を設けられるようにしています。またモデム系のトラブルの多くの情報を返せるように、関数の返り値ではなくAPIの実行結果(ex_result)として引数に設定した変数に結果を返す様にしています。

send_id
前記解説のセッションごとの管理IDとなります。ホスト名やポート番号等このIDごとに管理されますので、セッションごとにこのAPIをそれぞれコールします。また、後述のデータを送信する際などにはこのIDを意識して利用する必要があります。

hostname, port, protocol
このセッションで接続する相手と利用するプロトコルを指定します。ここでのhostnameはhost+domainを指します。指定された内容でのURLは以下のように構成されます。

Protocol_hostname

sni, auth_mode, auth_index
TLSを利用して接続する際に利用するパラメータとなります。TLS未使用時は、sni, auth_mode, auth_index共に0の入力で問題ありません。この部分の指定は後述のTLSの項で説明します。

use_usb
この引数は将来拡張の為の引数となりますので、0:無効の設定となります。


MMG_SetReceiveParam()

データ受信やソケット切断時のイベントの基本設定を行うAPIです。

MMG_SetReceiveParam(int32 timeout, uint8 sync, uint8 ex_result, uint8 send_id, uint8 rcv_databuf_id, uint8 rcv_event_id) 
    timeout  : API実行時のタイムアウト値
    sync     : APIの同期方式  (EAPI_SYNC_RESULTのみ利用可)
    ex_reslt : APIの実行結果  (0:成功, 1-99:警告, 100-199:エラー)
    send_id  : 送信ID 接続-送信までの管理ID (1-8)
    rcv_databuf_id :  受信に利⽤するFIFOバッファID (0〜31)
    rcv_event_id   :  データ受信やソケット切断時の発行されるイベントID (1〜254)

timeout, sync, ex_result
モデム系のAPIにある3つの引数で前記と同じ内容になります。

send_id:
前記解説のセッションごとの管理IDとなります。他のAPIと同様に利用したいセッションの番号に合わせて利用してください。

rcv_databuf_id
受信データを格納する「FIFOバッファ」のIDとなります。FIFOバッファのIDはシステム内で共通なため、他の機能で利用しているFIFOバッファIDと被らないように設定してください。

rcv_event_id
データを受信した際に発行されるイベントIDを指定します。データを受信した際やソケット切断時に指定したイベントIDで通知される為、ユーザタスク側で該当のイベント処理を記載する必要があります。(受信データの取り方等はサンプルコードが参考になります)

※送信だけのアプリケーションで受信データを想定する必要の無い場合は、このAPIで受信設定を行わなくても、MMG_ConnectSOCKET()のAPIでソケット接続してデータの送信は行えます。


MMG_ConnectSOCKET()

ソケット接続/切断を行うAPIです。

MMG_ConnectSOCKET(int32 timeout, uint8 sync, uint8 ex_result, uint8 send_id, uint8 connect) 
    timeout  : API実行時のタイムアウト値
    sync     : APIの同期方式  (EAPI_SYNC_RESULTのみ利用可)
    ex_reslt : APIの実行結果  (0:成功, 1-99:警告, 100-199:エラー)
    send_id  : 送信ID 接続-送信までの管理ID (1-8)
    connect  : ソケット接続/切断 (0:切断, 1:接続)

timeout, sync, ex_result, send_id
前記APIの説明と同様となります。

connect
ソケット接続と切断を指定します。MMG_SetConnectParam()で指定した内容でセッションを張りに行きます。このAPIでソケット接続完了後にデータの送受信が可能となります。


MMG_SendSOCKET()

UDP/TCP/TLSでデータ送信を行うAPIです。

MMG_SendSOCKET(int32 timeout, uint8 sync, uint8 ex_result, uint8 send_id, uint8 databuf_id, uint16 mov_target, uint8 burst, uint8 session, uint8 retain, uint16 send_success_num)
    timeout  : API実行時のタイムアウト値
    sync     : APIの同期方式  (EAPI_SYNC_RESULTのみ利用可)
    ex_reslt : APIの実行結果  (0:成功, 1-99:警告, 100-199:エラー)
    send_id  : 送信ID 接続-送信までの管理ID (1-8)
    databuf_id: 送信データのFIFOバッファID (0-31)
    mov_target: カメラ撮影画像送信の対象(0:最新, 1~:遡った数, 0xFFFF: 一番古い画像)
    burst    : FIFOバッファから連続送信するデータ数 (1~:指定数分を連続送信, 0xFFFF:FIFOバッファ内の全てのデータを送信)
    session  : 送信後の接続状態 (0:切断, 1:接続のまま / 1のみ利用可能)
    retain   : 送信失敗時にFIFOにデータ残す(0:消す, 1:残す)
    send_success_num: 送信成功した数の出力先

timeout, sync, ex_result, send_id
前記APIの説明と同様となります。

databuf_id
送信データの入っているFIFOバッファIDを指定します。FIFOバッファはシステム全体で0-31のIDがあり、これを各タスクで競合が無いように利用する仕組みとなっています。また、カメラ画像のデータは100番のIDがついた特別なバッファに入っており、このIDを指定することでカメラ画像が送信対象となります。FIFOバッファの使用例は次項で解説します。

mov_target
databuf_idに100番が指定された場合にこのパラメータが有効となります。送信対象とするカメラ画像の(最新/対象枚数/最古)を指定することで、該当の画像が送信されます。

burst
databuf_idで指定したバッファの中にあるデータパケットをいくつ送信するかを指定するパラメータです。例えば取得したデータをFIFOバッファに10個貯めておき、一回の送信で全て送信するような指定をすることが出来ます。

session
データ送信後にセッションを維持するか切断するかを選択するパラメータですが、現在は接続状態を維持する指定のみ有効となります。サーバーよってはデータを送信完了したタイミングでセッションを切断するものもありますので、その場合は送信後にセッション切断の状態となります。

retain
何らかの要因で送信が失敗した場合(ex_resultがエラーで返却された場合)、そのデータをFIFOに残すか消すかを指定出来ます。残す設定をした例として、(burst)の引数でFIFOバッファ内のパケット10個の送信に対して、2個目で送信失敗した場合は送信出来ていなかった残り9個のパケットがFIFOバッファに残った状態でAPIがリターンします。この場合、再度送信制御を行うことで、残りのバッファの再送が出来ることになります。
※再送する際には一度ソケット切断をして、再接続後に送信処理を行なってください。

send_success_num
送信成功したバッファの数が入ります。FIFOバッファから送信指定した枚数と同じ数が入っている場合に全て送信成功したと判断することが出来ます。


FIFOバッファへのデータ格納

送信データと受信データはFIFOバッファを経由してデータの出し入れを行います。ここではFIFOバッファにデータを格納する方法を示します。

[FIFOバッファのAPI]
API 概要
rcplib_FIFOBUF_Create() FIFOバッファの作成
rcplib_FIFOBUF_SetDate() FIFOバッファへデータの書込み
rcplib_FIFOBUF_GetData() FIFOバッファからデータの読込み

FIFOバッファは利用する前に「rcplib_FIFOBUF_Create() 」APIにて利用の宣言が必要となります。この時どれくらいの容量を確保するのか、何番のIDを利用するのかを指定します。ここで利用宣言したIDを送信や受信のAPIで指定する流れとなります。

[コード例]

下記コードでは送信バッファを確保して、JSONデータを格納する例を示します。

#incliude "modemMgrTask.h"
#incliude "eventLib.h"
#incliude "bufferLib.h"
   ~
#define BID  FIFOBUF_ID_RECIPE_0 //バッファ番号
json    snd_json[200]; //送信 Json data
uint16 len_json;       //送信 length
   ~
func event_init() {
    // FIFOバッファを定義
    rcplib_FIFOBUF_Create(BID,256,1,FIFOBUF_OVERWRITE_DISABLE);
}

func any_func() {
    uint8 data_mdm[200];

    // Jsonデータ準備
    makeJsonData(10,20);
    // 送信する文字列を設定 (uint8配列変換後,FIFOBUF書込)
    rcplib_FORMAT_Binary(data_mdm, 0, EAPI_VTYPE_STR, len_json, snd_json);
    rcplib_FIFOBUF_SetData(BID, len_json, data_mdm);
}

func makeJsonData(uint16 dat1, uint16 dat2) {
    // {"data1":xx,"data2":xx,"time":"2022/mm/dd hh:mm:ss"} を作る
    // json変数はローカルでは使用出来ないためグローバル変数を利用
    str time_str[20];
    rcplib_JSON_Clear(snd_json);
    rcplib_JSON_UpdateV(snd_json,"data1",dat1);
    rcplib_JSON_UpdateV(snd_json,"data2",dat2);
    rcplib_FORMAT_Time(time_str,"%Y/%m/%d %H:%M:%S",rcplib_RTC_GetTime(),9);
    rcplib_JSON_UpdateV(snd_json,"time",time_str);
    len_json = rcplib_STR_Len(snd_json);
}

TCPでのデータ送信例

ここまで解説してきたAPIの使用例としてTCPでデータ送信を行うコード例を示します。

[コード例]
#incliude "modemMgrTask.h"
#incliude "eventLib.h"
#incliude "bufferLib.h"
   ~
// TCP用定義
#define TO   30*1000         //timeout 30秒
#define SID  1               //送信管理番号
#define BID  FIFOBUF_ID_RECIPE_0 //バッファ番号
// 変数定義
json   snd_json[200]; //送信 Json data
uint16 len_json;       //送信 length
   ~
func event_init() {
    // FIFOバッファを定義
    rcplib_FIFOBUF_Create(BID,256,1,FIFOBUF_OVERWRITE_DISABLE);
}

func any_func() {
    str host[50];     host = "www.xxxxxxxxx.jp";
    uint16 port;      port = 8888;
    uint8  ex;        // API実行結果格納用
    uint16 snum;      //送信完了数格納用
    uint8 data_mdm[200];   //送信データ格納用

    // Jsonデータ準備
    makeJsonData(10,20);
    // 送信する文字列を設定 (uint8配列変換後,FIFOBUF書込)
    rcplib_FORMAT_Binary(data_mdm, 0, EAPI_VTYPE_STR, len_json, snd_json);
    rcplib_FIFOBUF_SetData(BID, len_json, data_mdm);

    // TCP送信基本設定
    MMG_SetConnectParam(TO, EAPI_SYNC_RESULT, ex, SID, host, port, MMG_PROTOCOL_TCP, 0,0,0,0);
    // ソケット接続
    MMG_ConnectSOCKET(TO, EAPI_SYNC_RESULT, ex, SID, MMG_SOCKET_OPEN);
    // TCPでデータ送信
    MMG_SendSOCKET(TO, EAPI_SYNC_RESULT, ex, SID, BID, 0, 1,MMG_SESSION_CONNECT, MMG_DATA_RETAIN, snum);
}

HTTP制御

アプリケーション層でHTTPでの送受信を行うAPIの解説をします。

HTTP送信に関連するAPI

HTTP送信に関連するAPIは以下の通りです。

API 内容
1 MMG_SetConnectParamHTTP() HTTP(S)接続の基本設定を行う
2 MMG_SendHTTP() HTTP(S)でデータ送信を行う

[HTTPのAPI制御]

HTTPでの接続とデータ送受信を行う為のAPI制御の模式図を以下に示します。

Protocol_http

HTTPでの送受信は前記TCP接続のAPIと共に利用します。APIの利用順番は以下の通り。

Protocol_http


MMG_SetConnectParamHTTP()

HTTP接続の基本設定を行うAPIです。

MMG_SetConnectParamHTTP(int32 timeout, uint8 sync, uint8 ex_result, uint8 send_id, str http_url, str http_header, uint8 http_method)
    timeout  : API実行時のタイムアウト値
    sync     : APIの同期方式  (EAPI_SYNC_RESULTのみ利用可)
    ex_reslt : APIの実行結果  (0:成功, 1-99:警告, 100-199:エラー)
    send_id  : 送信ID 接続-送信までの管理ID (1-8)
    http_url : 接続先のURL名 (255文字以下,Host名以下のPath部分)
    http_header: HTTPヘッダ情報 (767文字以下の文字列)
    http_method: HTTPメソッド (0:GET,1:POST,2:HEAD,3:PUT,4:DELETE,5:CONNECT)

timeout, sync, ex_result, send_id
前記APIの説明と同様となります。

http_url
Host名以下のパスの部分の文字列を指定します。文字列としては"/datadir"と区切り文字のスラッシュを含む形で指定します。
Protocol_path

http_urlには特殊なリプレイス文字を含めることで「HTTP送信時の時刻情報や端末のIMEIなどに置き換える機能」を利用することができます。
例えばhttp://iot.kyocera.jpに送信する場合のhttp_urlを"/KC4C_test/*rp_imei*******/"と設定することで、http://iot.kyocera.jp/KC4C_test/012345678901234/の様に端末のIMEI情報に置き換えて送信処理を行います。

3種類のリプレイス文字を利用できます。

リプレイス文字 リプレイスルール
*rp_timestamp**** LTE送信処理実行時のタイムスタンプを"YYYYMMDDhhmmssddd"という形式でリプレイス ※dddは[msec]
*rp_movietime**** USBカメラ画像撮影時のタイムスタンプを"YYYYMMDDhhmmssddd"という形式でリプレイス(カメラバッファからのHTTP送信処理の時のみ利用可) ※dddは[msec]
*rp_imei******* 端末のIMEI(15桁の数値の文字列)でリプレイス

http_header
HTTPのヘッダー文字列を指定します。例えばJSONの場合は以下の通り。

str header[100];
header = "Content-Type: application/json";

ヘッダー文字列は終端文字としてCRLFを設定することが必要なため実際は以下のようにCRLFの終端文字を付け加える処理が必要となります。

str header[100];
uint8 header_cr[100];
header = "Content-Type: application/json";

//HTTP headerにCRリターンコードをつける
rcplib_FORMAT_Binary(header_cr, 0, EAPI_VTYPE_STR, rcplib_STR_Len(header), header);
header_cr[rcplib_STR_Len(header)]  = 13;
header_cr[rcplib_STR_Len(header) + 1]  = 10;
rcplib_FORMAT_Value(header, header_cr, 0, EAPI_VTYPE_STR, rcplib_STR_Len(header) + 2);

※現在文字列定義としてエスケープ文字(\r\n)が対応出来ていないため上記処理が必要となります。今後UPDATE予定です。

http_method
HTTPのメソッドを指定します。GETなどのデータが受信されるメソッドは、MMG_SetReceiveParam()で指定したFIFOバッファにデータ格納後に指定したイベントIDで通知されてきます。


MMG_SendHTTP()

HTTPでデータ送信を行うAPIです。

MMG_SendHTTP(int32 timeout, uint8 sync, uint8 ex_result, uint8 send_id, uint8 databuf_id, uint16 mov_target, uint8 burst, uint8 session, uint8 retain, uint16 http_rsp_code, uint8 rcv_buff_id, uint16 send_success_num)
    timeout  : API実行時のタイムアウト値
    sync     : APIの同期方式  (EAPI_SYNC_RESULTのみ利用可)
    ex_reslt : APIの実行結果  (0:成功, 1-99:警告, 100-199:エラー)
    send_id  : 送信ID 接続-送信までの管理ID (1-8)
    databuf_id: 送信データのFIFOバッファID (0-31)
    mov_target: カメラ撮影画像送信の対象(0:最新, 1~:遡った数, 0xFFFF: 一番古い画像)
    burst    : FIFOバッファから連続送信するデータ数 (1~:指定数分を連続送信, 0xFFFF:FIFOバッファ内の全てのデータを送信)
    session  : 送信後の接続状態 (0:切断, 1:接続のまま / 1のみ利用可能)
    retain   : 送信失敗時にFIFOにデータ残す(0:消す, 1:残す)
    http_rsp_code: HTTP通信結果リザルトコード(2xx:成功, 4xx:失敗)
    rcv_buff_id: HTTPレスポンスBody出力先
    send_success_num: 送信成功した数の出力先

timeout~retain
引数は前記MMG_SendSOCKET()での説明と同じになります。

http_rsp_code
実際にHTTP通信を行なった際のサーバーからのレスポンスステータスコードが入ります。
200番台では成功/ 400番台では失敗となります。詳細はRFC 2616を参照ください。

rcv_buff_id
MMG_SetReceiveParam()で設定をした、受信用のバッファIDが返ります。

send_success_num
前記MMG_SendSOCKET()での説明と同じになります。


HTTPでのデータ送受信例

HTTPでデータ送信(POST)を行うコード例を示します。

[コード例]
#incliude "modemMgrTask.h"
#incliude "eventLib.h"
#incliude "bufferLib.h"
   ~
// HTTP用定義
#define TO   30*1000         //timeout 30秒
#define SID  1               //送信管理番号
#define BID  FIFOBUF_ID_RECIPE_0 //バッファ番号
// 変数定義
json   snd_json[200]; //送信 Json data
uint16 len_json;       //送信 length
   ~
func event_init() {
    // FIFOバッファを定義
    rcplib_FIFOBUF_Create(BID,256,1,FIFOBUF_OVERWRITE_DISABLE);
}

func any_func() {
    str host[50];     host = "xxx.xxxxxxxxx.jp";
    uint16 port;      port = 8888;
    str url[50];        url = "";
    str header[50]; header = "Content-Type: application/json";
    uint8 header_cr[100];
    uint8  ex;      // API実行結果格納用
    uint16 status;  //リザルトコード格納用
    uint8 rbuf;     // レスポンスボディ格納用ID
    uint16 snum;    //送信完了数格納用
    uint8 data_mdm[200];   //送信データ格納用

    // Jsonデータ準備
    makeJsonData(10,20);

    //headerにCRLFを付与
    //HTTP headerにCRリターンコードをつける
    rcplib_FORMAT_Binary(header_cr, 0, EAPI_VTYPE_STR, rcplib_STR_Len(header), header);
    header_cr[rcplib_STR_Len(header)]  = 13;
    header_cr[rcplib_STR_Len(header) + 1]  = 10;
    rcplib_FORMAT_Value(header, header_cr, 0, EAPI_VTYPE_STR, rcplib_STR_Len(header) + 2);

    // ソケット接続の基本設定
    MMG_SetConnectParam(TO, EAPI_SYNC_RESULT, ex, SID, host, port, MMG_PROTOCOL_HTTP, 0,0,0,0);
    // HTTP接続の基本設定
    MMG_SetConnectParamHTTP(TO, EAPI_SYNC_RESULT, ex, SID, url, header, MMG_METHOD_HTTPPOST);
    // ソケット接続
    MMG_ConnectSOCKET(TO, EAPI_SYNC_RESULT, ex, SID, MMG_SOCKET_OPEN);

    // 送信する文字列を設定 (uint8配列変換後,FIFOBUF書込)
    rcplib_FORMAT_Binary(data_mdm, 0, EAPI_VTYPE_STR, len_json, snd_json);
    rcplib_FIFOBUF_SetData(BID, len_json, data_mdm);
    // HTTPでデータ送信
    MMG_SendHTTP(TO, EAPI_SYNC_RESULT, ex, SID, BID, 0, 1, MMG_SESSION_CONNECT, MMG_DATA_NOTRETAIN, status, rbuf, snum);
}

// private function //
func makeJsonData(uint16 dat1, uint16 dat2) {
    // {"data1":xx,"data2":xx,"time":"2022/mm/dd hh:mm:ss"} を作る
    // json変数はローカルでは使用出来ないためグローバル変数を利用
    str time_str[20];
    rcplib_JSON_Clear(snd_json);
    rcplib_JSON_UpdateV(snd_json,"data1",dat1);
    rcplib_JSON_UpdateV(snd_json,"data2",dat2);
    rcplib_FORMAT_Time(time_str,"%Y/%m/%d %H:%M:%S",rcplib_RTC_GetTime(),9);
    rcplib_JSON_UpdateV(snd_json,"time",time_str);
    len_json = rcplib_STR_Len(snd_json);
}

HTTPでデータ取得(GET)を行うコード例を示します。

[コード例]
#incliude "modemMgrTask.h"
#incliude "eventLib.h"
#incliude "bufferLib.h"
   ~
// HTTP用定義
#define TO   30*1000         //timeout 30秒
#define SID  1               //送信管理番号
#define BID  FIFOBUF_ID_RECIPE_0 //バッファ番号
   ~
func event_init() {
    // FIFOバッファを定義
    rcplib_FIFOBUF_Create(BID,256,1,FIFOBUF_OVERWRITE_DISABLE);
}

func any_func() {
    str host[50];     host = "xxx.xxxxxxxxx.jp";
    uint16 port;      port = 80;
    str url[50];        url = "";
    str header[50]; header = "";
    uint8  ex;        // API実行結果格納用
    uint16 status;    //リザルトコード格納用
    uint8 rbuf;       // レスポンスボディ格納用ID
    uint16 snum;      //送信完了数格納用
    uint8 data_mdm[200];  //送信データ格納用
    uint8 recv_buf[128];  //受信データ格納用
    uint32 recv_size;
    str str_log_tmp[128];

    // ソケット接続の基本設定
    MMG_SetConnectParam(TO, EAPI_SYNC_RESULT, ex, SID, host, port, MMG_PROTOCOL_HTTP, 0,0,0,0);
    // HTTP接続の基本設定
    MMG_SetConnectParamHTTP(TO, EAPI_SYNC_RESULT, ex, SID, url, header, MMG_METHOD_HTTPGET);
    // ソケット接続
    MMG_ConnectSOCKET(TO, EAPI_SYNC_RESULT, ex, SID, MMG_SOCKET_OPEN);
    // 送信する文字列にNULLを設定
    rcplib_FIFOBUF_SetData(BID, 0, data_mdm);
    // HTTPでデータ送信
    MMG_SendHTTP(TO, EAPI_SYNC_RESULT, ex, SID, BID, 0, 1, MMG_SESSION_CONNECT, MMG_DATA_NOTRETAIN, status, rbuf, snum);

    // 受信データのUSBログを出力
    recv_size = rcplib_FIFOBUF_GetData(rbuf, 128, recv_buf);
    rcplib_FORMAT_String(l_str_log_out, "%d byte received:", recv_size);
    rcplib_LOG_Print(l_str_log_out);
    rcplib_FORMAT_String(l_str_log_out, "rcv: %s", recv_buf);
    rcplib_LOG_Print(l_str_log_out);
}

MQTT制御

アプリケーション層でMQTTでの送受信を行うAPIの解説をします。

MQTT送信に関連するAPI

MQTT送信に関連するAPIは以下の通りです。

API 内容
1 MMG_SetConnectParamMQTT() MQTT(S)接続の基本設定を行う
2 MMG_ConnectMQTT() MQTT接続を行う
3 MMG_SendMQTT() MQTT(S)でデータ送信を行う
[MQTTのAPI制御]

MQTTでの接続とデータ送受信を行う為のAPI制御の模式図を以下に示します。

Protocol_mqtt

MQTTでの送受信は前記TCP接続のAPIと共に利用します。APIの利用順番は以下の通り。

Protocol_mqtt


MMG_SetConnectParamMQTT()

MQTT接続の基本設定を行うAPIです。

MMG_SetConnectParamMQTT(int32 timeout, uint8 sync, uint8 ex_result, uint8 mq_conn_id, uint8 mq_clean, uint8 mq_keep_alive, str mq_client_id, str mq_user_name, str mq_user_pass, uint8 mq_will_qos, uint8 mq_will_retain, str mq_will_topic_name, str mq_will_payload) 
    timeout  : API実行時のタイムアウト値
    sync     : APIの同期方式  (EAPI_SYNC_RESULTのみ利用可)
    ex_reslt : APIの実行結果  (0:成功, 1-99:警告, 100-199:エラー)
    mq_conn_id : MQTT接続ID 接続-送受信の管理ID (1-4)
    mq_clean : clean sessionを指定 (0:disable, 1:enable)
    mq_keep_alive : keep alive時間を指定 (秒で指定)
    mq_client_id : クライアントIDの設定 (191文字以下で任意の文字列)
    mq_user_name : ユーザー名の設定 (127文字以下で任意の文字列)
    mq_user_pass : パスワードの設定 (255文字以下で任意の文字列)
    mq_will_qos : Will時のQoSの設定 (0:QoS0, 1:QoS1, 2:QoS2)
    mq_will_retain : Will時のretainの設定(0:retain無効, 1:retain有効)
    mq_will_topic_name : Will時のトピック名 (191文字以下で任意の文字列)
    mq_will_payload : Will時のメッセージ (127文字以下で任意の文字列)

timeout, sync, ex_result
前記APIの説明と同様となります。

mq_conn_id
MQTTセッションの管理IDです。TCPのセッション管理IDの様にMQTT接続もセッションごとに管理IDを持ち一連の接続は同じ管理IDを利用します。後述のPublishやSubscribeの設定APIにもこのIDを利用していきます。

mq_clean
このMQTTセッションの接続をクリーン・セッションとして接続するか、前回のセッション情報を引き継ぐかを選択します。前回のセッションを引き継ぐ場合は、クライアントIDも前回と同じ物を指定する必要があります。

mq_keep_alive
クライアントから一定時間のメッセージが無い場合でも、MQTTセッションを接続し続ける時間をMQTTブローカー側に通知します。この時間内にクライアントからのメッセージ通知が無い場合は、MQTTブローカー側から切断通知が発行されます。

mq_cliant_id
MQTTブローカーに接続する際のクライアントIDを指定します。MQTTブローカーによってはクライアントIDを指定しなくてもブローカー内でユニークなIDを生成してくれるものもありますが、セッション管理の観点から指定をしてください。

mq_user_name, mq_user_pass
パスワードによるクライアント認証を使用しているMQTTブローカーに対して設定するユーザー名とパスワードになります。パスワードによるクライアント認証をしていないMQTTブローカーに対しては空文字指定「""」で問題ありません。

mq_will_qos, mq_will_retain, mq_will_topic_name, mq_will_payload
予期せぬ切断が起こった場合にSubscriberにトピックとメッセージを残す内容をMQTTブローカーに指定します。


MMG_ConnectMQTT()

MQTT(S)接続/切断を行うAPIです。

MMG_ConnectMQTT(int32 timeout, uint8 sync, uint8 ex_result, uint8 send_id, uint8 mq_conn_id, uint8 connect) 
    timeout  : API実行時のタイムアウト値
    sync     : APIの同期方式  (EAPI_SYNC_RESULTのみ利用可)
    ex_reslt : APIの実行結果  (0:成功, 1-99:警告, 100-199:エラー)
    send_id  : 送信ID 接続-送信までの管理ID (1-8)
    mq_conn_id : MQTT接続ID 接続-送受信の管理ID (1-4)
    connect  : ソケット接続/切断 (0:切断, 1:接続)

timeout, sync, ex_result
前記APIの説明と同様となります。

send_id
TCPセッション管理用のIDを指定します。MMG_SetConnectParam()で指定したsend_idです。

mq_conn_id
MQTTセッション管理用のIDを指定します。MMG_SetConnectParamMQTT()で指定したmq_conn_idです。

connect
send_idで指定したTCPセッション上で、mq_conn_idで指定したMQTTセッションの接続/切断を指定します。プロトコル上ソケット接続後にこのAPIでMQTT接続の順番となり、切断時はMQTT切断後にソケット切断の順番が必要です。


MMG_SendMQTT()

MQTT(S)でデータ送信を行うAPIです。

MMG_SendMQTT(int32 timeout, uint8 sync, uint8 ex_result, uint8 send_id, uint8 mq_conn_id, uint8 mq_pub_qos, uint8 mq_pub_retain, str mq_pub_topic_name, uint8 databuf_id, uint16 mov_target, uint8 burst, uint8 session, uint8 retain, uint16 send_success_num)
    timeout : API実行時のタイムアウト値
    sync : APIの同期方式 (EAPI_SYNC_RESULTのみ利用可)
    ex_result : APIの実行結果 (0:成功, 1-99:警告, 100-199:エラー)
    send_id : 送信ID 接続-送信までの管理ID (1-8)
    mq_conn_id : MQTT接続ID 接続-送受信の管理ID (1-4)
    mq_pub_qos : pub時のQoSの設定 (0:QoS0, 1:QoS1, 2:QoS2)
    mq_pub_retain : pub時のretainの設定(0:retain無効, 1:retain有効)
    mq_pub_topic_name : pub時のトピック名 (191文字以下で任意の文字列)
    databuf_id : 送信データのFIFOバッファID (0-31)
    mov_target : カメラ撮影画像送信の対象(0:最新, 1~:遡った数, 0xFFFF: 一番古い画像)
    burst : FIFOバッファから連続送信するデータ数 (1~:指定数分を連続送信, 0xFFFF:FIFOバッファ内の全てのデータを送信)
    session : 送信後の接続状態 (0:切断, 1:接続のまま / 1のみ利用可能)
    retain : 送信失敗時にFIFOにデータ残す(0:消す, 1:残す)
    send_success_num : 送信成功した数の出力先

timeout, sync, ex_result, send_id, mq_conn_id
前記APIの説明と同様となります。
send_idのTCPセッション上のmq_conn_idのMQTTセッション上でデータ送信をします。

mq_pub_qos
Publish時のQoSを指定します。QoSの定義は以下の通り。

QoS0 : メッセージは最高1回配信され、送信先に届く保証はされない。
QoS1 : メッセージは最低1回は配信され、送信先に届く保証がされるが重複の可能性あり。
QoS2 : メッセージは正確に1回配信され、送信先に1回のみ到着が保証される。

一般的にQoS1が多く使われ、QoS2はブローカーによっては対応していない事もあるため、送信するブローカーの仕様に従ってください。

mq_pub_retain
MQTTブローカーに送信したメッセージを保持するかを指定します。Retain指定をしない場合は、Publishした段階でSubscribeしている機器にのみにしかメッセージが送られません。

mq_pub_topic_name
Pubishする先のトピック名を指定します。

databuf_id
トピック名に対するメッセージの内容の入ったFIFOバッファのIDを指定します。MQTTのトピック名はフォルダのイメージとなり、そのフォルダに後述のカメラ画像やJSONデータなどバッファに入っている内容を一つ又は複数送信します。

mov_target~send_success_num
前記、MMG_SendSocket(),MMG_SendHTTP()のパラメータと同様になります。


MQTTでのデータ送受信例

MQTTでデータ送信(Publish)を行うコード例を示します。
キー押下でJSONデータをPublishしてSubscribe登録も行います。
対象のトピックに他からPublishが入ると、受信イベントが呼ばれます。

[コード例]
#incliude "modemMgrTask.h"
#incliude "eventLib.h"
#incliude "bufferLib.h"
   ~
// 内部イベント
#define EVENT_RCV 2
// MQTT用定義
#define TO   30*1000         //timeout 30秒
#define SID  1               //送信管理番号
#define MID 1               //MQTT管理番号
#define MID_SUB 1               //MQTT Subscribe管理番号
#define BID  FIFOBUF_ID_RECIPE_0 //送信用バッファID
#define RID  FIFOBUF_ID_RECIPE_1 //受信用バッファID
#define CLIENT_ID "Client1"              //クライアントID
#define KEEP_ALIVE 100                   // keepalive 100秒
#define TOPIC_NAME "topic/pub"    // トピック名
   ~
// mainループ処理
void main_loop() {
   ~
    // 受信イベント
    else if(event_id == EVENT_RCV) {
        event_rcv(rcplib_TASK_GetArg(0), rcplib_TASK_GetArg(1));
    }  
   ~
func event_init() {
    // FIFOバッファを定義
    rcplib_FIFOBUF_Create(BID,256,1,FIFOBUF_OVERWRITE_DISABLE);
    rcplib_FIFOBUF_Create(RID,256,1,FIFOBUF_OVERWRITE_DISABLE);
}

func any_func() {
    str host[50];     host = "xxx.xxxxxxxxx.jp";
    uint16 port;      port = 1883;
    uint8  ex;        // API実行結果格納用
    uint16 snum;      //送信完了数格納用
    uint8 data_mdm[200];   //送信データ格納用

    // Jsonデータ準備
    makeJsonData(10,20);

    // ソケット接続の基本設定
    MMG_SetConnectParam(TO, EAPI_SYNC_RESULT, ex, SID, host, port, MMG_PROTOCOL_MQTT, 0,0,0,0);
    //MQTT接続の基本設定
    MMG_SetConnectParamMQTT(TO,EAPI_SYNC_RESULT,ex,MID,MMG_MQTT_CLEAN_ENABLE, KEEP_ALIVE, CLIENT_ID,"","",0,0,"","");
    //MQTT接続のSubscribe登録設定
    MMG_SetSubscriveParamMQTT(TO, EAPI_SYNC_RESULT, ex, MID, MID_SUB, 1, TOPIC_NAME, RID, EVENT_RCV);
    //受信設定
    MMG_SetReceiveParam(TO, EAPI_SYNC_RESULT, ex, SID, RID, EVENT_RCV);
    // ソケット接続
    MMG_ConnectSOCKET(TO, EAPI_SYNC_RESULT, ex, SID, MMG_SOCKET_OPEN);
    // MQTT接続
    MMG_ConnectMQTT(TO,EAPI_SYNC_RESULT,ex,SID,MID,MMG_MQTT_OPEN);

    // 送信する文字列を設定 (uint8配列変換後,FIFOBUF書込)
    rcplib_FORMAT_Binary(data_mdm, 0, EAPI_VTYPE_STR, len_json, snd_json);
    rcplib_FIFOBUF_SetData(BID, len_json, data_mdm);

    // MQTTでデータ送信
    MMG_SendMQTT(TO,EAPI_SYNC_RESULT,ex,SID,MID,1,0,TOPIC_NAME,BID,0,1,MMG_SESSION_CONNECT,MMG_DATA_NOTRETAIN,snum);

}

func event_rcv(uint16 response_code, uint8 rcv_datbuf_id) {
    uint8 recv_buf[128];
    str str_log_tmp[128];
    uint32 recv_size;
    uint8 ex_result;

    // 受信データのUSBログを出力
    recv_size = rcplib_FIFOBUF_GetData(RID, 128, recv_buf);
    rcplib_FORMAT_String(str_log_tmp, "%d byte received:", recv_size);
    rcplib_STR_Cat(str_log_tmp, "%s");
    rcplib_FORMAT_String(l_str_log_out, str_log_tmp, recv_buf);
    rcplib_LOG_Print(l_str_log_out);

    // サーバーからの切断応答受信時、ソケットClose処理を実行する
    if((response_code > MMG_WARN_END) && (response_code < MMG_ERR_END)) {
        MMG_ConnectSOCKET(TO, EAPI_SYNC_RESULT, ex_result, SID, MMG_SOCKET_CLOSE);
        rcplib_LOG_Print("*** Socket disconnected ***");
    }
}

func makeJsonData(uint16 dat1, uint16 dat2) {
    // {"data1":xx,"data2":xx,"time":"2022/mm/dd hh:mm:ss"} を作る
    // json変数はローカルでは使用出来ないためグローバル変数を利用
    str time_str[20];
    rcplib_JSON_Clear(snd_json);
    rcplib_JSON_UpdateV(snd_json,"data1",dat1);
    rcplib_JSON_UpdateV(snd_json,"data2",dat2);
    rcplib_FORMAT_Time(time_str,"%Y/%m/%d %H:%M:%S",rcplib_RTC_GetTime(),9);
    rcplib_JSON_UpdateV(snd_json,"time",time_str);
    len_json = rcplib_STR_Len(snd_json);
}

TLS制御

HTTPS、MQTTSでの接続を行うためには、TCPソケット接続時にTLSの情報を設定することでサーバー側と暗号認証をする事が出来ます。

TLSに関連するAPI

TLSに関連するAPIは以下の通りです。

API 内容
1 MMG_SetCertificate() TLS認証情報(CA証明書/クライアント証明書/秘密鍵/PSK)を設定する

[TLSのAPI制御]

TLSでクラウドと認証を行うAPI制御の模式図を以下に示します。

Protocol_tls

TLS認証処理はソケット接続時に行うため、ソケット接続前に認証情報をモデムタスクにインプットする順番で利用します。

Protocol_mqtt


MMG_SetCertificate()

TLS認証情報設定を行うAPIです。

MMG_SetCertificate(uint8 auth_index, uint8 type, uint8 data[], uint16 size)
    auth_index : 認証ID TLS認証-証明書の管理ID(1-5)
    type : 認証鍵設定対象
        MMG_AUTH_KIND_CACERT     // CA証明書
        MMG_AUTH_KIND_CLIENTCERT // クライアント証明書
        MMG_AUTH_KIND_CLIENTKEY  // 秘密鍵
        MMG_AUTH_KIND_PSK_ID     // PSKのIdentity
        MMG_AUTH_KIND_PSK_KEY    // PSKのkey
    data : 入力する認証鍵データ
    size : 入力サイズ (1-5120[Byte])

auth_index
TLS認証情報を管理するIDとなります。ソケット接続時にこのIDを指定することでその接続制御時に同時に認証作業が行われます。認証IDは1-5まで指定出来るため、TLSでセッションはれるサーバーは同時に5つとなります。

type
設定する対象の鍵の種類を指定します。いくつかある証明書や鍵は同時に設定出来ない為、同じ認証IDに対してこのtypeを変更した形でAPIを複数回をコールします。

data
入力する認証鍵の文字列を設定します。CA証明書では、「-----BEGIN CERTIFICATE-----」のHeaderと「-----END CERTIFICATE-----」のFooterで挟まれた文字列で、Header(CRLF) Key(CRLF) Footer(CRLF)の形で一つの文字変数にする必要があります。下記のコード例を参考にしてください。

size
入力する認証鍵の文字列サイズを指定します。


[コード例]

下記では、CA証明書、クライアント証明書、秘密鍵を設定しています。

// <認証系設定値(TLS向けCA証明書)>
#define CA_CERT_HEADER         "-----BEGIN CERTIFICATE-----"
#define CA_CERT_BODY           "MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx......"
#define CA_CERT_FOOTER         "-----END CERTIFICATE-----"
// <認証系設定値(TLS向けCLIENT証明書)>
#define CLIENT_CERT_HEADER     "-----BEGIN CERTIFICATE-----"
#define CLIENT_CERT_BODY       "MIIDWTCCAkGgAwIBAgIUFO968VEmB/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
#define CLIENT_CERT_FOOTER     "-----END CERTIFICATE-----"
// <認証系設定値(TLS向け秘密鍵)>
#define CLIENT_KEY_HEADER      "-----BEGIN RSA PRIVATE KEY-----"
#define CLIENT_KEY_BODY        "MIIEpQIBAAKCAQEAsAhlihr7LAav5n/Csxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
#define CLIENT_KEY_FOOTER      "-----END RSA PRIVATE KEY-----"

#define AUTH_INFO_MAX 5120   // 認証情報サイズ
#define AUTH_ID 1            // 認証番号

// <TLS認証関連のグローバル変数>
uint8 l_bin_auth_info[AUTH_INFO_MAX]; // TLS認証情報のuint8配列変数
uint16 l_auth_info_ofs; // TLS認証情報作成時のオフセット管理変数

// 初期化
func event_init() {
    // 認証情報関連の初期設定
    auth_init(AID);
    return(0);
}

// 認証情報関連の設定
func auth_init() {
    // ルート証明書設定
    rcpfunc_auth_init();
    rcpfunc_auth_convert(CA_CERT_HEADER);
    rcpfunc_auth_convert(CA_CERT_BODY);
    rcpfunc_auth_convert(CA_CERT_FOOTER);
    MMG_SetCertificate(AUTH_ID, MMG_AUTH_KIND_CACERT, l_bin_auth_info, l_auth_info_ofs - 1);
    // クライアント証明書設定
    rcpfunc_auth_init();
    rcpfunc_auth_convert(CLIENT_CERT_HEADER);
    rcpfunc_auth_convert(CLIENT_CERT_BODY);
    rcpfunc_auth_convert(CLIENT_CERT_FOOTER);
    MMG_SetCertificate(AUTH_ID, MMG_AUTH_KIND_CLIENTCERT, l_bin_auth_info, l_auth_info_ofs - 1);
    // 秘密鍵設定
    rcpfunc_auth_init();
    rcpfunc_auth_convert(CLIENT_KEY_HEADER);
    rcpfunc_auth_convert(CLIENT_KEY_BODY);
    rcpfunc_auth_convert(CLIENT_KEY_FOOTER);
    MMG_SetCertificate(AUTH_ID, MMG_AUTH_KIND_CLIENTKEY, l_bin_auth_info, l_auth_info_ofs - 1);
    return(0);
}

// 認証情報関連のデータの変換(CRLFを付けて文字列を接続)
func rcpfunc_auth_convert(str add_auth_content[AUTH_INFO_MAX]) {
    rcplib_FORMAT_Binary(l_bin_auth_info, l_auth_info_ofs, EAPI_VTYPE_STR, rcplib_STR_Len(add_auth_content), add_auth_content);
    l_bin_auth_info[l_auth_info_ofs + rcplib_STR_Len(add_auth_content)]  = 13;
    l_bin_auth_info[l_auth_info_ofs + rcplib_STR_Len(add_auth_content) + 1]  = 10;
    l_auth_info_ofs = l_auth_info_ofs + rcplib_STR_Len(add_auth_content);
    l_auth_info_ofs = l_auth_info_ofs + 2;
    return(0);
}
// 認証情報関連の初期化
func rcpfunc_auth_init() {
    uint16 bin_count;
    l_auth_info_ofs = 0;
    bin_count = 0;
    while(bin_count < AUTH_INFO_MAX) {
        l_bin_auth_info[bin_count] = 0;
        bin_count = bin_count + 1;
    }
}

証明書は「-----BEGIN CERTIFICATE-----」から始まり「-----END CERTIFICATE-----」で終了する文字列となります。ここでは#defineでそれぞれ持っていますが、実際に設定する文字列としては、CRLFの区切り文字の入った以下のような一連の文字列として設定が必要なため、rcpfunc_auth_convert()という関数にて変換をしています。

"-----BEGIN CERTIFICATE-----(CRLF)
MIIDQTCCAimgAwIBAgIvxxxxxxxxxxxxxx......(CRLF)
-----END CERTIFICATE-----(CRLF)"

※上記(CRLF)はASCIIコードの13番と10番です。

モデム制御サブアプリの概要

本製品では、モデム制御サブアプリ(sapp_modem.krs, sappModemAPI.hなど)を利用して通信プロトコル制御を行う手段も用意しております。

ここまでに説明した「MMG_」から始まる通信プロトコルAPIは全て同期処理を伴うAPIであるため直感的に利用しやすいメリットがある反面、例えば送信処理に時間を要する場合においてはAPIを実行しているユーザタスクでは送信処理が完了するまでの間、他の処理を実行することができないというデメリットも持ち合わせます。

モデム制御サブアプリを利用することで、データの送受信やソケット接続制御など通信プロトコルに関わる制御を全て独立して実行でき、ユーザタスクでは並列に別の処理が実行できます。
モデム制御サブアプリを利用する場合も、UDP/TCP/HTTP/MQTT/HTTPS/MQTTSを利用できます。ここではモデム制御サブアプリを利用して通信プロトコルをレシピ言語から制御する方法を紹介します。

モデム制御サブアプリ機能の特徴

モデム制御サブアプリ機能の特徴を以下に示します。

モデム制御サブアプリに関連するAPI

モデム制御サブアプリに関連するAPIは以下の通りです。

API 内容
1 MSA_SetLteSendId() 利用する送信ID(1~8)と「送信結果」や「データ受信」のコールバック登録を設定する
2 MSA_SendCameraImage() USBカメラの画像を送信する
3 MSA_SetAlwaysConnect() 常時セッション接続する送信ID(1~8)を設定/解除する ※UDP/TCP/MQTT(S)でのデータ受信待受け用途
[モデム制御サブアプリの事前のヘッダファイル設定とAPI制御]

モデム制御サブアプリで通信プロトコル制御を行うAPI制御の模式図を以下に示します。

Protocol_tls

ここではHTTPデータ送信の一例を示しましたが、TCP/UDP/HTTP(S)/MQTT(S)全てのプロトコルにおいて同様の利用方法で通信プロトコルの制御が可能です。
利用する通信プロトコルによって、編集するヘッダファイルが異なり、そのファイル名一覧は下表の通りとなります。

プロトコル ファイル名
1 UDP msa_config_udp.h
2 TCP msa_config_tcp.h
3 HTTP msa_config_http.h
4 MQTT msa_config_mqtt.h
5 HTTPS(CA証明のみ) msa_config_https_without_cli_cert.h
6 HTTPS(CA証明+クライアント証明) msa_config_https_with_cli_cert.h
7 MQTTS(CA証明のみ) msa_config_mqtts_without_cli_cert.h
8 MQTTS(CA証明+クライアント証明) msa_config_mqtts_with_cli_cert.h
9 HTTPS(CA証明のみ)利用のS3互換のストレージサービス msa_config_s3_storage_https.h
10 HTTPS(CA証明のみ)利用のAzure Blob Storage msa_config_azure_storage_rest_https.h
11 HTTPS(CA証明のみ)利用のAzure IoT Hub msa_config_azure_iot_sas_token_https.h
12 MQTTS(CA証明のみ)利用のAzure IoT Hub msa_config_azure_iot_sas_token_mqtts.h
13 HTTPS(CA証明のみ)利用のGCP IoT Core msa_config_gcp_iot_es256_https.h
14 MQTTS(CA証明のみ)利用のGCP IoT Core msa_config_gcp_iot_es256_mqtts.h

モデム制御サブアプリ内の設定値

モデム制御サブアプリには#defineによるいくつかの設定値を用意しています。
その#define名と設定値の用途は下表を参照ください。
ユースケースに合わせ任意の設定に変更してご利用いただけます。

#define名 設定値の用途 初期値 単位
1 TIMER_LTE_SEND_BUF_MONITOR_CYCLE 送信用FIFOバッファの監視周期 1 * 1000 [msec]
2 MSA_LTE_SEND_RETRY_MAX 送信失敗時の送信試行回数 3 [回]
3 MSA_LTE_SEND_RESUME_TIME 送信試行回数まで送信失敗し諦めた後、送信再開するまでの期間 5 * 60 [sec]
4 LTE_RECV_DATA_MAX 1回あたりの受信データ最大量(10[KB]以内で選択可能) 1024 [Byte]

MSA_SetLteSendId()

ソケット接続の基本設定を行うAPIです。

MSA_SetLteSendId(uint8 send_id, uint8 recv_mode, uint8 send_res_event_id, uint8 recv_data_event_id)
    send_id  : 送信ID(接続や送受信の管理ID) <1~8 (MSA_SEND_ID_01_LTE~MSA_SEND_ID_08_LTE)>
    recv_mode : 「送信結果イベント」と「データ受信イベント」のコールバック登録有効/無効設定 
    send_res_event_id : 送信結果のイベントを受けるイベントID <1~254, 使用しない場合は不問>
    recv_data_event_id : データ受信時にイベントを受けるイベントID <1~254, 使用しない場合は不問>

send_id
前記解説のセッションごとの管理IDとなります。ホスト名やポート番号等このIDごとに管理されますので、セッションごとにこのAPIをそれぞれコールします。モデム制御サブアプリを利用する際においてもIDを意識して利用する必要があります。

recv_mode
「送信結果イベント」と「データ受信イベント」のコールバック登録の設定値となります。
以下の設定値でそれぞれのコールバック設定を行うことができます。

send_res_event_id, recv_data_event_id
「送信結果イベント」と「データ受信イベント」のコールバックイベントIDを指定します。ユーザタスク側で該当のイベント処理を記載する必要があります。(詳細はソフトウェアリファレンスを参照)

※送信だけのアプリケーションで「送信結果」や「受信データ」を想定する必要の無い場合は、recv_modeにMSA_SID_API_RECEIVE_DISABLE:コールバック登録なしの設定をし、本イベント処理を使わずにデータの送信を行うこともできます。


MSA_SendCameraImage()

USBカメラの画像を送信するAPIです。

MSA_SendCameraImage(uint8 send_id, uint16 mov_target)
    send_id  : 送信ID(接続や送受信の管理ID) <1~8 (MSA_SEND_ID_01_LTE~MSA_SEND_ID_08_LTE)>
    mov_target : カメラの撮影画像送信時の対象指定 <0:最新の画像, 1~:最新から指定毎数分さかのぼった画像, 0xFFFF:一番古い画像>

本APIを利用する場合は、ユーザタスクの初期化処理内で「送信IDの登録APIであるMSA_SetLteSendId()」を予め実行しその登録した送信IDを本APIのsend_idに指定してカメラバッファからの送信命令を行います。
また、「送信IDの登録APIであるMSA_SetLteSendId()」の実行では必ず送信結果のコールバックイベントの登録を行ってください。
本APIによるUSBカメラの画像送信命令要求は必ず同時に1つのみとしてください。
⇒一度の本API実行に対して、送信結果のコールバックイベントを待ってから次のAPI実行を行うようにしてください。

send_id
前記解説のセッションごとの管理IDとなります。ホスト名やポート番号等このIDごとに管理されますので、セッションごとにこのAPIをそれぞれコールします。モデム制御サブアプリを利用する際においてもIDを意識して利用する必要があります。

mov_target
送信対象とするカメラ画像の(0:最新/1以上:対象枚数/0xFFFF:最古)を指定することで、該当の画像が送信されます。


MSA_SetAlwaysConnect()

常時セッション接続する送信ID(1~8)を設定/解除するAPIです。

MSA_SetAlwaysConnect(uint8 event_id, uint8 send_id, uint8 regist)
    event_id  : コールバックを受ける任意のイベントID <1~254>
    send_id  : 送信ID(接続や送受信の管理ID) <1~8 (MSA_SEND_ID_01_LTE~MSA_SEND_ID_08_LTE)>
    regist : 常時セッション接続の登録実行または登録解除設定 <MSA_ALWAYS_CONN_UNREGIST:登録解除, MSA_ALWAYS_CONN_REGIST:登録実行>

本APIを利用する場合は、ユーザタスクの初期化処理内で「送信IDの登録APIであるMSA_SetLteSendId()」を予め実行し、その登録した送信IDを本APIのsend_idに指定して常時セッション接続の登録/解除を行います。
また、本APIによる常時セッション接続の登録/解除要求は必ず同時に1つのみとしてください。
⇒一度の本API実行に対して、本APIのコールバックイベントを待ってから次のAPI実行を行うようにしてください。

event_id
本APIを実行し処理が完了した通知を受け取るイベントIDとなります。

send_id
前記解説のセッションごとの管理IDとなります。ホスト名やポート番号等このIDごとに管理されますので、セッションごとにこのAPIをそれぞれコールします。モデム制御サブアプリを利用する際においてもIDを意識して利用する必要があります。

regist
MSA_ALWAYS_CONN_REGISTを指定することで常時セッション接続の登録実行を、MSA_ALWAYS_CONN_UNREGISTを指定することで登録解除を要求できます。


レシピOTA

概要

本製品に書込まれているレシピ実行ファイルを、LTE通信により遠隔から変更する仕組み(Over The Air)をレシピで作成する方法を紹介します。
レシピをOver The Air(OTA)で更新することをROTAと示します。

ROTA_front

※ROTA機能はベータ版リリースとなります。

ROTAの特徴

ROTA機能の特徴を以下に示します。

ROTA機能

ROTA機能を実行するには、本製品に書込まれたレシピ実行ファイル内にROTAの仕組みが含まれていること、変更するレシピ実行ファイル、及びレシピ実行ファイルを配信するためのROTAサーバが必要です。

ROTAサーバの仕様

本製品とROTAサーバとはHTTP GETコマンドで通信を行います。そのため、HTTP GETコマンドに対応したWebサーバである必要があります。
HTTP GETコマンドによる通信はHTTPおよびHTTPSでの通信に対応していますが、セキュリティリスクの観点から閉域網の通信で無い場合は、HTTPSによる暗号化通信を行うことを推奨します。

ROTA_folder

[配置するファイルについて]

ROTAサーバに配置するファイル(rcp_info.bin, rcp_prog.bin)は、レシピ言語をビルドして作成された、「rcp_compress.zip」を展開した中身となります。

ROTA_zipfile

この2つのファイルをクラウドストレージに保存します。

[ROTA抑制について]

ROTAの実行を抑制したい場合は、ROTA用のファイル(rcp_info.bin, rcp_prog.bin)を削除することに加えて、「NO_UPDATE」というファイルを置いておくことでROTAの実行を抑制することが出来ます。

ROTA_noupdate


ROTA制御のAPI

ROTAを実行するAPIは以下となります。

API 内容
1 rcplib_KCS_RcpOTA() ROTAを実行する

rcplib_KCS_RcpOTA ()

本APIの実⾏により、ROTAが開始されます。
レシピ実行ファイルのダウンロードが完了すると⾃動的に本製品が再起動し、レシピ実行ファイルの更新が実行されます。ダウンロードしたレシピ実行ファイルのタイムスタンプを確認し、現在動作させているものと同じであれば更新は実⾏されません。

rcplib_KCS_RcpOTA(int32 timeout, uint8 sync, uint8 ex_result, str hosturl, uint8 rcv_event_id, str http_header, uint8 protocol, uint8 sni, uint8 auth_mode, uint8 auth_index) 
      timeout      :タイムアウト値[msec] <0:EAPI_TIMEOUT_ZERO>
      sync         :同期実行 <0:EAPI_SYNC_RESULT>
      ex_result    :API実行結果 <戻り値>
      hosturl      :接続先サーバのURL <最大127文字>
      rcv_event_id :コールバックを受ける任意のイベントID <1~254>
      http_header  :HTTPヘッダ指定 <非サポート(NULL)>
      protocol     :利用する通信プロトコル <MMG_PROTOCOL_HTTPS(HTTP/HTTPS)のみ>
      sni          :TLS通信利用時のSNI設定 <MMG_SNI_ENABLE(有効)のみ>
      auth_mode    :TLS通信利用時のmode設定 <MMG_AUTH_SRV(サーバー認証)のみ>
      auth_index   :認証ID(TLS接続や証明書設定の管理ID) <非サポート("1")>

timeout, sync
APIのタイムアウト設定と実行方法の指定ですが、ここでは両方とも「0」を指定します。

ex_result
ex_resultの戻り値は以下の定義です。
0:成功 (KCS_RCPOTA_RESULT_NO_ERROR)
1:失敗 - 無効なURL (KCS_RCPOTA_RESULT_ERR_INVALID_URL)
2:失敗 - 開始不可 (KCS_RCPOTA_RESULT_ERR_CANNOT_START)

hosturl
接続先のサーバのURL文字列を指定します。HTTP GETでレシピ実行ファイルを取得する際の該当ファイルが保存されているフォルダまでの指定となります。

ex) "http://xxxx.xxxx.com/KC4C-recipe/0001"

※機器ごとに更新するレシピ実行ファイルを変えたい場合などは、URLに特殊なリプレイス文字(*rp_imei*)を付与することで自動的に機器のユニーク番号であるIMEIに置き換える機能を利用する事ができます。

ex) "http://xxxx.xxxx.com/KC4C-recipe/*rp_imei*******/"
             ↓
    "http://xxxx.xxxx.com/KC4C-recipe/012345678901234/"

このAPIではHTTP/HTTPSの区別はurlの先頭部分で行います。このため、http:// 又は、 https:// の指定を意識する必要があります。

rcv_event_id
ROTAのAPIを実行した結果、エラーなど発生した場合の通知を受け取るイベントIDとなります。正しく書き換えが実行された場合は機器がリセットしますので、このイベントは発行されません。
イベントが通知された場合、該当イベントの第一引数(rcplib_TASK_GetArg(0)で取得)には以下の定義のコードが入ります。

resp_code   :ROTA開始後に更新完了しない詳細理由 
    0:成功 - タイムスタンプが同一 (KCS_RCPOTA_RESP_OK)
    1:失敗 - 通信エラー/ファイル無し、など (KCS_RCPOTA_RESP_ERR)
    2:終了 - NO_UPDATEファイル有 (KCS_RCPOTA_RESP_NO_UPDATE)
    3:中断 - FOTA中の中断 (KCS_RCPOTA_RESP_SUSPEND)
    4:再開 - FOTA後の再開 (KCS_RCPOTA_RESP_RESUME)

http_header (ベータ版非対応)
非サポートとなるためNULL指定「""」を入力してください。

protocol (ベータ版固定)
プロトコルはHTTP/HTTPSが利用できますがここは「MMG_PROTOCOL_HTTPS」固定で指定してください。HTTP/HTTPSの指定はurlの「http://, https://」で区別します。

sni (ベータ版固定)
TLS通信利用時のSNI設定ですが、MMG_SNI_ENABLEのみ指定可能です。

auth_mode (ベータ版固定)
TLS通信利用時の認証方式はサーバ認証のみ対応です。MMG_AUTH_SRVを指定。

auth_index (ベータ版固定)
TLS接続にかかわる証明書設定してある認証IDを指定しますが、「1」のみ指定可能です。証明書はキッティングツールで書込む「FOTA」用の証明書を利用します。


[コード例]
#incliude "modemMgrTask.h"
#incliude "eventLib.h"
#incliude "bufferLib.h"
   ~
// 内部非同期イベント
#define UNSYNC_EVENT_INIT 0
#define UNSYNC_EVENT_RECIPE_OTA_RECEIVE 1

// 接続先サーバ設定
#define USER_SETTING_HOST_NAME     "http://xxxx.xxxx.com"
#define USER_SETTING_HOST_SUB_URL  "/KC4C_test/*rp_imei*******/"
   ~
// mainループ処理
void main_loop() {
   ~
    // レシピOTAの応答イベント受信時の処理
    else if(event_id == UNSYNC_EVENT_RECIPE_OTA_RECEIVE) {
        event_recipe_ota_event_receive(rcplib_TASK_GetArg(0));
    }
   ~

// ROTA実行関数
func any_func() {
    rcplib_LOG_Print("uapp_1 recipe_ota_exe_proc start");

    str ex_result_msg[32];
    uint8 ex_result; ex_result = 0;
    uint8 ret; ret = 0;

    // 接続先URLの文字列 "http://xxxx.xxxx.com/KC4C_test/*rp_imei*******/" を生成
    str hosturl[MMG_URL_MAX];
    hosturl = USER_SETTING_HOST_NAME;
    rcplib_STR_Cat(hosturl, USER_SETTING_HOST_SUB_URL);

    // レシピOTAを実行
    ret = rcplib_KCS_RcpOTA(
                 EAPI_TIMEOUT_ZERO,
                 EAPI_UNSYNC_NORESULT,
                 ex_result,
                 hosturl,
                 UNSYNC_EVENT_RECIPE_OTA_RECEIVE,
                 "",
                 MMG_PROTOCOL_HTTPS,
                 MMG_SNI_ENABLE,
                 MMG_AUTH_SRV,
                 1
                 );

    if(ret != 0) {
        rcplib_LOG_Print("rcplib_KCS_RcpOTA execute Error!!");
        rcplib_FORMAT_String(ex_result_msg, "ex_result:%d", ex_result);
        rcplib_LOG_Print(ex_result_msg);

        if(ex_result == KCS_RCPOTA_RESULT_NO_ERROR) {
            // ret!=0の時、ex_result=0になることはなく、ここに入ることはありません。
            rcplib_LOG_Print("KCS_RCPOTA_RESULT_NO_ERROR");
        } else if(ex_result == KCS_RCPOTA_RESULT_ERR_INVALID_URL) {
            // 接続先サーバのURLの文字列の長さが128文字以上になっている場合のエラー
            rcplib_LOG_Print("KCS_RCPOTA_RESULT_ERR_INVALID_URL");
        } else if(ex_result == KCS_RCPOTA_RESULT_ERR_CANNOT_START) {
            // レシピOTAの実行要求が既に入って、実行中である(多重実行された時)、
            // または端末のFirmwareのOTAを実行中であり、レシピOTAの実行が出来ないことを示すエラー
            rcplib_LOG_Print("KCS_RCPOTA_RESULT_ERR_CANNOT_START");
        }
    } else {
        rcplib_LOG_Print("rcplib_KCS_RcpOTA execute OK!!");
    }

    return(0); 
}

// レシピOTAの応答イベント
// ※レシピバイナリの更新処理を実行した場合は、このイベントが通知されることなく更新のために端末が再起動されます。
func event_recipe_ota_event_receive(uint16 resp_code) {
    str resp_msg[32];
    rcplib_LOG_Print("***** RECIPE OTA response event received *****");
    rcplib_FORMAT_String(resp_msg, "resp_code:%d", resp_code);
    rcplib_LOG_Print(resp_msg);

    if(resp_code == KCS_RCPOTA_RESP_OK) {
        // 接続先サーバ上からダウンロードしたレシピバイナリのタイムスタンプを確認した結果、現在動作させているものと同じであり、
        // 更新処理が必要ないため実行しなかった場合に通知されるイベント
        rcplib_LOG_Print("KCS_RCPOTA_RESP_OK");
    } else if(resp_code == KCS_RCPOTA_RESP_ERR) {
        // 何らかの理由によりレシピOTAの実行に失敗したことを通知されるイベント(通信に失敗した/接続先サーバにファイルが見つからない等)
        rcplib_LOG_Print("KCS_RCPOTA_RESP_ERR");
    } else if(resp_code == KCS_RCPOTA_RESP_NO_UPDATE) {
        // 接続先サーバ上に"NO_UPDATE"ファイルが配置されており、更新処理を実行しなかったことを通知するイベント
        rcplib_LOG_Print("KCS_RCPOTA_RESP_NO_UPDATE");
    } else if(resp_code == KCS_RCPOTA_RESP_SUSPEND) {
        // 端末のFirmwareのOTAが実行され、レシピOTAの処理が中断したことを通知するイベント
        rcplib_LOG_Print("KCS_RCPOTA_RESP_SUSPEND");
    } else if(resp_code == KCS_RCPOTA_RESP_RESUME) {
        // 端末のFirmwareのOTAの実行が終了し、レシピOTAの処理が再開したことを通知するイベント
        rcplib_LOG_Print("KCS_RCPOTA_RESP_RESUME");
    }

    return(0);
}

TLSの利用

HTTPSでクラウドストレージにアクセスを行うためには、サーバー証明書(CA証明書)を設定する必要があります。ROTAで利用するサーバー証明書はキッティングツールで書き込むFOTAのサーバ証明書を利用します。ここでは、キッティングツールでの書き込み方を示します。(本リリース版ではFOTAの証明書とROTAの証明書を分ける予定です。)

[証明書の書込み]

証明書の書込みは、キッティングツールから行います。
以下の手順でサーバー証明書の書込みをしてください。

ROTA_kitting1

ROTA_kitting2

設定した証明書は、サンプルコード上の接続先のサーバー設定にて「https://」を指定することで、ここの証明書を使って指定のサーバに接続に行きます。