システム関連

制御処理

概要

レシピ言語ではデバイス制御や通信機能の他に、以下の機能APIが提供されています。
ここではこれら機能について説明します。

ログ機能

ログ機能はプログラムの実行過程を出力して状態を確認する機能、及び異常などを記録しておき、後にその内容を確認するための機能です。

ログ機能の特徴

レシピ言語で提供しているログ機能は以下の2種類あります。(即時確認したいログと、後で確認するログとで使い分けが可能です。)

※ログ1件の最大文字数は128文字です。
 フラッシュログは8028件まで保存可能です。


ログの出力設定
デバッグ用のログは初期設定では出力がOFFとなっております。USB Type-Cのシリアル通信に出力するには、端末の「USBポートからログの出力」設定を有効にする必要があります。キッティングツールの以下のメニューから設定を変更できます。

LOG_Setting

ログのWindowsでの受信方法
USB Type-CケーブルをWindowsPCに接続すると、Windows上からシリアル通信ポートはVirtualCOMポートとして認識されています。ターミナルエミュレーター等で該当のCOMポートに接続をして、以下の設定を行うことでデバッグ用のログを取得できます。


フラッシュログの取り出し方
フラッシュログは以下の手順でキッティングツールから読み出すことが出来ます。

LOG_Flash


ログ出力の制御API

ログ出力を制御するAPIは以下があります。

API 内容
1 rcplib_SYS_LogPrint() ログ出力を実行するAPI
2 rcplib_LOG_Print() デバッグ用のログにログ[D]として出力する
3 rcplib_LOG_Low() デバッグ用のログにログ[L]として出力する
4 rcplib_LOG_Flash() フラッシュログにログ[M]として出力する

rcplib_SYS_LogPrint()

ログ出力(デバッグ用、フラッシュ)を実行するAPIです。

rcplib_SYS_LogPrint(uint8 level, any data) 
    level : 出力するログレベルを指定
        ENGINE_LOG_D : 0 デバッグ用のログ[D]を出力
        ENGINE_LOG_L : 1 デバッグ用のログ[L]を出力
        ENGINE_LOG_M : 2 フラッシュログ[M]を出力
    data : (int||uint||float||str) の型が可能

他のAPIは使いやすいようにdefineにて以下のように定義されております。

rcplib_LOG_Print() / rcplib_LOG_Low / rcplib_LOG_Flash()

#define rcplib_LOG_Print(a) rcplib_SYS_LogPrint(ENGINE_LOG_D,a)
#define rcplib_LOG_Low(a) rcplib_SYS_LogPrint(ENGINE_LOG_L,a)
#define rcplib_LOG_Flash(a) rcplib_SYS_LogPrint(ENGINE_LOG_M,a)

ログの出力のされ方
ログは以下のように出力されます。

[UA1][D]uapp_1 initialize done

最初の[]はタスクを示します。

続く[]はログのレベルを示します。

[D][L][M]は、目的に応じて使い分けてください。


フラッシュログの出力のされ方

[2022/09/01 00:26:20] [UA1][M]uapp_1 initialize done (Flash Log)

ログの先頭にはUTC時刻が記録されます。UTC時刻ですので日本標準時の9時間前となります。
ただし、システム起動直後などでは以下のような時刻表示になります。

[2000/01/01 00:00:03] [UA1][M]uapp_1 initialize done (Flash Log)

システムが出力するFATALログについて
ユーザが出力記述したログ以外に、システムが[FATAL]ログや[H]ログを出力する場合があります。

[FATAL]ログの一例として、FIFOバッファを作成するAPIを実行する際にメモリの空き容量が不足している場合は以下のように[FATAL]ログが出力されます。

[UA1][FATAL]<FATAL>FIFOBUF_Create NOT_ENOUGH_SPACE bufferID=0

このケースにおいては[FATAL]ログを出力し、その後システムは再起動します。

[H]ログの一例として、ディスプレイに出力する文字上限の超えや、タスク間通信における最大データサイズも超えていた場合は以下のように[H]ログが出力されます。

[OLC][H]warn>engine_var.c:884 Characters are too long [01234567890123456] size[16]
[UA1][H]Insufficient argument buffer arg_r [34/26]

このケースにおいては、システム側で上限以上を切り捨て、適切な長さに短くし実行するします。致命的な状態では無いためシステムの再起動は行わず処理は継続します。


[コード例]

デバッグ用のログとフラッシュログの出力をします。

#incliude "logAPI.h"
   ~
func any_func() {

    // USBデバッグ機能有効化時のみ、USB(type-C) VirtualCOMポートにログ[D]を出力
    rcplib_LOG_Print("uapp_1 initialize done");

    // USBデバッグ機能設定に関わらず、Flash(不揮発領域への)ログ[M]を出力
    rcplib_LOG_Flash("uapp_1 initialize done (Flash Log)");

    return(0);
}

上記サンプルコードでは以下のようなログ出力になります。

デバック用のログ

[UA1][D]uapp_1 initialize done
[UA1][M]uapp_1 initialize done (Flash Log)

フラッシュログ

[2022/09/01 00:26:20] [UA1][M]uapp_1 initialize done (Flash Log)

不揮発メモリー領域

本端末には不揮発メモリーを搭載しており、機器の設定値や認証鍵などの保存に利用が可能です。不揮発メモリー領域はユーザ領域とシステム領域に分かれており、ユーザー領域の読み書きをAPIから操作することが出来ます。

不揮発メモリーの特徴

不揮発メモリー機能の特徴を以下に示します。

※不揮発メモリーは書換え保証回数が5000回となります。
 頻繁な書換えの用途には向きませんのでご注意願います。


不揮発メモリーの書換え
不揮発メモリーはNANDフラッシュメモリーを使用しているため、ハードウェア制限としての書換え保証回数があります。NANDフラッシュメモリーはまとまったブロック単位での書換えを行うため、小さなデータを頻繁に書換えるとその書き込んだエリアのブロック単位での劣化が起こる事から、本機能ではRAM空間上にミラー領域を設けて、不揮発メモリーにはまとまった単位で書込みを行う仕組みを取り入れてます。

不揮発

※Syncを実施せずに電源ケーブル抜去の電源断が行われた場合は、ミラー領域のデータは不揮発メモリーに書込みは行わません。


不揮発メモリーの制御API

不揮発メモリーを制御するAPIは以下があります。

API 内容
1 rcplib_NV_Read() 不揮発メモリーのデータを読み出す
2 rcplib_NV_Write() ミラー領域の不揮発メモリーに任意のデータを書き込む
3 rcplib_NV_Sync() ミラー領域の内容を不揮発メモリーに反映させる

rcplib_NV_Read()

不揮発メモリーのデータを読み出すAPIです。

rcplib_NV_Read(uint32 nv_id, uint32 index, uint8 data[]) 
    nv_id : 不揮発アイテムのID
        NV_RW_USER_DATA:ユーザ用不揮発領域
    index : NVのindex(indexが1つのみのアイテムは0を指定)
        ユーザ用不揮発領域はindexが1つのみの為、0を指定。
    data : データの出力先変数

nv_id
不揮発メモリー内は分類毎にアイテムIDをつけて管理されています。読み書きの際にはアドレスを指定するのではなく、このアイテムIDを指定して読み書きを行います。アイテムIDはflashLib.hに定義されており、Bluetooth®の設定やクラウド接続の秘密鍵の領域等が準備されています。(内容は各機能部での説明をご確認ください。)

index
不揮発メモリー内で分類されているアイテムは複数ブロックで構成されているものもあり、そのブロック番号を指定する場合にindexを設定します。ユーザー領域のNV_RW_USER_DATAは4096[byte]の一つのブロックで構成している為、0を指定します。

data
dataは読み込んだデータを入力出来る4096[byte]の領域を確保したuint8の配列を指定します。


rcplib_NV_Write()

不揮発メモリーにデータを書き込むAPIです。

rcplib_NV_Write(uint32 nv_id, uint32 index, uint8 data[], uint32 size) 
    nv_id : 不揮発アイテムのID
        NV_RW_USER_DATA:ユーザ用不揮発領域
    index : NVのindex(indexが1つのみのアイテムは0を指定)
        ユーザ用不揮発領域はindexが1つのみの為、0を指定。
    data : 書き込むデータ (uint8[], int, uint, float, strの変数型が可能)
    size : 書き込むデータの長さ

nv_id, index
rcplib_NV_Read()と同様です。

data, size
書き込むデータは指定された型(uint8[], int, uint, float, str)とsizeで書込みを行います。該当アイテムIDの先頭からの書込みとなる事から、NV_RW_USER_DATAの大きな領域の場合はrcplib_NV_Read()で読み込んだ4096[byte]の配列変数を編集して、その配列を書き込む運用になります。


rcplib_NV_Sync()

書き込んだミラー領域のデータ全てを不揮発メモリーに反映させるAPIです。

rcplib_NV_Sync()
    引数無し

このAPIが発行されると、不揮発メモリーに書込みを行います。前記の様に書込み回数に制約がありますので、考慮した上での制御が必要となります。


[コード例]

不揮発メモリのユーザ領域に読み書きするAPI使用例を示します。

#incliude "flashLib.h"
   ~
//////////////////////////////////////////////////////////////////
// internal variable //
//////////////////////////////////////////////////////////////////
uint8 l_user_nv[4096]; // ユーザーNVアイテム
   ~
func any_func1() {
   ~
    rcplib_NV_Read(NV_RW_USER_DATA, 0, l_user_nv);
    return(0);
}

func any_func2() {
{
   ~
    rcplib_NV_Write(NV_RW_USER_DATA, 0, l_user_nv, 4096);
    rcplib_NV_Sync();
    return(0);
}

JSON変数とLIST変数

クラウドへ送信するデータフォーマットを生成しやすいように、JSONとLIST形式を扱う変数を準備しています。

JSON変数、LIST変数の特徴

JSON変数とLIST変数の特徴は以下の通りです。

※ローカル変数定義が出来ない変数です。グローバル変数として定義が必要です。
※options設定(JSONtmpSizeとLISTtmpSize)でワークメモリの確保が必要です。


JSON変数のAPI

JSON変数を扱うAPIは以下の通り。

API 内容
1 rcplib_JSON_Clear() json変数をクリアする
2 rcplib_JSON_Delete() 指定したkeyを消去する
3 rcplib_JSON_UpdateV() 指定したkeyのvalueのデータを書き換える
4 rcplib_JSON_UpdateK() 指定したkeyのvalueのデータを文字列で書き換える
5 rcplib_JSON_SearchK() 指定したkeyの存在を確認する
6 rcplib_JSON_SearchN() 指定したkeyのvalueを数値形式で取得する
7 rcplib_JSON_SearchS() 指定したkeyのvalueを文字列形式で取得する
8 rcplib_JSON_IsEmpty() json変数のデータの有無を確認する
9 rcplib_JSON_GetEntry() json変数のデータ数を取得する

※各APIの内容はAPI仕様書を確認ください。


[コード例]
   ~
//////////////////////////////////////////////////////////////////
// internal variable //
//////////////////////////////////////////////////////////////////
json JsonA[500];
json JsonB[500];
   ~
//////////////////////////////////////////////////////////////////
// event driven task process //
//////////////////////////////////////////////////////////////////
options {
   ~
    JSONtmpSize = 1000;
   ~
}
   ~
func any_func() {
   ~
    rcplib_JSON_Clear(JsonA); // JsonAを初期化
    rcplib_JSON_Clear(JsonB); // JsonBを初期化
    rcplib_JSON_UpdateV(JsonB, "endpoint", "http://xxx.yyy.com/aaa"); // JsonBにデータ追加
    rcplib_JSON_UpdateV(JsonB, "updatetime", "20220901"); // JsonBにデータ追加
    rcplib_JSON_UpdateV(JsonA, "apache_conf", JsonB); // JsonAにデータ追加
   ~
    return(0);
}
   ~

この例の場合、JsonAには以下のような結果が得られます。

{
  "apache_conf" : {
    "endpoint" : "http://xxx.yyy.com/aaa", 
    "updatetime" : "20220901"
  }
}

LIST変数のAPI

LIST変数を扱うAPIは以下の通り。

API 内容
1 rcplib_LIST_Clear() list変数をクリアする
2 rcplib_LIST_Delete() 指定したindexデータを消去する
3 rcplib_LIST_Update() 指定したindexのvalueのデータを書き換える
4 rcplib_LIST_SearchN() 指定したindexのvalueを数値形式で取得する
5 rcplib_LIST_SearchS() 指定したindexのvalueを文字列形式で取得する
6 rcplib_LIST_IsEmpty() list変数のデータの有無を確認する
7 rcplib_LIST_GetEntry() list変数のデータ数を取得する
8 rcplib_LIST_GetRemainSize() list変数の残りのバッファサイズを取得する

※各APIの内容はAPI仕様書を確認ください。


[コード例]
   ~
//////////////////////////////////////////////////////////////////
// internal variable //
//////////////////////////////////////////////////////////////////
list ListA[500];
list ListB[500];
   ~
//////////////////////////////////////////////////////////////////
// event driven task process //
//////////////////////////////////////////////////////////////////
options {
   ~
    LISTtmpSize = 1000;
   ~
}
   ~
func any_func() {
   ~
    rcplib_LIST_Clear(ListA); // ListAを初期化
    rcplib_LIST_Clear(ListB); // ListBを初期化
    rcplib_LIST_Update(ListB, 0, 100); // ListBにデータ追加
    rcplib_LIST_Update(ListB, 1, -5.5); // ListBにデータ追加
    rcplib_LIST_Update(ListA, 0, "hello"); // ListAにデータ追加
    rcplib_LIST_Update(ListA, 1, ListB); // ListAにデータ追加
   ~
    return(0);
}
   ~

この例の場合、ListAには以下のような結果が得られます。

[
  "hello", [
    100, 
    -5.500000
  ]
]

FIFOバッファ

タスク間での大きなデータをやり取りや、モデムタスクにデータをわたす際に使用するためのバッファとしてFIFOバッファが準備されています。

FIFOバッファの特徴

FIFOバッファの特徴は以下の通りです。

FIFO_overview

FIFOバッファのタスク間での利用例
FIFOバッファを利用してタスク間でデータの共有をするには、片方のタスクでFIFOバッファの割当を行い、そのバッファIDをイベントでデータを共有したいタスクに送付します。受信したタスク側は該当のバッファIDを利用してデータを取得出来ます。
FIFO_use_modem


FIFOバッファに関連するAPI

FIFOバッファに関連するAPIは以下の通りです。

API 内容
1 rcplib_FIFOBUF_Create() FIFOバッファを定義する
2 rcplib_FIFOBUF_SetData() FIFOバッファにデータを書込む
3 rcplib_FIFOBUF_GetData() FIFOバッファのデータを読出す
4 rcplib_FIFOBUF_PeekData() バッファデータを読出し後データを残す

rcplib_FIFOBUF_Create()

FIFOバッファを定義するAPIです。

rcplib_FIFOBUF_Create(uint8 fifo_buff_id, uint32 buf_size, uint16 buf_depth, uint8 overwrite);
    fifo_buff_id : バッファID
        0~31 : 作成対象のバッファID指定
    buf_size : バッファサイズ指定
        バッファのトータルサイズを[Byte]単位で指定
    buf_depth : バッファに書込み可能なデータブロック数
        最大データブロック数を[個]単位で指定
    overwrite : 書込み時にバッファ容量不足時の挙動
        FIFOBUF_OVERWRITE_DISABLE : オーバーライト無効
        FIFOBUF_OVERWRITE_ENABLE : オーバーライト有効

fifo_buff_id
どの番号のバッファIDを利用するかを指定します。IDはシステム全体でユニークとして指定する必要があります。バッファを作成したタスクがそのバッファを管理するタスクになりますが、バッファ自体はIDを指定することで複数のタスクからアクセス出来ます。設計時に作成するタスクと利用するタスクの考慮が必要です。

buf_size
ここではバッファのトータルサイズを指定します。バッファの中には後述のbuf_depthで指定するブロック分のデータを入れる事が出来ますが、ここではそれらを含めて全体でどの大きさを確保するかを指定します。

buf_depth
バッファに書込みできるデータブロック数を指定します。個々のデータブロックは固定ではなく任意のサイズで書込みすることが出来ます。
FIFO_size_depth
バッファの容量が余っていたとしても、ここで指定したブロック数以上のデータを書込みしようとした場合、古い管理ブロックを削除して新しいデータが書き込まれます。
FIFO_size_depth

overwrite
新たに書込みするデータがバッファの残り容量を超える場合にどの様に処理を行うかを指定します。オーバーライト有効にした場合は最古のデータブロックを削除して新しいブロックを書込みします。無効の場合は書込みのAPIがエラーで返ります。
FIFO_size_depth


rcplib_FIFOBUF_SetData()

FIFOバッファにデータを書き込むAPIです。

rcplib_FIFOBUF_SetData(uint8 fifo_buff_id, uint16 size, uint8 data[])
    fifo_buff_id : バッファID
        0~31 : 対象のバッファID指定(0-31)
    size : 入力サイズ
        0-65535[Byte]で指定
    data : FIFOバッファに書き込むuint8配列

rcplib_FIFOBUF_GetData()

FIFOバッファからデータを読込むAPIです。

rcplib_FIFOBUF_GetData(uint8 fifo_buff_id, uint16 size, uint8 data[])
    fifo_buff_id : バッファID
        0~31 : 対象のバッファID指定(0-31)
    size : 出力サイズ
        0-65535[Byte]で指定
    data : FIFOバッファから読み出すuint8配列

rcplib_FIFOBUF_PeekData()

FIFOバッファからデータを読込んだあともデータを残すAPIです。

rcplib_FIFOBUF_PeekData(uint8 fifo_buff_id, uint16 size, uint8 data[]) 
    fifo_buff_id : バッファID
        0~31 : 対象のバッファID指定(0-31)
    size : 出力サイズ
        0-65535[Byte]で指定
    data : FIFOバッファから読み出すuint8配列

size
本APIで指定するsizeまたはdata配列サイズの小さい方に合わせてデータを取得します。
FIFO_read_size


FIFOバッファのシステムでの利用

システムとして提供されているモデムタスクとシリアルタスクはFIFOバッファを利用した作りになっています。これら機能を利用する際にはバッファの割当をする必要があります。(モデムタスクとシリアルタスクでバッファの割当者が違います。)
FIFO_use_modem

FIFO_use_serial


FIFOバッファの容量確保

FIFOバッファは管理するタスクで定義をしますが、OSでも利用するシステムメモリ約370[KB]の中から必要な領域を確保するため、タスクのオプション設定にて必要な大きさを定義する必要があります。
タスクのオプションは、ソースコードの前半部にあります。

options {
    BufferFIFOSize = 1024;
}

FIFOバッファをrcplib_FIFOBUF_Create()のAPIで定義するタスクは、このoptionsで管理するFIFOバッファのサイズを設定します。

一つのタスクで複数のFIFOバッファを定義する場合は、それらの複数のFIFOバッファの総量を設定する必要があります。


タイマー機能

レシピエンジンではユーザーアプリケーション(タスク)にタイマー機能を提供しています。各ユーザーアプリケーションでは、このタイマーを利用して定期処理が実行出来ます。

タイマーの特徴

タイマー機能の特徴は以下になります。

※最短1msで指定可能ですがシステム負荷が大きくなります。


タイマー制御API

タイマーを制御するAPIは以下があります。

API 内容
1 rcplib_TIMER_Create() タイマーを作成する
2 rcplib_TIMER_Start() タイマーを開始する
3 rcplib_TIMER_Stop() タイマーを停止する
4 rcplib_TIMER_SetPeriod() タイマーの周期を変更する
5 rcplib_TIMER_IsActive() タイマーの動作状態を取得する

rcplib_TIMER_Create()

タイマーを作成するAPIです。

rcplib_TIMER_Create(uint8 timer_id, uint32 interval, int8 auto_reload, uint8 event_id)
    timer_id : タイマーID (0-15)
    interval : タイマーの周期 (1~4294967295[msec])
    auto_reload : 自動の再開
        0:再開しない
        1:再開する
    event_id : タイマー満了時の発行イベントID

timer_id
タイマーIDは利用するタイマー毎に番号を設定し開始や停止のAPIで利用します。

interval
指定した数値でタイマーが稼働し満了時には指定したイベントが発行されます。

auto_reload
タイマー満了時に自動的に再開するかどうかを指定します。自動再開を指定した場合はタイマー満了時に即時再開するため正確な周期でイベントが発行されます。この場合タイマーはイベント発行後の処理とは同期を取らない為、イベントの処理がタイマー周期よりも長い場合は新たなイベントが発行され続けることになるので、イベントの処理はタイマー周期よりも短くする必要があります。

event_id
タイマーが満了した時に発行されるイベントの番号を指定します。


rcplib_TIMER_Start()

タイマーを開始するAPIです。

rcplib_TIMER_Start(uint8 timer_id)
    timer_id : タイマーID (0-15)

rcplib_TIMER_Stop()

タイマーを停止するAPIです。

rcplib_TIMER_Stop(uint8 timer_id)
    timer_id : タイマーID (0-15)

rcplib_TIMER_SetPeriod()

タイマーの周期を変更するAPIです。

rcplib_TIMER_SetPeriod(uint8 timer_id, uint32 interval)
    timer_id : タイマーID (0-15)
    interval : タイマーの周期 (1~4294967295[msec])

タイマー動作中にこのAPIが発行された場合は、該当IDのタイマーは停止されます。周期を変更後に再度タイマーを開始するにはrcplib_TIMER_Start()の発行が必要となります。

rcplib_TIMER_IsActive()

タイマーの動作状態を取得するためのAPIです。

rcplib_TIMER_IsActive(uint8 timer_id)
   戻り値 :
      0:Timer停止中(またはtimer_idが不正)
      1:Timer稼働中
[コード例]

タイマーAPIの使用例を示します。

// Timer Event
#ndefine UNSYNC_EVENT_TIMER0 1
// Timer ID
#define UA1_TIMER_ID_0 0
   ~

// mainループ処理
void main_loop() {
   ~
    else if(event_id == UNSYNC_EVENT_TIMER0) {
        // 1秒ごとにイベントが発行される
    }
   ~
}

// 初期化処理
func event_init() {
    // 1秒周期でタイマーを作成
    rcplib_TIMER_Create(UA1_TIMER_ID_0, 1000, 1, UNSYNC_EVENT_TIMER0);
    // タイマーを開始
    rcplib_TIMER_Start(UA1_TIMER_ID_0);
   ~
    return(0);
}

時間・時刻関連制御

本機器では内部で時刻や時間の管理をしており、その機能をAPIを通して利用が出来ます。
時間,時刻関連には以下の機能が提供されています。

ウェイト機能

ウェイト機能にてタスクを一定時間ウェイトさせることが可能です。
ウェイト実行機能の特徴:


ウェイト制御のAPI

API 内容
1 rcplib_SYS_Sleep() 指定時間のWaitを実行する

rcplib_SYS_Sleep()

ウェイト機能を実行するためのAPIです。

rcplib_SYS_Sleep(uint32 sleep)
   sleep : Wait時間設定 (1~4294967295[msec]で指定)
[コード例]
    rcplib_SYS_Sleep(1000); // 1秒のウェイトを実行する

この1秒間のウェイトの間は、このタスクの処理が全て止まります。イベントを受けた場合にもイベント処理は実行されませんが、イベント自体はイベントキューに貯まります。ウェイト後に該当関数が終了した段階で、イベント処理が実行されます。


システム稼働時間取得機能

システム稼働時間はSystem tickとも呼ばれ、CPU内で起動時間から一定時間でカウントし続けるクロック値を指します。システム稼働時間取得機能の特徴を以下の通り。

システム稼働時間取得のAPI

システム稼働時間取得するAPIは以下の通り。

API 内容
1 rcplib_SYS_GetTick() システム稼働時間を取得します

rcplib_SYS_GetTick()

rcplib_SYS_GetTick() 
   戻り値: uint64のシステム稼働時間
[コード例]
    uint64 tick; // 現在のシステムチックの取得先
    tick = rcplib_SYS_GetTick();

システム稼働時間は主に時間差を取る場合に使用されます。タイマー等の周期イベントは、イベント発行から実際の処理までのオーバーヘッドがあるため、完全に正確とは言えません。正確な時間差を測る場合は、内部のハードウェアで定期的にカウントされているシステム稼働時間を取得して差分を取ることが有効です。


現在時刻取得

本機器ではリルタイムクロック(RTC)を搭載しており、時刻を管理する機能がありますが、バックアップ電源を搭載していないため電源起動時には初期値に戻ります。起動後に無線通信にて時刻が取得出来た段階で時刻補正を行います。
現在時刻取得機能の特徴は以下の通り。


現在時刻取得のAPI

現在時刻を取得するAPIは以下の通り。

API 内容
1 rcplib_RTC_GetTime() 現在時刻を取得します

rcplib_RTC_GetTime()

rcplib_RTC_GetTime() 
   戻り値: uint64のUNIX時間(UTC)
[コード例]
    uint64 utc; // 現在のUNIX時間(UTC)の取得先
    utc = rcplib_RTC_GetTime();

以下のAPIにより、UNIX時間(UTC)を日本標準時の文字列に変換することができます。
このAPIはC言語のstrftime()関数の仕様に準拠しています。

    str str_time[32]; // 日本標準時のstring形式の出力先変数
    // 日本標準時のstring形式に変換
    rcplib_FORMAT_Time(str_time, "%Y/%m/%d(%A) %H:%M:%S", utc, 9);

この例の場合以下のような文字列が得られます。

    "2022/09/01(Thursday) 13:40:21"

時刻アラーム

システム管理の時刻を利用したアラーム機能を提供しています。
時刻アラーム機能の特徴を以下に示します。


時刻アラーム設定のAPI

時刻アラーム設定するAPIは以下の通り。

API 内容
1 rcplib_RTC_CreateAlarm() 時刻アラームの作成と設定をする
2 rcplib_RTC_DeleteAlarm() 時刻アラームを削除する
3 rcplib_RTC_ChangeAlarm() 時刻アラームを変更する

rcplib_RTC_CreateAlarm()

rcplib_RTC_CreateAlarm(uint8 event_id, uint32 time, uint8 oneshot)
   event_id : 設定時刻到達時の発行イベントID (1-254)
   time     : 設定するアラーム時刻 UNIX時間(UTC)で指定 (0~86399[秒])
   oneshot  : 繰り返しアラーム設定 (0:繰り返し, 1:ワンショット)

   戻り値 : 0~15 : 作成したRTCアラームのアラームID
           -1 : アラーム時刻不正、アラーム登録数(上限16個)を超過

rcplib_RTC_DeleteAlarm()

rcplib_RTC_DeleteAlarm(uint8 alarm_id) 
   alarm_id : 対象とするアラームID (0~15)

rcplib_RTC_ChangeAlarm()

rcplib_RTC_ChangeAlarm(uint8 alarm_id, uint32 time)
   alarm_id : 対象とするアラームID (0~15)
   time : 設定するアラーム時刻 UNIX時間(UTC)で指定 (0~86399[秒])
[コード例]

正午の12:00:00にイベント処理が実行されるコードは以下の通り。

//////////////////////////////////////////////////////////////////
// internal define //
//////////////////////////////////////////////////////////////////
// 内部非同期イベント
#define UNSYNC_EVENT_INIT 0
#define UNSYNC_EVENT_ALARM 1

// アラーム時間設定(本標準時の正午12:00:00のアラーム設定値)
#define USER_SETTING_ALARM_HOUR 12
#define USER_SETTING_ALARM_MINUTES 0
#define USER_SETTING_ALARM_SECOND 0
#define USER_SETTING_ALARM_TIME (USER_SETTING_ALARM_HOUR*3600+USER_SETTING_ALARM_MINUTES*60+USER_SETTING_ALARM_SECOND+54000)%86400

   ~
//////////////////////////////////////////////////////////////////
// internal variable //
//////////////////////////////////////////////////////////////////
uint8 l_alarm_id; // 設定中のアラームのID記憶用変数
   ~
// mainループ処理
void main_loop() {
   ~
    // アラーム通知時の処理
    else if(event_id == UNSYNC_EVENT_ALARM) {
        // アラーム満了時の処理をここに記載
    }
   ~
}
   ~
// 初期化処理
func event_init() {
    // アラームを作成
    l_alarm_id = rcplib_RTC_CreateAlarm(UNSYNC_EVENT_ALARM, USER_SETTING_ALARM_TIME, 1);
   ~
    return(0);
}

数学関数

取得した値をエッジで処理が出来るように、いくつかの数学関数を準備しています。

数学関数の種類

以下の関数に関するAPIを用意しています。


数学関数に関連するAPI

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

API 内容
1 rcplib_MATH_Sin() 正弦(sin)関数
2 rcplib_MATH_Cos() 余弦(cos)関数
3 rcplib_MATH_Tan() 正接(tan)関数
4 rcplib_MATH_Asin() 逆正弦(arcsin)関数
5 rcplib_MATH_Acos() 逆余弦(arccos)関数
6 rcplib_MATH_Atan() 逆正接(arctan)関数
7 rcplib_MATH_Log() 自然対数(log)関数
8 rcplib_MATH_Exp() 指数(exp)関数
9 rcplib_MATH_Log10() 常用対数(log10)関数
10 rcplib_MATH_Sqrt() 平方根(sqrt)関数
11 rcplib_MATH_Pow() べき乗(pow)関数
12 rcplib_MATH_Abs() 絶対値(abs)関数

数学関数のAPI定義

数学関数の引数型は(int /uint /double)が入力可能
計算結果が関数の戻り値となります。
rcplib_MATH_Sin(double x) 
rcplib_MATH_Cos(double x) 
rcplib_MATH_Tan(double x) 
rcplib_MATH_Asin(double x)
rcplib_MATH_Acos(double x) 
rcplib_MATH_Atan(double x) 
rcplib_MATH_Log(double x) 
rcplib_MATH_Exp(double x) 
rcplib_MATH_Log10(double x)
rcplib_MATH_Sqrt(double x) 
rcplib_MATH_Pow(double x, double y) 
rcplib_MATH_Abs(double x) 
[コード例]
func any_func() {
    //三角関数計算
    double rad;  rad = 60.0 * 3.141592 / 180.0;
    printMath("sin(%f) = ", rad, rcplib_MATH_Sin(rad));
    printMath("cos(%f) = ", rad, rcplib_MATH_Cos(rad));
    printMath("tan(%f) = ", rad, rcplib_MATH_Tan(rad));

    //逆三角関数計算
    double si;  si=rcplib_MATH_Sin(rad);
    double co;  co=rcplib_MATH_Cos(rad);
    double ta;  ta=rcplib_MATH_Tan(rad);
    printMath("asin(%f) = ", si, rcplib_MATH_Asin(si));
    printMath("acos(%f) = ", co, rcplib_MATH_Acos(co));
    printMath("atan(%f) = ", ta, rcplib_MATH_Atan(ta));

    //自然対数,常用対数,指数関数
    double x;  x=2;
    printMath("log(%2.0f) = ", x, rcplib_MATH_Log(x));
    printMath("log10(%2.0f) = ", x, rcplib_MATH_Log10(x));
    printMath("exp(%2.0f) = ", x, rcplib_MATH_Exp(x));

    //平方根、べき乗
    x=3;
    printMath("sqrt(%2.0f) = ", x, rcplib_MATH_Sqrt(x));
    printMath("pow(%2.0f, 3) = ", x, rcplib_MATH_Pow(x,3));

    //平方根、べき乗
    x=-3.141592;
    printMath("abs(%f) = ", x, rcplib_MATH_Abs(x));

    return(0);
}

// private function //
// Math計算表示
func printMath(str format[100], double data, double math)
{
    str msg1[100];
    str msg2[100];
    rcplib_FORMAT_String(msg1, format, data);
    rcplib_FORMAT_String(msg2, "%f", math);
    rcplib_STR_Cat(msg1,msg2);
    rcplib_LOG_Print(msg1);
    return(0);
}

Base64エンコーダ・デコーダ

バイナリデータをテキスト変換してURLやデータ内に埋め込む際に必要なBase64変換の機能を準備しています。

Base64エンコーダ・デコーダの特徴

Base64エンコーダ・デコーダ機能の特徴は以下の通りです。


Base64エンコーダ・デコーダのAPI

Base64エンコーダ・デコーダのAPIは以下の通り。

API 内容
1 rcplib_FORMAT_EncBase64() Base64のエンコードを行う
2 rcplib_FORMAT_DecBase64() Base64のデコードを行う

rcplib_FORMAT_EncBase64()

rcplib_FORMAT_EncBase64(str dst, str_arr src, uint16 src_len, uint8 type)
   dst : エンコード結果の出力先文字列変数
   src : 入力する文字列変数またはuint8配列
   src_len : 入力サイズ (0-32768[Byte]で指定)
   type : エンコード方式
         FORMAT_BASE64_NORMAL:通常
         FORMAT_BASE64_URL_SAFE:URLセーフ
         FORMAT_BASE64_PERCENT:%文字エンコード
      戻り値: 成功(0), 失敗(-1)

rcplib_FORMAT_DecBase64()

rcplib_FORMAT_DecBase64(str dst, uint8 src[], uint8 type) 
   dst : エンコード結果の出力先文字列変数
   src : 入力する文字列変数またはuint8配列
   type : エンコード方式
         FORMAT_BASE64_NORMAL:通常
         FORMAT_BASE64_URL_SAFE:URLセーフ
         FORMAT_BASE64_PERCENT:%文字エンコード
   戻り値: 成功(int32の出力サイズ), 失敗時 (-1)
[コード例]

入力が文字列変数のエンコードのケース

#include "cmd_str.h"
#include "cmd_format.h"
   ~
func any_func() {
    str str_src[9]; // 変換したい文字列変数
    str str_res1[32]; // 変換結果の出力先文字列変数
    rcplib_STR_SetString(str_src, "ABCDE"); // 変換したい文字を代入
    // Bse64エンコード実行
    rcplib_FORMAT_EncBase64(str_res1, str_src, rcplib_STR_Len(str_src), FORMAT_BASE64_NORMAL);
    // str_res1には"QUJDREU="が出力される
   ~
    return(0);
}

入力がuint8配列変数のエンコードのケース

#include "cmd_str.h"
#include "cmd_format.h"
   ~
func any_func() {
    uint8 bin_src[9]; // 変換したい配列変数
    str str_res2[32]; // 変換結果の出力先文字列変数
    rcplib_FORMAT_Binary(bin_src, 0, EAPI_VTYPE_STR, 5, "ABCDE"); // 変換したい文字をuint8配列変数に代入
    // Bse64エンコード実行
    rcplib_FORMAT_EncBase64(str_res2, str_src, 5, FORMAT_BASE64_NORMAL);
    // str_res1には"QUJDREU="が出力される
   ~
    return(0);
}

Base64デコーダ例

#include "cmd_str.h"
#include "cmd_format.h"
   ~
func any_func() {
   ~
    str str_src[9]; // 変換したい文字列変数
    uint8 bin_res[32]; // 変換結果の出力先文字列変数
    int32 res_size; // 変換結果の出力先文字列変数
    rcplib_STR_SetString(str_src, "QUJDREU="); // 変換したい文字を代入
    // Bse64デコード実行
    res_size = rcplib_FORMAT_DecBase64(bin_res, str_src, FORMAT_BASE64_NORMAL);
    // res_sizeには5が出力され、bin_resには"ABCDE"相当の0x41, 0x42, 0x43, 0x44, 0x45が出力される
   ~
    return(0);
}

タスク間通信

ユーザーアプリはそれぞれがタスクとして独立しており、マルチタスクでの動作をしています。それぞれのタスクは動作もメモリも独立していることから、連携するためにはメッセージを用いたタスク間通信と、データを共有する場合にはFIFOバッファを利用します。

タスク間通信の特徴

イベント送信
タスク間通信にはイベント送信を利用します。イベント処理には2種類の動作があります。

非同期処理では相手タスクにイベント送信をした際にイベントの通知のみを行い自タスクのコンテキストは続行される動きとなります。同期処理では相手タスクにイベント送信をした際に自タスクのコンテキストが相手が応答メッセージを受信するまで中断されます。

SDKで提供されるほとんどのAPIは、各機能のタスクに対するイベント送信を利用しています。いくつかのAPIの引数にある「非同期」「同期」はここでのイベント送信時の「非同期」「同期」と同義になります。

非同期処理
非同期処理の模式図を下記に示します。非同期処理ではイベントのみ相手タスクに通知される事から、相手タスクの優先度が同じ場合は相手タスクと並列で実行されます。相手タスクの優先度が高い場合は非同期でイベント送信をしたとしても、相手タスクの実行が優先されて自タスクが待たされる動きになりますのでご注意ください。
タスク間(非同期)

同期処理
同期処理の模式図を下記に示します。同期処理ではイベントを送信した際に自タスクのコンテキストは相手の応答が来るまで中断します。相手タスクが応答を返さない場合に中断を継続しないように、イベント発行のAPIにはタイムアウトが設定でき、一定時間応答の無い場合はコンテキストが戻され、中断していたタスク処理が再開します。
タスク間(同期)


タスク間通信のAPI

タスク間通信に関連するAPIは以下の通り。

API 内容
1 rcplib_TASK_InitArg() タスク間通信の引数管理情報を初期化する
2 rcplib_TASK_SetArg() 相手タスクに通知するイベント引数を追加
3 rcplib_TASK_GetArg() 相手タスクから引き渡された引数を取得
4 rcplib_TASK_SendEvent() 指定したタスクIDにイベントを送信
5 rcplib_TASK_SendResult() 同期イベントを受信した後の返答を送信

rcplib_TASK_InitArg()

タスク間通信の引数管理情報を初期化するAPIです。
イベント送信に付与する引数はrcplib_SetArg()で順番に設定される為、この関数で過去に設定された古い情報を初期化します。

rcplib_TASK_InitArg()
    引数無し

rcplib_TASK_SetArg()

相手タスクに通知するイベント引数を追加するAPIです。
引数をいくつか設定する場合は、このAPIを複数回コールします。
※引数は合計で26byteまでしか設定出来ません。

rcplib_TASK_SetArg(int32 type, any data) 
    type : 設定対象の型を指定
        EAPI_VTYPE_I8  : int8
        EAPI_VTYPE_I16 : int16
        EAPI_VTYPE_I32 : int32
        EAPI_VTYPE_I64 : int64
        EAPI_VTYPE_U8  : uint8
        EAPI_VTYPE_U16 : uint16
        EAPI_VTYPE_U32 : uint32
        EAPI_VTYPE_U64 : uint64
        EAPI_VTYPE_F32 : float
        EAPI_VTYPE_F64 : double
        EAPI_VTYPE_STR : str 
    data : 引数情報に追加するデータ

rcplib_TASK_GetArg()

相手タスクから引き渡された引数を取得するAPIです。
複数個の引数がある場合は、indexを指定することで該当の引数が取り出せます。

rcplib_TASK_GetArg(int32 index)
    index : 取り出す引数の番号を指定
    ※相手タスクが指定した形式int64||uint64||double||str等に応じた値が返る。

rcplib_TASK_SendEvent()

指定したタスクIDにイベントを送信するAPIです。

rcplib_TASK_SendEvent(uint8 task_id, uint8 event_id, uint8 sync, int32 timeout) 
    task_id  : イベント送信先のタスクID
    event_id : イベント送信先のイベントID
    sync : 同期処理/非同期処理設定
       EAPI_SYNC_RESULT  : 同期処理
       EAPI_UNSYNC_NORESULT : 非同期処理
    timeout : イベント応答までのタイムアウト時間
       EAPI_TIMEOUT_UNLIMITED : タイムアウトなし
       それ以外:単位[msec]で指定
[コード例]

uapp_1タスクからuapp_2タスクへ非同期処理でイベント送信を行います。
サンプルコード uapp_1

// イベントIDはuapp_2と共有
#define EVENT_TO_UA2 1
~

// イベント送信 (uint8, float, strの3種類の引数をつけてイベント送信)
func any_func() {
    uint8 snd_u8;  snd_u8=123;
    float snd_flt;  snd_flt=0.123;
    str snd_str[4]; snd_str="moji";

    //送信することをLogに出力    
    str logmsg1[50];
    str logmsg2[20];
    rcplib_FORMAT_String(logmsg1, "Send: %d, ", snd_u8);
    rcplib_FORMAT_String(logmsg2, "%.3f, ", snd_flt);
    rcplib_STR_Cat(logmsg1, logmsg2);
    rcplib_STR_Cat(logmsg1, snd_str);
    rcplib_LOG_Print(logmsg1);

    //uapp_2にイベントと引数を通知
    rcplib_TASK_SetArg(EAPI_VTYPE_U8, snd_u8);    //引数1つ目
    rcplib_TASK_SetArg(EAPI_VTYPE_F32, snd_flt);   //引数2つ目
    rcplib_TASK_SetArg(EAPI_VTYPE_STR, snd_str); //引数3つ目
    rcplib_TASK_SendEvent(EAPI_TASKID_UAPP2, EVENT_TO_UA2, EAPI_UNSYNC_NORESULT, EAPI_TIMEOUT_ZERO);

    rcplib_LOG_Print("Event Send to uapp_2");

    return(0);
}

サンプルコード uapp_2

// イベントIDはuapp_1と共有
#define EVENT_FROM_UA1 1
~
// mainループ処理
void main_loop() {
   ~
//uapp_1からのイベント受信
else if(event_id == EVENT_FROM_UA1) {
    event_rcv(
        rcplib_TASK_GetArg(0),  //引数1つ目
        rcplib_TASK_GetArg(1),  //引数2つ目
        rcplib_TASK_GetArg(2)   //引数3つ目
    );
   ~
}

// event rcv処理のイベント
func event_rcv(uint8 rcv_u8, float rcv_flt, str rcv_str[4]) {
    //受信したことをLogに出力    
    str logmsg1[50];
    str logmsg2[20];
    rcplib_FORMAT_String(logmsg1, "Recieve: %d, ", rcv_u8);
    rcplib_FORMAT_String(logmsg2, "%.3f, ", rcv_flt);
    rcplib_STR_Cat(logmsg1, logmsg2);
    rcplib_STR_Cat(logmsg1, rcv_str);
    rcplib_LOG_Print(logmsg1);

    return(0);
}

システム制御

ソフトウェアから再起動、電源OFF、CPUクロック変更機能を準備しています。

システム制御機能


システム制御のAPI

システム制御のAPIは以下の通り。

API 内容
1 rcplib_SYS_Reboot() 端末を再起動する
2 KCS_Shutdown() 電源をOFFにする
3 rcplib_PWRM_SetPerformanceMode() CPUのモードを変更する

rcplib_SYS_Reboot()

rcplib_SYS_Reboot() 
   引数なし

API実行後に再起動が行われます。再起動の前にrcplib_NV_Sync()が行われるので不揮発メモリーのミラー領域に変更があった場合は不揮発メモリーに書き込まれた後に再起動されます。

KCS_Shutdown()

KCS_Shutdown() 
   引数なし

API実行後に全タスクに電源オフイベントが通知され、各タスクでの電源OFF処理が終了した段階で電源オフが実行されます。USB type-Cの接続中においては仕様として電源OFFは出来ないため、この要求も無効として扱われます。

rcplib_PWRM_SetPerformanceMode()

rcplib_PWRM_SetPerformanceMode(uint8 mode) 
   mode : 
     0=CPU High performance mode
     1=CPU High performance modeの解除

高速で処理をしたい場合は、本APIの実行によりCPUをHigh performance mode(HPM)に移行させることで、CPUの動作周波数を上げてシステムの動作性能を上げることができます。また本APIによらず、カメラ利用時またはLTEデータ通信の一部機能を利用時にはCPU HPMで動作します。

※CPU HPMは473.6MHzで動作、通常時は96MHzで動作します。
※HPMはCPUの温度が上昇しますので、高温時の機能制限がかかる場合があリます。
※機能制限についてはホワイトペーパーの「9-2.高温時の機能制限」の項を参照ください。


システムからのイベント

アプリのタスク間イベントの他に、特別な場合にはシステムから全てのタスクに対してイベントが発行されます。

※機能制限についてはホワイトペーパーの「9-2.高温時の機能制限」の項を参照ください。

イベントの種類

"eventLib.h"に定義されており、以下の種類があります。

イベント 番号 説明
EAPI_EVENT_TASK_ENTER_FORCESTOP (60000) 端末電源OFF処理を開始した際のイベント
EAPI_EVENT_TASK_RESUME_FORCESTOP (60001) -予約-
EAPI_EVENT_TASK_ENTER_LIMIT (60002) 機能制限に遷移した際のイベント
EAPI_EVENT_TASK_RESUME_LIMIT (60003) 機能制限を解除した際のイベント
[コード例]

ユーザーアプリケーションのひな形の中に、システム発行のイベントに対するコードが記載されています。必要に応じてこの部分にコードに追記してください。

void main_loop(){
   ~
    // 強制停止通知
    else if(event_id == EAPI_EVENT_TASK_ENTER_FORCESTOP) {
        // ★端末電源OFF処理前に実行したい処理をここに記載すること★
        KCS_NotifyReadyShutdown();
    }
   ~
    // 機能制限通知
    else if(event_id == EAPI_EVENT_TASK_ENTER_LIMIT) {
        // ★機能制限に遷移した際に実行したい処理をここに記載すること★
    }
    // 機能制限解除通知
    else if(event_id == EAPI_EVENT_TASK_RESUME_LIMIT) {
        // ★機能制限を解除した際に実行したい処理をここに記載すること★
    }
   ~
}

強制停止通知時の注意事項
端末電源OFF処理のイベント通知部には既に以下API実行が記述されています。

        KCS_NotifyReadyShutdown();

これはシステムから電源OFFイベントを受けた際にシステムタスクに対して電源OFFに対する準備完了を通知する処理となっています。このため電源がOFFされる前に実行したい処理がある場合はこのAPI実行より前に処理を行う必要があります。
システムタスクは各タスクからの準備完了通知を持って電源OFFを実行しますが、60秒以内に準備完了通知を受信しない場合は強制的に端末の電源をOFFします。

システムからのイベント時のモデム機能
機能制限に遷移した際や、端末電源OFF処理を開始した際は、モデムの機能は停止するためLTEでのデータ送信はできません。この際には不揮発メモリーにデータを退避する事で、直前のデータを保存することが出来ます。