ESP32で温湿度計を作ってbeebotteを使いこなす


IoT情報を集約して蓄積し、配信したり可視化するのに、Beebotteはちょうどよいです。
MQTTで情報を集約して履歴を保存し、グラフにしたり、現在値をダッシュボードのように表示することができます。
また、集約した情報は、同様にMQTTで配信することができるため、複数の表示デバイスで表示することができそうです。
そこで、温湿度計を作りながら、Beebotteを使いこなします。

https://beebotte.com/home

動作環境としては、以下のESP32用のJavascript実行環境のファームウェアを使います。

電子書籍：M5StackとJavascriptではじめるIoTデバイス制御

https://poruruba.booth.pm/items/3735175

一般的なM5Stackモジュールで動作しますが、今回は温湿度計をつないで温湿度をBeebotteにアップするM5StickCと、Beebotteからの情報配信を受けて現在の温湿度を表示するM5Core2を使います。

以下いずれでも構いません。SHT30とDHT12のどちらかのライブラリを選択してください。
　https://www.switch-science.com/catalog/5690/
　https://www.switch-science.com/catalog/6344/
　https://www.switch-science.com/catalog/7254/
　https://www.switch-science.com/catalog/6559/
　https://www.switch-science.com/catalog/7367/
　https://www.switch-science.com/catalog/5755/

M5StickCの代わりにM5StickC-Plusでも構いませんし、M5Core2の代わりにM5Stack Fireでも構いません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/d756daf8-7857-b023-9194-01198740608d.png)

# beebotteへのチャネルの登録

まだbeebotteにアカウントを登録していない場合はサインアップしてください。

https://beebotte.com/home

チャネルを作ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/8f3d5890-a2b3-70d2-3250-9c15dc5c6172.png)

・チャネル名：Envdata
・リソース名１：temperature
・リソース型１：temperature
・SoS１：true
・リソース名２：humidity
・リソース型２：humidity
・SoS２：true

ちょうどデータ型としてtemperatureとhumidityがあるのでそれを選択します。
SoSは、有効にすると、MQTTでSubscribe登録したときに、現在値を通知してくれます。通常は、publishにより更新したときしか通知してくれないので、これは非常に便利です。

チャネルの作成が完了すると、Channel Tokenも同時に作成されます。後で使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/08e1c0d0-353a-94ca-2ffd-4fabceaf09ee.png)

次に、左側のナビベーションメニューからAccount Settingを選択し、Access Managementタブを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/683fa9c8-e657-62ae-195c-f7fb13031810.png)

Create New Tokenボタンを押下し、IAM Tokenを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/69ad6024-8f08-3636-95af-28e9aea14226.png)

Label：Readonly
Access Control Scopes：data:readをOn

Create Tokenボタンを押下すと、TOKENが生成されます。これがIAM Tokenです。後で使います。

# MQTTブローカへの接続

beebotteのMQTTブローカのURLは、ホスト名が「mqtt.beebotte.com」、ポート番号が1883です。
接続する際に、先ほど取得したトークンの指定が必要です。ユーザ名として指定します。
最初に作成したChannel Tokenは、該当チャネルに対してRead/Writeが可能です。ですので、温湿度計を繋いで温湿度をMQTTブローカにPublishするM5StickCで使います。
一方で、別に作成したIAM TokenはReadのみの権限を付与していまして、表示を行うM5Core2で使います。
パスワードは空文字です。

```js:main.js
mqtt.setServer('mqtt.beebotte.com', 1883);
mqtt.connect('QuickJS_ESP32_Send', 256, '[IAM TokenまたはChannel Token]', '');
```

ここで1点注意が必要でして、複数のクライアントから同時に接続する場合、必ず異なるクライアント名とするようにしてください。そうしないと、切断されてしまいます。

# MQTTへの温湿度データのPublish

まずは、コード量が少ないPublishの方から。

```js:main.js
import * as wire from "Wire";
import * as env from "Env";
import * as mqtt from "Mqtt";

wire.begin(32, 33);
mqtt.setServer('mqtt.beebotte.com', 1883);
mqtt.connect('QuickJS_ESP32_Send', 256, '[Channel Token]', '');

setInterval(() =>{
		var data = {
			data: env.dht12_readTemperature(),
			write: true
		};
		mqtt.publish('Envdata/temperature', JSON.stringify(data));

		var data = {
			data: env.dht12_readHumidity(),
			write: true
		};
		mqtt.publish('Envdata/humidity', JSON.stringify(data));

		console.log('published');
}, 600000);
```

利用するモジュールは、EnvとMqttとWireです。

Envは、DHT12とSHT30の2つの温湿度センサに対応していますので、接続しているものに合わせてください。また、Envを利用するには、事前にwireを初期化しておく必要があります。初期化とは、wire.begin(32, 33) といったように、SDAとSCLのPIN番号を指定します。

そして、setIntervalで、10分に一回、温湿度値の取得とPublishを実施しています。
BeebotteにPublishする際のMQTTのトピック名は決まっており、「beebotteのチャネル名/リソース名」という文字列です。今回の場合ですと、Envdata/temperatureとEnvdata/humidityです。REST APIであれば、一度に温度と湿度をアップロードすることもできるようですが、MQTTではその方法は分かりませんでした。なので、温度と湿度別々にPublishするようにしました。
実際のPublishするときのペイロードですが、以下のフォーマットです。時系列で値を残したいため、write: trueの指定を忘れずに。

```js
{
	"data": 値,
	"write": true
}
```

具体的な説明は以下にあります。

https://beebotte.com/docs/mqtt#bridge

# beebotteからの温湿度データの受信・表示

MQTTのSubscribeとLCDへの表示処理です。

温度値の変化と湿度値の変化の両方を受け取りたいため、トピック名は、Envdata/#を指定します。ワイルドカード指定と言えばわかりますでしょうか。
M5StickCでは、10分ごとにPublishしているため、同じ間隔で通知が来ます。

抜粋すると、以下のコードの部分です。

```js:main.js
mqtt.subscribe('Envdata/#', () =>{
	var payload = mqtt.getPayload();
	console.log('received: ' + payload.topic);

	if( payload.topic == 'Envdata/temperature'){
		temperature = JSON.parse(payload.payload).data;
	}else
	if( payload.topic == 'Envdata/humidity'){
		humidity = JSON.parse(payload.payload).data;
		
		var time = rtc.getTime();
		var date = rtc.getDate();
		updateView(date, time, temperature, humidity);
	}
});
```

で、以下が、LCD表示部分です。
ちょっと複雑に見えますが、文字列の位置や大きさを設定しているだけで、一つ一つは単純な処理です。

```js:main.js
function updateView(date, time, temperature, humidity){
	lcd.setFont(36);
	lcd.fillScreen(0);

	console.log('updateView');
	var temp_str = ("  " + temperature.toFixed(1)).slice(-4);
	var hum_str = ("  " + humidity.toFixed(1)).slice(-4);
	lcd.setTextSize(1.0);
	var date_height = lcd.fontHeight();
	lcd.setTextDatum(lcd.top_left);
	var date_str = toFixed2d(date.Month) + '/' + toFixed2d(date.Date) + ' ' + toFixed2d(time.Hours) + ':' + toFixed2d(time.Minutes);
	lcd.setCursor(lcd.width() / 2 - lcd.textWidth(date_str) / 2, 0);
	lcd.print(date_str);

	lcd.setTextDatum(lcd.top_left);

	lcd.setTextSize(2.4);
	lcd.setCursor(0, date_height + 10);
	lcd.print(temp_str.slice(0, -2));
	var pos = lcd.getCursor();
	lcd.setTextSize(1.0);
	lcd.print('℃');
	lcd.setCursor(pos.x, date_height  + 10 + pos.y);
	lcd.setTextSize(1.2);
	lcd.print('.' + temp_str.slice(-1));

	lcd.setTextSize(2.4);
	lcd.setCursor(lcd.width() / 2, date_height + 10);
	lcd.print(hum_str.slice(0, -2));
	var pos = lcd.getCursor();
	lcd.setTextSize(1.0);
	lcd.print('%');
	lcd.setCursor(pos.x, date_height  + 10 + pos.y );
	lcd.setTextSize(1.2);
	lcd.print('.' + hum_str.slice(-1));
}
```

また、時刻表示も兼ねているため、30秒ごとに、表示を更新しています。

```js:main.js
setInterval(() =>{
	var time = rtc.getTime();
	var date = rtc.getDate();
	updateView(date, time, temperature, humidity);
}, 30000);
```

結局まとめると以下になります。
LCD表示するためのLcdモジュール、現在日時を取得するためのRtc、MQTT通信するためのMqttモジュールを使います。

```js:main.js
import * as lcd from "Lcd";
import * as rtc from "Rtc";
import * as mqtt from "Mqtt";

lcd.setFont(36);
lcd.fillScreen(0);

mqtt.setServer('mqtt.beebotte.com', 1883);
mqtt.connect('QuickJS_ESP32_Recv', 256, '[IAM Token]', '');

var temperature = 0.0;
var humidity = 0.0;

mqtt.subscribe('Envdata/#', () =>{
	var payload = mqtt.getPayload();
	console.log('received: ' + payload.topic);

	if( payload.topic == 'Envdata/temperature'){
		temperature = JSON.parse(payload.payload).data;
	}else
	if( payload.topic == 'Envdata/humidity'){
		humidity = JSON.parse(payload.payload).data;
		
		var time = rtc.getTime();
		var date = rtc.getDate();
		updateView(date, time, temperature, humidity);
	}
});

setInterval(() =>{
	var time = rtc.getTime();
	var date = rtc.getDate();
	updateView(date, time, temperature, humidity);
}, 30000);

function updateView(date, time, temperature, humidity){
	lcd.setFont(36);
	lcd.fillScreen(0);

	console.log('updateView');
	var temp_str = ("  " + temperature.toFixed(1)).slice(-4);
	var hum_str = ("  " + humidity.toFixed(1)).slice(-4);
	lcd.setTextSize(1.0);
	var date_height = lcd.fontHeight();
	lcd.setTextDatum(lcd.top_left);
	var date_str = toFixed2d(date.Month) + '/' + toFixed2d(date.Date) + ' ' + toFixed2d(time.Hours) + ':' + toFixed2d(time.Minutes);
	lcd.setCursor(lcd.width() / 2 - lcd.textWidth(date_str) / 2, 0);
	lcd.print(date_str);

	lcd.setTextDatum(lcd.top_left);

	lcd.setTextSize(2.4);
	lcd.setCursor(0, date_height + 10);
	lcd.print(temp_str.slice(0, -2));
	var pos = lcd.getCursor();
	lcd.setTextSize(1.0);
	lcd.print('℃');
	lcd.setCursor(pos.x, date_height  + 10 + pos.y);
	lcd.setTextSize(1.2);
	lcd.print('.' + temp_str.slice(-1));

	lcd.setTextSize(2.4);
	lcd.setCursor(lcd.width() / 2, date_height + 10);
	lcd.print(hum_str.slice(0, -2));
	var pos = lcd.getCursor();
	lcd.setTextSize(1.0);
	lcd.print('%');
	lcd.setCursor(pos.x, date_height  + 10 + pos.y );
	lcd.setTextSize(1.2);
	lcd.print('.' + hum_str.slice(-1));
}

function toFixed2d(v){
	return ("00" + v).slice(-2);
}
```

これで、日時の表示が定期的に更新されるとともに、新しい温湿度値を取得したときにも表示が更新されるようになりました。


# beebotteダッシュボードを作成

ついでに、beebotteのダッシュボードを作成します。今回は、温湿度の時系列のグラフ表示と、現在の温湿度を表示するウィジェットを配置します。

左側のナビゲーションメニューからDashboardsを選択し、「Create Dashboard」ボタンを押下します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/469bd8e7-568e-6c50-c6da-9f3b61dae293.png)

Add Widget＋ボタンを押下し、Multi-Line Chartを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/bb713c69-1441-6a38-4a5d-c1665560e0bd.png)

Your textには以下を指定します。

```
<h1>{{Envdata.temperature}} ℃</h1>
<h1>{{Envdata.humidity}} %</h1>
```

その他の書き方は以下を参照してください。

https://beebotte.com/docs/dash

Saveすると以下のように表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/5d7cbd88-b97d-f87b-0bb9-cf2a16acf677.png)

次に、温湿度データの右側の歯車をクリックし、「Get Shared Link」をクリックします。
そうすると、Shared Linkというリンクができていますので、そのURLをメモります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/2678f105-c99b-3216-b9d9-c3f83859ef35.png)

このURLをこのページを見てもよいと思う人に公開すれば、この値を参照できるようになります。相手には以下のように表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/ac5bf079-7928-c79c-62dd-620cb24c7388.png)

# 本ファームウェアのサポートサイト

〇電子書籍：M5StackとJavascriptではじめるIoTデバイス制御

https://poruruba.booth.pm/items/3735175

〇ESP32のファームウェアのサポートサイト

https://github.com/poruruba/QuickJS_ESP32_IoT_Device

以上