海外でも「あのボタン」を押したい！

ソラコムの松井です。

ラスベガスで行われた AWSの年次カンファレンス re:Invent に参加した後、シアトル(正確には隣町のベルビュー)にある SORACOM Global HQ オフィスにて二週間ほど勤務する事になり、海外生活満喫中です。幸い天気が良い日が続いていて、オフィスの窓からはコーヒーでお馴染みのマウントレーニアも見えます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/30051/9966c9c6-73d2-cc5b-35b0-4ff3b027ecfb.png)

ただ、日本と違って KDDI の LTE-M 電波が飛んでいないので「あのボタン」を押すことが出来ません。そろそろ禁断症状が出てきたので、海外でも押すことが出来るボタンを Wio 3G を使って作ってみたいと思います。

# 用意するもの
- [Wio 3G SORACOM Edition](https://soracom.jp/products/module/wio_3g_soracom_edition/)
![Wio 3G SORACOM Edition](https://soracom.jp/img/products_wio_3g_soracom_edition.png)

- [Grove Button(P)](http://wiki.seeedstudio.com/Grove-Button/)
![](https://github.com/SeeedDocument/Grove_Button/raw/master/img/Button.jpg)

※今回は右側のパネルタイプを使っています

- バッテリー
[Wio 3G ハードウェアリファレンス](https://seeedjp.github.io/Wiki/Wio_cell_lib_for_Arduino/hardware-ja.html)にある注意書きをよく読みます。

> Wio 3Gのバッテリ端子は、JST PH型コネクタです。次の点に注意してください。
> 
> - 極性が正しいこと。（電池の＋側の線が、コネクタ横に＋と書かれているほうに繋がること。）
> - 690mAh以上の容量のリチウムイオン電池であること。（690mAで充電するため、これ以下の容量の電池を繋いで充電すると、大変危険な場合があります。）
> - 充電中は決して放置せず、絶えず目を離さないようにしてください。
> - リチウムイオン電池の取り扱いには、十分な注意を要します。ご使用による機器の破壊や事故、これらに伴う被害・損害については当社では一切の責任を負いかねます。

今回使ったのは某キットについていた 2000 mAh のバッテリーなので、上記の条件は満たすようです。

適当なプラケースに穴を開けて詰め込めば、完成です(見た目はかなり怪しい)。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/30051/0fd99f09-e8c1-4352-7a14-c1e455e37a23.png)

# 構成
Wio 3G から USSD を使ってデータを送信し、SORACOM Funnel から AWS IoT Core にデータを送信し、ルールエンジンを使ってLambdaをキックしています。

![architecture](https://qiita-image-store.s3.amazonaws.com/0/30051/4165f713-9d04-7b5c-e4b3-7ea17cbd3aae.png)

USSD って何だ？って思った人はこちらのブログをどうぞ。
→ [SORACOM Beam / Funnel / Harvest が USSD からの入力をサポート](https://blog.soracom.jp/blog/2018/07/04/beam-ussd-support/)

# コード
USSD を送信するのはとても簡単です。 `SendUSSD` 命令を使います。

### [SendUSSD](https://github.com/SeeedJP/Wiki/blob/master/Wio_cell_lib_for_Arduino/reference-ja.md#sendussd)

```cpp
bool SendUSSD(const char* in, char* out, int outSize)
```

#### 引数

|引数|説明|
|:--|:--|
|in|送信するデータ。|
|out|受信したデータを取得する変数。|
|outSize|outのバイト数。|


送信データのフォーマットは SORACOM Global カバレッジの Funnel 設定時に表示されていますが、 `*901021*{value}#` というフォーマットになります。今回はクリックタイプに応じて 1/2/3 を `{value}` のところに入れて送ります。

全体のコードはこのようになります。

```c++:button-ussd.ino
#include <WioCellLibforArduino.h>
WioCellular Wio;

#define SERVICE (2) // 1:Beam 2:Funnel 3:Harvest
#define BUTTON_PIN  (WIO_D38)
#define INTERVAL    (100)
#define LED_VALUE (10)

#define SINGLE  (1)
#define DOUBLE  (2)
#define LONG    (3)

#define DOUBLE_THRESHOLD  (1000)
#define LONG_THRESHOLD  (2000)

void setup() {
  delay(200);

  SerialUSB.begin(115200);
  SerialUSB.println("");
  SerialUSB.println("--- START ---------------------------------------------------");

  SerialUSB.println("### I/O Initialize.");
  Wio.Init();
  Wio.PowerSupplyLed(true);
  Wio.LedSetRGB(0,0,LED_VALUE);
  pinMode(BUTTON_PIN, INPUT);
  
  SerialUSB.println("### Setup completed.");
  Wio.LedSetRGB(0, 0, 0);
}

void loop() {
  unsigned long t;
  int click_type;

  if(digitalRead(BUTTON_PIN))
  {
    click_type=SINGLE;
    t=millis();    
    while(digitalRead(BUTTON_PIN))
    {
      if(millis() > t + LONG_THRESHOLD)
      {
        click_type=LONG;
        break;
      }
      delay(INTERVAL);
    }
    if(click_type!=LONG)
    {
      t=millis();
      while(millis() < t + DOUBLE_THRESHOLD)
      {
        if(digitalRead(BUTTON_PIN))
        {
          click_type=DOUBLE;
          break;
        }
        delay(10);
      }
    }
    send_event(click_type);
  }
  
  delay(INTERVAL);
}

void send_event(int click_type){
  Wio.LedSetRGB(LED_VALUE,LED_VALUE,LED_VALUE);
  SerialUSB.println("### Power supply ON.");
  Wio.PowerSupplyCellular(true);
  delay(500);

  SerialUSB.println("### Turn on or reset.");
  if (!Wio.TurnOnOrReset()) {
    SerialUSB.println("### ERROR! ###");
    return;
  }

  SerialUSB.println("### Registering location.");
  if (!Wio.WaitForCSRegistration()) {
    SerialUSB.println("### ERROR! ###");
    return;
  }

  SerialUSB.println("### Sending USSD.");

  char message[256];
  char response[256];

  sprintf(message,"*9010%d1*%d#",SERVICE,click_type);

  if (!Wio.SendUSSD(message, response, sizeof (response))) {
  Wio.LedSetRGB(LED_VALUE,0,0);
    SerialUSB.println("### ERROR! ###");
    return;
  }
  SerialUSB.print("### Received response: ");
  SerialUSB.println(response);
  if(strcmp(response,"sent to Funnel")==0)
  {
    Wio.LedSetRGB(0, LED_VALUE, 0);  
  }
  else
  {
    Wio.LedSetRGB(LED_VALUE, 0, 0);  
  }
  delay(2000);
  Wio.LedSetRGB(0, 0, 0);
}
```

# 動作デモ
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/j3tm0t0api?ref_src=twsrc%5Etfw">@j3tm0t0api</a> 3Gボタン <a href="https://t.co/K3uy5foGWY">pic.twitter.com/K3uy5foGWY</a></p>&mdash; moto (@j3tm0t0) <a href="https://twitter.com/j3tm0t0/status/1071185347944411136?ref_src=twsrc%5Etfw">2018年12月7日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

なんやかんやする時間は LTE-M ボタンとあんまり変わらないですね。

送信される MQTT メッセージは

```
{
  "operatorId": "OP0000000000",
  "timestamp": 1544225579545,
  "destination": {
    "resourceUrl": "https://data.iot.us-west-2.amazonaws.com/button",
    "service": "aws-iot",
    "provider": "aws"
  },
  "credentialsId": "AWS-j3tm0t0",
  "payloads": {
    "ussdString": "*901021*1#",
    "ussdDataCodingScheme": 15,
    "value": "1",
    "imsi": "295050000000000"
  },
  "sourceProtocol": "ussd",
  "imsi": "295050000000000"
}
```

のようになります。(1-click 経由のイベントオブジェクトとは形が異なる点に注意が必要です)。

# まとめ
これで海外旅行中に寂しくなっても、いつでも「元気やで」って言ったり「ニャーン」したり出来ますね〜。
今週末はこれを持ってカナダまでプチ旅行に行ってこようと思います！