スマートハレタ HACKS (その1) ハード構成の調査と表示制御をやってみる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359377/414ad826-ad0a-3ce1-56a8-bb9d0bf42c20.png)

# はじめに

## 免責

本記事はライオン株式会社様とは全く無関係の情報です。本内容について同社に問い合わせすることはしないで下さい。
また、内容については正確性を期すように注意しておりますが、一切保証いたしません。実施は各人の責任で行ってください。本記事を参考にしたことにより直接的または間接的に発生したいかなる損害等にも理由に関わらず一切責任を負いません。

## 本記事の目的

本記事は、スマートハレタに搭載された各デバイスをどのようにしたら自分で作成したソフトウェアで制御できるかを、独自に調べて整理したものです。
私と同じく、このハードに興味を持った方のご参考になれば幸いです。

本記事ではざっとハード構成を調べたのち、ドットマトリクスLEDに好きな絵を描いて遊んでみます。

ちなみに、(その1)とか書いておりますが、続きがあるかは不明です。(笑)

## スマートハレタとは?

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359377/341ee1c4-d962-42d8-c29f-a25245a6bbe6.png)

スマートハレタは、ライオン株式会社による洗剤「ハレタ」向けのスマートデバイスで、本体上部のカップに「ハレタ」のボトルを乗せると、その重量を監視し、残量等を確認して詰め替え時期の提案や、WiFiを通じた天気情報に基づく干し方の提案をしてくれる楽しいデバイスです。
[スマートハレタ 公式ページ](https://top.lion.co.jp/products/hareta/smart/)

本機はキャンペーンで配布する非売品のようで、2019/1/15から2019/2/28までの応募による抽選で、1000台が配布されたとの事です。

## で、なんでHACKするの?

実は私も応募して当選し、本来の用途で使用して喜んでいた一ユーザーでした。
2019年4月下旬に品物が届き、セットアップし、何となくネットワークスキャンツール"Fing"でスキャンするとMACアドレスから"Espressif"と出るわけです。電子工作でESP-WROOM-32やM5Stackと戯れている人だったら、Bluetoothにも対応している事からひょっとしてESP32が入っているのでは?と思うのは自然かもしれません。

※以下のFingのスクショの「スマートハレタ」は私が手入力で設定したものです。
![result_fing.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359377/cc26c027-da41-bc2d-6f2d-f76a96ddee56.jpeg)

で、ちょこっと分解してみたわけです。すると、やはりESP32(ESP-WROOM-32)を搭載していることが分かりました。さらに面白いことに、他のデバイスも結構アキバ(秋月電子や千石電商等)で入手可能なデバイスを中心に構成されている為、いじることが楽しめそうだなと感じました。

![20190427_184353.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359377/e9f48a24-4b93-5193-f6dc-46c73a72874b.jpeg)

頂いたスマートハレタはこのまま、ハレタのパートナーとして使い続けたいのでメルカリをちらっと見たら、あるわあるわ・・・800円送料込みくらいで購入できました。後述しますが中身は結構色々詰まってて楽しめそうです。

# ハードの主な構成

実際に自分の好きな工作をこれでやるとしても、まずこのハードがどういう構成でどう使用できるかが分かっていないとどうにもなりません。なので最低限、どんな感じの構成になっているのか調べてみます。

## 全体構成
以下はスマートハレタを分解して中の部品を撮影したものです。
LEDマトリクス基板とメイン基板間は、ストレインゲージとメイン基板間はコネクタで抜き差しできるようになっており、その他は直にケーブルがはんだ付けされています。LEDモジュール基板は本体の左右側面に配置されていて、状態に応じて本体全体を光らせることができます。ストレインゲージはひずみゲージとも呼ばれるセンサで、金属片に取り付けられ、洗剤を載せるカップにネジ付けされています。これで載せた洗剤の重量から残量がわかる、という仕組みでしょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359377/99a9e2da-4c41-933c-6bf5-eeae928c32dc.png)

## メイン基板
それでは、メイン基板から見ていきます。部品実装面のみです。(裏側はコネクタや線出し用のランドがあるのみです)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359377/b33e58e5-7d32-995d-42bd-92a98df7d812.png)


### CPU
センター上部あるのは、電子工作やM5Stack等でもお馴染み、メインCPUであるESP32のモジュール ESP-WROOM-32です。このモジュール1つでマイコン、WiFi、BT/BLE、ブート用フラッシュ(4MB)を搭載しています。右上にあるピンヘッダは、ESP-WROOM-32モジュールを扱った方にはおなじみのシリアルとGPIO0端子でソフトウェアの書き換えモードに遷移させ、ソフトウェアを流し込む際に使用するもののようです。

### 時計周辺
左上の缶のようなものはスーパーキャパシタで、その下にあるリアルタイムクロック(RTC; 時計)IC DS1307のバックアップ電源を担っているようです。本ICは5V系が必要ですので後述のレベルシフタを介して接続されています。

### 重量測定

重量測定にはストレインゲージとADコンバータ [AVIA SEMICONDUCTOR HX711](http://www.aviaic.com/Download/hx711F_EN.pdf.pdf) を使用しているようです。HX711はCh.AとCh.Bの2chの入力があり、Ch.Aは64倍、128倍のPGA(Programmable Gain Amp)、Ch.Bは32倍固定です。
PD_SCKにクロックを入力するとDOUTからA/D変換結果を出力するようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359377/07f78c3a-1430-1c86-2fa6-1c16a17b6fcf.png)
HX711仕様書(http://www.aviaic.com/Download/hx711F_EN.pdf.pdf) より

### レベルシフタ
ESP32はのI/O電圧は3.3Vである為、ESP-WROOM-32下にある双方向レベルシフタ [TXS0108E](http://www.tij.co.jp/product/jp/TXS0108E) 
により5V系に変換しています。これは前述のRTCや、本体のLED周辺の回路、重量測定に用いるAD変換ICが5V動作仕様であるために挿入されているようです。

下図ではESP32とTXS0108Eが複数の箱に別れていますが、機能的に分かれている為分けただけですので、デバイスとしてはそれぞれ1個です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359377/6786b0c7-7340-61c9-5fa0-a3d52efb64ff.png)


### オーディオ出力

オーディオデコーダーIC VS1053b とオーディオアンプ LM386 は、本機の音声再生に使用されているようです。
VS1053bは、Ogg Vorbis、MP3、AAC、WMA、FLACといったコーデックのデコーダーと、オーディオ入出力に加え、GM規格の音色配列を持つMIDI音源まで搭載されているチップです。MIDIの出音はYoutube等で検索すると先人の作例が出てきます。

## LED基板
それでは、もう一つの大きな基板、LED基板を見ていきたいと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359377/669a8bd9-d4be-3f8c-4960-4d6f9c365c94.png)

LED基板は、写真右側の白いコネクタを通してメイン基板と繋がっています。信号の内訳は、5V電源、シリアルLED信号、ドットマトリクスLED制御用I2Cシリアルです。
基板は表面にLEDが並び、裏面にドットマトリクスLEDドライバIC [HOLTEK HT16K33](https://www.holtek.com/productdetail/-/vg/16K33)が5つ実装されています。I2C Scanのプログラムを走らせると、70h～74hの5アドレスがそれぞれのICに設定されていることがわかりました。
i2c_scanner : https://playground.arduino.cc/Main/I2cScanner/

i2c_scannerを実行する場合にはI2C端子アサインがESP32 Arduinoのデフォルトではないため、以下のようにWire.beginに引数の追加が必要です。

```c++:i2c_scanner.inoの一部
void setup()
{
  // Wire.begin();    // ★←この記述を
  Wire.begin(25, 26); // ★これに変更する

  Serial.begin(9600);
  while (!Serial);             // Leonardo: wait for serial monitor
  Serial.println("\nI2C Scanner");
}
```

### ドットマトリクスLED部

LEDは24ドット×24ドット構成になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359377/cee50480-97b8-66cf-ade8-0e4df0091a47.png)

ドットマトリクスLEDは8×8構成のモジュールが9つ並んで構成されています。HT16K33は1個で8×16のドットマトリクスLEDを制御できます。従って、24×24の場合には5個(4.5個)のICが必要となります。5つのLEDドライバHT16K33はそれぞれI2Cスレーブアドレス70h～74hがアサインされていますが、スレーブアドレス毎のLEDとの対応付けは以下のようでした。HT16K33の詳細仕様については割愛しますが、ドットマトリクス8×16の領域にデータを送信する際、8x8の2つの領域にわけて8ビット毎に設定していきます。下図の矢印と"LSB"はこの8ビット毎のデータの配置の方向とビットの並び方向を示しています。領域Eのみ、8x8を担当しています。

| 領域 | スレーブアドレス |
|:-:|:-:|
|A|70h|
|B|71h|
|C|72h|
|D|73h|
|E|74h|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359377/2714142c-6b61-dcbe-94a4-5a46e9701575.png)

### シリアルLED部

本基板の下部にはシリアルLEDが1個接続されています。シリアルLEDは、LEDとLEDを制御するコントローラーが1パッケージになった便利なデバイスです。
本機で使用されているのはWS2812B系の1線のシリアル制御LEDのようです。これは、シリアルのパルス信号のH期間とL期間の時間で"1"と"0"を表現するシリアルになっています。以下はWS2812Bの場合のシリアルタイミングです。
(実際に使用されているデバイスはWS2812Bではない可能性もあります。その場合、タイミングは微妙に下記とは異なる可能性があります)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359377/1b2d3cbd-48b1-80f1-dbb6-629a4b662480.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359377/9a80808e-7225-804d-943d-5500f2cd937d.png)
[WS2812Bデータシート](http://www.world-semi.com/DownLoadFile/108) より引用

LEDを以下のようにカスケード接続し、PIX1のDINにマイコン等から24ビット(1個のLEDの色情報)を超えるデータを送出すると、そのままPIX1のDOからそのデータが送出されます。3つのLEDがある場合には24×3=72ビットのデータを送出する事で各LED内のコントローラーにデータが届きます。その後、RES期間(WS2812Bであれば280[μs]以上)PIX1のDINにLowを入力すると、各LEDコントローラーは、それぞれの制御しているLEDの発光状態に反映させます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359377/3ecb0665-18c8-72df-d05c-a8480322c9db.png)
[WS2812Bデータシート](http://www.world-semi.com/DownLoadFile/108) より引用

本機では、ESP32から出力されたシリアルLED制御信号が本基板のシリアルLEDに接続され、その後にLEDモジュール子基板×2に繋がっています。ESP32からは、1+8+8=17個のLED制御信号を出力すれば全LEDの制御ができるものと考えられます。

## マイコンの書き換え方法

### Arduino IDEでのESP32開発

ESP32のマイコンの書き換えは、ESP32のメーカーであるEspressif Systemsが提供するesptoolというツールを使用します。これはコマンドラインから使用するツールですが、Arduino IDEにESP32の環境をセットアップして書き換える場合には、ツール内部からesptoolを呼び出してくれるので、特に難しい手順はありません。Arduino IDEへのESP32の環境のセットアップはBoard Managerから簡単に行うことができます。以下のサイトに詳しく説明されています。
[Installation instructions using Arduino IDE Boards Manager](https://github.com/espressif/arduino-esp32/blob/master/docs/arduino-ide/boards_manager.md) (英文)

### メイン基板のマイコンの書き換え

* 注意 : マイコンに自分で作成したデータを書き込むと、元々書き込まれていた内容に上書きされるため元々の機能は失われますのでご注意ください。元々のデータをバックアップしておきたい場合にはesptoolでflashのリードあたりで検索して実施して下さい。

ESP-WROOM-32内のプログラムは、内蔵のSPIフラッシュメモリに書き込まれています。書き換えには、USBシリアル変換を介してPCと接続します。

ESP32を書き換える場合に使用する信号は、以下の4つです。

|信号名|内容|メイン基板での端子位置|
|:---:|:---:|:---:|
|TXD0|UART0のデータ送信|4|
|RXD0|UART0のデータ受信|5|
|EN|ESP32のリセット|無し|
|GPIO0|GPIO0 リセット解除時タイミングはブートモード設定|3|

TXD0、RXD0は一般的にUARTで見かける信号ですので説明は割愛します。ENはESP32のリセット信号です。GPIO0はリセット解除のタイミングでLowに接続していた場合に、ESP32がUARTからのファームウェア書き換えモードになります。

従って、最低限PCと接続しなければならない信号はUARTのTXDとRXDです。また、ESP32のボード(ESP32-DevKitC等)では、RTSとDTRを使用してPC側からブートモードの設定やリセットのアサートも出来ます。今回は手元にあった適当なUSBシリアル変換を使用しました。FTDI社などのチップを搭載したモジュールが秋月電子等で販売されている(例 : [FT231X USBシリアル変換モジュール](http://akizukidenshi.com/catalog/g/gK-06894/))ので、よろしければどうぞ。

今回は、USBシリアル変換基板のTXDをメイン基板のRXD0に、USBシリアル変換基板のRXDをメイン基板のTXD0に接続し、それぞれのGNDも接続し、ブート状態に入れる際は、GPIO0とGNDをリード線で接続し、ACアダプタを差し込むことにしました。

## ドットマトリクスLEDの制御

ドットマトリクスLEDは前述の通りI2Cで接続されています。

|GPIO番号|信号名|
|:-:|:-:|
|25|I2C SDA|
|26|I2C SCL|

### ドットマトリクスを点灯するプログラムを作成してみる

ArduinoのI2CドライバであるWireの初期化は以下のようになります。setClockメソッドは、I2Cバスの速度を指定する為のものですが、ICの仕様上400kHz(400000)を指定可能ですが、実際に設定してテストしたところ通信エラーが発生したため、半分の200kHzにしたところ、手元環境では安定しています。

```C++:I2Cドライバの初期化
  Wire.begin(25, 26);
  Wire.setClock(100000L);
```

しかし、元々のソフトウェアが入っている状態のSCLを観測すると、83.33[kHz]であったことから、本回路が想定するI2C SCLクロック周波数はI2C Standardの100[kHz]な気がします。なので設定は100[kHz]にしてみます。

* 元々のソフトウェアが動作している時のSCLロジアナ波形
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359377/548315ee-72fc-346f-ab11-90fccc24bd88.png)


次に、ドットマトリクスLEDドライバ HT16K33の初期化を行います。まずは内蔵オシレータを有効にし(System Setup Registerの設定)、LED表示を有効にし(Display Setup Registerの設定)、LEDの輝度を設定します。以下のサンプルコードでは、brightnessという変数に一旦代入して操作するようにしているので、好きな輝度になるよう0～15の範囲で設定してください。値が大きいほど輝度が高くなります。

```C++:HT16K33の初期化
static void initializeLedDriver(uint8_t addr)
{
  /**
     System Setup Register : Turn on system oscillator
  */
  Wire.beginTransmission(addr);
  Wire.write(0x21);
  Wire.endTransmission();

  /**
     Display Setup Register
  */
  Wire.beginTransmission(addr);
  Wire.write(0x81);
  Wire.endTransmission();

  /**
     Digital Dimming Data Input (LED Duty configuration)
  */
  {
    uint8_t brightness = 10; /* 0 - 15 */
    Wire.beginTransmission(addr);
    Wire.write(0xe0 | brightness);
    Wire.endTransmission();
  }
}
```
上記initializeLedDriver関数はスレーブアドレスを引数に取るようにしているので、0x70～0x74の範囲で呼び出します。

```C++:各LEDドライバを初期化する
void setupDriver(void)
{
  uint8_t a;
  for (a = 0x70; a <= 0x74; a++) {
    initializeLedDriver(a);
  }
}
```
setupDriver関数が実行できれば、ドットマトリクスLEDは点灯できる状態になりますので、LEDを適当なパターンで点灯させてみます。
以下のプログラムでは、各LEDコントローラの1つ目の8×8をベタ塗り白、2つ目の8×8を千鳥配置のパターンを塗るものです。

```C++:各LEDドライバに発光パターンを送信する
void sendToDriver(uint8_t addr)
{
  Wire.beginTransmission(addr);
  Wire.write(0x00);
  for (int i = 0; i < 8; i++) {
    // First 8x8 Area
    Wire.write(0xff);

    // Second 8x8 Area
    if(i & 1){
      Wire.write(0xaa);
    } else {
      Wire.write(0x55);
    }
  }
  Wire.endTransmission();
}
```
これを実行した様子はこちらです。LEDマトリクス基板のところで説明したような並びのパターンで表示されていることが分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359377/8dbb3179-5cc5-05a2-b68d-51997996d6a4.png)

このソースコードをgithubの以下に掲載しています。
https://github.com/yishii/SmartHaretaHacks/tree/master/SmartHaretaDotMatrixSimpleTest

## シリアルLEDの制御

WS2812BタイプのシリアルLEDは、シリアルLEDの説明で記載した通り1本のデジタル信号出力で複数個のカスケード接続されたLEDを個別制御できるものです。本機では、以下のようにGPIOがアサインされていました。

|GPIO番号|信号名|
|:-:|:-:|
|27|SERIAL LED OUT|

### シリアルLEDを制御するプログラムを作成してみる

ESP32でWS2812b等のシリアルLEDを制御する為のライブラリをネット検索していると、FastLEDというライブラリが人気のようです。そこで、FastLEDをインストールしてやってみます。
ArduinoIDEの「スケッチ」→「ライブラリをインクルード」→「ライブラリを管理」と辿るとライブラリマネージャが開きますので、FastLEDをセットアップします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359377/0abe19b5-4fa2-8765-c310-a6722f8dbd55.png)

あとは以下のように初期化すると、制御できます。短いので全部掲載します。
GPIO 27に接続した17個のLEDに対してデジタルRGBのカラーを一秒ごとに設定しています。

```C++:SmartHaretaNeoPixelTest.ino
#include <FastLED.h>

#define NUM_LEDS 17
#define DATA_PIN 27

CRGB leds[NUM_LEDS];

void setup()
{
  FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
}

void loop(void)
{
  static int cnt;
  int i;

  for (i = 0; i < 17; i++) {
    leds[i] = CRGB(
                (cnt & 4) ? 0x30 : 0x00,
                (cnt & 2) ? 0x30 : 0x00,
                (cnt & 1) ? 0x30 : 0x00);
  }
  FastLED.show();
  cnt++;
  delay(1000);
}

```
なお、上記ソースコードは以下に掲載しております。
https://github.com/yishii/SmartHaretaHacks/blob/master/SmartHaretaNeoPixelTest/SmartHaretaNeoPixelTest.ino

## デモプログラム

今回ドットマトリクスLEDを制御するデモを作成しました。本デモでは、ビットマップのキャラクタを描画したりしています。以下で公開しておりますので、よろしければどうぞ。
https://github.com/yishii/SmartHaretaHacks/blob/master/SmartHaretaDisplayDemo/SmartHaretaDisplayDemo.ino

内部にフレームバッファを確保し、任意の(x, y)座標に点を打ってるsetPixel、unsetPixel関数、矩形のビットマップの貼り付け処理など作成しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359377/ad22c462-6b1f-3c0a-3baa-efb351444a03.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359377/414ad826-ad0a-3ce1-56a8-bb9d0bf42c20.png)

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr"># スマートハレタ をもらったので使ってたけど、ハードいじるのも面白そうだったのでメルカリでもう一個買ってみた。<br><br>中のソフト(ESP32)いじって好きな表示して遊んでるけど、いつもの電子工作と違って、ちゃんと筐体に入ってうごくから楽しいよ <a href="https://t.co/CUwwotGAzg">pic.twitter.com/CUwwotGAzg</a></p>&mdash; Yasuhiro ISHII (@yishii) <a href="https://twitter.com/yishii/status/1123849455202177025?ref_src=twsrc%5Etfw">2019年5月2日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">こんな感じで24×24のドットマトリクスが入ってます。 <a href="https://t.co/HsStnBDZ3p">pic.twitter.com/HsStnBDZ3p</a></p>&mdash; Yasuhiro ISHII (@yishii) <a href="https://twitter.com/yishii/status/1123851013121880065?ref_src=twsrc%5Etfw">2019年5月2日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## 最後に

ちょっと長文になってしまいました。すいません。
次回は音出したいっすねー!

間違いとかあればご指摘いただけますとありがたいです。