編集履歴一覧に戻る
Cybozu_kintoneのアイコン画像

Cybozu_kintone が 2024年11月20日19時55分53秒 に編集

初版

タイトルの変更

+

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

タグの変更

+

SPRESENSE

+

HTTP

+

LTE

+

kintone

+

REST

+

JSON

メイン画像の変更

メイン画像が設定されました

記事種類の変更

+

セットアップや使用方法

ライセンスの変更

+

(MIT) The MIT License

本文の変更

+

この記事ではSONY SpresenseのLTE 拡張ボードを使い、クラウドサービスのkintoneにHTTPリクエストをする方法を案内します。 # SONY Spresenseとは SONY Spresense (以降、Spresense)は低消費電力でありながら、GPS受信機能とハイレゾリューションオーディオコーデックを搭載したIoT用ボードコンピュータです。この記事ではSpresenseメインボードをLTE拡張ボードにとりつけて、kintoneにHTTPリクエストをする方法を案内します。 # この記事で作るもの この記事ではkintoneでスコアランキングアプリを作り、SpresenseのLTE経由でランキングを取得したり、スコアを追加する方法を案内します。 ![キャプションを入力できます](https://camo.elchika.com/f3d2d6f1036e208b99d6bb9346cabfce1de38132/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f32303437356336382d353434372d343230342d383165352d3331383434383961386532652f36616438383333632d373766642d343963382d383533362d386531333464346131623664/) # 連携の作り方 ## kintoneアプリの準備 まずはkintone側の準備から説明します。 ### kintone環境の準備する kintoneの環境が無い方は、開発者サイトから1年間無償で使用できる『開発者ライセンス』という名のkintone環境を取得出来ます。下記のサイトで『開発者ライセンスを申し込む』をクリックし、フォームに必要事項を入力して下さい。申込後、メールでkintone環境の情報が送られてきます。所要時間はおおよそ10分程度です。 [https://cybozu.dev/ja/kintone/developer-license/:embed:cite] ### kintoneアプリを作成する kintoneで作るウェブデータベースは『アプリ』と呼ばれます。 まず、ゲームのスコアを管理するための[kintoneアプリを作成](https://jp.cybozu.help/k/ja/id/040638.html)します。プレイヤー名、スコア、難易度等、必要な情報をイメージしながら作りましょう。 <figure class="figure-image figure-image-fotolife" title="kintoneアプリに配置するフィールド">[f:id:kintoneGeeks:20240618132808p:plain]<figcaption>kintoneアプリに配置するフィールド</figcaption></figure> この記事で作成するアプリに必要なフィールドは下記のデーブルに記載したので、参考にしてください。フィールドを配置したら、[一つ一つ設定を変更することが出来る](https://jp.cybozu.help/k/ja/id/040554.html#form_set_form_30)ので、表示名、フィールドコードやオプションなどを変更します。ちなみに**表示名**はUI上でユーザに見えるフィールドの名前で、**フィールドコード**はフィールドのユニークな識別子になります。フィールドコードは外部からHTTPリクエストをする際に重要な設定となります。 | **フィールドタイプ** | **表示名** | **フィールドコード** | **その他の設定** | |----------------------|------------|----------------------|----------------------| | 文字列(1行) | プレイヤー名 | playername | | | 数値 | スコア | score | 単位記号に『points』を追加して後ろに付ける | | ラジオボタン | 難易度 | difficulty | 選択肢に 『Easy』『Medium』『Hard』 を追加する | フィールドの配置や設定が終わったら、右上の青いボタンからアプリを公開します。 ### kintoneアプリにレコードを追加する ウェブデータベースを作成出来ましたが、データがまだ入っていません。適当に[手動でいくつかデータ(レコード)を追加](https://jp.cybozu.help/k/ja/id/040715.html)しましょう。 <figure class="figure-image figure-image-fotolife" title="レコードが登録されたkintoneアプリ">[f:id:kintoneGeeks:20240621210928p:plain]<figcaption>レコードが登録されたkintoneアプリ</figcaption></figure> ### kintoneアプリからAPIトークンを発行する 他のシステムと連携をするために、kintoneアプリから認証用のトークンを発行します。アプリの設定画面から[APIトークンを生成します](https://jp.cybozu.help/k/ja/id/040471.html)。「レコード閲覧」と「レコード追加」のアクセス権にチェックを入れます。APIトークンを手元でメモした後に、設定を保存してアプリを更新します。 <figure class="figure-image figure-image-fotolife" title="適切な権限設定が付与されたAPIトークン">[f:id:kintoneGeeks:20240621211109p:plain]<figcaption>適切な権限設定が付与されたAPIトークン</figcaption></figure> これでkintoneアプリの準備が出来ました。 ## Spresenseの準備 次に、Spresense側で必要なデバイスや設定を準備していきます。 ### 必要なデバイスを揃える SpresenseでLTE通信するためには下記のデバイスが必要です: - [SPRESENSE メインボード](https://developer.sony.com/ja/spresense/products/spresense-main-board/) - [SPRESENSE LTE拡張ボード](https://developer.sony.com/ja/spresense/products/spresense-lte-ext-board/) - MicroSDカード - nanoSIMカード <figure class="figure-image figure-image-fotolife" title="本記事で使用するSpresenseのパーツ、MicroSDとSIMカード">[f:id:kintoneGeeks:20240618154220j:plain]<figcaption>本記事で使用するSpresenseのパーツ、MicroSDとSIMカード</figcaption></figure> ### SIMカードをアクティベートする まずはSIMカードで通信が出来るようにアクティベートをしましょう。アクティベートする方法は各社で方法が異なるので、SIMカードのマニュアルの説明を良く読んでアクティベートをします。 この記事では『さくらのセキュアモバイルコネクト』を使用しており、さくらのクラウドアカウントからモバイルゲートウェイを作成してからSIMカードをアクティベートする必要がありました。 SIMカードのアクティベートが出来たら、LTE拡張ボードに差し込みます。 ### Spresenseの開発環境を準備する Spresense Arduino スタートガイドに沿ってSpresense Arduino Libraryをインストールし、 Arduino IDE 、USB ドライバとSpresense Arduino board packageをインストールします。プログラミング環境の設定をし、LEDのスケッチを動かすところまで進めて、ボードへの書き込みがきちんと出来るかを確認します。 [https://developer.sony.com/spresense/development-guides/arduino_set_up_ja.html:embed:cite] ### LTE環境関連のスケッチを動かす ここからは、Spresenseの開発サイトで案内されているLTEチュートリアルを元にいくつかのスケッチを試し、少しずつ動作確認をしていきます。 [https://developer.sony.com/spresense/development-guides/arduino_tutorials_ja.html#_tutorial_lte:embed:cite] 各スケッチは【ファイル】→【スケッチ例】→【LTE】から開くことが出来ます。また、各スケッチの結果はシリアルモニタに表示されるので、【ツール】→【シリアルモニタ】でArduino IDEに表示させましょう。 #### LteTestModem のスケッチ例を動かす LteTestModemのスケッチを動かします。特にコードに変更は必要ありません。Spresenseに書き込みをしたら、シリアルモニタに下記のような情報が出力されます。 ```ps1 IMEI: 351521105181572 VERSION: RK_03_00_00_22_13151_002 RAT: LTE-M (LTE Cat-M1) ``` #### LteScanNetworks.ino のスケッチ例を動かす LteScanNetworksのスケッチを動かします。コード内のAPN情報を、自分のSIMカードに合わせたものに変更します。 ```cpp // APN name #define APP_LTE_APN "sakura" ``` Spresenseに書き込みをし、しばらくしたらコンソールに下記のような情報が定期的に出力されます。 ```ps1 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カードに合わせたものに変更します。 ```cpp // APN name #define APP_LTE_APN "sakura" ``` Spresenseに書き込みをし、しばらくしたらコンソールに色々な情報が出力されます。 ```ps1 ;;; ;;;;;` ;;;;: .;; ;; ,;;;;;, ;;. `;, ;;;; ;;; ;;:;;; ;;;;;; .;; ;; ,;;;;;: ;;; `;, ;;;:;; ,;:; ;; ;; ;; ;; .;; ;; ,;, ;;;,`;, ;; ;; ;; ;: ;; ;; ;; ;; .;; ;; ,;, ;;;;`;, ;; ;;. ;: ;; ;;;;;: ;; ;; .;; ;; ,;, ;;`;;;, ;; ;;` ,;;;;; ;;`;; ;; ;; .;; ;; ,;, ;; ;;;, ;; ;; ;; ,;, ;; .;; ;;;;;: ;;;;;: ,;;;;;: ;; ;;, ;;;;;; ;; ;; ;; ;;` ;;;;. `;;;: ,;;;;;, ;; ;;, ;;;; ``` 今までのスケッチ例で別の情報が出力された場合、SIMカードが正しく動作してない可能性があります。 ここまで問題なく出力されたら、Spresenseの準備は出来たので、次に進みましょう。 ## SpresenseからkintoneにHTTPリクエスト Spresenseからkintoneに対してHTTPリクエストを行いますが、kintoneが稼働しているcybozu.comのサーバにはHTTPではなくHTTPSで通信を行う必要があります。Spresenseにcybozu.comのサーバが安全なサーバだと伝えるために、cybozu.comの証明書をSpresenseに保管する必要があります。 ### 証明書の準備をする ブラウザによって証明書の取得方法がことなります。この記事ではChromeブラウザで入手する方法を案内します。 まずkintoneにログインし、URL近くのメニューから「この接続は保護されています」を選択し、「証明書は有効です」をクリックします。 <figure class="figure-image figure-image-fotolife" title="Chromeでcybozu.comの証明書を取得する場合">[f:id:kintoneGeeks:20240622020650p:plain]<figcaption>Chromeでcybozu.comの証明書を取得する場合</figcaption></figure> 続いて「詳細」タブを選択し、一番上のCertification Authorityと記載されてる箇所を選択します。この状態で下のエクスポートボタンをクリックします。このページではデフォルトで違う箇所が選択されるので、間違えてそちらをエクスポートしないように気をつけてください。Certification Authorityと記載されてる方をエクスポートしてください。 <figure class="figure-image figure-image-fotolife" title="正しい証明書を選択してエクスポートする">[f:id:kintoneGeeks:20240621233851p:plain]<figcaption>正しい証明書を選択してエクスポートする</figcaption></figure> 保存方法が色々と提示されますが、その中から「DER エンコードバイナリ形式の単一の証明書」を選んで保存します。 <figure class="figure-image figure-image-fotolife" title="どのエンコーディングで証明書を保存するかを選択する">[f:id:kintoneGeeks:20240621234037p:plain]<figcaption>どのエンコーディングで証明書を保存するかを選択する</figcaption></figure> 証明書がダウンロード出来たら、SDカードをパソコンに接続し、CERTSディレクトリを作成し、その中に証明書ファイルを入れます。証明書のファイル名は好きなものにします。 <figure class="figure-image figure-image-fotolife" title="SDカードリーダー等を使って証明書を保存する">[f:id:kintoneGeeks:20240621234125p:plain]<figcaption>SDカードリーダー等を使って証明書を保存する</figcaption></figure> SDカードに保存が出来たら、SDカードをSpresenseのLTE拡張ボード内に差し込みます。LTE拡張ボードにも電源供給をします。 <figure class="figure-image figure-image-fotolife" title="SIMカードとSDカードがセットされ、電源供給されたSpresenseとLTE拡張ボード">[f:id:kintoneGeeks:20240621215148p:plain]<figcaption>SIMカードとSDカードがセットされ、電源供給されたSpresenseとLTE拡張ボード</figcaption></figure> ### コードの下準備をする スケッチ例からLteHTTPSecureClientを選択します。ArduinoHttpClientライブラリ、Arduino_JSONライブラリとURLEncodeライブラリをインストールし、コードの上部にライブラリをincludeします。 ```cpp #include <ArduinoHttpClient.h> #include <Arduino_JSON.h> #include <UrlEncode.h> ``` 他のスケッチ例のように、APN情報を更新します ```cpp // APN name #define APP_LTE_APN "sakura" ``` SDカード内の証明書のパスを更新します ```cpp #define ROOTCA_FILE "CERTS/cybozu.cer" ``` URLやパスを指定する箇所でご自身のkintone環境のサブドメインを含めたcybozu.comのサーバURLを指定し、getやpostのパスをコメントアウトします。 ```cpp char server[] = "cy-hwg.cybozu.com"; //char getPath[] = "/get"; //char postPath[] = "/post"; ``` ### レコードの取得をする まずはkintoneの[複数のレコードを取得するAPI](https://cybozu.dev/ja/kintone/docs/rest-api/records/get-records/)を使って複数のレコードをアプリから取得します。 loop内の処理を下記のように書き換えます。接続先のcybozu.comのサブドメイン、アプリIDやAPIトークンは自分のものに変更して下さい。 ```cpp 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のレスポンスも出力されます。 ```ps1 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](https://cybozu.dev/ja/kintone/docs/rest-api/records/add-record/)を使ってレコードを追加します。 loop内の処理を下記のように書き換えます。接続先のcybozu.comのサブドメイン、アプリIDややAPIトークンは自分のものに変更して下さい。 ```cpp 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もレスポンスされます。 ```ps1 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アプリを確認すれば、レコードが追加されてるはずです。 <figure class="figure-image figure-image-fotolife" title="Spresense経由で追加された新しいレコード">[f:id:kintoneGeeks:20240622012747p:plain]<figcaption>Spresense経由で追加された新しいレコード</figcaption></figure> ## コードの改良点 通信状況によっては、GETリクエストでJSONのレスポンスが手に入るまで時間がかかるかもしれません。 kintoneのレコード取得のAPIは[いくつかのパラメータ](https://cybozu.dev/ja/id/ba1703e9391653c667ce958b/#request)を付与することが出来ます。これを駆使することにより、返ってくるJSONのサイズを大幅に縮小することができ、手元に返ってくる時間を減らすことが出来ます。 `fields` パラメータはJSONに返ってくるフィールドを制限することが出来ます。今回の例ではアプリ作成時にフィールドは3つしか設定してませんが、それ以外にもデフォルトで備わっている「作成者」や「更新者」(これは誰がレコードを作ったり更新したかを記録するフィールド)といったフィールド情報がレスポンスに返ってきます。 `query` パラメータは条件がマッチするレコードだけが取得するように出来ます。また、データの昇順/降順の指定や最大取得件数の指定も出来ます。 レスポンスに含まれるフィールドを`playername`、`score`と`difficulty`のみにして、difficultyに「Hard」が含まれ、scoreの高い順に2件だけレコードを取得する場合、コードでURLを指定する部分を下記のように変更出来ます: ```cpp 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が出力されるので、レスポンス時間を短縮することが出来ます。 ```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} ``` 状況に合わせて、最低限の情報の取得で済ませられるように調整してみましょう!