M5Stack用3G拡張ボードを使う際、PIN(16,17)を利用するデバイスの対応

## どういうこと？

[M5Stack用3G拡張ボード](https://soracom.jp/products/kit/3g_module_m5stack/) は、M5Stack本体との通信に GPIO PIN の `(16, 17)` をUARTで利用します。一方で、その他の拡張ボードでも通信に同じPINを使うケースがあります。例えば [M5Stack 用 GPS 拡張ボード](https://www.switch-science.com/catalog/3861/)です。

これらのボードを組み合わせて利用する場合、標準の状態ではPINが競合するため利用ができませんので、これについて対応する方法を記載しています。

※ 不可逆な改造を含みますのでAt Your Own Riskでお願いしますー

## M5Stack 用 3G 拡張ボード側では対応できません
M5Stack用3G拡張ボード(以下、3G 拡張ボード)にはピンアサイン(通信に使用するPIN)を変更する機能がありません。そのため、その他の拡張ボード側で対応※する必要があります。

※ その他の拡張ボード側にピンアサインを変更する機能が求められますし、ない場合は、、、残念ですが、どちらか一方を使うことになります (-人-)

## M5Stack用GPS拡張ボードの場合 （ピンアサイン変更可能モジュール）

M5Stack用GPS拡張ボードはピンアサイン変更機能が存在するため、3G拡張ボードと組み合わせて利用することが可能です。

具体的にはハードウェア側、ソフトウェア側それぞれの対応を行い、`(16, 17)` から `(13, 5)` に変更します。

### 準備

- カッター
- ハンダごて、ハンダ、ハンダ吸取
- テスター
- 銅線など（オプション）

### ハードウェア編

※ 不可逆な改造を含みますのでAt Your Own Riskでお願いしますー

- `(16, 17)` の「白い線」をカッターで切断します。（正確には白い線の下に実装されている回路パターンを切り離します）
- `(13, 5)` のターミナル(盛り上がってる部分)を銅線（もしくはハンダ自体）でつなげます

※ テスターを当てながら、切断ができているか、接続ができているかを確認しましょう

![9f9e3cc4d5dc87a9be01794a414888fc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22754/9aaa917d-4322-b53f-4519-0ed8fcc46746.png)
実際にやった様子
![2a37efe3a297b3b4a669fdc468c0eab6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22754/451b5219-3eaf-03ca-35f0-88037e13503b.png)

`(16, 17)` に戻す場合は、 `(13, 5)` を切断（ハンダ吸い取り）をし、`(16, 17)`  を接続します。

### ソフトウェア編

`(13, 5)` を利用する最小限のコードは以下の通りです。

```c++
 HardwareSerial M5StackGPS(1);
 void setup() {
 	M5StackGPS.begin(9600, SERIAL_8N1, 13, 5);
 }
```

[arduino-esp32](https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/HardwareSerial.h#L100)では `Serial2` までが定義されていますが、`(13, 5)` を指定している `HardwareSerial` は定義されていないため、直接 `(13, 5)` をUART通信用ピンとして指定します。

`HardwareSerial` インスタンス生成時の引数 `1` については、[0 ~ 2 のうちの整数を指定](https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/HardwareSerial.cpp#L35)すればOKなのですが、 `0` を指定するとコンソール用のUARTと競合することがわかっているため、`1` もしくは `2` を指定します。

その後、`begin` の引数で利用するピン指定が上書きされるため、インスタンスを生成するためだけのおまじないだと理解いただければ。

※ ~~ちなみに arduino-esp32 では SoftwareSerial が利用できません~~ → [使えました](https://qiita.com/ma2shita/items/37d403fb7a79814d4d4c#espsoftwareserial)

### 3G拡張ボードと組み合わせて使う例

これまでの作業で3G拡張ボードとの競合は解消されたので、同時に利用することができるようになります。

位置情報を取得し、SORACOM Harvest Data へ送信するコード (UnifiedEndpoint 利用) の例です。

- 事前準備で http://arduiniana.org/libraries/tinygpsplus/ をライブラリ追加してください

```c++:m5stack_with_gps_and_3g.ino
#include <M5Stack.h>
#include <string.h>
#define CONSOLE Serial
​
/* Required: change the PIN assignment from 16,17 to 13,5 on the GPS module of M5Stack. */
HardwareSerial M5StackGPS(1); /* place of `1` required 1 or 2 (overrided by  `#begin`) see: https://github.com/espressif/arduino-sp32/blob/master/cores/esp32/HardwareSerial.cpp#L33 */
#include <TinyGPS++.h>
TinyGPSPlus gps;
​
#define TINY_GSM_MODEM_UBLOX
#include <TinyGsmClient.h>
#define MODEM Serial2 /* 3G modem serial port */
TinyGsm modem(MODEM);
TinyGsmClient ctx(modem);
​
void modem_begin(TinyGsm &modem) {
  modem.restart();
  while (!modem.waitForNetwork());
  modem.gprsConnect("soracom.io", "sora", "sora");
  while (!modem.isNetworkConnected());
  IPAddress ipaddr = modem.localIP(); CONSOLE.println(ipaddr);
}
​
 void setup() {
  M5.begin();
  M5StackGPS.begin(9600, SERIAL_8N1, 13, 5);/* Setup for GPS module */
  MODEM.begin(115200, SERIAL_8N1, 16, 17);/* Setup for 3G module */
}
​
void loop() {
  /* Get from GPS */
  while (M5StackGPS.available() > 0)
    gps.encode(M5StackGPS.read());
  if (!gps.location.isValid()) {
    CONSOLE.println("gps.location is invalid");
    return;
  }
  /* Create JSON */
  char payload[1024];
  sprintf(payload, "{\"lat\": %.7f, \"lng\": %.7f, \"alt\": %.1f}",
    gps.location.lat(), gps.location.lng(), gps.altitude.meters());
  CONSOLE.println(payload);
​
  /* checking connect status before send */
  if (!modem.isGprsConnected()) {
    CONSOLE.println("reconnecting");
    modem_begin(modem);
  }
  /* connect to host */
  if (!ctx.connect("uni.soracom.io", 80)) {
    Serial.println(F("Connect failed."));
    return;
  }
  Serial.println(F("connected."));
  /* send request */
  ctx.println("POST / HTTP/1.0");
  ctx.println("Host: uni.soracom.io");
  ctx.println("Content-Type: application/json");
  char content_length_hdr[32];
  sprintf(content_length_hdr, "Content-Length: %lu", strlen(payload));
  ctx.println(content_length_hdr);
  ctx.println();
  ctx.println(payload);
  CONSOLE.println("sent.");
  /* receive response */
  while (ctx.connected()) {
    String line = ctx.readStringUntil('\n');
    CONSOLE.println(line);
    if (line == "\r") {
      CONSOLE.println("headers received.");
      break;
    }
  }
  char body[1*1024] = {0};
  ctx.readBytes(body, sizeof(body)); /* body */
  ctx.stop();
  CONSOLE.println(body);
​
  delay(5000);
}
```

## 参考資料
- [GitHub / m5stack/m5-docs GPS](https://github.com/m5stack/m5-docs/blob/master/docs/ja/module/gps.md)
- [GPS拡張ボード 詳細](https://github.com/m5stack/m5-docs/blob/master/docs/assets/img/product_pics/module/module_gps_06.png)
- [GPS拡張ボード 回路図](https://github.com/m5stack/m5-docs/blob/master/docs/assets/img/product_pics/module/gps_sch.png)
- [arduino-esp32 / HardwareSerial.h](https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/HardwareSerial.h)
- [arduino-esp32 / HardwareSerial.cpp](https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/HardwareSerial.cpp)
- [UART(HardwareSerial)
 / Arduinoで遊ぶページ](https://garretlab.web.fc2.com/arduino/lab/uart/)

EoT