Squama Ethernet でI2C通信 (ついでにUDPで送信も) (Arduino)

## はじめに  
今回も[前回に](https://qiita.com/MMH_hatooth/items/17a77712ee005a8a025e)引き続き、Squama Ethernetを使います。
テーマはI2C通信です。

## ハードウェア  
### I2Cデバイス
I2Cのテストのため、16bit ADCのADS1115を使いたいと思います。
AdafruitがADS1115の[基板](https://www.adafruit.com/product/1085)を販売しているのでArduinoライブラリもあります。
最近はAmazonでも同等基板が売っているので入手性も良いです。(例えば[こちら](https://www.amazon.co.jp/gp/product/B07TZ56M65/ref=ppx_yo_dt_b_asin_title_o03_s00?ie=UTF8&psc=1))

データシートも一応見ましょうね。(日本語訳するなら全部やってほしいですねｗ [こちら](https://www.tij.co.jp/jp/lit/ds/symlink/ads1115.pdf?ts=1622102944439&ref_url=https%253A%252F%252Fwww.tij.co.jp%252Fproduct%252Fjp%252FADS1115))

### I2Cアドレス
データシート参照してください。
![スクリーンショット 2021-05-28 094820.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17169/1a0dafc2-646e-3c5a-2687-05430f247430.png)


### センシングするデバイス  
これは何でも良いのですけれど、手元にあったロードセルを使います。
秋月電子で[売っている](https://akizukidenshi.com/catalog/g/gP-13042/)やつですね。

### ブレッドボードによる配線  
FritzingにSquama Ethernetが無いので、代わりにArduino Pro Miniで記載していますが、下記のように配線します。
I2CのSDAとSCLは使用する基板ごと適宜読み替えていただければと思います。
Amazonで買ったら2個付いているので、2個使ってしまいましょう。
(また、後述のソフトでは、ロードセルを1つしか接続していません。手元に4つ無くて…すみません…)
![ADS1115_I2C_sample_ブレッドボード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17169/afcb8009-364c-40d0-aa5b-8964486e9b6a.png)

## ソフトウェア
### ADS1115 ライブラリ
Adafruitのライブラリは[こちら](https://github.com/adafruit/Adafruit_ADS1X15)。
事前にArduinoにインポートしてください。

### ソースコード  
```c++
#include <SPI.h>
#include <Ethernet2.h>
#include <EthernetUdp2.h>

#include <Adafruit_ADS1X15.h>

// ------ADS1115-------
Adafruit_ADS1115 ads_1;  /* Use this for the 16-bit version */
Adafruit_ADS1115 ads_2;  /* Use this for the 16-bit version */
Adafruit_ADS1115 ads_3;  /* Use this for the 16-bit version */
float multiplier_1 = 0.1875F;
float multiplier_2 = 0.1875F;
float multiplier_3 = 0.1875F;

// ------Ethernet-------
byte mac[] = {
  0xA8, 0x61, 0x0A, 0xAE, 0x66, 0xC7
};

IPAddress ip(192, 168, 0, 11);   // 自身のIPアドレス
unsigned int localPort = 10000;

IPAddress destIP (192, 168, 0, 1);  //相手のPCのIPアドレス
int destPort = 20000;  //send側ポート

EthernetUDP Udp;


float set_maltiplier(adsGain_t gain) {
  float ret = 0;
  if (gain == GAIN_TWOTHIRDS) {
    ret = 0.1875;
  } else if (gain == GAIN_ONE) {
    ret = 0.125;
  } else if (gain == GAIN_TWO) {
    ret = 0.0625;
  } else if (gain == GAIN_FOUR) {
    ret = 0.03125;
  } else if (gain == GAIN_EIGHT) {
    ret = 0.015625;
  } else if (gain == GAIN_SIXTEEN) {
    ret = 0.0078125;
  }
  return ret;
}

float setup_ads(Adafruit_ADS1115 &ads, int address, adsGain_t gain) {
  ads.setGain(gain);
  // i2c address
  ads.begin(address);
  return set_maltiplier(gain);
}

void setup() {
  // put your setup code here, to run once:
  // ------Ethernet-------
  Ethernet.init(10);  // CS信号設定. Most Arduino shields
  Ethernet.begin(mac, ip);
  Udp.begin(localPort);

  // ------ADS1115-------
  multiplier_1 = setup_ads(ads_1, 0x49, GAIN_SIXTEEN);
  multiplier_2 = setup_ads(ads_2, 0x49, GAIN_ONE);
  multiplier_3 = setup_ads(ads_3, 0x48, GAIN_ONE);
}

void loop() {
  // put your main code here, to run repeatedly:
  int16_t results = ads_1.readADC_Differential_0_1();
  float multi = results * multiplier_1;
  int16_t results_2 = ads_2.readADC_Differential_2_3();
  float multi_2 = results_2 * multiplier_2;
  int16_t results_3 = ads_3.readADC_Differential_0_1();
  float multi_3 = results_3 * multiplier_3;
  int16_t results_4 = ads_3.readADC_Differential_2_3();
  float multi_4 = results_4 * multiplier_3;

  sendUDP(results, multi, results_2, multi_2, results_3, multi_3, results_4, multi_4);
}

void sendUDP(int16_t i_val, float val, int16_t i_val_2, float val_2, int16_t i_val_3, float val_3, int16_t i_val_4, float val_4) {
  // UDP SEND
  int digit = 4;
  Udp.beginPacket(destIP, destPort);
  Udp.print(millis() / 1000, DEC);
  Udp.write(",");
  Udp.print(i_val);
  Udp.write(",");
  Udp.print(val, digit);
  Udp.write(",");
  Udp.print(i_val_2);
  Udp.write(",");
  Udp.print(val_2, digit);
  Udp.write(",");
  Udp.print(i_val_3);
  Udp.write(",");
  Udp.print(val_3, digit);
  Udp.write(",");
  Udp.print(i_val_4);
  Udp.write(",");
  Udp.print(val_4, digit);

  Udp.write("\n");
  Udp.endPacket();
}
```

ほとんどサンプルを利用しています。
1つの基板で2つのゲインを使用したかったので、`ads_1`と`ads_2`で分けていますが、ライブラリの改良で1つにまとめることが出来そうですね。

また、ADS1115の通信機能として、`single-shot mode`と`continuous mode`というのがあります。
今回は１つずつのチャンネルを `single-shot mode` にてデータ受信しているため、`continuous mode` より時間がかかっています。
`continuous mode`のテストも行いましたが、複数chを受け取るため今回使用していない`ALRT`Pinを使って処理しないといけないっぽいため、諦めました。
1ch分なら雑な設計かもしれませんが`ALRT`Pinを使わずに`continuous mode`で受信できます。

ちなみに、UDPのデータレートですが、ADS1115を2つ使って4ch分のデータをUDPでPCに送信しているのをWireSharkで確認したところ、大体20FPSでした。

### データの受信と簡単なビジュアライズ
今回もUnityでUDPを受信しています。左上の文字列が受信データとなります。
4chのうち、1chにしかロードセルを付けておらず、それ以外のchは3.3Vをそのまま突っ込んでいたりしていますのでご了承ください。

その1chのロードセルから受けた値を赤線グラフにしているのと、その値で円のScaleを変えています。
ロードセルに加える力に応じて電圧値が変化するので、グラフと円のサイズが変わるのが分かるかと思います。
![forGif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17169/66c2010a-23e7-0e49-58c1-1206f032169f.gif)

## おわりに
Squama Ethernet でI2C通信してみました。
やはり基板サイズが小さめでイーサネット接続できるSquama Ethernet はイイですね。便利。

課題としては、`continuous mode`でしょうか。20FPSではちょっと遅いですもんね。
I2C通信の速度アップで高速に出来るかもしれません。

最後に、ADS1115のゲインのことやUnityの部分など、結構端折って書いているため、備忘録感が強くなってしまっています。
もっとちゃんと書くべきですね…