kintoneGeeks blog

kintoneに関連する情報を発信しています

SONY Spresense と LTE拡張ボードでkintoneにHTTPリクエストする

この記事ではSONY SpresenseのLTE 拡張ボードを使い、クラウドサービスのkintoneにHTTPリクエストをする方法を案内します。

SONY Spresenseとは

SONY Spresense (以降、Spresense)は低消費電力でありながら、GPS受信機能とハイレゾリューションオーディオコーデックを搭載したIoT用ボードコンピュータです。この記事ではSpresenseメインボードをLTE拡張ボードにとりつけて、kintoneにHTTPリクエストをする方法を案内します。

この記事で作るもの

この記事ではkintoneでスコアランキングアプリを作り、SpresenseのLTE経由でランキングを取得したり、スコアを追加する方法を案内します。

SpresenseのLTE拡張でkintoneにHTTPリクエストをする

連携の作り方

kintoneアプリの準備

まずはkintone側の準備から説明します。

kintone環境の準備する

kintoneの環境が無い方は、開発者サイトから1年間無償で使用できる『開発者ライセンス』という名のkintone環境を取得出来ます。下記のサイトで『開発者ライセンスを申し込む』をクリックし、フォームに必要事項を入力して下さい。申込後、メールでkintone環境の情報が送られてきます。所要時間はおおよそ10分程度です。

cybozu.dev

kintoneアプリを作成する

kintoneで作るウェブデータベースは『アプリ』と呼ばれます。

まず、ゲームのスコアを管理するためのkintoneアプリを作成します。プレイヤー名、スコア、難易度等、必要な情報をイメージしながら作りましょう。

kintoneアプリに配置するフィールド

この記事で作成するアプリに必要なフィールドは下記のデーブルに記載したので、参考にしてください。フィールドを配置したら、一つ一つ設定を変更することが出来るので、表示名、フィールドコードやオプションなどを変更します。ちなみに表示名はUI上でユーザに見えるフィールドの名前で、フィールドコードはフィールドのユニークな識別子になります。フィールドコードは外部からHTTPリクエストをする際に重要な設定となります。

フィールドタイプ 表示名 フィールドコード その他の設定
文字列(1行) プレイヤー名 playername
数値 スコア score 単位記号に『points』を追加して後ろに付ける
ラジオボタン 難易度 difficulty 選択肢に 『Easy』『Medium』『Hard』 を追加する

フィールドの配置や設定が終わったら、右上の青いボタンからアプリを公開します。

kintoneアプリにレコードを追加する

ウェブデータベースを作成出来ましたが、データがまだ入っていません。適当に手動でいくつかデータ(レコード)を追加しましょう。

レコードが登録されたkintoneアプリ

kintoneアプリからAPIトークンを発行する

他のシステムと連携をするために、kintoneアプリから認証用のトークンを発行します。アプリの設定画面からAPIトークンを生成します。「レコード閲覧」と「レコード追加」のアクセス権にチェックを入れます。APIトークンを手元でメモした後に、設定を保存してアプリを更新します。

適切な権限設定が付与されたAPIトークン

これでkintoneアプリの準備が出来ました。

Spresenseの準備

次に、Spresense側で必要なデバイスや設定を準備していきます。

必要なデバイスを揃える

SpresenseでLTE通信するためには下記のデバイスが必要です:

本記事で使用するSpresenseのパーツ、MicroSDとSIMカード

SIMカードをアクティベートする

まずはSIMカードで通信が出来るようにアクティベートをしましょう。アクティベートする方法は各社で方法が異なるので、SIMカードのマニュアルの説明を良く読んでアクティベートをします。

この記事では『さくらのセキュアモバイルコネクト』を使用しており、さくらのクラウドアカウントからモバイルゲートウェイを作成してからSIMカードをアクティベートする必要がありました。

SIMカードのアクティベートが出来たら、LTE拡張ボードに差し込みます。

Spresenseの開発環境を準備する

Spresense Arduino スタートガイドに沿ってSpresense Arduino Libraryをインストールし、 Arduino IDE 、USB ドライバとSpresense Arduino board packageをインストールします。プログラミング環境の設定をし、LEDのスケッチを動かすところまで進めて、ボードへの書き込みがきちんと出来るかを確認します。

developer.sony.com

LTE環境関連のスケッチを動かす

ここからは、Spresenseの開発サイトで案内されているLTEチュートリアルを元にいくつかのスケッチを試し、少しずつ動作確認をしていきます。

developer.sony.com

各スケッチは【ファイル】→【スケッチ例】→【LTE】から開くことが出来ます。また、各スケッチの結果はシリアルモニタに表示されるので、【ツール】→【シリアルモニタ】でArduino IDEに表示させましょう。

LteTestModem のスケッチ例を動かす

LteTestModemのスケッチを動かします。特にコードに変更は必要ありません。Spresenseに書き込みをしたら、シリアルモニタに下記のような情報が出力されます。

IMEI: 351521105181572
VERSION: RK_03_00_00_22_13151_002
RAT: LTE-M (LTE Cat-M1)

LteScanNetworks.ino のスケッチ例を動かす

LteScanNetworksのスケッチを動かします。コード内のAPN情報を、自分のSIMカードに合わせたものに変更します。

// APN name
#define APP_LTE_APN "sakura"

Spresenseに書き込みをし、しばらくしたらコンソールに下記のような情報が定期的に出力されます。

Current carrier: SAKURA Internet
Signal Strength: -78 [dBm]
LTE networks scanner
=========== APN information ===========
Access Point Name  : sakura
Authentication Type: CHAP
User Name          : 
Password           : 
attach succeeded.
IP address: xxx.xxx.x.x
Current carrier: SAKURA Internet
Signal Strength: -78 [dBm]

LteWebClient のスケッチ例を動かす

LteWebClientのスケッチを動かします。こちらもコード内のAPN情報を自分のSIMカードに合わせたものに変更します。

// APN name
#define APP_LTE_APN "sakura"

Spresenseに書き込みをし、しばらくしたらコンソールに色々な情報が出力されます。

    ;;;   ;;;;;`  ;;;;:  .;;  ;; ,;;;;;, ;;. `;,  ;;;;   
    ;;;   ;;:;;;  ;;;;;; .;;  ;; ,;;;;;: ;;; `;, ;;;:;;  
   ,;:;   ;;  ;;  ;;  ;; .;;  ;;   ,;,   ;;;,`;, ;;  ;;  
   ;; ;:  ;;  ;;  ;;  ;; .;;  ;;   ,;,   ;;;;`;, ;;  ;;. 
   ;: ;;  ;;;;;:  ;;  ;; .;;  ;;   ,;,   ;;`;;;, ;;  ;;` 
  ,;;;;;  ;;`;;   ;;  ;; .;;  ;;   ,;,   ;; ;;;, ;;  ;;  
  ;;  ,;, ;; .;;  ;;;;;:  ;;;;;: ,;;;;;: ;;  ;;, ;;;;;;  
  ;;   ;; ;;  ;;` ;;;;.   `;;;:  ,;;;;;, ;;  ;;,  ;;;; 

今までのスケッチ例で別の情報が出力された場合、SIMカードが正しく動作してない可能性があります。

ここまで問題なく出力されたら、Spresenseの準備は出来たので、次に進みましょう。

SpresenseからkintoneにHTTPリクエスト

Spresenseからkintoneに対してHTTPリクエストを行いますが、kintoneが稼働しているcybozu.comのサーバにはHTTPではなくHTTPSで通信を行う必要があります。Spresenseにcybozu.comのサーバが安全なサーバだと伝えるために、cybozu.comの証明書をSpresenseに保管する必要があります。

証明書の準備をする

ブラウザによって証明書の取得方法がことなります。この記事ではChromeブラウザで入手する方法を案内します。

まずkintoneにログインし、URL近くのメニューから「この接続は保護されています」を選択し、「証明書は有効です」をクリックします。

Chromeでcybozu.comの証明書を取得する場合

続いて「詳細」タブを選択し、一番上のCertification Authorityと記載されてる箇所を選択します。この状態で下のエクスポートボタンをクリックします。このページではデフォルトで違う箇所が選択されるので、間違えてそちらをエクスポートしないように気をつけてください。Certification Authorityと記載されてる方をエクスポートしてください。

正しい証明書を選択してエクスポートする

保存方法が色々と提示されますが、その中から「DER エンコードバイナリ形式の単一の証明書」を選んで保存します。

どのエンコーディングで証明書を保存するかを選択する

証明書がダウンロード出来たら、SDカードをパソコンに接続し、CERTSディレクトリを作成し、その中に証明書ファイルを入れます。証明書のファイル名は好きなものにします。

SDカードリーダー等を使って証明書を保存する

SDカードに保存が出来たら、SDカードをSpresenseのLTE拡張ボード内に差し込みます。LTE拡張ボードにも電源供給をします。

SIMカードとSDカードがセットされ、電源供給されたSpresenseとLTE拡張ボード

コードの下準備をする

スケッチ例からLteHTTPSecureClientを選択します。ArduinoHttpClientライブラリ、Arduino_JSONライブラリとURLEncodeライブラリをインストールし、コードの上部にライブラリをincludeします。

#include <ArduinoHttpClient.h>
#include <Arduino_JSON.h>
#include <UrlEncode.h>

他のスケッチ例のように、APN情報を更新します

// APN name
#define APP_LTE_APN "sakura"

SDカード内の証明書のパスを更新します

#define ROOTCA_FILE "CERTS/cybozu.cer"

URLやパスを指定する箇所でご自身のkintone環境のサブドメインを含めたcybozu.comのサーバURLを指定し、getやpostのパスをコメントアウトします。

char server[] = "cy-hwg.cybozu.com";
//char getPath[] = "/get";
//char postPath[] = "/post";

レコードの取得をする

まずはkintoneの複数のレコードを取得するAPIを使って複数のレコードをアプリから取得します。

loop内の処理を下記のように書き換えます。接続先のcybozu.comのサブドメイン、アプリIDやAPIトークンは自分のものに変更して下さい。

void loop()
{
  // Set certifications via a file on the SD card before connecting to the server
  File rootCertsFile = theSD.open(ROOTCA_FILE, FILE_READ);
  tlsClient.setCACert(rootCertsFile, rootCertsFile.available());
  rootCertsFile.close();

  // HTTP GET method
  Serial.println("Starting GET request...");
  client.beginRequest();
  Serial.println("Begenning request");
  //Create endpoint URL for kintone Get Records API
  String baseURL = "https://cy-hwg.cybozu.com/k/v1/records.json";
  String appURL = "?app=70";
  String GETURL = baseURL + appURL;
  Serial.println(GETURL);
  //Make API request
  client.get(GETURL);
  client.sendHeader("X-Cybozu-API-Token", "edtlR7VzFTiQY7ODPOihoCHmeALwYoJYy6Mk8akw");
  Serial.println("Ending request...");
  client.endRequest();
  Serial.println("Request has ended");

  // read the status code and body of the response
  Serial.println("Reading status code...");
  int statusCode = client.responseStatusCode();
  Serial.print("Status code: ");
  Serial.println(statusCode);
  Serial.println("Reading response...");
  String response = client.responseBody();
  Serial.println("Response: " + response);  
  Serial.println("Wait five seconds");

  sleep(5);
}

シリアルモニタに情報が出力され、しばらくするとレコード情報が入ったJSONのレスポンスも出力されます。

Starting GET request...
Begenning request
https://cy-hwg.cybozu.com/k/v1/records.json?app=70
Ending request...
Request has ended
Reading status code...
Status code: 200
Reading response...
Response: {"records":[{"difficulty":{"type":"RADIO_BUTTON","value":"Medium"},"score":{"type":"NUMBER","value":"23000"},"レコード番号":{"type":"RECORD_NUMBER","value":"5"},"更新者":{"type":"MODIFIER","value":{"code":"cy-pioneer","name":"cy-pioneer"}},"作成者":{"type":"CREATOR","value":{"code":"cy-pioneer","name":"cy-pioneer"}},"playername":{"type":"SINGLE_LINE_TEXT","value":"QED"},"$revision":{"type":"__REVISION__","value":"1"},"更新日時":{"type":"UPDATED_TIME","value":"2024-06-18T04:34:00Z"},"作成日時":{"type":"CREATED_TIME","value":"2024-06-18T04:34:00Z"},"$id":{"type":"__ID__","value":"5"}},{"difficulty":{"type":"RADIO_BUTTON","value":"Easy"},"score":{"type":"NUMBER","value":"13000"},"レコード番号":{"type":"RECORD_NUMBER","value":"4"},"更新者":{"type":"MODIFIER","value":{"code":"cy-pioneer","name":"cy-pioneer"}},"作成者":{"type":"CREATOR","value":{"code":"cy-pioneer","name":"cy-pioneer"}},"playername":{"type":"SINGLE_LINE_TEXT","value":"Kiri"},"$revision":{"type":"__REVISION__","value":"1"},"更新日時":{"type":"UPDATED_TIME","value":"2024-06-18T04:34:00Z"},"作成日時":{"type":"CREATED_TIME","value":"2024-06-18T04:34:00Z"},"$id":{"type":"__ID__","value":"4"}},{"difficulty":{"type":"RADIO_BUTTON","value":"Hard"},"score":{"type":"NUMBER","value":"35000"},"レコード番号":{"type":"RECORD_NUMBER","value":"3"},"更新者":{"type":"MODIFIER","value":{"code":"cy-pioneer","name":"cy-pioneer"}},"作成者":{"type":"CREATOR","value":{"code":"cy-pioneer","name":"cy-pioneer"}},"playername":{"type":"SINGLE_LINE_TEXT","value":"ボブ"},"$revision":{"type":"__REVISION__","value":"1"},"更新日時":{"type":"UPDATED_TIME","value":"2024-06-18T04:34:00Z"},"作成日時":{"type":"CREATED_TIME","value":"2024-06-18T04:34:00Z"},"$id":{"type":"__ID__","value":"3"}},{"difficulty":{"type":"RADIO_BUTTON","value":"Hard"},"score":{"type":"NUMBER","value":"34000"},"レコード番号":{"type":"RECORD_NUMBER","value":"2"},"更新者":{"type":"MODIFIER","value":{"code":"cy-pioneer","name":"cy-pioneer"}},"作成者":{"type":"CREATOR","value":{"code":"cy-pioneer","name":"cy-pioneer"}},"playername":{"type":"SINGLE_LINE_TEXT","value":"HAL"},"$revision":{"type":"__REVISION__","value":"1"},"更新日時":{"type":"UPDATED_TIME","value":"2024-06-18T04:34:00Z"},"作成日時":{"type":"CREATED_TIME","value":"2024-06-18T04:34:00Z"},"$id":{"type":"__ID__","value":"2"}},{"difficulty":{"type":"RADIO_BUTTON","value":"Easy"},"score":{"type":"NUMBER","value":"23000"},"レコード番号":{"type":"RECORD_NUMBER","value":"1"},"更新者":{"type":"MODIFIER","value":{"code":"cy-pioneer","name":"cy-pioneer"}},"作成者":{"type":"CREATOR","value":{"code":"cy-pioneer","name":"cy-pioneer"}},"playername":{"type":"SINGLE_LINE_TEXT","value":"ゆき"},"$revision":{"type":"__REVISION__","value":"1"},"更新日時":{"type":"UPDATED_TIME","value":"2024-06-18T04:34:00Z"},"作成日時":{"type":"CREATED_TIME","value":"2024-06-18T04:34:00Z"},"$id":{"type":"__ID__","value":"1"}}],"totalCount":null}
Wait five seconds

取得する件数が多かったり、通信環境の影響でレコード情報を取得するまでの時間がかかる場合、後述するコードの改良点を参考にして下さい。

レコードを追加する

kintoneの1件のレコードを登録するAPIを使ってレコードを追加します。

loop内の処理を下記のように書き換えます。接続先のcybozu.comのサブドメイン、アプリIDややAPIトークンは自分のものに変更して下さい。

void loop()
{
  // Set certifications via a file on the SD card before connecting to the server
  File rootCertsFile = theSD.open(ROOTCA_FILE, FILE_READ);
  tlsClient.setCACert(rootCertsFile, rootCertsFile.available());
  rootCertsFile.close();

  // Make JSON Request Body
  JSONVar jsonbody;
  jsonbody["app"] = 70;
  jsonbody["record"]["playername"]["value"] = "Gyozadon";
  jsonbody["record"]["difficulty"]["value"] = "Hard";
  jsonbody["record"]["score"]["value"] = "52000";

  String jsonbody_string = JSON.stringify(jsonbody);
  Serial.println("Post Body: " + jsonbody_string);

  // HTTP POST method
  Serial.println("Starting POST request...");
  client.beginRequest();
  Serial.println("Begenning request");

  //Create endpoint URL for kintone Add Record API
  String POSTURL = "https://cy-hwg.cybozu.com/k/v1/record.json";

  //Make API request
  client.post(POSTURL);
  client.sendHeader("X-Cybozu-API-Token", "edtlR7VzFTiQY7ODPOihoCHmeALwYoJYy6Mk8akw");
  client.sendHeader("Content-Type", "application/json");
  client.sendHeader("Content-length",jsonbody_string.length());
  client.beginBody();
  client.print(jsonbody_string);
  Serial.println("Ending request...");
  client.endRequest();
  Serial.println("Request has ended");

  // read the status code and body of the response
  Serial.println("Reading status code...");
  int statusCode = client.responseStatusCode();
  Serial.print("Status code: ");
  Serial.println(statusCode);
  Serial.println("Reading response...");
  String response = client.responseBody();
  Serial.println("Response: " + response);  
  Serial.println("Wait five seconds");

  sleep(5);
}

シリアルモニタに情報が出力され、しばらくするとレコードIDが入ったJSONもレスポンスされます。

Post Body: {"app":70,"record":{"playername":{"value":"Gyozadon"},"difficulty":{"value":"Hard"},"score":{"value":"52000"}}}
Starting POST request...
Begenning request
Ending request...
Request has ended
Reading status code...
Status code: 200
Reading response...
Response: {"id":"27","revision":"1"}

ブラウザ上のkintoneアプリを確認すれば、レコードが追加されてるはずです。

Spresense経由で追加された新しいレコード

コードの改良点

通信状況によっては、GETリクエストでJSONのレスポンスが手に入るまで時間がかかるかもしれません。

kintoneのレコード取得のAPIはいくつかのパラメータを付与することが出来ます。これを駆使することにより、返ってくるJSONのサイズを大幅に縮小することができ、手元に返ってくる時間を減らすことが出来ます。

fields パラメータはJSONに返ってくるフィールドを制限することが出来ます。今回の例ではアプリ作成時にフィールドは3つしか設定してませんが、それ以外にもデフォルトで備わっている「作成者」や「更新者」(これは誰がレコードを作ったり更新したかを記録するフィールド)といったフィールド情報がレスポンスに返ってきます。

query パラメータは条件がマッチするレコードだけが取得するように出来ます。また、データの昇順/降順の指定や最大取得件数の指定も出来ます。

レスポンスに含まれるフィールドをplayernamescoredifficultyのみにして、difficultyに「Hard」が含まれ、scoreの高い順に2件だけレコードを取得する場合、コードでURLを指定する部分を下記のように変更出来ます:

  String baseURL = "https://cy-hwg.cybozu.com/k/v1/records.json";
  String appURL = "?app=70";
  String fieldsparam = "&fields[0]=playername&fields[1]=difficulty&fields[2]&";
  String queryparam = urlEncode("difficulty in ( \"Hard \") order by score desc limit 2");
  String GETURL = baseURL + appURL + fieldsparam + queryparam;
  Serial.println(GETURL);

下記のような制限された情報のJSONが出力されるので、レスポンス時間を短縮することが出来ます。

{"records":[{"difficulty":{"type":"RADIO_BUTTON","value":"Hard"},"playername":{"type":"SINGLE_LINE_TEXT","value":"Gyozadon"}},{"difficulty":{"type":"RADIO_BUTTON","value":"Medium"},"playername":{"type":"SINGLE_LINE_TEXT","value":"QED"}},{"difficulty":{"type":"RADIO_BUTTON","value":"Easy"},"playername":{"type":"SINGLE_LINE_TEXT","value":"Kiri"}},{"difficulty":{"type":"RADIO_BUTTON","value":"Hard"},"playername":{"type":"SINGLE_LINE_TEXT","value":"ボブ"}},{"difficulty":{"type":"RADIO_BUTTON","value":"Hard"},"playername":{"type":"SINGLE_LINE_TEXT","value":"HAL"}},{"difficulty":{"type":"RADIO_BUTTON","value":"Easy"},"playername":{"type":"SINGLE_LINE_TEXT","value":"ゆき"}}],"totalCount":null}

状況に合わせて、最低限の情報の取得で済ませられるように調整してみましょう!