SONY Spresense用 LTE-M Addon board 接続 テスト

弊社(合同会社ネクストステップ) で販売中の **SONY Spresense用 LTE-M Addon board**の
LTE-Mへの接続のテスト方法です。
SIMカードは **さくらのセキュアモバイルSIM** を使っています。

#1. LTE-M Addon board の取り付けと設定##
LTE-M Addon board は出荷時、Status と NetLight LEDに電源を供給しない設定となっていますので
2つのLEDを点灯させたい場合は ジャンパー(JP1) を半田でショートさせてください。

Spresenseメインボードへの取り付けは写真を参考にしてください。
なお、テストの際はアンテナは必ず取り付けてください。
![Spresense_LTE-M.fw.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/32e13ef5-44d4-294f-e378-16bca56e7d50.png)

#2. ターミナルを使っての 接続テスト##
Arduino ide のサンプルプログラム **MultiSerial** を Spresense用に変更

```MultiSerial.ino
/*
  Multiple Serial test

  Receives from the main serial port, sends to the others.
  Receives from serial port 1, sends to the main serial (Serial 0).

  This example works only with boards with more than one serial like Arduino Mega, Due, Zero etc.

  The circuit:
  - any serial device attached to Serial port 1
  - Serial Monitor open on Serial port 0

  created 30 Dec 2008
  modified 20 May 2012
  by Tom Igoe & Jed Roach
  modified 27 Nov 2015
  by Arturo Guadalupi

  This example code is in the public domain.
*/

#define PWKEY 20

void setup() {
  // initialize both serial ports:
  Serial.begin(115200);
  Serial2.begin(115200, SERIAL_8N1 | SERIAL_RTSCTS) ;

  pinMode(PWKEY, OUTPUT);
  digitalWrite(PWKEY, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(300);                       // wait for a second
  digitalWrite(PWKEY, LOW);    // turn the LED off by making the voltage LOW
  delay(5000);                       // wait for a second
  Serial.println("Serial monitor started.");
}

void loop() {
  // read from port 1, send to port 0:
  if (Serial2.available()) {
    int inByte = Serial2.read();
    Serial.write(inByte);
  }

  // read from port 0, send to port 1:
  if (Serial.available()) {
    int inByte = Serial.read();
    Serial2.write(inByte);
  }
}
```

上記のスケッチを書き込み後、ターミナルから以下のようにATコマンドを送信
※ 改行コードは **CR及びLF** を選択　ボーレートは**115200**
※ 送信した文字が表示されない場合は **ATE1** コマンドを送信
※ 各コマンドの意味は[SIMCOM社のSIM7080Gでさくらセキュアモバイルの通信テスト](https://qiita.com/kaz19610303/items/83f2b722fc1264888fab)を参照

```
at

OK
AT+CGDCONT=1,"IP","sakura"

OK
AT+CNCFG=1,1,"sakura"

OK
AT+COPS=1,2,"44020"

OK
AT+COPS?

+COPS: 1,2,"44020",7

OK
AT+CPIN?

+CPIN: READY

OK
AT+CNMP=38

OK
AT+CMNB=1

OK
AT+CSQ

+CSQ: 31,99

OK
AT+CGREG?

+CGREG: 0,5

OK
AT+CGNAPN

+CGNAPN: 1,"sakura"

OK
AT+CPSI?

+CPSI: LTE CAT-M1,Online,440-20,0x4A58,113648221,143,EUTRAN-BAND8,3750,3,3,-7,-68,-47,16

OK
AT+CNACT=0,1

OK

+APP PDP: 0,ACTIVE
AT+CNACT?

+CNACT: 0,1,"192.168.0.12"
+CNACT: 1,0,"0.0.0.0"
+CNACT: 2,0,"0.0.0.0"
+CNACT: 3,0,"0.0.0.0"

OK
AT+SNPING4="google.com",3,16,1000

+SNPING4: 1,142.250.196.110,47
+SNPING4: 2,142.250.196.110,118
+SNPING4: 3,142.250.196.110,119

OK

```

##3. Tynygsm WebClient.ino でのテスト##
TinyGSMのサンプルプログラム **WebClient.ino** を Spresense SIM7090G LTE-M Addon board 用に修正
[TinyGSMはこちらから入手できます](https://github.com/vshymanskyy/TinyGSM)
**修正箇所**
・モジュールの#define を TINY_GSM_MODEM_SIM7080 に変更
・#define SerialAT Serial  →　#define SerialAT Seria2 に変更
・APN の設定を sakura に変更
　　const char apn[]      = "sakura";
・SIM7090G　を アクティブ にする処理を追加
　　pinMode(PWKEY, OUTPUT);
　　digitalWrite(PWKEY, HIGH);   // turn the LED on (HIGH is the voltage level)
　　delay(500);                  // wait for a second
　　digitalWrite(PWKEY, LOW);    // turn the LED off by making the voltage LOW

```Spresense_WebClient.ino
/**************************************************************
 *
 * This sketch connects to a website and downloads a page.
 * It can be used to perform HTTP/RESTful API calls.
 *
 * TinyGSM Getting Started guide:
 *   https://tiny.cc/tinygsm-readme
 *
 **************************************************************/

// Select your modem:
// #define TINY_GSM_MODEM_SIM800
// #define TINY_GSM_MODEM_SIM808
// #define TINY_GSM_MODEM_SIM868
// #define TINY_GSM_MODEM_SIM900
// #define TINY_GSM_MODEM_SIM7000
// #define TINY_GSM_MODEM_SIM7000SSL
#define TINY_GSM_MODEM_SIM7080
// #define TINY_GSM_MODEM_SIM5360
// #define TINY_GSM_MODEM_SIM7600
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_SARAR4
// #define TINY_GSM_MODEM_M95
// #define TINY_GSM_MODEM_BG96
// #define TINY_GSM_MODEM_A6
// #define TINY_GSM_MODEM_A7
// #define TINY_GSM_MODEM_M590
// #define TINY_GSM_MODEM_MC60
// #define TINY_GSM_MODEM_MC60E
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_XBEE
// #define TINY_GSM_MODEM_SEQUANS_MONARCH

// Set serial for debug console (to the Serial Monitor, default speed 115200)
#define SerialMon Serial

// Set serial for AT commands (to the module)
// Use Hardware Serial on Mega, Leonardo, Micro
#ifndef __AVR_ATmega328P__
#define SerialAT Serial2

// or Software Serial on Uno, Nano
#else
#include <SoftwareSerial.h>
SoftwareSerial SerialAT(2, 3);  // RX, TX
#endif

// Increase RX buffer to capture the entire response
// Chips without internal buffering (A6/A7, ESP8266, M590)
// need enough space in the buffer for the entire response
// else data will be lost (and the http library will fail).
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 650
#endif

// See all AT commands, if wanted
// #define DUMP_AT_COMMANDS

// Define the serial console for debug prints, if needed
#define TINY_GSM_DEBUG SerialMon

// Range to attempt to autobaud
// NOTE:  DO NOT AUTOBAUD in production code.  Once you've established
// communication, set a fixed baud rate using modem.setBaud(#).
#define GSM_AUTOBAUD_MIN 9600
#define GSM_AUTOBAUD_MAX 115200

// Add a reception delay, if needed.
// This may be needed for a fast processor at a slow baud rate.
// #define TINY_GSM_YIELD() { delay(2); }

// Uncomment this if you want to use SSL
// #define USE_SSL

// Define how you're planning to connect to the internet.
// This is only needed for this example, not in other code.
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false

// set GSM PIN, if any
#define GSM_PIN ""

// Your GPRS credentials, if any
const char apn[]      = "sakura";
const char gprsUser[] = "";
const char gprsPass[] = "";

// Your WiFi connection credentials, if applicable
const char wifiSSID[] = "YourSSID";
const char wifiPass[] = "YourWiFiPass";

// Server details
const char server[]   = "vsh.pp.ua";
const char resource[] = "/TinyGSM/logo.txt";

#include <TinyGsmClient.h>

// Just in case someone defined the wrong thing..
#if TINY_GSM_USE_GPRS && not defined TINY_GSM_MODEM_HAS_GPRS
#undef TINY_GSM_USE_GPRS
#undef TINY_GSM_USE_WIFI
#define TINY_GSM_USE_GPRS false
#define TINY_GSM_USE_WIFI true
#endif
#if TINY_GSM_USE_WIFI && not defined TINY_GSM_MODEM_HAS_WIFI
#undef TINY_GSM_USE_GPRS
#undef TINY_GSM_USE_WIFI
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
#endif

#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm        modem(debugger);
#else
TinyGsm        modem(SerialAT);
#endif

#ifdef USE_SSL
TinyGsmClientSecure client(modem);
const int           port = 443;
#else
TinyGsmClient  client(modem);
const int      port = 80;
#endif
#define PWKEY 20

void setup() {
  // Set console baud rate
  SerialMon.begin(115200);
  delay(10);

  // !!!!!!!!!!!
  // Set your reset, enable, power pins here
  // !!!!!!!!!!!
  pinMode(PWKEY, OUTPUT);
  digitalWrite(PWKEY, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(500);                       // wait for a second
  digitalWrite(PWKEY, LOW);    // turn the LED off by making the voltage LOW

  SerialMon.println("Wait...");

  // Set GSM module baud rate
  TinyGsmAutoBaud(SerialAT, GSM_AUTOBAUD_MIN, GSM_AUTOBAUD_MAX);
  // SerialAT.begin(9600);
  delay(6000);

  // Restart takes quite some time
  // To skip it, call init() instead of restart()
  SerialMon.println("Initializing modem...");
  modem.restart();
  // modem.init();

  String modemInfo = modem.getModemInfo();
  SerialMon.print("Modem Info: ");
  SerialMon.println(modemInfo);

#if TINY_GSM_USE_GPRS
  // Unlock your SIM card with a PIN if needed
  if (GSM_PIN && modem.getSimStatus() != 3) { modem.simUnlock(GSM_PIN); }
#endif
}

void loop() {
#if TINY_GSM_USE_WIFI
  // Wifi connection parameters must be set before waiting for the network
  SerialMon.print(F("Setting SSID/password..."));
  if (!modem.networkConnect(wifiSSID, wifiPass)) {
    SerialMon.println(" fail");
    delay(10000);
    return;
  }
  SerialMon.println(" success");
#endif

#if TINY_GSM_USE_GPRS && defined TINY_GSM_MODEM_XBEE
  // The XBee must run the gprsConnect function BEFORE waiting for network!
  modem.gprsConnect(apn, gprsUser, gprsPass);
#endif

  SerialMon.print("Waiting for network...");
  if (!modem.waitForNetwork()) {
    SerialMon.println(" fail");
    delay(10000);
    return;
  }
  SerialMon.println(" success");

  if (modem.isNetworkConnected()) { SerialMon.println("Network connected"); }

#if TINY_GSM_USE_GPRS
  // GPRS connection parameters are usually set after network registration
  SerialMon.print(F("Connecting to "));
  SerialMon.print(apn);
  if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
    SerialMon.println(" fail");
    delay(10000);
    return;
  }
  SerialMon.println(" success");

  if (modem.isGprsConnected()) { SerialMon.println("GPRS connected"); }
#endif

  SerialMon.print("Connecting to ");
  SerialMon.println(server);
  if (!client.connect(server, port)) {
    SerialMon.println(" fail");
    delay(10000);
    return;
  }
  SerialMon.println(" success");

  // Make a HTTP GET request:
  SerialMon.println("Performing HTTP GET request...");
  client.print(String("GET ") + resource + " HTTP/1.1\r\n");
  client.print(String("Host: ") + server + "\r\n");
  client.print("Connection: close\r\n\r\n");
  client.println();

  uint32_t timeout = millis();
  while (client.connected() && millis() - timeout < 10000L) {
    // Print available data
    while (client.available()) {
      char c = client.read();
      SerialMon.print(c);
      timeout = millis();
    }
  }
  SerialMon.println();

  // Shutdown

  client.stop();
  SerialMon.println(F("Server disconnected"));

#if TINY_GSM_USE_WIFI
  modem.networkDisconnect();
  SerialMon.println(F("WiFi disconnected"));
#endif
#if TINY_GSM_USE_GPRS
  modem.gprsDisconnect();
  SerialMon.println(F("GPRS disconnected"));
#endif

  // Do nothing forevermore
  while (true) { delay(1000); }
}
```

上記のすれけっちを実行したときのターミナル表示

```terminal.log
15:50:01.532 -> cxd56_farapiinitialize: Mismatched version: loader(20175) != Self(20585)
15:50:01.579 -> cxd56_farapiinitialize: Please update loader and gnssfw firmwares!!
15:50:02.325 -> Wait...
15:50:02.325 -> [1679] Trying baud rate 115200 ...
15:50:05.222 -> [3691] Modem responded at rate 115200
15:50:11.238 -> Initializing modem...
15:50:22.254 -> Modem Info: 
15:50:32.269 -> Waiting for network... success
15:50:48.024 -> Network connected
15:50:48.024 -> Connecting to sakura success
15:50:49.097 -> GPRS connected
15:50:49.097 -> Connecting to vsh.pp.ua
15:50:49.561 ->  success
15:50:49.561 -> Performing HTTP GET request...
15:50:50.262 -> [48694] ### Got Data: 0
15:50:50.262 -> [48697] ### Closed:  0
15:50:50.356 -> HTTP/1.1 200 OK
15:50:50.356 -> Server: nginx/1.10.3 (Ubuntu)
15:50:50.356 -> Date: Tue, 23 Nov 2021 06:50:50 GMT
15:50:50.356 -> Content-Type: text/plain; charset=UTF-8
15:50:50.356 -> Content-Length: 121
15:50:50.356 -> Connection: close
15:50:50.356 -> X-DNS-Prefetch-Control: off
15:50:50.356 -> X-Frame-Options: SAMEORIGIN
15:50:50.356 -> Strict-Transport-Security: max-age=15552000; includeSubDomains
15:50:50.356 -> X-Download-Options: noopen
15:50:50.356 -> X-Content-Type-Options: nosniff
15:50:50.356 -> X-XSS-Protection: 1; mode=block
15:50:50.356 -> Access-Control-Allow-Origin: *
15:50:50.356 -> Access-Control-Allow-Headers: X-Requested-With
15:50:50.403 -> Accept-Ranges: bytes
15:50:50.403 -> Cache-Control: public, max-age=0
15:50:50.403 -> Last-Modified: Wed, 27 Sep 2017 09:03:12 GMT
15:50:50.403 -> ETag: W/"79-15ec2936080"
15:50:50.403 -> 
15:50:50.403 -> 
15:50:50.403 ->   _____            _____  _____  _____
15:50:50.403 ->     |  | |\ | \_/ |  ___ |_____ |  |  |
15:50:50.403 ->     |  | | \|  |  |_____| _____||  |  |
15:50:50.403 -> 
15:50:50.403 -> 
15:50:50.403 -> Server disconnected
15:51:06.643 -> GPRS disconnected
```

次回は この LTE-M Addon board を使って **CO2センサー** の計測データをサーバーにアップするサンプルを公開します。